Customizing site appearance

Understading layers and skins

Creating a new skin

A Skin is a tagging interface for associating media, javascript and CSS resources to a renderer

1) Create a new Layer to your skin

Build a new interface inherit from ICustomLayer

class ICustomLayer(ICustomLayer):
    """skin layer"""

Define an utility providing ISkin with the custom label and the layer interface

@utility_config(name='Custom skin', provides=ISkin)
class CustomSkin(object):
    """custom root skin"""

    label = _("Custom: skin")
    layer = ICustomLayer

2) Declare the layer adapter

@adapter_config(context=(Interface, ICustomLayer, Interface), provides=IResources)
class CustomSkinResourcesAdapter(ContextRequestViewAdapter):
"""Custom skin resources adapter"""

    def get_resources(self):
        mycms.need()

We have defined a Multiadapter with context=(context, request, view).

Note

In the ZMI website you can now change the default graphical theme by you custom skin

../_images/select_skin.png

Adding resources library

from fanstatic import Library, Resource
from pyams_default_theme import pyams_default_theme

#Library(name, folder_path)
library = Library('mycms', 'resources')

#Resource(library, path_to_static)
mycms_css = Resource(library, 'css/mystyle.css',)


mycms_js = Resource(library, 'js/pyams-default.js',
                    depends=(pyams_default_theme, )
                    bottom=True
                    )

Resource can include others resources already defined with depends attribute, here pyams_default_theme.

Overriding templates

The simplest is to create a new class that inherits from the existing Renderer and modify this template. After that all you have to define a new adapter name and a new label.

1
2
3
4
5
6
7
8
9
# New custom contact paragraph renderer

@adapter_config(name='custom', context=(IContactParagraph, IPyAMSLayer), provides=ISharedContentRenderer)
@template_config(template='templates/contact-custom.pt', layer=IPyAMSLayer)
class ContactParagraphCustomRenderer(ContactParagraphDefaultRenderer):
    """Context paragraph custom renderer"""

    label = _("Custom contact renderer")
    #settings_interface = IContactParagraphDefaultRendererSettings

In this example, we have defined an adapter named ‘custom’ with IContactParagraph, IPyAMSLayer as context and provides ISharedContentRenderer interface.

Using @template_config() decorator, the renderer will be displayed in html container according to the template templates/contact-custom.pt

The new renderer inherit of ContactParagraphDefaultRenderer, have a new label (line 8) and this associated settings_interface is not modify(line 9)

Tip

You can override the template of a renderer easily with the function pyams_template.template.override_template() It’s takes the context and the new template path as params.

Creating custom renderer

We can define a new settings for the renderer, to do that we start by creating an interface:

a) Create setting interface for the renderer

class IPhotoRendererSettings(Interface):
    """Custom renderer settings interface"""


    display_photo = Bool(title=_("Show photo?"),
                         description=_("Display contact photo"),
                         required=True,
                         default=True)

    can_display_photo = Attribute("Check if photo can be displayed")

We have created an interface with two attributes display_photo and can_display_photo Then we create an implemantation of the interface

@implementer(IPhotoRendererSettings)
class PhotoRendererSettings(Persistent, Location):
    """Custom renderer settings"""

    display_photo = FieldProperty(IPhotoRendererSettings['display_photo'])

    @property
    def can_display_photo(self):
        contact = IContactParagraph(self.__parent__)
        if not contact.photo:
            return False
        return self.display_photo

b) Create an adapter for the render setting interface

With @adapter_config() we declare a new adapter that applies to a context and provide the interface of
renderer settings
PHOTO_RENDERER_SETTINGS_KEY = 'pyams_content.contact.renderer:photo'

@adapter_config(context=IContactParagraph, provides=IPhotoRendererSettings)
def custom_renderer_settings_factory(context):
    """Contact paragraph default renderer settings factory"""
    return get_annotation_adapter(context, PHOTO_RENDERER_SETTINGS_KEY,
                                  CustomRendererSettings)

In this example the settings interface adapter is defined with IContactParagraph as context and provide IPhotoRendererSettings.

c) Create an adapter for the render interface

@adapter_config(context=(IContactParagraph, IPyAMSLayer), provides=ISharedContentRenderer)
@template_config(template='templates/contact-custom.pt', layer=IPyAMSLayer)
class PhotoRenderer(BaseContentRenderer):
    """Context paragraph custom renderer"""

    label = _("Custom contact renderer")
    settings_interface = IPhotoRendererSettings

Add settings interface to the renderer settings_interface = IPhotoRendererSettings

Tip

When a setting_interface is associated to a renderer, you can access to settings attributes through the template

How to define or change a template for a specific skin?

Override the default template for a renderer

If you want to modify the template for a particular rendering mode, you can use the function override_template()

from pyams_template.template import override_template

from my_website.skin.public.layer import ICustomLayer
from pyams_default_theme.component.keynumber.portlet import KeyNumberPortletHorizontalRenderer


override_template(context=KeyNumberPortletHorizontalRenderer,
                                template="templates/keynumber-horizontal.pt",
                                layer=ICustomLayer
                                )

This new template can be applied to a particular Skin by specifying on which layer to use this renderer (ex: layer=IMyWebsiteLayer)

Redefine the default template for a renderer

You must redefine an adapter to add new variables or static resources for your new template,

# import interfaces
from my_website.skin.public.layer import ICustomLayer

from pyams_content.component.keynumber.portlet.interfaces import IKeyNumberPortletSettings
from pyams_portal.interfaces import IPortletRenderer, IPortalContext

# import packages
from my_website.skin.public import my_carousel_init ,my_carousel_animation

from pyams_default_theme.component.keynumber.portlet import KeyNumberPortletHorizontalRenderer
from pyams_template.template import template_config
from pyams_utils.adapter import adapter_config
from zope.interface import Interface


@adapter_config(context=(IPortalContext, IBaseLayer, Interface, IKeyNumberPortletSettings),
                provides=IPortletRenderer)
@template_config(template='templates/keynumber-horizontal.pt', layer=ICustomLayer)
class MyCustomKeyNumberPortletHorizontalRenderer(KeyNumberPortletHorizontalRenderer):
        """Key numbers portlet horizontal renderer"""

        resources = (my_carousel_init, my_carousel_animation)

The attribute resources is used to load in the template static resources. The application will automatically integrate resource content when the template is calling.