Opal Plugins are Django apps on the server side, and collections of angular.js models for the client.
Getting started with your plugin
The Opal commandline tool will bootstrap your plugin for you - just run:
$ opal startplugin yourcoolplugin
Adding Discoverable Functionality
A common pattern for plugins is to add functionality that other plugins or applications can use by inheriting a base class that you define in a file with a magic name. (In much the same way that Django provides models.)
For example, if you're creating an appointments plugin that helps people to book and schedule
appointments in clinics, you would create a base
Clinic class that can be subclassed to
create specific clinics.
class Clinic(opal.core.discoverable.DiscoverableFeature): module_name = 'clinics'
We can then create clinics in any installed app, and they will be available from
class OutpatientsClinic(Clinic): name = 'Outpatients' # Add your custom clinic functionality here e.g. def book_appointment(self, date, patient): pass Clinic.list() # -> Generator including OutPatientsClinic Clinic.get('outpatients) # -> OutpatientsClinic
Getting a plugin directory
Each plugin has a
directory() method that returns
the file system location of the module.
Add an urls.py, then add to your plugin class as YourPlugin.urls
Naturally, these can point to views in your plugin!
There are some restricted namespaces for these...
Opal uses Django Rest Framework to provide APIs, and you may add to these from your plugin.
By convention, APIs live in
yourplugin/api.py. You are expected to provide a
rest_framework.viewsets.ViewSet subclass, which you then detail as the
of your plugin.
# yourplugin/api.py from rest_framework.viewsets import ViewSet from rest_framework.response import Response class PingViewSet(ViewSet): def list(self, request): return Response('pong') # yourplugin/plugin.py from opal.core.plugins import OpalPlugin from yourplugin import api class YourPlugin(OpalPlugin): apis = [ ('ping', api.PingViewSet) ]
These APIs will then be available and self-documenting from the standard Opal url
Developers are strongly encouraged to ensure that APIs which
serve patient data are restricted to logged in users. Django Rest Framework
have an extensive permissioning system.
Opal ships with the base ViewSet class
opal.core.api.LoginRequiredViewset which adds the Django
Rest Framework permission class IsAuthenticated to your viewset.
class PingViewSet(LoginRequiredViewset): def list(self, request): return Response('pong')
Adding Actions to the sidebar
Actions can be added to the sidebar by setting the
actions attribute of your plugin.
Actions is expected to be an iterable of strings which are templates to be included in
the sidebar. By convention, actions will live in
And then in the template:
<p ng-show="episode.category == 'YourEpisodeCategory'"> <button class="btn btn-primary" ng-click="alert('Boom!')"> <i href="fa fa-warning"></i> ALERT ME </button> </p>
Adding dependencies globally to our angular modules
Dependencies listed in
angular_module_deps will be added to all Angular modules (as long as they
use the OPAL.module() API. If not, you're on your own. We could monkey patch angular.module, but we
won't for now.
Adding Menu items
Plugins may add items to the main Opal Menu by setting the
This attribute should be an iterable of
from opal.core import menus class YourPlugin(OpalPlugin): menuitems = [ menus.MenuItem(href="/your-url", icon="fa-user") ]
Add to installed apps Add to requirements if appropriate
Adding extra markup to the tag
Any templates you define in the property .head_extra will be included in the