Writing a Storage Provider

From Creative Commons
Revision as of 14:38, 17 April 2006 by Nathan Yergler (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search


A storage provider is a special type of P6 extension that adds support for a different destination for works. Possible destinations might include an FTP server, a network share (for internal customizations), or a website with an upload API. A storage provider was formerly considered a large-scale customization of ccPublisher; recent changes to the architecture (Issue 58, Issue 52) have begun to merge the model of storage providers and extensions. This is a good thing.

Overview

A storage provider is implemented as a Python package. The package should contain the code necessary for processing files and any user interface elements. It will also include a ZCML file, configure.zcml, which registers the storage provider and any adapters, subcribers, etc it requires.

For the purposes of this documentation we will implement the Self-Hosting Provider. The Self-Hosting Provider does not directly move files. Instead it takes the metadata and file information provided, along with a user-supplied verification URL, and generates HTML to be used in the user's web page.

Basic Code Implementation

We begin by creating a Python package, selfhost.

 ./selfhost
    __init__.py
    configure.zcml
    storage.py

The __init__.py file simply makes the directory into a Python package; as such it can remain empty.

storage.py will contain our storage provider. The barebones implementation might look like this:

"""
Basic implementation of a Storage provider.
"""

import zope.interface
import zope.component

import p6
import p6.api
import p6.extension.interfaces
import interfaces
import p6.storage.common

class SelfHostStorage(p6.storage.common.CommonStorageMixin):
    zope.interface.implements(interfaces.IStorage)
    
    id = 'SELFHOST_STORAGE'
    name = 'Self Hosted'
    description = 'Create HTML for emebedding in your own web page for files hosted on your own web server.'
    
    def __init__(self):

        self.registerEvents()

    def validate(self, event=None):
        """Handle L{p6.storage.events.IValidate} events by simply
        updating the progress bar."""
        
        update = p6.ui.events.UpdateStatusEvent(delta=20,
                                                message='validating submission data...')

        zope.component.handle(update)

    def store(self, event=None):
        """Handle L{p6.storage.events.IStore} events by simply
        updating the progress bar; fire a L{p6.storage.events.WorkStored}
        event after the "storage" process is complete.
        """
        
        update = p6.ui.events.UpdateStatusEvent(delta=20,
                                                message='in Store...')

        zope.component.handle(update)

        return {}

This implementation does three things which are important to note:

  1. It subclasses p6.storage.common.CommonStorageMixin, which implements some helper methods.
  2. It calls self.registerEvents(). registerEvents connects our class to the event dispatch mechanism.
  3. It implements the validate and store methods; these are called by the event dispatch system (which is why we needed to register) for the two primary stages of the storage provider life-cycle: validation (making sure we have everything we need) and storage (actually performing our operation).

Note that in both the validate and store methods, we create an UpdateStatusEvent and then call zope.component.handle with it. This communicates progress information back to the user interface. In a real implementation you might use these events to periodically show the user progress (percent complete, etc) information.

Contributing to the User Interface

Distributing Your Storage Provider