Additional hook tips! Documentation on how to modify a wtforms form.
[mediagoblin.git] / docs / source / pluginwriter / api.rst
1 .. MediaGoblin Documentation
2
3 Written in 2013 by MediaGoblin contributors
4
5 To the extent possible under law, the author(s) have dedicated all
6 copyright and related and neighboring rights to this software to
7 the public domain worldwide. This software is distributed without
8 any warranty.
9
10 You should have received a copy of the CC0 Public Domain
11 Dedication along with this software. If not, see
12 <http://creativecommons.org/publicdomain/zero/1.0/>.
13
14 .. _plugin-api-chapter:
15
16 ==========
17 Plugin API
18 ==========
19
20 This documents the general plugin API.
21
22 Please note, at this point OUR PLUGIN HOOKS MAY AND WILL CHANGE.
23 Authors are encouraged to develop plugins and work with the
24 MediaGoblin community to keep them up to date, but this API will be a
25 moving target for a few releases.
26
27 Please check the :ref:`release-notes` for updates!
28
29
30 How are hooks added? Where do I find them?
31 -------------------------------------------
32
33 Much of this document talks about hooks, both as in terms of regular
34 hooks and template hooks. But where do they come from, and how can
35 you find a list of them?
36
37 For the moment, the best way to find available hooks is to check the
38 source code itself. (Yes, we should start a more official hook
39 listing with descriptions soon.) But many hooks you may need do not
40 exist yet: what to do then?
41
42 The plan at present is that we are adding hooks as people need them,
43 with community discussion. If you find that you need a hook and
44 MediaGoblin at present doesn't provide it at present, please
45 `http://mediagoblin.org/pages/join.html <talk to us>`_! We'll
46 evaluate what to do from there.
47
48
49 :mod:`pluginapi` Module
50 -----------------------
51
52 .. automodule:: mediagoblin.tools.pluginapi
53 :members: get_config, register_routes, register_template_path,
54 register_template_hooks, get_hook_templates,
55 hook_handle, hook_runall, hook_transform
56
57 Configuration
58 -------------
59
60 Your plugin may define its own configuration defaults.
61
62 Simply add to the directory of your plugin a config_spec.ini file. An
63 example might look like::
64
65 [plugin_spec]
66 some_string = string(default="blork")
67 some_int = integer(default=50)
68
69 This means that when people enable your plugin in their config you'll
70 be able to provide defaults as well as type validation.
71
72
73 Context Hooks
74 -------------
75
76 View specific hooks
77 +++++++++++++++++++
78
79 You can hook up to almost any template called by any specific view
80 fairly easily. As long as the view directly or indirectly uses the
81 method ``render_to_response`` you can access the context via a hook
82 that has a key in the format of the tuple::
83
84 (view_symbolic_name, view_template_path)
85
86 Where the "view symbolic name" is the same parameter used in
87 ``request.urlgen()`` to look up the view. So say we're wanting to add
88 something to the context of the user's homepage. We look in
89 mediagoblin/user_pages/routing.py and see::
90
91 add_route('mediagoblin.user_pages.user_home',
92 '/u/<string:user>/',
93 'mediagoblin.user_pages.views:user_home')
94
95 Aha! That means that the name is ``mediagoblin.user_pages.user_home``.
96 Okay, so then we look at the view at the
97 ``mediagoblin.user_pages.user_home`` method::
98
99 @uses_pagination
100 def user_home(request, page):
101 # [...] whole bunch of stuff here
102 return render_to_response(
103 request,
104 'mediagoblin/user_pages/user.html',
105 {'user': user,
106 'user_gallery_url': user_gallery_url,
107 'media_entries': media_entries,
108 'pagination': pagination})
109
110 Nice! So the template appears to be
111 ``mediagoblin/user_pages/user.html``. Cool, that means that the key
112 is::
113
114 ("mediagoblin.user_pages.user_home",
115 "mediagoblin/user_pages/user.html")
116
117 The context hook uses ``hook_transform()`` so that means that if we're
118 hooking into it, our hook will both accept one argument, ``context``,
119 and should return that modified object, like so::
120
121 def add_to_user_home_context(context):
122 context['foo'] = 'bar'
123 return context
124
125 hooks = {
126 ("mediagoblin.user_pages.user_home",
127 "mediagoblin/user_pages/user.html"): add_to_user_home_context}
128
129
130 Global context hooks
131 ++++++++++++++++++++
132
133 If you need to add something to the context of *every* view, it is not
134 hard; there are two hooks hook that also uses hook_transform (like the
135 above) but make available what you are providing to *every* view.
136
137 Note that there is a slight, but critical, difference between the two.
138
139 The most general one is the ``'template_global_context'`` hook. This
140 one is run only once, and is read into the global context... all views
141 will get access to what are in this dict.
142
143 The slightly more expensive but more powerful one is
144 ``'template_context_prerender'``. This one is not added to the global
145 context... it is added to the actual context of each individual
146 template render right before it is run! Because of this you also can
147 do some powerful and crazy things, such as checking the request object
148 or other parts of the context before passing them on.
149
150
151 Adding static resources
152 -----------------------
153
154 It's possible to add static resources for your plugin. Say your
155 plugin needs some special javascript and images... how to provide
156 them? Then how to access them? MediaGoblin has a way!
157
158
159 Attaching to the hook
160 +++++++++++++++++++++
161
162 First, you need to register your plugin's resources with the hook.
163 This is pretty easy actually: you just need to provide a function that
164 passes back a PluginStatic object.
165
166 .. autoclass:: mediagoblin.tools.staticdirect.PluginStatic
167
168
169 Running plugin assetlink
170 ++++++++++++++++++++++++
171
172 In order for your plugin assets to be properly served by MediaGoblin,
173 your plugin's asset directory needs to be symlinked into the directory
174 that plugin assets are served from. To set this up, run::
175
176 ./bin/gmg assetlink
177
178
179 Using staticdirect
180 ++++++++++++++++++
181
182 Once you have this, you will want to be able to of course link to your
183 assets! MediaGoblin has a "staticdirect" tool; you want to use this
184 like so in your templates::
185
186 staticdirect("css/monkeys.css", "mystaticname")
187
188 Replace "mystaticname" with the name you passed to PluginStatic. The
189 staticdirect method is, for convenience, attached to the request
190 object, so you can access this in your templates like:
191
192 .. code-block:: html
193
194 <img alt="A funny bunny"
195 src="{{ request.staticdirect('images/funnybunny.png', 'mystaticname') }}" />
196
197
198 Additional hook tips
199 --------------------
200
201 This section aims to explain some tips in regards to adding hooks to
202 the MediaGoblin repository.
203
204 WTForms hooks
205 =============
206
207 We haven't totally settled on a way to tranform wtforms form objects,
208 but here's one way. In your view::
209
210 from mediagoblin.foo.forms import SomeForm
211
212 def some_view(request)
213 form_class = hook_transform('some_form_transform', SomeForm)
214 form = form_class(request.form)
215
216 Then to hook into this form, do something in your plugin like::
217
218 import wtforms
219
220 class SomeFormAdditions(wtforms.Form):
221 new_datefield = wtforms.DateField()
222
223 def transform_some_form(orig_form):
224 class ModifiedForm(orig_form, SomeFormAdditions)
225 return ModifiedForm
226
227 hooks = {
228 'some_form_transform': transform_some_form}