Apps and Plugins¶
Cantemo Apps allows anyone to extend the features and capabilities of Cantemo. We have enabled areas in Cantemo to be extended by implementing interfaces so that you can override or enhance the functionality of Cantemo to suit your needs. You create Apps by using the Plugin system.
The Cantemo plugin system is based upon a component architecture which creates functional interfaces and services which allow components to easily extend each others’ functionality. Each component can offer its own API by declaring extension points.
We have created extension points in Cantemo which are detailed in this documentation, we also create extension points in the theme to allow you to inject basic information and functionality into a page.
As well as this, Cantemo raises many events which can be used to trigger functionality decoupled from the main Cantemo code base. Signals are raised at key moments such as pre-saving of a record or post-saving, and Receivers are notified of such events to trigger extra functionality.
Creating Apps¶
Apps are created as Python modules (single .py files), or as Python packages (folders with __init__.py files) and are placed within the plugins/ directory. On startup Cantemo reads the plugin directory for modules and packages and loads them. Any code that is using Plugin Extensions will be placed into the PluginEnvironment.
It is important to note that the apps do have an impact on the running of the Cantemo framework, any code should raise the appropriate exceptions and die safely, otherwise the performance or running of Cantemo can be directly affected.
My First Cantemo App¶
Starting from Cantemo 2.0 there is a Cantemo App skeleton creator for easily creating Cantemo Apps. We will use this to create a basic App.
After logging into a system containing Cantemo we change to the directory that contains Cantemo and run a management command which creates the basic App skeleton from a template. We will call it myfirstportalapp:
cd /opt/cantemo/portal
./manage.py start_portal_app myfirstportalapp
Now there is a portal app in /opt/cantemo/portal/portal/plugins/myfirstportalapp
The app you created will contain the following files:
/opt/cantemo/portal/portal/plugins/myfirstportalapp
/opt/cantemo/portal/portal/plugins/myfirstportalapp/plugin.py
/opt/cantemo/portal/portal/plugins/myfirstportalapp/templates
/opt/cantemo/portal/portal/plugins/myfirstportalapp/templates/myfirstportalapp
/opt/cantemo/portal/portal/plugins/myfirstportalapp/templates/myfirstportalapp/navigation.html
/opt/cantemo/portal/portal/plugins/myfirstportalapp/templates/myfirstportalapp/index.html
/opt/cantemo/portal/portal/plugins/myfirstportalapp/urls.py
/opt/cantemo/portal/portal/plugins/myfirstportalapp/views.py
/opt/cantemo/portal/portal/plugins/myfirstportalapp/__init__.py
- Now restart Cantemo Web::
systemctl restart portal-web.service
Now your app should be registered and the admin overview should look like this
Now you may edit the source in /opt/cantemo/portal/portal/plugins/myfirstportalapp/ and start developing your app! You will see there are files there which use the plugin infrastructure to register the app, create some basic templates which add to the navigation and create a page.
Example code¶
As part of our commitment to developers we supply example code to github.
Please look at https://github.com/Cantemo
App Packaging¶
Simple Apps can be placed in a single file (python module) and placed within the plugins directory, and the plugin system will import each file and register the Plugin interface directly. More complex Apps, such as those adding functionality with additional pages can be placed in their own directory as a python package. The start_portal_app command creates an own directory for the basic files needed for a package.
The python package is also placed within the plugins directory, and must contain an __init__.py
file which imports any module within the package so that the Plugin loader can register the plugin.
Both modules and packages can reference any library that the python library has access too as well as the Cantemo framework.
App Package Structure¶
The plugins will be stored in a directory called plugins, and under this each plugin will have its own directory.
plugins/
050_plugin_name_example/
context_processors/
middleware_classes/
templates/
tests/
urls.py
models.py
051_plugin_name_example/..
These directories can have a set structure so that the plugin architecture can find the plugins it needs for each section of the site.
The naming of the plugins will decide the order that they will be included.
App Registration¶
Optionally Apps can be registered so that they show up in the admin menu. This will allow the administrators to see what version of your App they are running, and for you to display your company name and optionally the website. For example in a plugins.py write the following:
import logging
from portal.pluginbase.core import Plugin, implements
from portal.generic.plugin_interfaces import IPluginURL, IPluginBlock, \
IAppRegister
log = logging.getLogger(__name__)
class MyNewAppRegister(Plugin):
implements(IAppRegister)
def __init__(self):
self.name = "My New App"
self.plugin_guid = "1ff0ada4-a3a2-1aea-a7b1-6e5371975c56"
log.debug('Registered My New App')
def __call__(self):
_app_dict = {
'name': self.name,
'version': versionnumber,
'author': 'Codemill AB',
'author_url': 'www.cantemo.com',
'notes': 'Copyright 2012. All Rights Reserved'}
return _app_dict
mynewappplugin = MyNewAppRegister()
This registers a new IAppRegister type plugin. It then returns information to the system. If you are copying the above then you want to change some of the information:
self.name - Change to the name of your plugin
self.plugin_guide - Change to a unique number.
version - The version number of this plugin
author - Your company name
author_url - Your website address.
notes - Any other text information you want to show up.
App Removal¶
If you are removing apps, make sure to also remove all the compiled .pyc files from the plugin directory.
Alternatively bundle a script which cleans the package away, removes the .pyc files and removes the database models (using database migrations.
App Database Integration¶
You can use the Django models functionality to create database tables for storing and retrieving data for you App. The Django documentation contains very good documentation on how to do this.
Database Migration¶
For Database migration you should use Django’s migration framework.
Creating a migration is done using: manage.py makemigrations <app name>
More information can be found here: https://docs.djangoproject.com/en/1.11/topics/migrations/
Apps and avoiding import errors¶
The Apps can be imported before the rest of the Cantemo code, therefore we need to put in place measures to stop any cyclic import problems from happen. It is recommended that imports are not put at the head of python modules, but imported within functions that need them:
from portal.pluginbase.core import *
from portal.utils.plugin_interfaces import IPluginBlock
from portal.generic.plugin_interfaces import ITemplateChooser
class MyBlockPlugIn(Plugin):
""" PLUGIN EXAMPLE
This plugin will return the first user in the system:
"""
implements(IPluginBlock)
def __init__(self):
self.name = "myblock"
def return_string(self, tagname, *args):
from django.contrib.auth.models import User
u = User.objects.get(pk=1)
return {'guid':'9748872c-3310-11e1-9d7c-27a06e9d671c', \
'template':u.username }
pluginblock1 = MyBlockPlugIn()
This App is potentially unsafe and might cause import errors:
from portal.pluginbase.core import *
from portal.utils.plugin_interfaces import IPluginBlock
from portal.generic.plugin_interfaces import ITemplateChooser
from django.contrib.auth.models import User
class MyBlockPlugIn(Plugin):
""" PLUGIN EXAMPLE
This plugin will return the first user in the system:
"""
implements(IPluginBlock)
def __init__(self):
self.name = "myblock"
def return_string(self, tagname, *args):
u = User.objects.get(pk=1)
return {'guid':'9748872c-3310-11e1-9d7c-27a06e9d671c', \
'template':u.username }
pluginblock1 = MyBlockPlugIn()
Please make sure to follow that convention to make Apps import safely. A better version that checks for the existence of the user would be:
from portal.pluginbase.core import *
from portal.utils.plugin_interfaces import IPluginBlock
from portal.generic.plugin_interfaces import ITemplateChooser
class MyBlockPlugIn(Plugin):
""" PLUGIN EXAMPLE
This plugin will return the first user in the system:
"""
implements(IPluginBlock)
def __init__(self):
self.name = "myblock"
self.guid = "a85a9ec4-3310-11e1-92de-efb93402adcb"
def return_string(self, tagname, *args):
from django.contrib.auth.models import User
try:
u = User.objects.get(pk=1)
except User.DoesNotExist:
u = None
return {'guid':self.guid, 'template': u}
pluginblock1 = MyBlockPlugIn()
Apps in other development languages.¶
This is out of scope for this document, but Python’s support for creating modules in other languages and having inter-operability is very good.
For extended system support please contact us for our integration with messages queues in particular Redis, which can be used by other services using other languages to provide extra functionality.
Development Reference¶
More information on different areas of Cantemo for development.
- Archive Plugins
- Caching
- Comments
- Dashboard Widgets
- Download Plugin Interface
- Events
- Built in events
- Loading external pages
- Generic Helper Views
- JavaScript and Cantemo
- Javascript PubSub
- Media Preview
- Metadata Form Plugins
- Recycle Bin
- Request Plugins
- Search Plugins
- Search Page Customization with JavaScript and HTML Plugins
- Template Plugins
- Thumbnails
- Transcoder Plugins
- URL Plugins
- Users
- User Plugin
- View Plugins