===========
SilvaLayout
===========

Introduction
============

SilvaLayout contains user interface components to develop your own
Silva public website layouts. It extends Silva so that it becomes
possible to select entirely different skins for different parts of a
website. It focuses on making the creation, deployment and maintenance
of complicated skins manageable. 

It does not invent its own new technology to do so. Instead, it does
this by Zope 3 technologies through Five_. Five is a system that
allows developers to use Zope 3 technology within Zope 2. Zope 3 and
Five are bundled with Zope 2.

.. _Five: http://codespeak.net/z3/five

For installation instructions see the INSTALL.txt.

Using it from Silva
===================

Using the Silva UI (SMI), navigate to the properties tab for Silva
root.  Press the 'settings' button to view the layout settings. On
this screen you can select a skin from the list of skins that are
currently installed into your Zope setup.

To view the skin, click 'view public version' in the lower right
corner and the content will become visible in the selected skin. You
can select different skins in each publication in the site.

Creating new layouts
====================

A layout package is a Zope product that typically consists of a
combination of HTML templates, resources such as images and javascript
files, and of view related behavior expressed as Python code in Python
module. You can place all files in one directory, or, when there are
many files, organize it in a number of subdirectories - whatever best
suits your project.

Creating skins 
==============

If you want to create new skins, you can do this in a your own Zope
extension, called a Zope product. You can add as many skins in a
single Zope product as you like, or alternatively use a new Zope
product for each skin. A SilvaLayout based Zope product functions as a
Silva extension which adds its skins to any Silva that exists in that
Zope installation.

How to create a SilvaLayout based product
-----------------------------------------

* create a new product to be placed in the Zope ``Products``
  directory; you can base this on the ``SilvaLayoutTemplate`` product
  in the ``SilvaLayout/examples`` directory if you like.

  A Zope product is a directory that needs at least an (empty)
  ``__init__.py`` file. This is needed so that Python can recognize
  the product as an package, and Zope can recognize it as a product
  (zope extension).

* create a ``configure.zcml`` file in this directory. This hooks
  templates together using Zope Configuration Markup Language into a
  skin. Zope will automatically pick up the ``configure.zcml`` file in
  your product.

* A skin consists of one or more layers. When using SilvaLayout, your
  skin consists of at least your own layer, and the ``silvadefault``
  layer, as well as the basic zope ``default`` layer. 
  
  Having such multiple layers is very powerful, if you want to reuse
  bits of layout in multiple other layouts, or want to build a layout
  based on another layout but overriding pieces of it; you can
  reuse the same layer in multiple layouts.

* Let's define a layer in ZCML. A layer consists of one or more view and/or
  resource definitions::

    <!-- we are creating a layer called mylayer -->
    <browser:layer 
      name="mylayer" 
      />

    <!-- we define the standard template for all Silva containers for
         mylayer. This defines a 'view' -->
    <browser:page
      name="index.html"
      for="Products.Silva.interfaces.IContainer"
      class="Products.SilvaLayout.browser.views.Container"
      template="maintemplate.pt"
      permission="zope2.View"
      layer="mylayer"
      />

    <!-- we also make sure all images in the directory 'images' 
         are available to our layer -->
    <browser:resourceDirectory
      name="images"
      directory="images"
      permission="zope2.View"
      layer="mylayer"
      />

    <!-- now we define the skin and of which layers it consists -->
    <browser:skin
      name="myskin"
      layers="mylayer silvadefault default"
      />

