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. See Extending ccPublisher for an overview of extensions and their capabilities.
Contents
Overview
A storage provider is implemented as a Python module or 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. For full examples, see the Self Hosting Provider or Internet Archive Provider implementations in Subversion.
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.
Contributing to the User Interface
Extensions can contribute to the user interface of a P6-based application through the implementation of Extension Points. An Extension Point is essentially a deferred user interface definition -- the application can define any number of Extension Points in place of actual pages. When the framework reaches the Extension Point it queries registered providers for any pages each would like included and substitutes them in place. Contributing to the user interface involves three basic steps:
- Determine which extension points you need to implement; this can be done by examining
app.zcml
for the application. - Create your user interface class.
- Register your user interface class for the extension point.
See the Extension Point documentation for more details.
Distributing Your Storage Provider
We are currently working on finalizing details for distribution. See Distributing Extensions.