Writing a Storage Provider
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.
Contents
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:
- It subclasses
p6.storage.common.CommonStorageMixin
, which implements some helper methods. - It calls
self.registerEvents()
. registerEvents connects our class to the event dispatch mechanism. - 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.