Shotwell Architecture Overview: Publishing Extension Point
The Shotwell Pluggable Publishing API allows you to write plugins that upload photos and videos to web services. The Shotwell distribution includes publishing support for four core services: Facebook, Flickr, Picasa Web Albums, and YouTube. To enable Shotwell to connect to additional services, developers like you write publishing plugins, dynamically-loadable shared objects that are linked into the Shotwell process at runtime. Publishing plugins are just one of several kinds of plugins supported by the Shotwell Pluggable Interfaces Technology.
The Valadoc of the publishing extension point is here.
1. The Host / Plugin Architecture
You enable Shotwell to connect to additional web services by writing a publishing plugin that supports each desired service. Your plugin must be capable of bidirectional communication with the Shotwell process that hosts it. For example, the Shotwell process tells your plugin when it is allowed to access the network, and your plugin uses services provided by the Shotwell process to get information about the items the user wishes to publish, to set up the publishing user interface and to handle errors.
The part of the Shotwell process that drives and provides services for your plugin is called the host. Your plugin communicates with the host through well-defined interfaces. To cleanly separate your plugin from Shotwell, this document distinguishes between plugin space and host space. In general, if an interface is in plugin space, it is your responsibility to provide a class that implements it, whereas interfaces in host space are provided for you by Shotwell. In the overview class diagram above, host space and plugin space are separated by a dashed red line. Within plugin space, two interfaces are shaded red. When you develop a publishing plugin, you provide classes that implement these two, shaded interfaces.
In the overview class diagram, the only relationship that spans the boundary between host space and plugin space is the two-way association between the PluginHost and the Publisher. This relationship is the point of contact between Shotwell and its publishing plugins. As a plugin developer, you write a Publisher, which communicates with and takes advantage of the services provided by its associated PluginHost.
The PluginHost provides your Publisher with the following kinds of services:
User interface: the PluginHost provides facilities for placing custom panels of widgets called panes in the on-screen publishing dialog box to help you solicit information from the user and keep the user informed about the progress of the publishing interaction. The PluginHost also allows publishers to prevent the user from switching to another publishing service when the currently running service is performing non-interruptible network activity and to configure the text on the close/cancel button that appears in the lower-right-hand corner of the on-screen publishing dialog box.
Publishable information & management: in Shotwell parlance, publishables are the underlying media items (such as photos or videos) that your plugin actually uploads to a remote service. The PluginHost provides you with information about the number and type of publishables and allows you serialize publishables to a temporary directory on disk to pool them for upload to the remote service.
Configuration management: your Publisher may need to store persistent configuration information over many invocations of Shotwell. Network session keys and the name of the remote photo album to publish to are examples of this kind of persistent information. The PluginHost provides you with the ability to get and set this information to a configuration system provided by the operating system.
Error handling: the PluginHost allows your Publisher to notify the user when errors have occurred and to halt the publishing process.
2. Inside your Publisher
When writing a Publisher to tie in with Shotwell’s host object, there are several things to keep in mind. First, only one Publisher can be running at any given time. When a Publisher is in the running state, it has exclusive access to the network and the services provided by the host object. Your publisher can send network messages, access the configuration system, and manipulate the user interface only when it is in the running state. A Publisher enters the running state when its start( ) method is called, and exits the running state when its stop( ) method is called. Publishers are created in a non-running state.
Second, your Publisher drives the entire publishing workflow once it enters the running state. Inside your Publisher, you create user-interface panes and attach signal handlers to the widgets in them. You send messages to remote endpoints over the network and process their responses. The host object provides you with facilities to help you do these things, but ultimately, the responsibility for sequencing the service login and publishing interactions lies with you. Likewise, because your Publisher drives the user interface, it runs in the main UI thread, so you shouldn’t perform any blocking operations and should try to return from event processing functions as quickly as possible.
Interface Reference
1. Publisher
Represents a connection to a publishing service. Developers of publishing plugins provide a class that implements this interface. At any given time, only one Publisher can be running. When a publisher is running, it is allowed to access the network and has exclusive use of the shared user-interface and configuration services provided by the PluginHost. Publishers are created in a non-running state and do not begin running until start( ) is invoked. Publishers run until stop( ) is invoked. When stop( ) is called, publishers should relinquish the PluginHost reference theyre passed in start( ). This ensures that non-running publishers dont destructively interfere with the actively running publisher.
1.1. void start ()
Makes this enter the running state and endows this with exclusive access to the services provided by the PluginHost. Through hosts interface, this can install user interface panes and query configuration information. Only running services should perform network operations.
1.2. void stop ( )
Causes this to enter a non-running state. this should stop all network operations and cease use of the shared services provided by the PluginHost.
1.3. bool is_running ( )
Returns true if this is in the running state; false otherwise.
1.4. Service get_service ( )
Returns a Service object describing the service to which this connects.
2. PluginHost
Provides an interface through which the developers of publishing plugins can query and make changes to the publishing environment. For example, through the PluginHost, plugins can get a list of the photos and videos to be published, install and remove user-interface panes in the publishing dialog box, and request that the items to be uploaded be serialized to a temporary directory on disk. plugins can use the services of the PluginHost only when their Publisher is in the running state. This ensures that non-running publishers dont destructively interfere with the actively running publisher.
2.1. void installdialogpane (!DialogPane pane, ButtonMode mode = ButtonMode.CANCEL)
Attempts to install pane in the on-screen publishing dialog box, making pane visible and allowing it to interact with the user. mode allows you to set the text displayed on the close/cancel button in the lower-right-hand corner of the on-screen publishing dialog box when pane is installed. If mode is ButtonMode.CLOSE, the button will have the title Close. If mode is ButtonMode.CANCEL, the button will be titled Cancel. You should set mode depending on whether a cancellable action is in progress. For example, if your publisher is in the middle of uploading 3 of 8 videos, then mode should be ButtonMode.CANCEL. However, if the publishing operation has completed and the success pane is displayed, then mode should be ButtonMode.CLOSE, because all cancellable publishing actions have already occurred. If an error has posted, the PluginHost will not honor this request.
2.2. void installstaticmessage_pane (string text, ButtonMode mode = ButtonMode.CANCEL)
Attempts to install a pane in the on-screen publishing dialog box that contains the static text text. text appears centered in the publishing dialog box and is drawn in the system font. This is a convenience method only; similar results could be achieved by manually constructing a Gtk.Label widget, wrapping it inside a DialogPane, and installing it manually with a call to install_dialog_pane( ). To provide visual consistency across publishing services, however, always use this convenience method instead of constructing label panes when you need to display static text to the user. As with install_dialog_pane( ), mode allows you to set the text displayed on the close/cancel button in the lower-right-hand corner of the on-screen publishing dialog box when pane is installed. If mode is ButtonMode.CLOSE, the button will have the title Close. If mode is ButtonMode.CANCEL, the button will be titled Cancel. You should set mode depending on whether a cancellable action is in progress. For example, if your publisher is in the middle of uploading 3 of 8 videos, then mode should be ButtonMode.CANCEL. If an error has posted, the PluginHost will not honor this request.
2.3. void installpangomessage_pane (string markup, ButtonMode mode = ButtonMode.CANCEL)
Works just like install_static_message_pane( ) but allows markup to contain Pango text formatting tags as well as unstyled text.
2.4. void installaccountfetchwaitpane ( )
Attempts to install a pane displaying the static text Fetching account information… in the on-screen publishing dialog box, making it visible to the user. This is a convenience method only; similar results could be achieved by calling install_static_ message_pane( ) with an appropriate text argument. To provide visual consistency across publishing services and to allow Shotwell to handle internationalization, however, you should always use this convenience method whenever you need to tell the user that youre querying account information over the network. Queries such as this are almost always performed immediately after the user has logged in to the remote service. If an error has posted, the PluginHost will not honor this request.
2.5. void installloginwait_pane ( )
Works just like install_account_fetch_wait_pane( ) but displays the static text Logging in… As with install_account_fetch_wait_pane( ), this is a convenience method, but you should you use it provide to visual consistency and to let Shotwell handle internationalization. See the discussion of install_account_fetch_wait_pane( ) for more information. If an error has posted, the PluginHost will not honor this request.
2.6. void installwelcomepane (string msg, LoginCallback onclick)
Attempts to install a pane displaying the text msg above a push button labeled Login in the on-screen publishing dialog box, making it visible to the user. When the user clicks the Login button, youll be notified of the users action through the callback onclick. Every Publisher should provide a welcome pane to introduce the service and explain service-specific features or restrictions. To provide visual consistency across publishing services and to allow Shotwell to handle internationalization, always use this convenience method; dont contruct and install welcome panes manually. If an error has posted, the PluginHost will not honor this request.
2.7. void installsuccesspane (!MediaType media_type)
Attempts to install a pane in the on-screen publishing dialog box notifying the user that his or her publishing operation completed successfully. The text displayed depends on media_type. For example, if media_type is MediaType.PHOTO, the displayed text reads n photos were successfully published whereas if media_type is MediaType.VIDEO, the displayed text will read n videos were sucessfully published. To provide visual consistency across publishing services and to allow Shotwell to handle internationalization, always use this convenience method; dont contruct and install success panes manually. If an error has posted, the PluginHost will not honor this request.
2.8. void setdialogdefault_widget (Gtk.Widget widget)
Makes widget the default widget for the publishing dialog. After a call to this method, widget will be activated when the user presses the [ENTER] key anywhere in the on-screen publishing dialog box. Under certain circumstances, the PluginHost may not honor this request.
2.9. void setservicelocked (bool is_locked)
Toggles whether the service selector combo box in the upper-right-hand corner of the on-screen publishing dialog box is sensitive to input. When is_locked is true, the combo box is insensitive. It appears greyed out and the user is prevented from switching to another publishing service. When is_locked is false, the combo box is sensitive, allowing the user to freely switch from the current service to another service. Publishers should lock the service when they are performing non-interruptible file or network operations, since switching to another publishing service will halt whatever service is currently running. Under certain circumstances, the PluginHost may not honor this request.