Need help extending/modifying a specific addon

So at work I'm tasked to figure out if I can make some modifications to an existing open-source project. These changes are (I think) closed source in nature, (since they contain some business logic)

My knowledge of python is good enough, but my knowledge of plone is really really euhm.. bad.
I read through documentation, I tried seeing if any decent LLM could help guide me through it, but I just keep getting stuck.

To be specific I'm trying to add some basic modifications to the Osha.Oira addin, (which in turn is a modification of the euphorie addon).
These 2 addons do some heavy modifications on the original plone base, as in. Your site turns into something completely else.
That's not really what I care about though, what I do care about is how to properly extend on an extensions.
I've been figuring things out, and was able to make a modification I needed by directly editting the oira addin source files once they were installed through buildout. Now ofcourse, that's not what I want to do. That was just for trying to tinker with it and see what it does.

Anyway, to get to the point.
This oira addin, specifies a OSHAStartExtender interface and a form extender and a page..I think?

from euphorie import MessageFactory as _
from euphorie.client.browser.session import Start
from logging import getLogger
from osha.oira.client.interfaces import IOSHAClientSkinLayer
from osha.oira.client.model import NewsletterSubscription
from plone import api
from plone.memoize.view import memoize
from plone.z3cform.fieldsets.extensible import FormExtender
from plone.z3cform.fieldsets.interfaces import IFormExtender
from sqlalchemy import sql
from z3c.form.field import Fields
from z3c.saconfig import Session
from zope import schema
from zope.component import adapter
from zope.interface import implementer
from zope.interface import Interface


logger = getLogger(__name__)


class IOSHAStartExtender(Interface):
    """Add a some fields, ie.

    a checkbox that allows to subscribe to the newsletter
    """

    tool_subscription = schema.Bool(
        title=_(
            "label_tool_subscription",
            # XXX
            # default="Send me occasional e-mails with news about the tool 'Tool name'",
            default="Send me occasional e-mails with news about this tool",
        ),
        required=False,
        default=True,
    )


@implementer(IFormExtender)
@adapter(Interface, IOSHAClientSkinLayer, Start)
class OSHAStartExtender(FormExtender):
    fields = Fields(IOSHAStartExtender)

    @property
    @memoize
    def webhelpers(self):
        return api.content.get_view("webhelpers", self.context, self.request)

    @memoize
    def update(self):
        """Omit the group id field if we are not the creator."""
        if self.webhelpers.can_edit_session:
            self.add(self.fields)


class OSHAStart(Start):
    def get_tool_subscriptions(self):
        """In principle we can have multiple rows.

        Normally it is 0 or 1.
        """
        account = self.webhelpers.get_current_account()
        return Session.query(NewsletterSubscription).filter(
            sql.and_(
                NewsletterSubscription.account_id == account.id,
                NewsletterSubscription.zodb_path == self.context.session.zodb_path,
            )
        )

    def is_tool_subscription_enabled(self):
        return self.get_tool_subscriptions().count() > 0

    def set_tool_subscription(self, value):
        """Check if we need to save or delete the subscription from the tool
        bound to the current session."""
        if value:
            if not self.is_tool_subscription_enabled():
                account = self.webhelpers.get_current_account()
                Session.add(
                    NewsletterSubscription(
                        account_id=account.id,
                        zodb_path=self.context.session.zodb_path,
                    )
                )
        else:
            for subscription in self.get_tool_subscriptions():
                Session.delete(subscription)

    def updateWidgets(self):
        super().updateWidgets()
        if "tool_subscription" in self.widgets:
            if not self.is_tool_subscription_enabled():
                self.widgets["tool_subscription"].value = []

    def _set_data(self, data):
        tool_subscription = bool(data.pop("tool_subscription", None))
        self.set_tool_subscription(tool_subscription)
        super()._set_data(data)

and their zcml contains this

  <configure package="euphorie.client.browser">
    <browser:page
        name="start"
        for="euphorie.client.adapters.session_traversal.ITraversedSurveySession"
        class="osha.oira.client.browser.session.OSHAStart"
        template="templates/start.pt"
        permission="euphorie.client.ViewSurvey"
        layer="osha.oira.client.interfaces.IOSHAClientSkinLayer"
        />
  </configure>