* ``browser:page`` - this ZCML directive is used to define a view. 

  The ``name`` attribute determines under which name the view will be
  available through the web. ``index.html`` is the name used when
  looking at an object without specifying an explicit view.

  The ``for`` attribute specifies for which interfaces the view will
  be used; in the example the view is defined for any Silva container,
  such as a Folder or a Publication. The interface is a so-called
  "dotted name". Internally such a name refers to a piece of code in a
  Python module (in this case to the ``IContainer`` interface
  definition in the ``interfaces.py`` module in the ``Silva`` package),
  but you can treat it just as an opaque name. We'll give a listing
  of some useful interfaces below.

  With ``template`` the Zope Page Template is specified which will be
  used for rendering the container a HTML. 

  The optional ``class`` attribute specifies a Python class in a
  Python module. You see that the class is also defined using a dotted
  name. This class is called a *view class*. It offers an API which
  can be useful from within the template to help rendering the
  view. (see below for examples). In SilvaLayout, a number of useful
  view classes are exposed which you will likely want to use. Some
  you'll use as a base class for your own public view; others help
  with particular purposes, such as tree generation and the generation
  of absolute URLs for objects - you will sometimes want to override
  these for your own objects.

  The ``permission`` attribute determines what permission a user needs
  to access this page. This depends on the user's role in the location
  in Silva the user is trying to view. For SilvaLayout, normally
  ``zope2.View`` is the right permission to use.

* The ``browser:resourceDirectory`` directive makes all the resources
  in the subdirectory ``images`` accessible through URLs.

* The ``browser:skin`` directive defines a skin. A skin defines the
  layout of a particular section of a Silva site, and is what can be
  selected from the Silva UI for a particular publication (or the
  Silva Root).

  It consists of the ``mylayer`` layer, in which it will look first
  for defined views such as pages. If a specific view can not found in
  there, it will look in the ``silvadefault`` layer, and if it still
  cannot be found, it will look in the standard Zope ``default``
  layer. If the view still cannot be found, the system will give an
  error -- the user likely is trying to access Silva through a URL
  that does not exist.

Zope 3 and SilvaLayout for designers
------------------------------------

Zope 3 (and Five, exposing it to Zope 2) are of course large
topics. If you are a programmer we recommend you look at the material
available on the web, or the various Zope 3 books. Here we try to give
you a short introduction with just the basics for designers. We are
assuming you're familiar with the types of objects in Silva, and are
familiar with Zope Page Templates (ZPT), Zope's page templating
language.

ZPTs in Zope 3 are the same as those in Zope 2. They however do expose
some extra names you can use, in particular ``context`` and ``view``.

``context`` is the content object in Silva (such as a Document or
Folder) that the user is currently viewing through this template. You
can call methods on it defined in the standard Silva API. This is very
similar to the way ``here`` works in ZPTs in non-Five Zope 2. For
example::

  context/get_title

``view`` exposes the view API. A view API is very similar to the
content object API, but exposes extra view-related API. They for
instance offer a convenience method to access Silva metadata easily
from your layout (see below).

Methods on views typically implement complicated view functionality in
efficient python, which can then be used inside your ZPT template. If
you are not a Python programmer, you'll have to find one and tell them
what you would like to have on your view API. Once written, it can be
hooked into your views using the ``class`` attribute on your ``page``
directive.

For instance, the standard ``SilvaLayout.browser.silvaview.SilvaView``
has has a method 'title' that can be called like this in a TALES
expression::

  view/title

Access to Silva metadata fields has never been easier with the SilvaView
base class::

  <meta name="Keywords"
    tal:attributes="content view/metadata/silva-extra/keywords"
  />

If you make your own view class for public display of a content
object, you'll likely want to subclass it from
``SilvaLayout.browser.silvaviews.SilvaView`` to get this functionality
too.

Views can reuse other views -- this way you can reuse common bits,
such as a header or navigation elements, inside multiple templates
without having to repeat yourself. You access them the following way::

  context/@@browsertitle

The ``@@`` notation indicates to Zope to only look for *views* called
``browsertitle`` (such as registered with ``name`` in
``browser:page``), not a content object in Silva that happens to be
called that way by an author. We recommend the use of ``@@`` when
reusing a view to avoid such name clashes.

