================================
Developer level changes in Silva
================================

From 1.4 to 1.5
===============

More efficient method to get metadata values
--------------------------------------------

There is a new method on the metadata service to get values from the
metdata system in a vastly more efficient way.

Where you used to get a binding for the object, and then ask the binding
for a metadata value in a particular metadata set, like so:

  binding = self.service_metadata.getMetadata(object)
  value = binding.get('set_id', 'element_id')

we've now implemented a method that can by-pass the binding, and that seems
to give a nice performance boost:

  value = self.service_metadata.getMetadataValue(
      object, 'set_id', 'element_id')

This should be used everywhere it's possible to do so, at least in the
public views. Notable exceptions are: If you are doing something more than
just getting a value from the metadata system, for instance setting
metadata, you might just as well get the binding.

Five
----

Zope 2.8 includes a significant portion of Zope 3, and includes Five. Silva
does need a later version of Five than included with Zope 2.8 however,
namely Five 1.2, which can be easily installed as a product and which
ships with the Five tarball.

Zope 3 interfaces
-----------------

Silva now uses Zope 3 interfaces throughout, instead of Zope 2 interfaces.

If you use interfaces in your own code, import the `Interface` base class like
this::

  from zope.interface import Interface

Instead of using `__implements__`, state that your class implements
interfaces using the `implements` class annotation::

  from zope.interface import implements

  ...
 
  class Foo:
      implements(IFoo)

You can check whether an instance provides an interface (i.e. whether
its class implements an interface or the interface directly provides
an interface) using `providedBy` (in Zope 2 this used to be 
'isImplementedBy')::

  IFoo.providedBy(instance)

In the rare case you want to check whether a *class* implements an
interface (i.e. whether its instances will provide that interface), you
use `implementedBy` (this used to be `isImplementedByInstancesOf` in
Zope 2)::

  IFoo.implementedBy(some_class)

Five-based i18n
---------------

Silva now uses Zope 3 based i18n infrastructure, and
PlacelessTranslationService is not in use anymore.

All the translations of a product (such as `Silva`, `SilvaDocument`,
etc) are now in a directory, i18n which is registered in that
product's `configure.zcml` with the following simple directive::

    <!-- i18n -->
  <i18n:registerTranslations directory="i18n" />

The i18n directory has the following structure::

* a `.pot` file, such as `silva.pot`

* an `__init__.py` file.

* for each language, a subdirectory, such as `de`.

This language subdirectory follows a standard pattern derives from the
standard `gettext` tool. It contains a single subdirectory called
`LC_MESSAGES`. This directory contains a `.po` file and a `.mo` file,
such as `silva.mo` and `silva.po`.

The `.mo` file is a compiled version of the `.po` file. Zope only
consults the `.mo` files when looking for translations, so be sure to
update the `.mo` file whenever you change the `.po` file. You can do
this (on linux) with the standard `msgfmt` tool, with a command like
this::

  msgfmt silva.po -o silva.mo

Zope 3 will likely evolve ways to make the generation of `.mo` files
more automatic in the future and Silva will follow.

Translating in Python code
--------------------------