Now what I need want to try and do, is add another field to this object.
I've tried doing a whole bunch of things, but none of them seems to work.
Can someone help me out here and point me in the right direction?

I've tried creating a new layer, overriding the page in my configuration.zcml , andd though my py files seem to load, the actul overriding objects are never called. (I tried raising some exceptions)

from euphorie.client.browser.session import Start, IStartFormSchema
from z3c.form import form
from zope import schema
from zope.interface import Interface, implementer
from euphorie import MessageFactory as _
from osha.oira.client.browser.session import OSHAStart
from euphorie import MessageFactory as _
from euphorie.client.browser.session import Start
from plone.z3cform.fieldsets.extensible import FormExtender
from plone.z3cform.fieldsets.interfaces import IFormExtender
from zope import schema
from zope.component import adapter
from zope.interface import implementer
from zope.interface import Interface
# from osha.oira.client.interfaces import IOSHAClientSkinLayer
from z3c.form.field import Fields
from osha.oira.client.browser.session import OSHAStartExtender, IOSHAStartExtender
from oira.sweco.interfaces import ISwecoLayer
from oira.sweco.browser.custom_schema import ICustomStartFormSchema

class IProjectExtender(IOSHAStartExtender):
    """Add project field to the start form"""
    
    project = schema.Choice(
        title=_("label_session_title", 
                default="Pick a project for your Risk Assessment"),
        vocabulary="session_titles_vocabulary",
        required=True,
        description=_("session_title_tooltip", 
                     default="Select your project")
    )
@implementer(IFormExtender)
@adapter(IOSHAStartExtender, ISwecoLayer, ICustomStartFormSchema)
class ProjectFormExtender(OSHAStartExtender):
    """Extends the start form with project field"""
    
    fields = Fields(IProjectExtender)
    
    def update(self):
        super().update()
        raise Exception("I AM WORKING??!?")
        self.add(self.fields)
        

class SwecoStart(OSHAStart):
    """Extended start for whathaveyou"""
    project = schema.Choice(
        title=_("label_session_title", 
                default="Pick a project for your Risk Assessment"),
        vocabulary="session_titles_vocabulary",
        required=True,
        description=_("session_title_tooltip", 
                     default="Select your project")
    )

    def __init__(self, *args, **kwargs):
        raise Exception("SwecoStart Loaded")
        super(*args, **kwargs)
    def _set_data(self, data): 
        raise Exception("SwecoStart Loaded")       

and in my own zcml

<configure
    xmlns="http://namespaces.zope.org/zope"
    xmlns:browser="http://namespaces.zope.org/browser"
    xmlns:plone="http://namespaces.plone.org/plone"
    i18n_domain="oira.sweco">

  <!-- Set overrides folder for Just-a-Bunch-Of-Templates product -->
  <include package="z3c.jbot" file="meta.zcml" />
    <plone:static
      name="oira.sweco"
      type="plone"
      directory="static"
      />

    <!-- <configure package="osha.oira.client.browser"> -->
    <configure package="euphorie.client.browser">

      <browser:page
        name="start"
        for="euphorie.client.adapters.session_traversal.ITraversedSurveySession"
        template="templates/start.pt"
        class="oira.sweco.browser.session.SwecoStart"
        permission="euphorie.client.ViewSurvey"
        layer="oira.sweco.interfaces.ISwecoLayer"
        />

        </configure>
    <adapter factory=".session.ProjectFormExtender" />    



</configure>


any help would be appreciated

Let me answer in a generic way about how to deal with customizations of add-ons in Plone (from easy to complex):

  • template modifications -> z3c.jbot
  • redefining views -> ZCHL overrides
  • simple or complex patches of one or more methods: Python monkey patching
  • complexer modifications: fork the related repository, perform your modifications in your own fork, include your fork in your buildout using mr.developer

You need to override the "start" browser page in your product. It already defines a layer:

layer="oira.sweco.interfaces.ISwecoLayer"

You need to create a more specific layer, inheriting from "oira.sweco.interfaces.ISwecoLayer".

See: plone - Override view (from other package), fix browser layers priority - Stack Overflow

1 Like