├── INSTALL.txt ├── README.txt ├── docs ├── plugin_style.txt └── usage.txt └── plugins ├── __init__.py ├── models.py ├── templatetags ├── __init__.py └── plugin.py └── views.py /INSTALL.txt: -------------------------------------------------------------------------------- 1 | To install django-plugins simply place the included plugins directory on your python path. You can also symlink it to your site packages directory. 2 | 3 | Then add ``plugins`` to your ``INSTALLED_APPS`` in your settings.py file for the django project you wish to use django-plugins with, make sure you also have ``django.contrib.admin`` there if you wish to use the admin for managing your plugins. 4 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | ============== 2 | Django Plugins 3 | ============== 4 | 5 | This is a generic plugin application for Django. 6 | 7 | For install instructions look at INSTALL.txt and for complete docs check the docs/ folder. 8 | -------------------------------------------------------------------------------- /docs/plugin_style.txt: -------------------------------------------------------------------------------- 1 | ============ 2 | Plugin Style 3 | ============ 4 | 5 | Django Plugins takes advantage of duck typing in order to be most efficient. This documentation will explain what a plugin file should look like. 6 | 7 | Here is an example plugin: 8 | 9 | ``` 10 | class FooBar(object): 11 | def __init__(self, entry): 12 | self.entry = entry 13 | 14 | def __call__(self): 15 | self.entry.text = self.entry.text.replace('foo', 'bar') 16 | return self.entry 17 | 18 | class Meta: 19 | name = 'FooBar the Plugin' 20 | author = 'Alex' 21 | acts_on = 'Entry' 22 | 23 | plugin_class = FooBar 24 | ``` 25 | 26 | The plugin has 3 parts. First is the ``__init__`` method, this should take whatever parameters the plugin needs, typically the object to be acted on, in the example plugin it takes a single object named entry. Second is the ``__call__`` method, which is a Python "magic method" that allows you to call an instance of the object, this should do whatever the plugin is supposed to do and returns the value, in the example the plugin takes the ``text`` attribute of the entry object and replaces all the instances of 'foo' in the text with 'bar', it then returns the entry object. Finally, each plugin should have an inner Meta class, this should define 3 variables, name, which is the name of the plugin, author, the name of the author, and acts_on, which should have the name of the class the plugin takes as a parameter(strictly speaking it can take any class, so long as it defines all of the attributes and methods that the plugin makes use of). 27 | 28 | The plugin file should have a global variable named ``plugin_class`` which contains the class of the plugin, in the example the plugin's class is named FooBar, and the plugin_class variable just contains the class(note, this is not an instance of the class, it is the class itself). 29 | -------------------------------------------------------------------------------- /docs/usage.txt: -------------------------------------------------------------------------------- 1 | ===== 2 | Usage 3 | ===== 4 | 5 | These are instructions on how to use django-plugins itself, for instructions on how to build a plugin see ``plugin_style.txt``. 6 | 7 | You can add, remove, activate, and deactivate plugins from within the django admin. 8 | 9 | When you wish to add a plugin simply go into the admin, click on Plugin, and then click on Add plugin. Select the plugin file you wish to add, whether you would like it to be active immediattely upon being added, and then select save. 10 | 11 | To delete a plugin(permanately, as opposed to deactivateing it), simply go the plugins page, click the plugin you want to delete, and press delete. 12 | 13 | To change whether a plugin is active, go to that plugin's page and either check or uncheck active, and press save. 14 | -------------------------------------------------------------------------------- /plugins/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex/django-plugins/d99d822ae58b15f0370c45c94e0aac019641d8a8/plugins/__init__.py -------------------------------------------------------------------------------- /plugins/models.py: -------------------------------------------------------------------------------- 1 | import imp 2 | import os 3 | 4 | from django.db import models 5 | 6 | class Plugin(models.Model): 7 | plugin_file = models.FileField(upload_to='plugins/') 8 | active = models.BooleanField() 9 | title = models.CharField(max_length=255, editable=False) 10 | author = models.CharField(max_length=255, editable=False) 11 | acts_on = models.CharField(max_length=255, editable=False) 12 | 13 | def __unicode__(self): 14 | return self.title 15 | 16 | def save(self, *args, **kwargs): 17 | super(Plugin, self).save(*args, **kwargs) 18 | plg = self.get_class() 19 | if hasattr(plg, 'Meta'): 20 | self.title = plg.Meta.name 21 | self.author = plg.Meta.author 22 | self.acts_on = plg.Meta.acts_on 23 | else: 24 | self.title = '' 25 | self.author = '' 26 | self.acts_on = '' 27 | super(Plugin, self).save(*args, **kwargs) 28 | 29 | def delete(self, *args, **kwargs): 30 | extra_file = '%sc' % self.get_plugin_file_filename() 31 | try: 32 | os.remove(extra_file) 33 | except OSError: 34 | pass 35 | super(Plugin, self).delete(*args, **kwargs) 36 | 37 | def get_class(self): 38 | path, filename = os.path.split(self.get_plugin_file_filename()) 39 | filename, ext = os.path.splitext(filename) 40 | fp, pathname, description = imp.find_module(filename, [path]) 41 | module = imp.load_module(filename, fp, pathname, description) 42 | return module.plugin_class 43 | 44 | class Admin: 45 | fields = ( 46 | (None, { 47 | 'fields': ('plugin_file',) 48 | }), 49 | (None, { 50 | 'fields': ('active',) 51 | }), 52 | ) 53 | list_display = ('title', 'author', 'acts_on', 'active') 54 | list_filter = ('active', 'acts_on') 55 | -------------------------------------------------------------------------------- /plugins/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex/django-plugins/d99d822ae58b15f0370c45c94e0aac019641d8a8/plugins/templatetags/__init__.py -------------------------------------------------------------------------------- /plugins/templatetags/plugin.py: -------------------------------------------------------------------------------- 1 | import copy 2 | 3 | from django import template 4 | 5 | from plugins.models import Plugin 6 | 7 | register = template.Library() 8 | 9 | 10 | class ApplyPluginNode(template.Node): 11 | def __init__(self, object_name, varname=None): 12 | if varname: 13 | self.varname = varname 14 | else: 15 | self.varname = object_name 16 | self.obj = template.Variable(object_name) 17 | 18 | def render(self, context): 19 | obj = copy.copy(self.obj.resolve(context)) 20 | plgs = Plugin.objects.filter(active=True, acts_on=obj.__class__.__name__) 21 | for plg in plgs: 22 | plg_cls = plg.get_class() 23 | inst = plg_cls(obj) 24 | obj = inst() 25 | context[self.varname] = obj 26 | return '' 27 | 28 | 29 | def apply_plugin(parser, token): 30 | """ 31 | Applies all the active plugins that act on the class of the given object to the given object. 32 | 33 | Usage:: 34 | 35 | {% apply_plugin [Object] %} 36 | {% apply_plugin [Object] as [varname] %} 37 | """ 38 | bits = token.contents.split() 39 | if len(bits) == 4: 40 | if bits[2] != 'as': 41 | raise template.TemplateSyntaxError, "apply_plugin tag's second argument must be 'as'" 42 | return ApplyPluginNode(bits[1], bits[3]) 43 | elif len(bits) == 2: 44 | return ApplyPluginNode(bits[1]) 45 | else: 46 | raise template.TemplateSyntaxError, "apply_plugin tag takes either 1 or 3 arguments" 47 | register.tag(apply_plugin.__name__, apply_plugin) 48 | -------------------------------------------------------------------------------- /plugins/views.py: -------------------------------------------------------------------------------- 1 | # Create your views here. 2 | --------------------------------------------------------------------------------