================================
Image and Link Libraries in Kupu
================================

Abstract
--------

This document describes the Library feature of the Kupu WYSIWYG
editor. It defines the behaviour on both the client and the server and
serves as a specification for the XML protocol that is used for their
interaction.


Motivation
----------

Kupu is a visual editor for content management systems, with a target
audience of regular users without a high technical profile.  For this
audience, Word is the incumbent.

In addition to text, Kupu users need a simple, usable, and
high-performance system for getting images and hyperlinks into their
pages.  Sometimes they want to *browse* to find these assets.
Sometimes they want to *search*, particularly for large collections.

Regarding usability, open source CMS projects don't have many
browser-side developers with good usability backgrounds.  Since Kupu
is an oscom.org project, it is a goal to move the UI rendering from
the server (where talent would be dispersed among projects) to the
client (where one UI can work for many servers).  Therefore, the Kupu
Library only requests XML from the CMS, not HTML.

Note: Though Kupu will work with IE 5.5 sans service packs, Kupu
requires MS XML 3.0 or higher.  Any IE 5.5 that is up-to-date on
security updates, and any IE 6 or Mozilla 1.3+ can use the Kupu
Library.


Definitions
-----------

Library

  A static or dynamic collection of resources and collections defined
  at a top level. Libraries have no parents from a UI perspective,
  where as the collection they represent might have a parent
  collection.

Collection

  A static or dynamic collection of resources and collections,
  modelled after WebDAV collections.

Resource

  A named and URI-locatable object with associated metadata, such as
  title, description, preview, size, etc.


Process overview 
----------------

1. The user opens a drawer in Kupu. Kupu requests a list of available
   libraries. The server answers with a list of libraries possibly
   containing URIs at which to get the content listing of each library.

2. Upon user request, Kupu loads the contents of a library and present
   it to the user. Already opened libraries are not reloaded from the
   server

3. A user selects a resource at which point Kupu presents the
   associated metadata and may optionally load a preview image.

4. Double clicking on a collection or selecting a new library triggers
   step 2 again.

5. Clicking on a resource and clicking "Insert" (for an image) or
   "Link" (for links) updates the document and closes the drawer.

6. If the cursor is on a link or image, the drawer allows editing the
   image/link attributes.


List of libraries
-----------------

Kupu issues a simple HTTP GET request to a URL defined in the
'libraries' attribute of the iframe. The server returns XML conforming
to the following schema::

 <grammar xmlns="http://relaxng.org/ns/structure/1.0">

   <start>
     <ref name="librariesDocumentElement" />
   </start>

   <define name="librariesDocumentElement">
     <element name="libraries">
       <zeroOrMore>
         <ref name="library" />
       </zeroOrMore>
     </element>
   </define>

   <define name="library"
     <element name="library">
       <interleave>
         <ref name="commonToAllItems" />
         <choice>
           <ref name="collectionItems" />
           <ref name="itemsSource" />
         </choice>
       </interleave>
       </element>
   </define>

   <define name="commonToAllItems">
     <attribute name="id">
       <!-- Must be unique among all libraries, resources and collections. -->
       <text />
     </attribute>

     <interleave>
       <element name="uri">
         <text />
       </element>

       <optional>
         <element name="label">
           <text />
         </element>
       </optional>

       <element name="title">
         <text />
       </element>

       <optional>
         <element name="icon">
           <text />
         </element>
       </optional>
     </interleave>
   </define>

   <define name="collectionItems">
     <element name="items">
       <zeroOrMore>
         <ref name="collectionItem" />
       </zeroOrMore>
       <optional>
         <element name="uploadbutton" />
       </optional>
     </element>
   </define>

   <define name="itemsSource">
     <element name="src">
       <text />
     </element>
   </define>

   <define name="collectionItem">
     <choice>
       <element name="resource">
         <ref name="commonToAllItems" />
         <ref name="extraResourceInfo" />
       </element>
       <element name="collection">
         <ref name="commonToAllItems" />
         <ref name="itemsSource" />
       </element>
     </choice>
   <define>

   <define name="extraResourceInfo">
     <interleave>
       <optional>
         <element name="preview">
           <text />
         </element>
       </optional>

       <optional>
         <element name="size">
           <text />
         </element>
       </optional>

       <optional>
         <element name="type">
           <text />
         </element>
       </optional>
     </interleave>
   </define>

 </grammar>


