Do not output xml-rpc debugging
[ikiwiki] / plugins / pythondemo
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3 #
4 # pythondemo — demo Python ikiwiki plugin
5 #
6 # Copyright © martin f. krafft <madduck@madduck.net>
7 # Released under the terms of the GNU GPL version 2
8 #
9 __name__ = 'pythondemo'
10 __description__ = 'demo Python ikiwiki plugin'
11 __version__ = '0.1'
12 __author__ = 'martin f. krafft <madduck@madduck.net>'
13 __copyright__ = 'Copyright © ' + __author__
14 __licence__ = 'GPLv2'
15
16 from proxy import IkiWikiProcedureProxy
17
18 import sys
19 def debug(s):
20     sys.stderr.write(__name__ + ':DEBUG:%s\n' % s)
21     sys.stderr.flush()
22
23 proxy = IkiWikiProcedureProxy(__name__, debug_fn=None)
24
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)])
29
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.
39     #
40     debug("hook `getopt' called with arguments %s" % str(args))
41     args = proxy.getargv()
42     if '--demo' in args:
43         args = [i for i in args if i != '--demo']
44     proxy.setargv(args)
45 proxy.hook('getopt', getopt_demo)
46
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)
58
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)
65
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)
76
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
81     # content.
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)
86
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.
97     #
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.)
105     #
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.
112     #
113     kwargs = _arglist_to_dict(args)
114     debug("hook `preprocess' called with arguments %s" % kwargs)
115     del kwargs['preview']
116     del kwargs['page']
117     del kwargs['destpage']
118     ret = 'foobar preprocessor called with arguments:'
119     for i in kwargs.iteritems():
120         ret += ' %s=%s' % i
121     return ret
122 # put [[!foobar ...]] somewhere to try this
123 proxy.hook('preprocess', preprocess_demo, id='foobar')
124
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.
129     #
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)
136
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
141     # %links.
142     #
143     # The function is passed named parameters "page" and "content". Its return
144     # value is ignored.
145     #
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     # TODO: this yields "Can't use string ("1") as an ARRAY ref while "strict
152     # refs" in use at /home/madduck/code/ikiwiki/IkiWiki/Render.pm line 17,
153     # <GEN1> line 476."
154     raise NotImplementedError
155 proxy.hook('scan', scan_demo)
156
157 def htmlize_demo(proxy, *args):
158     # Runs on the raw source of a page and turns it into html. The id
159     # parameter specifies the filename extension that a file must have to be
160     # htmlized using this plugin. This is how you can add support for new and
161     # exciting markup languages to ikiwiki.
162     #
163     # The function is passed named parameters: "page" and "content" and should
164     # return the htmlized content.
165     kwargs = _arglist_to_dict(args)
166     debug("hook `htmlize' called with arguments %s" % kwargs)
167     return kwargs['content']
168 proxy.hook('htmlize', htmlize_demo)
169
170 def pagetemplate_demo(proxy, *args):
171     # Templates are filled out for many different things in ikiwiki, like
172     # generating a page, or part of a blog page, or an rss feed, or a cgi.
173     # This hook allows modifying the variables available on those templates.
174     # The function is passed named parameters. The "page" and "destpage"
175     # parameters are the same as for a preprocess hook. The "template"
176     # parameter is a HTML::Template object that is the template that will be
177     # used to generate the page. The function can manipulate that template
178     # object.
179     #
180     # The most common thing to do is probably to call $template->param() to
181     # add a new custom parameter to the template.
182     # TODO: how do we call $template->param()?
183     kwargs = _arglist_to_dict(args)
184     debug("hook `pagetemplate' called with arguments %s" % kwargs)
185     raise NotImplementedError
186 #proxy.hook('pagetemplate', pagetemplate_demo)
187
188 def templatefile_demo(proxy, *args):
189     # This hook allows plugins to change the template that is used for a page
190     # in the wiki. The hook is passed a "page" parameter, and should return
191     # the name of the template file to use, or undef if it doesn't want to
192     # change the default ("page.tmpl"). Template files are looked for in
193     # /usr/share/ikiwiki/templates by default.
194     #
195     kwargs = _arglist_to_dict(args)
196     debug("hook `templatefile' called with arguments %s" % kwargs)
197     return None #leave the default
198 proxy.hook('templatefile', templatefile_demo)
199
200 def sanitize_demo(proxy, *args):
201     # Use this to implement html sanitization or anything else that needs to
202     # modify the body of a page after it has been fully converted to html.
203     #
204     # The function is passed named parameters: "page" and "content", and
205     # should return the sanitized content.
206     kwargs = _arglist_to_dict(args)
207     debug("hook `sanitize' called with arguments %s" % kwargs)
208     return kwargs['content']
209 proxy.hook('sanitize', sanitize_demo)
210
211 def format_demo(proxy, *args):
212     # The difference between format and sanitize is that sanitize only acts on
213     # the page body, while format can modify the entire html page including
214     # the header and footer inserted by ikiwiki, the html document type, etc.
215     #
216     # The function is passed named parameters: "page" and "content", and
217     # should return the formatted content.
218     kwargs = _arglist_to_dict(args)
219     debug("hook `format' called with arguments %s" % kwargs)
220     return kwargs['content']
221 proxy.hook('format', format_demo)
222
223 def delete_demo(proxy, *args):
224     # Each time a page or pages is removed from the wiki, the referenced
225     # function is called, and passed the names of the source files that were
226     # removed.
227     debug("hook `delete' called with arguments %s" % str(args))
228 proxy.hook('delete', delete_demo)
229
230 def change_demo(proxy, *args):
231     # Each time ikiwiki renders a change or addition (but not deletion) to the
232     # wiki, the referenced function is called, and passed the names of the
233     # source files that were rendered.
234     debug("hook `change' called with arguments %s" % str(args))
235 proxy.hook('change', change_demo)
236
237 def cgi_demo(proxy, *args):
238     # Use this to hook into ikiwiki's cgi script. Each registered cgi hook is
239     # called in turn, and passed a CGI object. The hook should examine the
240     # parameters, and if it will handle this CGI request, output a page
241     # (including the http headers) and terminate the program.
242     #
243     # Note that cgi hooks are called as early as possible, before any ikiwiki
244     # state is loaded, and with no session information.
245     debug("hook `cgi' called with arguments %s" % str(args))
246     raise NotImplementedError
247 #proxy.hook('cgi', cgi_demo)
248
249 def auth_demo(proxy, *args):
250     # This hook can be used to implement a different authentication method
251     # than the standard web form. When a user needs to be authenticated, each
252     # registered auth hook is called in turn, and passed a CGI object and
253     # a session object.
254     #
255     # If the hook is able to authenticate the user, it should set the session
256     # object's "name" parameter to the authenticated user's name. Note that if
257     # the name is set to the name of a user who is not registered, a basic
258     # registration of the user will be automatically performed.
259     #
260     # TODO: how do we set the session parameter?
261     debug("hook `auth' called with arguments %s" % str(args))
262     raise NotImplementedError
263 #proxy.hook('auth', auth_demo)
264
265 def sessioncgi_demo(proxy, *args):
266     # Unlike the cgi hook, which is run as soon as possible, the sessioncgi
267     # hook is only run once a session object is available. It is passed both
268     # a CGI object and a session object. To check if the user is in fact
269     # signed in, you can check if the session object has a "name" parameter
270     # set.
271     debug("hook `sessioncgi' called with arguments %s" % str(args))
272     raise NotImplementedError
273 #proxy.hook('sessioncgi', sessioncgi_demo)
274
275 def canedit_demo(proxy, *args):
276     # This hook can be used to implement arbitrary access methods to control
277     # when a page can be edited using the web interface (commits from revision
278     # control bypass it). When a page is edited, each registered canedit hook
279     # is called in turn, and passed the page name, a CGI object, and a session
280     # object.
281     #
282     # If the hook has no opinion about whether the edit can proceed, return
283     # undef, and the next plugin will be asked to decide. If edit can proceed,
284     # the hook should return "". If the edit is not allowed by this hook, the
285     # hook should return an error message for the user to see, or a function
286     # that can be run to log the user in or perform other action necessary for
287     # them to be able to edit the page.
288     #
289     # This hook should avoid directly redirecting the user to a signin page,
290     # since it's sometimes used to test to see which pages in a set of pages
291     # a user can edit.
292     #
293     # TODO: how do we return a function?
294     debug("hook `canedit' called with arguments %s" % str(args))
295     raise NotImplementedError
296 #proxy.hook('canedit', canedit_demo)
297
298 def editcontent_demo(proxy, *args):
299     # This hook is called when a page is saved (or previewed) using the web
300     # interface. It is passed named parameters: content, page, cgi, and
301     # session. These are, respectively, the new page content as entered by the
302     # user, the page name, a CGI object, and the user's CGI::Session.
303     #
304     # It can modify the content as desired, and should return the content.
305     kwargs = _arglist_to_dict(args)
306     debug("hook `editcontent' called with arguments %s" % kwargs)
307     return kwargs['content']
308 proxy.hook('editcontent', editcontent_demo)
309
310 def formbuilder_setup_demo(proxy, *args):
311     # These hooks allow tapping into the parts of ikiwiki that use
312     # CGI::FormBuilder to generate web forms. These hooks are passed named
313     # parameters: cgi, session, form, and buttons. These are, respectively,
314     # the CGI object, the user's CGI::Session, a CGI::FormBuilder, and
315     # a reference to an array of names of buttons to go on the form.
316     #
317     # Each time a form is set up, the formbuilder_setup hook is called.
318     # Typically the formbuilder_setup hook will check the form's title, and if
319     # it's a form that it needs to modify, will call various methods to
320     # add/remove/change fields, tweak the validation code for the fields, etc.
321     # It will not validate or display the form.
322     #
323     # Just before a form is displayed to the user, the formbuilder hook is
324     # called. It can be used to validate the form, but should not display it.
325     #
326     # TODO: how do we modify the form?
327     kwargs = _arglist_to_dict(args)
328     debug("hook `formbuilder_setup' called with arguments %s" % kwargs)
329     raise NotImplementedError
330     return kwargs['content']
331 #proxy.hook('formbuilder_setup', formbuilder_setup_demo)
332
333 def formbuilder_demo(proxy, *args):
334     # These hooks allow tapping into the parts of ikiwiki that use
335     # CGI::FormBuilder to generate web forms. These hooks are passed named
336     # parameters: cgi, session, form, and buttons. These are, respectively,
337     # the CGI object, the user's CGI::Session, a CGI::FormBuilder, and
338     # a reference to an array of names of buttons to go on the form.
339     #
340     # Each time a form is set up, the formbuilder_setup hook is called.
341     # Typically the formbuilder_setup hook will check the form's title, and if
342     # it's a form that it needs to modify, will call various methods to
343     # add/remove/change fields, tweak the validation code for the fields, etc.
344     # It will not validate or display the form.
345     #
346     # Just before a form is displayed to the user, the formbuilder hook is
347     # called. It can be used to validate the form, but should not display it.
348     # TODO: how do we modify the form?
349     kwargs = _arglist_to_dict(args)
350     debug("hook `formbuilder' called with arguments %s" % kwargs)
351     raise NotImplementedError
352     return kwargs['content']
353 #proxy.hook('formbuilder', formbuilder_demo)
354
355 def savestate_demo(proxy, *args):
356     # This hook is called wheneven ikiwiki normally saves its state, just
357     # before the state is saved. The function can save other state, modify
358     # values before they're saved, etc.
359     #
360     # TODO: how?
361     debug("hook `savestate' called with arguments %s" % str(args))
362     raise NotImplementedError
363 #proxy.hook('savestate', savestate_demo)
364
365 proxy.run()