2 # -*- coding: utf-8 -*-
4 # pythondemo — demo Python ikiwiki plugin
6 # Copyright © martin f. krafft <madduck@madduck.net>
7 # Released under the terms of the GNU GPL version 2
9 __name__
= 'pythondemo'
10 __description__
= 'demo Python ikiwiki plugin'
12 __author__
= 'martin f. krafft <madduck@madduck.net>'
13 __copyright__
= 'Copyright © ' + __author__
16 from proxy
import IkiWikiProcedureProxy
20 sys
.stderr
.write(__name__
+ ':DEBUG:%s\n' % s
)
23 proxy
= IkiWikiProcedureProxy(__name__
, debug_fn
=None)
25 def _arglist_to_dict(args
):
26 if len(args
) % 2 != 0:
27 raise ValueError, 'odd number of arguments, cannot convert to dict'
28 return dict([args
[i
:i
+2] for i
in xrange(0, len(args
), 2)])
30 def getopt_demo(proxy
, *args
):
31 # This allows for plugins to perform their own processing of command-line
32 # options and so add options to the ikiwiki command line. It's called
33 # during command line processing, with @ARGV full of any options that
34 # ikiwiki was not able to process on its own. The function should process
35 # any options it can, removing them from @ARGV, and probably recording the
36 # configuration settings in %config. It should take care not to abort if
37 # it sees an option it cannot process, and should just skip over those
38 # options and leave them in @ARGV.
40 debug("hook `getopt' called with arguments %s" % str(args
))
41 args
= proxy
.getargv()
43 args
= [i
for i
in args
if i
!= '--demo']
45 proxy
.hook('getopt', getopt_demo
)
47 def checkconfig_demo(proxy
, *args
):
48 # This is useful if the plugin needs to check for or modify ikiwiki's
49 # configuration. It's called early in the startup process. The function is
50 # passed no values. It's ok for the function to call error() if something
51 # isn't configured right.
52 debug("hook `checkconfig' called with arguments %s" % str(args
))
53 # check that --url has been set
54 url
= proxy
.getvar('config', 'url')
55 if url
is None or len(url
) == 0:
56 proxy
.error('--url has not been set')
57 proxy
.hook('checkconfig', checkconfig_demo
)
59 def refresh_demo(proxy
, *args
):
60 # This hook is called just before ikiwiki scans the wiki for changed
61 # files. It's useful for plugins that need to create or modify a source
62 # page. The function is passed no values.
63 debug("hook `refresh' called with arguments %s" % str(args
))
64 proxy
.hook('refresh', refresh_demo
)
66 def needsbuild_demo(proxy
, *args
):
67 # This allows a plugin to manipulate the list of files that need to be
68 # built when the wiki is refreshed. The function is passed a reference to
69 # an array of pages that will be rebuilt, and can modify the array, either
70 # adding or removing files from it.
71 # TODO: how do we modify the array? Joey sees no solution...
72 # we could just return the array and expect ikiwiki to use that...
73 debug("hook `needsbuild' called with arguments %s" % str(args
))
74 raise NotImplementedError
75 #proxy.hook('needsbuild', needsbuild_demo)
77 def filter_demo(proxy
, *args
):
78 # Runs on the raw source of a page, before anything else touches it, and
79 # can make arbitrary changes. The function is passed named parameters
80 # "page", "destpage", and "content". It should return the filtered
82 kwargs
= _arglist_to_dict(args
)
83 debug("hook `filter' called with arguments %s" % kwargs
);
84 return kwargs
['content']
85 proxy
.hook('filter', filter_demo
)
87 def preprocess_demo(proxy
, *args
):
88 # Each time the directive is processed, the referenced function
89 # (preprocess in the example above) is called, and is passed named
90 # parameters. A "page" parameter gives the name of the page that embedded
91 # the preprocessor directive, while a "destpage" parameter gives the name
92 # of the page the content is going to (different for inlined pages), and
93 # a "preview" parameter is set to a true value if the page is being
94 # previewed. All parameters included in the directive are included as
95 # named parameters as well. Whatever the function returns goes onto the
96 # page in place of the directive.
98 # An optional "scan" parameter, if set to a true value, makes the hook be
99 # called during the preliminary scan that ikiwiki makes of updated pages,
100 # before begining to render pages. This parameter should be set to true if
101 # the hook modifies data in %links. Note that doing so will make the hook
102 # be run twice per page build, so avoid doing it for expensive hooks. (As
103 # an optimisation, if your preprocessor hook is called in a void contets,
104 # you can assume it's being run in scan mode.)
106 # Note that if the htmlscrubber is enabled, html in PreProcessorDirective
107 # output is sanitised, which may limit what your plugin can do. Also, the
108 # rest of the page content is not in html format at preprocessor time.
109 # Text output by a preprocessor directive will be linkified and passed
110 # through markdown (or whatever engine is used to htmlize the page) along
111 # with the rest of the page.
113 kwargs
= _arglist_to_dict(args
)
114 debug("hook `preprocess' called with arguments %s" % kwargs
)
115 del kwargs
['preview']
117 del kwargs
['destpage']
118 ret
= 'foobar preprocessor called with arguments:'
119 for i
in kwargs
.iteritems():
122 # put [[!foobar ...]] somewhere to try this
123 proxy
.hook('preprocess', preprocess_demo
, id='foobar')
125 def linkify_demo(proxy
, *args
):
126 # This hook is called to convert WikiLinks on the page into html links.
127 # The function is passed named parameters "page", "destpage", and
128 # "content". It should return the linkified content.
130 # Plugins that implement linkify must also implement a scan hook, that
131 # scans for the links on the page and adds them to %links.
132 kwargs
= _arglist_to_dict(args
)
133 debug("hook `linkify' called with arguments %s" % kwargs
)
134 return kwargs
['content']
135 proxy
.hook('linkify', linkify_demo
)
137 def scan_demo(proxy
, *args
):
138 # This hook is called early in the process of building the wiki, and is
139 # used as a first pass scan of the page, to collect metadata about the
140 # page. It's mostly used to scan the page for WikiLinks, and add them to
143 # The function is passed named parameters "page" and "content". Its return
146 kwargs
= _arglist_to_dict(args
)
147 debug("hook `scan' called with arguments %s" % kwargs
)
148 links
= proxy
.getvar('links', kwargs
['page'])
149 debug("links for page `%s' are: %s" % (kwargs
['page'], links
))
150 proxy
.setvar('links', kwargs
['page'], links
)
151 proxy
.hook('scan', scan_demo
)
153 def htmlize_demo(proxy
, *args
):
154 # Runs on the raw source of a page and turns it into html. The id
155 # parameter specifies the filename extension that a file must have to be
156 # htmlized using this plugin. This is how you can add support for new and
157 # exciting markup languages to ikiwiki.
159 # The function is passed named parameters: "page" and "content" and should
160 # return the htmlized content.
161 kwargs
= _arglist_to_dict(args
)
162 debug("hook `htmlize' called with arguments %s" % kwargs
)
163 return kwargs
['content']
164 proxy
.hook('htmlize', htmlize_demo
)
166 def pagetemplate_demo(proxy
, *args
):
167 # Templates are filled out for many different things in ikiwiki, like
168 # generating a page, or part of a blog page, or an rss feed, or a cgi.
169 # This hook allows modifying the variables available on those templates.
170 # The function is passed named parameters. The "page" and "destpage"
171 # parameters are the same as for a preprocess hook. The "template"
172 # parameter is a HTML::Template object that is the template that will be
173 # used to generate the page. The function can manipulate that template
176 # The most common thing to do is probably to call $template->param() to
177 # add a new custom parameter to the template.
178 # TODO: how do we call $template->param()?
179 kwargs
= _arglist_to_dict(args
)
180 debug("hook `pagetemplate' called with arguments %s" % kwargs
)
181 raise NotImplementedError
182 #proxy.hook('pagetemplate', pagetemplate_demo)
184 def templatefile_demo(proxy
, *args
):
185 # This hook allows plugins to change the template that is used for a page
186 # in the wiki. The hook is passed a "page" parameter, and should return
187 # the name of the template file to use, or undef if it doesn't want to
188 # change the default ("page.tmpl"). Template files are looked for in
189 # /usr/share/ikiwiki/templates by default.
191 kwargs
= _arglist_to_dict(args
)
192 debug("hook `templatefile' called with arguments %s" % kwargs
)
193 return None #leave the default
194 proxy
.hook('templatefile', templatefile_demo
)
196 def sanitize_demo(proxy
, *args
):
197 # Use this to implement html sanitization or anything else that needs to
198 # modify the body of a page after it has been fully converted to html.
200 # The function is passed named parameters: "page" and "content", and
201 # should return the sanitized content.
202 kwargs
= _arglist_to_dict(args
)
203 debug("hook `sanitize' called with arguments %s" % kwargs
)
204 return kwargs
['content']
205 proxy
.hook('sanitize', sanitize_demo
)
207 def format_demo(proxy
, *args
):
208 # The difference between format and sanitize is that sanitize only acts on
209 # the page body, while format can modify the entire html page including
210 # the header and footer inserted by ikiwiki, the html document type, etc.
212 # The function is passed named parameters: "page" and "content", and
213 # should return the formatted content.
214 kwargs
= _arglist_to_dict(args
)
215 debug("hook `format' called with arguments %s" % kwargs
)
216 return kwargs
['content']
217 proxy
.hook('format', format_demo
)
219 def delete_demo(proxy
, *args
):
220 # Each time a page or pages is removed from the wiki, the referenced
221 # function is called, and passed the names of the source files that were
223 debug("hook `delete' called with arguments %s" % str(args
))
224 proxy
.hook('delete', delete_demo
)
226 def change_demo(proxy
, *args
):
227 # Each time ikiwiki renders a change or addition (but not deletion) to the
228 # wiki, the referenced function is called, and passed the names of the
229 # source files that were rendered.
230 debug("hook `change' called with arguments %s" % str(args
))
231 proxy
.hook('change', change_demo
)
233 def cgi_demo(proxy
, *args
):
234 # Use this to hook into ikiwiki's cgi script. Each registered cgi hook is
235 # called in turn, and passed a CGI object. The hook should examine the
236 # parameters, and if it will handle this CGI request, output a page
237 # (including the http headers) and terminate the program.
239 # Note that cgi hooks are called as early as possible, before any ikiwiki
240 # state is loaded, and with no session information.
241 debug("hook `cgi' called with arguments %s" % str(args
))
242 raise NotImplementedError
243 #proxy.hook('cgi', cgi_demo)
245 def auth_demo(proxy
, *args
):
246 # This hook can be used to implement a different authentication method
247 # than the standard web form. When a user needs to be authenticated, each
248 # registered auth hook is called in turn, and passed a CGI object and
251 # If the hook is able to authenticate the user, it should set the session
252 # object's "name" parameter to the authenticated user's name. Note that if
253 # the name is set to the name of a user who is not registered, a basic
254 # registration of the user will be automatically performed.
256 # TODO: how do we set the session parameter?
257 debug("hook `auth' called with arguments %s" % str(args
))
258 raise NotImplementedError
259 #proxy.hook('auth', auth_demo)
261 def sessioncgi_demo(proxy
, *args
):
262 # Unlike the cgi hook, which is run as soon as possible, the sessioncgi
263 # hook is only run once a session object is available. It is passed both
264 # a CGI object and a session object. To check if the user is in fact
265 # signed in, you can check if the session object has a "name" parameter
267 debug("hook `sessioncgi' called with arguments %s" % str(args
))
268 raise NotImplementedError
269 #proxy.hook('sessioncgi', sessioncgi_demo)
271 def canedit_demo(proxy
, *args
):
272 # This hook can be used to implement arbitrary access methods to control
273 # when a page can be edited using the web interface (commits from revision
274 # control bypass it). When a page is edited, each registered canedit hook
275 # is called in turn, and passed the page name, a CGI object, and a session
278 # If the hook has no opinion about whether the edit can proceed, return
279 # undef, and the next plugin will be asked to decide. If edit can proceed,
280 # the hook should return "". If the edit is not allowed by this hook, the
281 # hook should return an error message for the user to see, or a function
282 # that can be run to log the user in or perform other action necessary for
283 # them to be able to edit the page.
285 # This hook should avoid directly redirecting the user to a signin page,
286 # since it's sometimes used to test to see which pages in a set of pages
289 # TODO: how do we return a function?
290 debug("hook `canedit' called with arguments %s" % str(args
))
291 raise NotImplementedError
292 #proxy.hook('canedit', canedit_demo)
294 def editcontent_demo(proxy
, *args
):
295 # This hook is called when a page is saved (or previewed) using the web
296 # interface. It is passed named parameters: content, page, cgi, and
297 # session. These are, respectively, the new page content as entered by the
298 # user, the page name, a CGI object, and the user's CGI::Session.
300 # It can modify the content as desired, and should return the content.
301 kwargs
= _arglist_to_dict(args
)
302 debug("hook `editcontent' called with arguments %s" % kwargs
)
303 return kwargs
['content']
304 proxy
.hook('editcontent', editcontent_demo
)
306 def formbuilder_setup_demo(proxy
, *args
):
307 # These hooks allow tapping into the parts of ikiwiki that use
308 # CGI::FormBuilder to generate web forms. These hooks are passed named
309 # parameters: cgi, session, form, and buttons. These are, respectively,
310 # the CGI object, the user's CGI::Session, a CGI::FormBuilder, and
311 # a reference to an array of names of buttons to go on the form.
313 # Each time a form is set up, the formbuilder_setup hook is called.
314 # Typically the formbuilder_setup hook will check the form's title, and if
315 # it's a form that it needs to modify, will call various methods to
316 # add/remove/change fields, tweak the validation code for the fields, etc.
317 # It will not validate or display the form.
319 # Just before a form is displayed to the user, the formbuilder hook is
320 # called. It can be used to validate the form, but should not display it.
322 # TODO: how do we modify the form?
323 kwargs
= _arglist_to_dict(args
)
324 debug("hook `formbuilder_setup' called with arguments %s" % kwargs
)
325 raise NotImplementedError
326 return kwargs
['content']
327 #proxy.hook('formbuilder_setup', formbuilder_setup_demo)
329 def formbuilder_demo(proxy
, *args
):
330 # These hooks allow tapping into the parts of ikiwiki that use
331 # CGI::FormBuilder to generate web forms. These hooks are passed named
332 # parameters: cgi, session, form, and buttons. These are, respectively,
333 # the CGI object, the user's CGI::Session, a CGI::FormBuilder, and
334 # a reference to an array of names of buttons to go on the form.
336 # Each time a form is set up, the formbuilder_setup hook is called.
337 # Typically the formbuilder_setup hook will check the form's title, and if
338 # it's a form that it needs to modify, will call various methods to
339 # add/remove/change fields, tweak the validation code for the fields, etc.
340 # It will not validate or display the form.
342 # Just before a form is displayed to the user, the formbuilder hook is
343 # called. It can be used to validate the form, but should not display it.
344 # TODO: how do we modify the form?
345 kwargs
= _arglist_to_dict(args
)
346 debug("hook `formbuilder' called with arguments %s" % kwargs
)
347 raise NotImplementedError
348 return kwargs
['content']
349 #proxy.hook('formbuilder', formbuilder_demo)
351 def savestate_demo(proxy
, *args
):
352 # This hook is called wheneven ikiwiki normally saves its state, just
353 # before the state is saved. The function can save other state, modify
354 # values before they're saved, etc.
357 debug("hook `savestate' called with arguments %s" % str(args
))
358 raise NotImplementedError
359 #proxy.hook('savestate', savestate_demo)