Overriding field serialzers in collective.exportimport

I'm Exporting plone content with collecive.exportimport.

First observation is that the form-based export at the /@@export_content view is so much easier than the old command-line and script way. Thank you.

My problem is "Rich Text Fields need to be exported differently". And this solution can work for any field, really, but the Rich Text serializer is probably the most in-demand customization.

The solution is to first add a customization to the exporter: GitHub - collective/collective.exportimport: Export and import content and other data from and to Plone

Once that's done, we add a configure.zcml and serializer.py to our own customization module:

in serializer.py - add the new field seializer. Follow the example in collective.exportimport serializer.py, or go deeper by looking at the restapi serialzers if you wish:

This is a proof of concept, so I just want to hit a debug, and then do whatever the system was doing before I mucked with it. my pattern is to just call debug and then super() when I'm at this stage.

(I left out a bunch of imports for brevity)

in my custom serialzer.py:

from collective.exportimport.serializer import RichttextFieldSerializerWithRawText

@adapter(IRichText, IDexterityContent, IRawRichTextMarker)
class RichTextFieldSerializerWithImageUrls(RichttextFieldSerializerWithRawText):
    def __call__(self):
        import pdb; pdb.set_trace()
        super(RichTextFieldSerializerWithImageUrls).__call__(self)

And register it in your custom configure.zcml


<adapter factory=".serializer.RichTextFieldSerializerWithImageUrls" />

Restart your plone instance and you should get this error - which means you're on the right path!

raise ConfigurationConflictError(conflicts)
zope.configuration.config.ConfigurationConflictError: Conflicting configuration actions

Now, all we have to do is be a bit more specific about what interfaces our adapter adapts. I chose to be specific to MY Dextiery Content Type rather than All Dexterity Content Types (It's called a story):

So, IDextieryContent changes to IStory - which is the schema definition in my own custom Dexterity Type.

from rfasite.content.content.story import IStory

@adapter(IRichText, IStory, IRawRichTextMarker)
class RichTextFieldSerializerWithImageUrls(RichttextFieldSerializerWithRawText):
    def __call__(self):
        import pdb; pdb.set_trace()
        super(RichTextFieldSerializerWithImageUrls).__call__(self)

And now "stories" with "Rich Text Fields" get special treatment. I will now replace that __call__() method with what I want!

MY QUESTION

I find it tedious to specify a specific dexterity content type here. what I WANT to do is add an installation layer interface or a browser layer interface - like "My custom exporter is installed, so it's more specific"

@adapter(IMyContentExporterLayer, IRichText, IDexterityContent, IRawRichTextMarker)

But that adapter wasn't being found during export. getmultiadapter() wasn't finding it. How can I accomplish registering an adapter that overrides the default in colletive.exportimport using an entire installation layer?

Provide your own Marker for the request, perhaps?

alsoProvides(self.request, IYourRawRichTextMarker)
@adapter(IRichText, IDexterityContent, IYourRawRichTextMarker)
class RichTextFieldSerializerWithImageUrls(RichttextFieldSerializerWithRawText):
    def __call__(self):
        # do what you want ?

My thoughts exactly! Which is a browser layers do, mark the request:

My product uses IRFASpecific on all it's requests.

self.request.__provides__
directlyProvides(WSGIRequest, IPloneFormLayer, IDiscussionLayer,...
 **IRFASpecific**, ...
, IPloneRestapiLayer, IRFAEnglish, IDefaultBrowserLayer, IMigrationMarker,
 IRawRichTextMarker)
@adapter(IRichText, IDexterityContent, IRawRichTextMarker, **IRFASpecific**)
class RichTextFieldSerializerWithImageUrls(RichttextFieldSerializerWithRawText):

Which surprised me that it didn't work....

But you gave me the hint. Replace the request marker (IRawRichTextMarker), don't append another interface marker.

@adapter(IRichText, IDexterityContent, **IRFASpecific**)
class RichTextFieldSerializerWithImageUrls(RichttextFieldSerializerWithRawText):

But now wouldn't this adapter match more than we want? Maybe not?

And this drops me into that whole "More specific interface" and "Request Markers" problems I'm still running into. There always seems to be another request marker that I need to go subclass to get "more specific".

I think I'm using browser layers for more than they are meant for, and it sometimes works for me.

You seem to suggest inventing a new Layer Interface whenever this situation pops up, and maybe that's the answer? Because then you can be specific on what Interface you're targeting:

class IYourRawRichTextMarker(IRawRichTextMarker)

Is there something about ZCA that I still don't seem to understand? I seem to stumble into this problem where two markers are 'equally specific' and I get undefined behaviors deepening on the order the interfaces get listed in __provides__. Then I think I need to go back to my custom layer and subclass that Interface from every interface I want to be "more specific" on, but that seems to get me into trouble too and seems to get bloaty.

I wish I could just see my product installed on the request and it 'just works'. But maybe that's a bad way of thinking about it.