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+

2 thoughts on “Including widget specific Javascripts in Plone using Python conditions

  1. With the visual editor, Plone takes the approach of delivering the javascript to all logged in users, on the assumption that they are likely to need it at some point and it’s faster to deliver everything as one merged file than many small files. (I consider it a bug that Products.TinyMCE requires a separate request for each of it’s plugins’ javascript files.)

    Registry conditions are evaluated for every rendered page, so unless you have a huge amount of page specific javascript it’s probably not worth trying to be too clever about it.

Leave a Reply

Your email address will not be published. Required fields are marked *