Normally your i18n-ed code will just mark up page templates with
i18n:translate, or return a message id (_('Something') to the page
template. The page template will then take care of looking up
translation and doing any interpolation.

Sometimes you want to explicitly force translation (and interpolation)
from Python code, for instance when your Python code is generating a
snippet of HTML. Before Silva 1.5, this was done by calling
`unicode()` on the message id. This doesn't work anymore, and has
been replaced by something more explicit. Instead, you do the following::

  from zope.i18n import translate

which is also legal in Python scripts. And then in your code::

  text = translate(msg_id)

This forces translation and interpolation to happen directly,
returning the translated string. Whenever you see interpolation
characters such as ${foo} in the Silva UI, you know you probably have
a missing `translate()` call in there; replace the `unicode()` call
with a `translate()` call.

Zope 3 adapters
---------------

Quite a few adapters in the `adapters` directory have been converted
to use Zope 3 patterns. That is, Silva looks them up by interface now
in the standard Zope 3 pattern. They also get registered through ZCML.

Here's an example of some ZCML registrations::

  <adapter
    for="Products.Silva.interfaces.IGhost"
    provides=".interfaces.IIndexable"
    factory=".indexable.GhostIndexableAdapter"
    />
  
  <adapter
    for="Products.Silva.interfaces.IContainer"
    provides=".interfaces.IIndexable"
    factory=".indexable.ContainerIndexableAdapter"
    />
  
  <adapter
    for="Products.Silva.interfaces.ISilvaObject"
    provides=".interfaces.IIndexable"
    factory=".indexable.IndexableAdapter"
    />

And here's how you look it up in Python code::

  indexable = IIndexable(my_content_object)

You can then call methods on 'indexable'; to see which methods exist
you can consult `adapters/interfaces.py`.

From 1.3 to 1.4
===============

* For running the unit tests, ZopeTestCase 0.9.8 is now required.

From 1.2 to 1.3
===============

* Metadata sets can have a 'category' property. This property can be used
  to define special purpose metadata sets. This is used now for the
  tab_settings SMI screen and the tab_metadata.

* The minimal_role and category properties are exported and imported
  to/from the XML metadata set definitions.

* setValuesFromRequest now uses Formulator to validate user input. This
  solves the infamous 'checkbox' problem.

* MetadataTool.setMetadataValues() has been removed.

From 1.1 to 1.2
===============

* the 'action_paste' and 'action_paste_to_ghost' methods on Folder have
  a new signature, instead of returning a list of messages it now returns
  (message_type, messages) where message_type can be 'feedback' and 'error'
  and messages is the list that used to be returned.

* tab_edit for VersionedContent views has changed to make use of a new
  macro, ``macro_tab_edit/macros/editor``, instead of
  ``macro_index/macros/master`` directly. ``macro_tab_edit`` does use
  ``macro_index`` indirectly.

  A default tab_edit is supplied in VersionedContent
  views. SilvaDocument however overrides this with its own tab_edit.
  You can also do in your own product.

  The following slots are available in ``macro_tab_edit/macros/editor``:

    * editor_css - add in editor-specific CSS stylesheets.

    * editor_refresher - a slot to put in special refreshing code for
      editors that are sensitive to Zope session timeouts, such as the
      forms editor.

    * editor_selection - add extra fields above the main editor content area
      to allow the user to switch editors, such as between kupu and the
      forms editor.

    * editor_content - the main editor content area, showing for instance
      the forms editor or kupu.

* The public view directory layout changed due to some change in the way
  we register views. For some functionality on the VersionManagementAdapter,
  we needed to get a rendered version, which wasn't allowed the way the views
  were previously set up (fetching the version happened in the render scripts,
  each script would render a specific version).

  Changes:

    * instead of registering a 'public' view for VersionedContent
      objects, they are now registered for Version objects.

    * instead of 2 scripts 'render_view' and 'render_preview' only
      'render' is called for both view and preview, if you want to
      make a distinction between the two, you can register a new
      view_type called 'preview' to another subdirectory and place a
      'render' script in that, the general pattern is that if you want
      to use only 1 script you place it directly in the
      'public/<meta_type>' directory, if you want to have seperate
      scripts for view and preview, create a 'view' and 'preview'
      directory in that dir ('public/<meta_type>/view' and
      'public/<meta_type>/preview')

    * the 'model' variable in the render script does not point to the
      VersionedContent object anymore, but instead it refers to the
      Version object (of course it still refers to the object itself
      in case the object is not of a versioned type)

From 1.0 to 1.1
===============

* The previously deprecated method 'archive_file_import' on Folder.py and
  the ImportArchive module have been removed. Code that depended on this
  should be rewritten to use the archivefileimport adapter.

* XSLT support for document renderers. While this shouldn't break
  existing code, it opens up new possibilities for extension
  writers. You can register your own XSLT templates with Silva that
  handle content rendering. Read doc/xslt_renderers_howto.txt for more   
  information

* new parser in forms-based editor. The existing parser for bold,
  italic and the like in the forms based editor has been scrapped and
  replaced with a parser that uses a simple HTML subset. The new
  parser should be more predictable for authors and a lot
  faster. Developers who have written their own widgets-based editors
  may need to modify code. If you already use the Silva-1.0
  infrastructure for finding the parser, the changes should be minimal
  and be limited to the UI, however. The Silva-1.0 pattern is::

    supp = context.editorsupport.getMixedContentSupport(model, node)
    supp.parse(text)

* new method on Folder objects, get_public_tree_all().  Returns a list
  of all published content objects (including containers) below this
  object (not including this object itself), including non-transparent
  containers like publications.
