7c1e108b50b4ff7e67541a48cd3cca08bb0ea638
1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU Affero General Public License for more details.
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 This module implements the plugin api bits.
20 Two things about things in this module:
22 1. they should be excessively well documented because we should pull
23 from this file for the docs
25 2. they should be well tested
31 Plugins are structured like any Python project. You create a Python package.
32 In that package, you define a high-level ``__init__.py`` module that has a
33 ``hooks`` dict that maps hooks to callables that implement those hooks.
35 Additionally, you want a LICENSE file that specifies the license and a
36 ``setup.py`` that specifies the metadata for packaging your plugin. A rough
37 file structure could look like this::
40 |- setup.py # plugin project packaging metadata
41 |- README # holds plugin project information
42 |- LICENSE # holds license information
43 |- myplugin/ # plugin package directory
44 |- __init__.py # has hooks dict and code
50 1. All the modules listed as subsections of the ``plugins`` section in
51 the config file are imported. MediaGoblin registers any hooks in
52 the ``hooks`` dict of those modules.
54 2. After all plugin modules are imported, the ``setup`` hook is called
55 allowing plugins to do any set up they need to do.
61 from functools
import wraps
63 from mediagoblin
import mg_globals
66 _log
= logging
.getLogger(__name__
)
69 class PluginManager(object):
70 """Manager for plugin things
74 This is a Borg class--there is one and only one of this class.
77 # list of plugin classes
80 # map of hook names -> list of callables for that hook
83 # list of registered template paths
84 "template_paths": set(),
86 # list of registered routes
91 """This is only useful for testing."""
92 # Why lists don't have a clear is not clear.
96 self
.template_paths
.clear()
99 self
.__dict
__ = self
.__state
101 def register_plugin(self
, plugin
):
102 """Registers a plugin class"""
103 self
.plugins
.append(plugin
)
105 def register_hooks(self
, hook_mapping
):
106 """Takes a hook_mapping and registers all the hooks"""
107 for hook
, callables
in hook_mapping
.items():
108 if isinstance(callables
, (list, tuple)):
109 self
.hooks
.setdefault(hook
, []).extend(list(callables
))
111 # In this case, it's actually a single callable---not a
113 self
.hooks
.setdefault(hook
, []).append(callables
)
115 def get_hook_callables(self
, hook_name
):
116 return self
.hooks
.get(hook_name
, [])
118 def register_template_path(self
, path
):
119 """Registers a template path"""
120 self
.template_paths
.add(path
)
122 def get_template_paths(self
):
123 """Returns a tuple of registered template paths"""
124 return tuple(self
.template_paths
)
126 def register_route(self
, route
):
127 """Registers a single route"""
128 self
.routes
.append(route
)
130 def get_routes(self
):
131 return tuple(self
.routes
)
134 def register_routes(routes
):
135 """Registers one or more routes
137 If your plugin handles requests, then you need to call this with
138 the routes your plugin handles.
140 A "route" is a `routes.Route` object. See `the routes.Route
142 <http://routes.readthedocs.org/en/latest/modules/route.html>`_ for
145 Example passing in a single route:
147 >>> from routes import Route
148 >>> register_routes(Route('about-view', '/about',
149 ... controller=about_view_handler))
151 Example passing in a list of routes:
153 >>> from routes import Route
154 >>> register_routes([
155 ... Route('contact-view', '/contact', controller=contact_handler),
156 ... Route('about-view', '/about', controller=about_handler)
162 Be careful when designing your route urls. If they clash with
163 core urls, then it could result in DISASTER!
165 if isinstance(routes
, (tuple, list)):
167 PluginManager().register_route(route
)
169 PluginManager().register_route(routes
)
172 def register_template_path(path
):
173 """Registers a path for template loading
175 If your plugin has templates, then you need to call this with
176 the absolute path of the root of templates directory.
180 >>> my_plugin_dir = os.path.dirname(__file__)
181 >>> template_dir = os.path.join(my_plugin_dir, 'templates')
182 >>> register_template_path(template_dir)
186 You can only do this in `setup_plugins()`. Doing this after
187 that will have no effect on template loading.
190 PluginManager().register_template_path(path
)
194 """Retrieves the configuration for a specified plugin by key
198 >>> get_config('mediagoblin.plugins.sampleplugin')
200 >>> get_config('myplugin')
202 >>> get_config('flatpages')
203 {'directory': '/srv/mediagoblin/pages', 'nesting': 1}}
207 global_config
= mg_globals
.global_config
208 plugin_section
= global_config
.get('plugins', {})
209 return plugin_section
.get(key
, {})