Accessing resources, such as CSS files, images, and so on, happen
through URLs. All resources are prefixed using ++resource++, thus this 
refers to some resource (an image named 'foo.jpg')::

  path/to/site/++resource++foo.jpg

Resources can also be specified as groups, using all files directory,
such as with the ``resourceDirectory`` directive described above. Individual
resources are then below the name given to this resourceDirectory::

  path/to/site/++resource++images/bar.jpg

It's important that a resource such as an image is accessed only
through a single URL from all pages in a site, so that a browser can
cache it. We currently have a rather complicated way to do this, which
should be improved in the future.

In the ``request`` object SilvaLayout places a ``resourcebase``
object, which can be used to retrieve the base URL of root of the site
under which the resources are available. For convenience you can use
``tal:define`` somewhere in your template so you can later easily get
to it::

  tal:define="resourcebase request/resourcebase/absolute_url"

Later on, you can then use ``resourcebase`` variable to refer to
actual resources::

    <link rel="stylesheet" tal:attributes="href
      string:${resourcebase}/++resource++styles/tree.css"/>

Some useful interfaces
----------------------

An interface is not more than a design contract, or a description of
an API - it specifies which methods and attributes a class
implementing the interface should have. Interfaces are identified
into ZCML using dotted names. Silva defines a number of useful
interfaces you can connect views to using the ``browser:page`` directive:

* ``Products.Silva.interfaces.ISilvaObject`` - all Silva objects
  provide this interface. If you want to connect your view to any
  Silva object, use this in the ``for`` attribute of a
  ``browser:page`` directive.

* ``Products.Silva.interfaces.IContainer`` - all Silva containers,
  such as Folders, Publications and the Silva Root provide this
  interface.

* ``Products.Silva.interfaces.IFolder`` - Silva Folders (but not
  publications) provide this interface.

* ``Products.Silva.interfaces.IPublication`` - Silva Publications
  (but not Silva Folders) provide this interface. Silva Root is
  also considered to be a publication.

* ``Products.Silva.interfaces.IRoot`` - Only the Silva Root provide
  this interface.

* ``Products.Silva.interfaces.IVersionedContent`` - Only content
  objects that can have multiple versions, such as Silva Documents or
  Ghosts, provide this interface.

* ``Products.Silva.interfaces.IGhost`` - only ghosts provide this
  interface.

More interfaces exist and each interface exposes an API that can be
used through ``context``. To find out more, look in the
``interfaces.py`` file in the Silva core (to be found in
``Products/Silva/interfaces.py``).

Creating and overriding view classes
------------------------------------

The view classes supplied by the SilvaLayout already provide means to
implement TOC trees, filtering and easy access to metadata from page
templates. Overriding view behaviour or adding new view classes is a
little more involved. Acquiring familiarity with Python, Zope 3
interfaces and adapters is recommended.


Using configobject
------------------

To use configobjects in your own layout (let's say 'MyLayout'), you
must follow these steps:

- Create Formulator xml forms (for instance by creating formulator
  forms in zope, and exporting them) that have the customization
  fields for your object. Say you have one in a file 'myform.form'.

- In code, for instance a file my_config.py, create form classes like
  so:

    from Products.SilvaLayout.configobj import FormInfo

    class SilvaLayoutForm1(FormInfo):
        form_path = 'myform.form'
        skin_id = 'MyLayout'

    ...

- In your configure.zcml put (be sure to give each form a unique
  name):

    <adapter
      factory=".configobj.WUWLayoutForm"
      name="WLF1"
      provides="Products.SilvaLayout.configobj.ILayoutConfigurationForm"
      for="Products.SilvaLayout.interfaces.ILayoutConfiguration" />

    ...

That's it. In Silva you can now add a 'Layout Configuration' object,
selecting the relevant skin, on which the configuration parameters can
be set for that subtree of the site.

To access the parameters from your code or templates, you can just get
them from context/config_layout/[fieldname].
