
**********************************************************************
Upgrade to ikaaro 0.20.0
System Administrators
**********************************************************************

Scripts
====================

The signature of the "icms-start.py" script has changed concerning the
way to detach, or not, from the console:

                  Before                    Now
                  -----------------------   -----------------------
  (don't detach)  icms-start.py -d <path>   icms-start.py <path>
  (detach)        icms-start.py <path>      icms-start.py -d <path>

Type "icms-start.py --help" to learn the details of the new signature.


Logs
====================

The log files have been moved to the new "log" directory:

  Before            Now
  ---------------   -------------------
  access_log        log/access
  error_log         log/error
  spool_log         log/spool
  spool_error_log   log/spool_error


Configuration File
====================

It is higly recommended to define the new "contact-email" variable of
the configuration file.


Update the database
====================

As usual instances must be updated following the standard procedure:

  1. Make a backup

  2. Update the database:

     $ icms-update.py xxx

  3. Rebuild the catalog:

     $ icms-update-catalog.py xxx

But there are some important changes concerning the second step.  Now
the "icms-update.py" script writes a log detailing the objects that
failed to upgrade, if any.  This log is named "log/update".

If some objects fail to upgrade, check the update log file and try to
resolve the problems, then run again the "icms-update.py" script.

If you don't know how to resolve a problem, ask to the mailing list.


**********************************************************************
Upgrade to ikaaro 0.20.0
Developers
**********************************************************************

Note that the documentation below is not complete.  For any specific
problem, please ask the mailing list.

Imports
====================

There have been many other improvements to the code reorganization, as
a consequence import statements need to be updated.

Most important, "itools.cms" has been changed to "ikaaro":

  from itools.cms import XXX                     # Before
  from ikaaro import XXX                         # Now

There have been many other improvements to the code reorganization,
among them the modules "forum", "future", "tracker" and "wiki" have
become sub-packages:

  Before                         Now
  ----------------------------   ---------------------------------
  itools.cms.base.Handler        ikaaro.base.DBObject
  itools.cms.handlers.Metadata   ikaaro.metadata.Metadata
  itools.cms.html.XHTMLFile      ikaaro.html.WebPage
  itools.cms.future.Dressable    ikaaro.future.dressable.Dressable
  itools.cms.future.OrderAware   ikaaro.future.order.OrderAware

The module "catalog" has been removed.  The functions "schedule_to_index",
"schedule_to_unindex" and "schedule_to_reindex" have been replaced by
calls to the "context.server" methods "add_object", "remove_object" and
"change_object" (see section "Indexing").


Architecture
====================

Now the distinction between objects stored in the database, and the
objects that make up the user interface (reached through the "/ui"
path) is more clear.

UI Objects
--------------------

As before, UI objects are at the same time "handlers".  However, we do
not get to them using the "get_handler" method, but the "get_object"
method.  The pattern that follows is very common, and needs to be
updated:

  # Before
  handler = self.get_handler('/ui/...')
  return stl(handler, namespace)

  # Now
  handler = self.get_object('/ui/...')
  return stl(handler, namespace)

Database Objects
--------------------

On the other hand, the database objects are not handlers anymore.  The
relationship between database objects and itools handlers is not defined
by inheritance, but by aggregation.

One of the effects is that the expression to call a method of a handler
has changed:

  Before                Now
  -------------------   ---------------------------
  self.do_something()   self.handler.do_something()

Also, in the rare occassions where our class inherited directly from a
handler class, we need to update our code.  Now we specify what handler
class we want to use through the new variable "handler_class":

  # Before
  class MyObject(BaseObject, HandlerClass):
      ...

  # Now
  class MyObject(BaseObject):
      class_handler = HandlerClass
      ...

Related to this, there are many method calls that need to be updated.
These are some common patterns:

  Before                      Now
  --------------------------  ------------------------------
  self.search_handlers(...)   self.search_objects(...)
  self.get_metadata()         self.metadata

And:

(1) # Before
    handler = self.get_handler(path)

    # Now
    object = self.get_object(path)
    handler = object.handler

(2) # Before
    handler, metadata = self.get_object(path)

    # Now
    object = self.get_object(path)
    object.metadata
    object.handler

(3) # Before
    handler, metadata = self.set_object(name, handler, metadata)

    # Now (XXX)
    cls.make_object(cls, container, name, **kw)



Metadata
====================

The architecture of the metadata has been radically changed.  The first
visible effect is that we don't have prefixes anymore:

  # Before
  title = self.get_property('dc:title')

  # Now
  title = self.get_property('title')

Definition
--------------------

Before the metadata was defined globally through a registry, now it is
centered on the object.  This is the common pattern to update:

  # Before
  from itools.schemas import Schema, register_schema

  class MySchema(Schema):
      class_uri = 'http://example.com/my_xml_namespace'
      class_prefix = 'myschema'

      datatypes = {
         'field1': DataType,
         'field2': DataType,
      }

  register_schema(MySchema)

  # Now
  class MyObject(BaseObject):

      @classmethod
      def get_metadata_schema(cls):
          schema = BaseObject.get_metadata_schema()
          schema['field1'] = DataType
          schema['field2'] = DataType
          return schema


Indexing
====================

As with the metadata, the definition of the fields to be indexed has
been de-centralized:

  # Before
  class MyRoot(Root):

      _catalog_fields = Root._catalog_fields + [
          KeywordField('field1'),
          KeywordField('field2')]

  class MyObject(BaseObject):

      def get_catalog_indexes(self):
          ...

  # Now
  class MyObject(BaseObject):

      def get_catalog_fields(self):
          fields = BaseObject.get_catalog_fields(self)
          fields.append(KeywordField('field1'))
          fields.append(KeywordField('field2'))
          return fields

      def get_catalog_values(self):
          ...


The "schedule_to_index", "schedule_to_unindex" and "schedule_to_reindex"
functions have been removed.  They are replaced by:

  # Before                    # Now
  schedule_to_index(obj)      context.server.add_object(obj)
  schedule_to_unindex(obj)    context.server.remove_object(obj)
  schedule_to_reindex(obj)    context.server.change_object(obj)


Upgrading
====================

The last aspect that has been de-centralized is the upgrade code.  Be
sure every custom class defines the right "class_version" variable, and
includes the required update methods; for instance:

  class MyObject(BaseObject):
      class_id = 'myobject'
      class_version = '20071216'

      def update_20071215(self):
          BaseObject.update_20071215(self)

      def update_20071216(self):
          BaseObject.update_20071216(self)


New Instances
====================

The way to create new instances is much different.  For example, this
is what a Wiki looks like, before and after:

  # Before
  class WikiFolder(Folder):
      def new(self):
          Folder.new(self)
          cache = self.cache
          page = WikiPage()
          cache['FrontPage'] = page
          cache['FrontPage.metadata'] = page.build_metadata()

  # Now
  class WikiFolder(Folder):
      @staticmethod
      def _make_object(cls, folder, name):
          Folder._make_object(cls, folder, name)
          metadata = WikiPage.build_metadata()
          folder.set_handler('%s/FrontPage.metadata' % name, metadata)

Two things to note:

  - Now we don't need to create the "FrontPage" file, because at the
    beginning it is empty.

  - The new API is more "procedural", while the old API had a more
    "declarative" flavour.

new_instance_form/new_instance
------------------------------

Also, the "new_instance_form" and "new_instance" methods are now static:

  # Before
  @classmethod
  def new_instance_form(cls, context):
      BaseObject.new_instance_form.im_func(cls, context)
      ...

  @classmethod
  def new_instance(cls, container, context):
      BaseObject.new_instance.im_func(cls, container, context)
      ...

  # Now
  @staticmethod
  def new_instance_form(cls, context):
      BaseObject.new_instance_form(cls, context)
      ...

  @staticmethod
  def new_instance(cls, container, context):
      BaseObject.new_instance(cls, container, context)
      ...


Other API changes
====================

These are some other patterns that need to be updated:

(1) # Before
    root.send_email(from_addr, to_addr, subject, body)

    # Now
    root.send_email(to_addr, subject, from_addr=from_addr, text=body)


Obsolete Code
====================

This is the list of obsolete code that has been removed:

 - ListOfUsers


UNCLASSIFIED
============

Changed:

  Before                      Now
  -------------------------   ------------------
  /;change_language?lang=fr   ?language=fr

Removed:

  - The "get_pathtoroot" method.
  - The "owner" metadata.
  - The "contact_email" variable (moved to the configuration file).