For example::

 <libraries>

   <library id="http://path/to/folder">
     <title>Current Folder</title>
     <icon>http://path/to/icon.jpg</icon>
     <src>http://server/current/folder/resources.kupu</src>
   </library>

   <library id="http://path/to/other/someotherfolder">
     <title>Some Other Folder</title>
     <icon>http://path/to/icon.jpg</icon>
     <src>http:/server/other/folder/resources.kupu</src>
   </library>

   <library id="recentitems">
     <title>Recent Items</title>
     <icon>http://path/to/icon.jpg</icon>
     <items />
   </library>

 </libraries>


Kupu parses this XML to a DOM managed in JavaScript and displays it
using an XSLT stylesheet. The DOM tree is stored throughout the whole
Kupu session.


Browser-side architecture
------------------------------------

When the drawer is opened, Kupu loads the XML data about the libraries
into a JS property that holds an XML DOM.  The Sarissa cross-browser
abstraction for XML (http://sarissa.sf.net) is used.

As the user clicks, more XML data are loaded if that collection hasn't
been visited yet.  The loaded XML is then appended into the XML DOM.
The node that was clicked on has an attribute set to mark it as
selected.  Kupu then runs an XSLT on the XML DOM to re-generate the
drawer contents (as HTML), and updates the HTML node with the output.
The use of XSLT and XPath give increased performance, lower line
count, and better IE/Mozilla compatibility.

As an implementation note, XML is extremely compressible.  With
mod_deflate and other server-compression approaches, at least a
thousand entries can be encoded in 100 Kb.

User opens on a library
-----------------------

In case a collection or library has been active before, it is
deselected (by removing the 'selected' attribute on the DOM node). The
DOM node of the library the user has chosen is selected (by setting
the 'selected' attribute). Also, visually deselect the before selected
library (by unsetting a CSS class) and mark the newly selected library
(with a CSS class).

In case its <library> element provides the <src> subelement, an HTTP
GET to that URI is made, the returned XML retrieved, turned into a DOM
tree and the resulting <items> node of the result appended to the
library DOM node. That step is not made in case the <library> node
directly provides the <items> node.

Now, the XSLT is executed again, presenting the newly selected node
(by querying for the node with the 'selected' attribute) and its
contents.

When a library's contents has to be retrieved with an extra request,
it is returned according to the following schema (using definitions
from above)::

 <grammar xmlns="http://relaxng.org/ns/structure/1.0">

   <start>
     <ref name="libraryDocumentElement" />
   </start>

   <define name="libraryDocumentElement">
     <element name="library">
       <ref name="commonToAllItems" />
       <ref name="collectionItems" />
     </element>
   </define>

 </grammar>


For example::

 <library id="some_unique_id">
   <title>Current folder</title>
   <uri>http://server/current/folder</uri>
   <icon>http://server/folder.ico</icon>
   <items>
     <resource id="another_unique_id">
       <title>Foo img</title>
       <uri>http://server/current/folder/foo.jpg</uri>
       <icon>http://server/image.ico</icon>
     </resource>
     <collection id="a_collections_unique_id">
       <title>Some collection</title>
       <uri>http://server/current/folder/collection</uri>
       <icon>http://server/folder.ico</icon>
     </collection>
   </items>
 </library>


User opens a collection
-----------------------

Unselect the previously selected collection and try to execute one of
three cases in order:

  * The node of selected collection is queried for an attribute
    'loadedInNode'. If it exists, it refers to an id of a node which
    already contains that collection's items. Select that node and
    execute the XSLT to present changes.

  * The selected collection node does not have a 'loadedInNode'
    attribute. Therefore, the selected collection's URI is read and the
    document element's children are queried with an XPath to check
    whether the collection with that URI was already loaded before and
    attached to the document element. If so, the selected collection
    node receives an attribute 'loadedInNode' with the value of the
    corresponding collection node below the document element, which is
    selected (by setting the 'selected' attribute). The XSLT is executed
    to present the changes.

  * The selected collection node has no 'loadedInNode' attribute and
    there is no preloaded collection node of that URI. The selected
    node's <src> subelement is read. An HTTP GET request is made to that
    URI and the items XML data retrieved and turned into a DOM tree. The
    <collection> node of that resulting tree is given a new unique ID,
    appended to the document element of the library DOM tree and
    selected (by setting the 'selected' attribute). The selected
    collection node receives an attribute 'loadedInNode' with the value
    that newly generated id. The XSLT is executed to present the
    changes.

When a collection's contents has to be retrieved with an extra
request, it is returned according to the following schema (using
definitions from above)::

  <grammar xmlns="http://relaxng.org/ns/structure/1.0">

    <start>
      <ref name="collectionDocumentElement" />
    </start>

    <define name="collectionDocumentElement">
      <element name="collection">
        <ref name="commonToAllItems" />
        <ref name="collectionItems" />
      </element>
    </define>

  </grammar>

This grammar is modeled after the WebDAV response grammar, without
using the namespace parts of WebDAV. For example::

  <collection id="some_unique_id">
    <title>Some folder</title>
    <uri>http://server/some/folder</uri>
    <icon>http://server/folder.ico</icon>

    <items>
      <resource id="another_unique_id">
        <title>Foo img</title>
        <uri>http://server/some/folder/foo.jpg</uri>
        <icon>http://server/image.ico</icon>
      </resource>

      <collection id="a_subfolders_unique_id">
        <title>Some subfolder</title>
        <uri>http://server/some/folder/subfolder</uri>
        <icon>http://server/folder.ico</icon>
        <src>http://server/some/folder/resources.kupu</src>
      </collection>
    </items>

  </library>


If a collection's parent collection shall be accessible, then the
server has to return an entry for it explicitly.


Searching
---------

For searching, Kupu sends an HTTP POST request to the server (to a
configurable URI). The server does the search based on the POST form
values and return the results as if they were the contents of a
library. The document element of the returned XML, <library>, does not
have to have a unique id nor an icon subelement.

The POST request can contain an optional parameter for the size of the
result set. The server can enforce an upper limit itself, and if the
request parameter is higher, ignore it. By default, this value is 500.

Kupu treats the search result as a library, with the exception of
generating a unique id and icon for it. The search result library is
attached to the root node of the DOM tree and thus visible and later
accessible via the library list.


Implementation on the Plone side
--------------------------------

On the Plone side, a set of Page Templates is responsible for
generating the XML. They are aided by a special tool, KupuLibraryTool.

In order to use the XML generation for inclusion of media objects,
such as images and photos, as well as for searching documents that one
can link to from Kupu, the library tool keeps a mapping from resource
types to portal types. It provides a management interface in which the
site administrator can define, which portal types are to be treated as
collections, as linkable documents, as insertable media objects, etc.

The library tool also keeps a list of available libraries on the
site. The site administrator can add, modify, delete and reorder
libraries in the tool's management interface. The searching aspect of
Kupu Libraries is handled by the portal catalog.

Permissions used by the library tool:

- Kupu: Query libraries

  This permission is required for all users who want to query
  libraries from the library tool.

- Kupu: Manage libraries

  This permissions is required for all administrators who need to add,
  edit and delete existing libraries.

In order to be able to present special previews for resources without
having to load the resource itself in Kupu, a portal type can provide
an action called 'kupupreview', pointing to the URL where the object's
preview can be retrieved. If a portal_type does not provide that
action, preview is disabled for it.

Example: Preview action for CMFPhoto showing each photo's thumbnail
version:

  - Go to the portal_types tool and click on the 'Photo' portal
    type. Then click on its 'Actions' tab.

  - Down below enter information for a new action:

    Name:       Kupu Preview
    Id:         kupupreview
    Action:     string:${object_url}/variant/thumbnail
    Condition:  [empty]
    Permission: View
    Category:   object
    Visible?    False

  - Hit the 'Add' button.


Futures
-------

  * Cached XSLT (need Sarissa support for this)

  * Deeper standards support (WebDAV/DASL)


