]> scripts.mit.edu Git - www/ikiwiki.git/blob - doc/todo/Add_a_plugin_to_list_available_pre-processor_commands.mdwn
New patch taking comments into account
[www/ikiwiki.git] / doc / todo / Add_a_plugin_to_list_available_pre-processor_commands.mdwn
1 I've found myself wanting to know which [[plugins]] are switched on so I know which pre-processor commands I can use.  The attached [[patch]] adds a new plugin that generates the list of available plugins. -- [[Will]]
2
3 > Good idea, I do see a few problems:
4
5 > - preprocessor directives do not necessarily have the same name as the
6 >   plugin that contains them (for example, the graphviz plugin adds a graph
7 >   directive). Won't keys `%{IkiWiki::hooks{preprocess}}` work?
8
9 >>> Er, yeah - that's a much better solution. :) -- and done
10
11 > - "listplugins" is a bit misnamed since it only does preprocessor directives.
12
13 >>> Yes.  Initially this was going to list all enabled plugins.  Then when searching
14 >>> for enabled plugins I changed my mind and decided that a list of pre-processor
15 >>> directives was more useful.  I'll fix that too. -- changed to `listpreprocessors`
16
17 > - comment was copied from version plugin and still mentions version :-)
18
19 >>> :-) -- fixed
20
21 > - Seems like [[ikiwiki/formatting]] could benefit from including the
22 >   list.. however, just a list of preprocessor directive names is not
23 >   the most user-friendly thing that could be put on that page. It would
24 >   be nice if there were also a short description and maybe an example of
25 >   use. Seems like the place to include that info would be in the call
26 >   to `hook()`.
27 >   (Maybe adding that is more involved than you want to go though..)
28
29 > --[[Joey]]
30
31 >> Adding a whole new hook for a usage example is more effort than I
32 >> wanted to go to.  I was thinking of either:
33
34 >>> Just to clarify, I meant adding new parameters to the same hook call
35 >>> that registers the plugin. --[[Joey]]
36
37 >>    - Adding a configuration for a wiki directory.  If a matching page is in the
38 >>      specified wiki directory then the plugin name gets turned into a link to that
39 >>      page
40 >>    - Adding configuration for an external URL.  Each plugin name is added as
41 >>       a link to the plugin name appended to the URL.
42
43 >>The first option is easier to navigate and wouldn't produce broken links,
44 >>but requires all the plugin documentation to be local.  The second option
45 >>can link back to the main IkiWiki site, but if you have any non-standard
46 >>plugins then you'll get broken links.
47 >>
48 >>Hrm.  After listing all of that, maybe your idea with the hooks is the better
49 >>solution.  I'll think about it some more. -- [[Will]]
50
51 >>> I've also run into this problem with the websetup plugin, and
52 >>> considered those ideas too. I don't like the external url, because
53 >>> ikiwiki.info may be out of sync with the version of ikiwiki being used.
54 >>> (Or maybe it's gone! :-) The first idea is fine, except for the bloat
55 >>> issue. If turning on listpreprocessors and/or websetup means adding
56 >>> hundreds of pages (and of kilobytes) to your wiki, that could be an
57 >>> incentive to not turn them on..
58 >>>
59 >>> Hmm.. maybe the thing to do is to use _internal pages for the plugins;
60 >>> then the individual pages would not be rendered, and your inlines would
61 >>> still work. Although I don't know how websetup would use it then, and
62 >>> also they would have to be non-internal for ikiwiki's own docwiki. Hmm.
63 >>> Maybe these are two different things; one is a set of pages describing
64 >>> preprocessor directives, and the second a set of pages describing
65 >>> plugins. They're so closely related though it seems a shame to keep
66 >>> them separate..
67 >>> --[[Joey]]
68
69 >>> I started implementing the hook based solution, and decided I didn't like
70 >>> it because there was no nice way to rebuild pages when the preprocessor
71 >>> descriptions changed.  So instead I assumed that the the [[plugins]] pages
72 >>> would be moved into the underlay directory.  This plugin then uses an
73 >>> `inline` directive to include those pages.  You can use the `inline`
74 >>> parameter to decide if you want to include all the descriptions or
75 >>> just the titles.  There is also an option to auto-create default/blank
76 >>> description pages if they are missing (from a template).  As preprocessor
77 >>> commands don't list unless they have a description page, auto-creation
78 >>> is enabled by default.
79 >>>
80 >>>  There are three new templates that are needed.  These are for:
81 >>>
82 >>>  - The auto-created description pages are generated from `preprocessor-description.tmpl`.
83 >>>  - When only pre-processor names are listed, the `listpreprocessors-listonly.tmpl` template is used.
84 >>>  - When pre-processor descriptions are included inline, the `listpreprocessors-inline.tmpl` template is used.
85 >>>
86 >>> -- [[Will]]
87
88 >>>> Just a quick note: pages are only created for pre-processor commands
89 >>>> that exist when the `refresh` hook is called.  This is before the [[shortcuts]] are
90 >>>> processed.  However, the list of available pre-processor commands will include
91 >>>> shortcuts if they have description pages (the list is generated later, after the
92 >>>> shortcuts have been added).  While this was unplanned, it seems a reasonable
93 >>>> tradeoff between including all the large number of shortcuts and including none. -- [[Will]]
94
95 >>>>>> I think that using an inline is elegant! However, I don't understand
96 >>>>>> why it has to create stub description pages? I doubt that, if a
97 >>>>>> directive is missing a page, the stub will be filled out in many
98 >>>>>> wikis. And it adds a lot of complexity, particularly committing a
99 >>>>>> bunch of generated pages to revision control when the user just
100 >>>>>> wants a plugin list seems undesirable.
101 >>>>>>
102 >>>>>> Seems to me it could use the inline for pages that exist, and append
103 >>>>>> to the bottom a generated text for anything that is currently missing.
104 >>>>>> The generated text could even have a page creation link in it if
105 >>>>>> you wanted.
106 >>>>>> --[[Joey]]
107
108 >>>>>>> I kinda agree about the page generation.  I don't like mixing an
109 >>>>>>> inlined and a list though.  Besides which, that ends
110 >>>>>>> up keeping much of complexity of the page generation because
111 >>>>>>> the code still has to detect which pages are missing.  I've added
112 >>>>>>> a patch that uses a list of wikilinks instead.  This way available
113 >>>>>>> pages get linked correctly, and missing pages get normal creation
114 >>>>>>> links.  The old patch is still here if you decide you prefer that. -- [[Will]]
115
116 Note that because there are double square brackets in the source, this might not
117 display quite right.
118
119     #!/usr/bin/perl
120     # Ikiwiki listpreprocessors plugin.
121     package IkiWiki::Plugin::listpreprocessors;
122     
123     use warnings;
124     use strict;
125     use IkiWiki 2.00;
126     
127     sub import { #{{{
128         hook(type => "getsetup", id => "listpreprocessors", call => \&getsetup);
129         hook(type => "preprocess", id => "listpreprocessors", call => \&preprocess);
130         hook(type => "checkconfig", id => "listpreprocessors", call => \&checkconfig);
131     } # }}}
132     
133     sub getsetup () { #{{{
134         return
135                 plugin => {
136                         safe => 1,
137                         rebuild => undef,
138                 },
139                 preprocessor_description_dir => {
140                         type => "string",
141                         description => "The ikiwiki directory that contains plugin descriptions.",
142                         safe => 1,
143                         rebuild => 1,
144                 },
145     } #}}}
146     
147     my @earlyPluginList;
148     
149     sub checkconfig () { #{{{
150     
151         if (!defined $config{plugin_description_dir}) {
152                 $config{plugin_description_dir} = "ikiwiki/plugin/";
153         }
154         
155         @earlyPluginList = sort( keys %{ $IkiWiki::hooks{preprocess} } );
156     } #}}}
157     
158     sub preprocess (@) { #{{{
159         my %params=@_;
160         
161         my @pluginlist;
162         
163         if (! defined $params{generated}) {
164                 @pluginlist = sort( keys %{ $IkiWiki::hooks{preprocess} } );
165         } else {
166                 @pluginlist = @earlyPluginList;
167         }
168         
169         my $result = '<ul class="listpreprocessors">';
170     
171         foreach my $plugin (@pluginlist) {
172                 $result .= '<li class="listpreprocessors">[[' . $config{plugin_description_dir} . $plugin . ']]</li>';
173         }
174     
175         $result .= "</ul>";
176         
177         print $result;
178         
179         return IkiWiki::preprocess($params{page}, $params{destpage}, 
180                 IkiWiki::filter($params{page}, $params{destpage}, $result));
181     } # }}}
182     
183     1
184
185 ----
186
187 Here is the main listpreprocessors plugin. (Note, because this has double
188 square brackets in the source, it isn't quite displaying correctly - look
189 at the page source for details.)  New template files follow:
190
191     #!/usr/bin/perl
192     # Ikiwiki listpreprocessors plugin.
193     package IkiWiki::Plugin::listpreprocessors;
194     
195     use warnings;
196     use strict;
197     use Encode;
198     use IkiWiki 2.00;
199     
200     sub import { #{{{
201         hook(type => "getsetup", id => "listpreprocessors", call => \&getsetup);
202         hook(type => "preprocess", id => "listpreprocessors", call => \&preprocess);
203         hook(type => "refresh", id => "listpreprocessors", call => \&refresh);
204     } # }}}
205     
206     sub getsetup () { #{{{
207         return
208                 plugin => {
209                         safe => 1,
210                         rebuild => undef,
211                 },
212                 preprocessor_description_dir => {
213                         type => "string",
214                         description => "The ikiwiki directory that contains plugin descriptions.",
215                         safe => 1,
216                         rebuild => 1,
217                 },
218                 preprocessor_description_autocreate => {
219                         type => "boolean",
220                         description => "Should pre-processor command descriptions be automatically created from a template.",
221                         safe => 1,
222                         rebuild => 1,
223                 },
224     } #}}}
225     
226     sub gendescription ($$) { #{{{
227         my $plugin=shift;
228         my $page=shift;
229         my $file=$page.".".$config{default_pageext};
230         my $template=template("preprocessor-description.tmpl");
231         $template->param(page => $page, plugin => $plugin);
232         writefile($file, $config{srcdir}, $template->output);
233         if ($config{rcs}) {
234                 IkiWiki::rcs_add($file);
235         }
236     } #}}}
237     
238     sub refresh () { #{{{
239         eval q{use File::Find};
240         error($@) if $@;
241     
242         if (defined $config{preprocessor_description_autocreate} && ! $config{preprocessor_description_autocreate}) {
243                 return; # create pages unless they explicitly ask us not to
244         }
245     
246         if (!defined $config{preprocessor_description_dir}) {
247                 $config{preprocessor_description_dir} = "ikiwiki/plugin/";
248         }
249         
250         my @pluginlist = sort( keys %{ $IkiWiki::hooks{preprocess} } );
251         my %pluginpages;
252     
253         if (@pluginlist) {
254                 my ($plugin,$page);
255                 
256                 foreach $plugin (@pluginlist) {
257                         $pluginpages{$plugin} = $config{preprocessor_description_dir} . $plugin;
258                 }
259     
260                 my %pages;
261                 foreach my $dir ($config{srcdir}, @{$config{underlaydirs}}, $config{underlaydir}) {
262                         find({
263                                 no_chdir => 1,
264                                 wanted => sub {
265                                         $_=decode_utf8($_);
266                                         if (IkiWiki::file_pruned($_, $dir)) {
267                                                 $File::Find::prune=1;
268                                         }
269                                         elsif (! -l $_) {
270                                                 my ($f)=/$config{wiki_file_regexp}/; # untaint
271                                                 return unless defined $f;
272                                                 $f=~s/^\Q$dir\E\/?//;
273                                                 return unless length $f;
274                                                 return if $f =~ /\._([^.]+)$/; # skip internal page
275                                                 if (! -d _) {
276                                                         $pages{pagename($f)}=$f;
277                                                 }
278                                         }
279                                 }
280                         }, $dir);
281                 }
282     
283                 if ($config{rcs}) {
284                         IkiWiki::disable_commit_hook();
285                 }
286                 
287                 my $needcommit = 0;
288                 
289                 while (($plugin,$page) = each %pluginpages) {
290                         if (! exists $pages{$page}) {
291                                 $needcommit = 1;
292                                 gendescription($plugin,$page);
293                         }
294                 }
295                 
296                 if ($config{rcs}) {
297                         if ($needcommit) {
298                                 IkiWiki::rcs_commit_staged(
299                                         gettext("automatic pre-processor description generation"),
300                                         undef, undef);
301                         }
302                         IkiWiki::enable_commit_hook();
303                 }
304         }
305     } #}}}
306     
307     sub preprocess (@) { #{{{
308         my %params=@_;
309         
310         if (!defined $config{plugin_description_dir}) {
311                 $config{plugin_description_dir} = "ikiwiki/plugin/";
312         }
313         
314         my @pluginlist = sort( keys %{ $IkiWiki::hooks{preprocess} } );
315         foreach my $plugin (@pluginlist) {
316                 $plugin = $config{plugin_description_dir} . $plugin;
317         }
318         my $pluginString = join (' or ', @pluginlist);
319         
320         my $result = "[[!inline pages=\"$pluginString\" feeds=\"no\" show=0 sort=\"title\"";
321         
322         if (defined $params{inline}) {
323                 $result .= ' template=\"listpreprocessors-listonly\" archive="yes"';
324         } else {
325                 $result .= ' template=\"listpreprocessors-inline\" archive="no"';
326         }
327         
328         $result .= "]]";
329         
330         return IkiWiki::preprocess($params{page}, $params{destpage}, 
331                 IkiWiki::filter($params{page}, $params{destpage}, $result));
332     } # }}}
333     
334     1
335
336 --------
337
338 This is what I was using for `listpreprocessors-inline.tmpl`:
339
340     <div class="listpreprocessorsinline">
341     
342     <div class="inlineheader">
343     
344     <span class="header">
345     <a href="<TMPL_VAR PAGEURL>"><TMPL_VAR TITLE></a>
346     </span>
347     
348     </div><!--.inlineheader-->
349     
350     <div class="inlinecontent">
351     <TMPL_VAR CONTENT>
352     </div><!--.inlinecontent-->
353     
354     </div><!--.listpreprocessorsinline-->
355
356 --------
357
358 This is what I was using for `listpreprocessors-listonly.tmpl`:
359
360     <p class="listpreprocessors"><a href="<TMPL_VAR PAGEURL>"><TMPL_VAR TITLE></a></p>
361
362 --------
363
364 This is what I was using for `preprocessor-description.tmpl`:
365
366     The <TMPL_VAR plugin> preprocessor command currently has no description.
367     
368     Maybe you should edit this page to add one.