Sebastien
Sebastien

Reputation: 33

Plone 4 : How to customize a method in Archetypes content types?

I have tried, under Plone 4.3.3, to customize a class method of an archetype content type in one of my products.

I have a product bsw.produit_1 with a content type MyContent defined as follows:

class MyContent(base.ATCTContent):

    implements(IMyContent)

    meta_type = "MyContent"
    schema = MyContent`

    def ma_fonction(self):

        ......
        return res

I want to modify the code of my function ma_fonction in another product. I have tried using an adapter and following the plone docs, but without success.

The class where I wish to customize the function:

class CustomClass(object):
    """  """

    implements(IMyContent)
    adapts(IMyContent)

    def at_post_payment_script(self, obj_transaction):
        """ """

            ......
            # My new code
            return res

The configure.zcml where I declared my adapter:

  <adapter for="bsw.produit_1.content.mycontent.MyContent"
           provides="bsw.produit_1.interfaces.IMyContent"
           factory=".customclass.CustomClass" />

In my zcml declaration, I've also tried putting archetypes.schemaextender.interfaces.ISchemaExtender as provides or putting the interface IMyContent for for instead of the class.

None of these worked, every time, the customized code is not executed. Does anybody have a solution for this?

Upvotes: 3

Views: 362

Answers (2)

keul
keul

Reputation: 7819

The simplest method you can use (although not politically correct!) is monkey-patching.

Take a look at collective.monkeypatcher, you simply need a configuration like that (in your 3rd party product):

<monkey:patch
    description=""
    class="your.package.MyContent"
    original="ma_fonction"
    replacement=".monkeys.new_ma_fonction"
    />

Then in your package create also a monkeys.py module with the new method inside:

def new_ma_fonction(self):
    # do stuff
    return res

Upvotes: 1

Mathias
Mathias

Reputation: 6839

The solution you need depends on what you want to achieve.

But archetypes.schemaextender is the wrong solution. schemaextender is there to modify the schema, this includes:

  • fields order
  • field/widget attributes
  • schemata
  • setter/getter of a field
  • new fields
  • override fields

To implement your own adaptera is definitely the right approach.

First you need to implement a adapter for the default behavior. Second, you need to adapt the context and the request. The request is important, since that's a way to define a more specific adapter if your other product is installed.

Python code for the default implementation (adapter.py):

from zope.component import adapts
from zope.interface import Interface
from zope.interface import implements


class IBehavior(Interface):
    def __init__(context, request)
        """Adapts context and request"""

    # ... more ...


class DefaultBehavior(object):
    implements(IBehavior)
    adapts(IMyContent, Interface)  # IMPORTAN two discriminators 

    def __init__(self, context, request):
        self.context = context
        self.request = request

    def __call__(self):

        # your default implementation goes here.

Register the adapter with zcml:

<adapter factory=".adapter.DefaultBehavior" />

Your now able to call the default adapter in ma_fonction

from zope.component import getMultiAdapter


class MyContent(base.ATCTContent)

    def ma_fonction(self):
        adapter = getMultiAdapter((self, self.REQUEST), IDefaultBehavior)
        return adapter()

Now you can implement a more specific adapter in your other product using a browserlayer. Check documentation, how to register a browserlayer

In your otherpackage you can now register a adapter which implements the same IBehavior interface, but also adapts your browserlayer.

from other.package.interfaces import IOtherPackageLayer
from zope.component import adapts
from zope.interface import implements


class DifferenBehavior(object):
    implements(IBehavior)
    adapts(IMyContent, IOtherPackageLayer)  # IMPORTAN adapt the browserlayer not Interface 

    def __init__(self, context, request):
        self.context = context
        self.request = request

    def __call__(self):

        # your different implementation goes here.

Register also with zcml:

<adapter factory=".adapter.DifferenBehavior" />

Your ma_fonctionnow calls the default adapter, if the other package is not installed. And the different adapter if the other package is installed.

Upvotes: 5

Related Questions