.. _customskin: Customizing site appearance =========================== Understading layers and skins ----------------------------- .. _skinhowto: 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` .. code-block:: python class ICustomLayer(ICustomLayer): """skin layer""" Define an utility providing ISkin with the custom label and the layer interface .. code-block:: python @utility_config(name='Custom skin', provides=ISkin) class CustomSkin(object): """custom root skin""" label = _("Custom: skin") layer = ICustomLayer 2) Declare the layer adapter ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python @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 .. image:: ../_static/select_skin.png Adding resources library ------------------------ .. code-block:: python 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 ) :py:class:`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. .. code-block:: python :linenos: # 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 :py:class:`IContactParagraph`, :py:class:`IPyAMSLayer` as context and provides :py:class:`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 :py:class:`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 :py:func:`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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python 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 .. code-block:: python @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 :py:func:`@adapter_config()` we declare a new adapter that applies to a context and provide the interface of renderer settings .. code-block:: python 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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python @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 .. _templatehowto: 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 :py:func:`override_template` .. code-block:: python 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 :ref:`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, .. code-block:: python # 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 :py:attr:`resources` is used to load in the template static resources. The application will automatically integrate resource content when the template is calling.