================================ 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:: For example:: Current Folder http://path/to/icon.jpg http://server/current/folder/resources.kupu Some Other Folder http://path/to/icon.jpg http:/server/other/folder/resources.kupu Recent Items http://path/to/icon.jpg 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 element provides the subelement, an HTTP GET to that URI is made, the returned XML retrieved, turned into a DOM tree and the resulting node of the result appended to the library DOM node. That step is not made in case the node directly provides the 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):: For example:: Current folder http://server/current/folder http://server/folder.ico Foo img http://server/current/folder/foo.jpg http://server/image.ico Some collection http://server/current/folder/collection http://server/folder.ico 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 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 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):: This grammar is modeled after the WebDAV response grammar, without using the namespace parts of WebDAV. For example:: Some folder http://server/some/folder http://server/folder.ico Foo img http://server/some/folder/foo.jpg http://server/image.ico Some subfolder http://server/some/folder/subfolder http://server/folder.ico http://server/some/folder/resources.kupu 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, , 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)