Including widget specific Javascripts in Plone using Python conditions

Here is described a way to include Javascript for certain widgets or certain pages only in Plone CMS

  • Javascripts are processed through portal_javascripts utility automatically, making them suitable for production deployment (minimized, cached). Plone does all this for you. Also the same rules apply for CSS and portal_css tool.
  • A special condition is created in Python code to determine whether to include the script or not on a page: it checks whether we are on a page needing the script and then signals portal_javascript which in turn puts the script to HTML <head> section
  • Javascripts are served from a static media folder in a Plone add-on utilizing Grok framework. Static media folder is mapped to unique resource names spaces (++resource++yourpythonmodule.name), so that there are no file conflicts between different add-ons.
  • These instructions mainly are designed for Dexterity content type subsystem, but can be used with any content types by replacing the behavior check with a marker interface check

Dexterity content types can use Dexterity behaviors control panel to add new behaviors on your content types. One such a behavior is to include certain Javascript files when these content types are viewed or edited.

The example here shows how to include a Javascript if the following conditions are met

  • You are developing a Plone add-on as a Python package (as oppose, to say, edit files directly through-the-web)
  • Content type has a certain Dexterity behavior applied on it
  • Different files are served for view (visitors) and edit modes (content editing interface)

Note: There is no easy way currently directly check whether a certain widget and widget mode is active on a particular view. Thus, we do some assumptions and checks manually if we are on “edit” view.

Plone portal_javascripts is a site specific registry. Available javascripts are kept in the site database, so that you can fine-tune their settings on-line, through-the-web through Zope application server management interface. When Plone add-on is installed, a subsystem called GenericSetup can import database setting changes on XML based profiles. There is a tool called portal_setup which allows you easily to export, import or put parts of this setting updates to your add-on installer.

We import Javascript files to portal_javascripts using GenericSetup file jsregistry.xml:

<?xml version="1.0"?>
<object name="portal_javascripts">

        <!-- View mode javascript -->
        <javascript
                id="++resource++yourcompany.app/integration.js"
                authenticated="False"
                cacheable="True" compression="safe" cookable="True"
                enabled="True" expression="context/@@integration_javascript"
                inline="False"
                />

        <!-- Edit mode javascript -->
        <javascript
                id="++resource++yourcompany.app/integration.edit.js"
                authenticated="False"
                cacheable="True" compression="safe" cookable="True"
                enabled="True" expression="context/@@edit_integration_javascript"
                inline="False"
                />

</object>

Then we create the special conditions using Grok views and Python code. This code would go to yourcompany/app/browser/views.py Python module:

# Zope imports
from Acquisition import aq_inner
from zope.interface import Interface
from five import grok
from zope.component import getMultiAdapter

from yourcompany.app.behavior.lsmintegration import IYourWidgetIntegration

class IntegrationJavascriptHelper(grok.CodeView):
    """ Used by portal_javascripts to determine when to include our
        custom Javascript integration code.

    This view is referred from the expression in jsregistry.xml.
    """

    # The view is available on every content item type
    grok.context(Interface)
    grok.name("integration_javascript")

    def render(self):
        """ Check if we are in a specific content type.

        Check that the Dexerity content type has a certain
        behavior set on it through Dexterity settings panel.

        Alternative, just check for a marker interface here.
        """

        # render() methot is a the only traversable
        # Grok CodeView method. It can be used for rendering
        # HTML code, but also for utility views
        # to return raw Python data

        try:
            # Check if a Dexterity behavior is available on the current context object
            # - if it is not, behavior adapter will raise TypeError
            avail = IYourWidgetIntegration(self.context)
        except TypeError:
            return False

        # If called directly from the browser like
        # http://localhost:8080/Plone/integration_javascript
        # will return HTTP 204 No Content

        return True

class EditModeIntegrationJavascriptHelper(IntegrationJavascriptHelper):
    """ Used by portal_javascripts to determine when to include our custom Javascript
        integration code *on edit pages* only.

    Subclass the existing checked and add more limiting conditions.
    """
    grok.name("edit_integration_javascript")

    def render(self):
        """
        @return True: If this template is rendered "Edit view" of the item
        """

        if not IntegrationJavascriptHelper.render(self):
            # We are not even on the correct content type
            return False

        # This is a hacked together as Plone does not provide a real
        # mechanism to separate edit views to other views.
        # We simply check if the current view URI ends with "edit"

        path = self.request.get("PATH_INFO", "")

        if path.endswith("/edit") or path.endswith("/@@edit"):
            return True

        return False

Question: how much Grok is included with Plone 4.1: at least parts of its are, but would this example work with vanilla Plone 4.01 without including five.grok manually as a dependency?

\"\" Subscribe to RSS feed Follow me on Twitter Follow me on Facebook Follow me Google+