867306f0daf95620b6b2cdf6a83e80c4821ebfae
[ikiwiki.git] / doc / todo / auto-create_tag_pages_according_to_a_template.mdwn
1 It would be great if I could tell ikiwiki to automatically instantiate pages for each [[tag|/tags]], according to a template, especially when `$tagbase` is set.
2
3 Tags are mainly specific to the object to which they’re stuck. However, I often use them the other way around, too: as concepts. And sometimes I’d like to see all pages related to a given concept (“tagged with a given tag”). The only way to do this with ikiwiki is to instantiate a page for each tag and slap a map on it. This is quite tedious and I’d really love to see Ikiwiki do so by default for all tags.
4
5 Also see: <http://madduck.net/blog/2008.01.06:new-blog/> and <http://users.itk.ppke.hu/~cstamas/code/ikiwiki/autocreatetagpage/>
6
7 [[!tag wishlist plugins/tag patch patch/core]]
8
9 I would love to see this as well. -- dato
10
11 ---
12
13 I have create a patch to [[tag.pm|plugins/tag]] for add the option for auto create tag pages.
14 A new setting is used to enable or disable auto-create tag pages, `tag_autocreate`.
15 The new tag file is created during the preprocess phase. 
16 The new tag file is then complied during the change phase.
17
18 _tag.pm from version 3.01_
19
20
21         --- tag.pm      2009-02-06 10:26:03.000000000 -0700
22         +++ tag_new.pm  2009-02-06 12:17:19.000000000 -0700
23         @@ -14,6 +14,7 @@
24                         hook(type => "preprocess", id => "tag", call => \&preprocess_tag, scan => 1);
25                         hook(type => "preprocess", id => "taglink", call => \&preprocess_taglink, scan => 1);
26                         hook(type => "pagetemplate", id => "tag", call => \&pagetemplate);
27         +       hook(type => "change", id => "tag", call => \&change);
28          }
29          
30          sub getopt () {
31         @@ -36,6 +37,36 @@
32                                                         safe => 1,
33                                                         rebuild => 1,
34                                         },
35         +               tag_autocreate => {
36         +                       type => "boolean",
37         +                       example => 0,
38         +                       description => "Auto-create the new tag pages, uses autotagpage.tmpl ",
39         +                       safe => 1,
40         +                       rebulid => 1,
41         +               },
42         +}
43         +
44         +my $autocreated_page = 0;
45         +
46         +sub gen_tag_page($)    {
47         +       my $tag=shift;
48         +
49         +       my $tag_file=$tag.'.'.$config{default_pageext};
50         +       return if (-f $config{srcdir}.$tag_file);
51         +
52         +       my $template=template("autotagpage.tmpl");
53         +       $template->param(tag => $tag);
54         +       writefile($tag_file, $config{srcdir}, $template->output);
55         +       $autocreated_page = 1;
56         +
57         +       if ($config{rcs}) {
58         +               IkiWiki::disable_commit_hook();
59         +               IkiWiki::rcs_add($tag_file);
60         +               IkiWiki::rcs_commit_staged(
61         +                       gettext("Automatic tag page generation"),
62         +                       undef, undef);
63         +               IkiWiki::enable_commit_hook();
64         +       }
65          }
66          
67          sub tagpage ($) {
68         @@ -47,6 +78,10 @@
69                                         $tag=~y#/#/#s; # squash dups
70                         }
71          
72         +       if (defined $config{tag_autocreate} && $config{tag_autocreate} ) {
73         +               gen_tag_page($tag);
74         +       }
75         +
76                         return $tag;
77          }
78          
79         @@ -125,4 +160,18 @@
80                         }
81          }
82          
83         +sub change(@) {
84         +       return unless($autocreated_page);
85         +       $autocreated_page = 0;
86         +
87         +       # This refresh/saveindex is to complie the autocreated tag pages
88         +       IkiWiki::refresh();
89         +       IkiWiki::saveindex();
90         +
91         +       # This refresh/saveindex is to fix the Tags link
92         +       # With out this additional refresh/saveindex the tag link displays ?tag
93         +       IkiWiki::refresh();
94         +       IkiWiki::saveindex();
95         +}
96         +
97
98
99 This uses a [[template|wikitemplates]] called `autotagpage.tmpl`, here is my template file:
100
101     \[[!inline pages="link(<TMPL_VAR TAG>)" archive="yes"]]
102
103
104 A quirk I have not figured out is during the `sub change`, see my comments in the code.
105 I am not sure if that is the best way to handle it.
106
107 [[!tag patch]]
108 -- Jeremy Schultz <jeremy.schultz@uleth.ca>
109
110 No, this doesn't help:
111
112         +       # This refresh/saveindex is to fix the Tags link
113         +       # With out this additional refresh/saveindex the tag link displays ?tag
114         +       IkiWiki::refresh();
115         +       IkiWiki::saveindex();
116
117 On the second extra pass, it doesn't notice that it has to update the "?"-link. If I run ikiwiki once more, it is updated. I don't know yet how this should be fixed, because I don't know the internals of ikiwiki well enough. Something inhibits detecting the need to update in refresh() in Render.pm; perhaps, this condition: 
118
119                 if (! $pagemtime{$page}) {
120                    ...
121                                 push @add, $file;
122                    ...
123                 }
124
125 is not satisfied for the newly created tag page. I shall put debug msgs into Render.pm to find out better how it works. --Ivan Z.
126
127 ---
128
129 I've made another attempt at fixing this
130
131 The current progress can be found at my [git repository][gitweb] on branch
132 `autotag`:
133
134         git://git.liegesta.at/git/ikiwiki
135
136 [gitweb]: http://git.liegesta.at/?p=ikiwiki.git;a=shortlog;h=refs/heads/autotag (gitweb for branch autotag)
137
138 It's not entirely finished yet, but already quite usable. Testing and comments
139 on code quality, implementation details, as well as other patches would be
140 appreciated.
141
142 Here's what it does right now:
143
144 * enabled by setting `tag_autocreate=1` in the configuration.
145 * Tag pages will be created in `tagbase` from the template `autotag.tmpl`.
146 * Will correctly render all links, and dependencies. Well, AFAIK.
147 * When a tag page is deleted it will automatically recreated from template. (I
148 consider this a feature, not a bug)
149 * Requires a rebuild on first use.
150 * Adds a function `add_autofile()` to the plugin API, to do all this.
151
152 Todo/Bugs:
153
154 * Will still create a page even if there's a page other than `$tag` under
155 `tagbase` satisfying the tag link.
156 * Call from `IkiWiki.pm` to `Render.pm`, which adds a module dependency in the
157 wrong direction.
158 * Add files to RCS.
159 * Unit tests.
160 * Proper documentation.
161
162 --[[David_Riebenbauer]]
163
164 > Starting review of this. Some of your commits are to very delicate,
165 > optimised, and security-sensitive ground, so I have to look at them very
166 > carefully. --[[Joey]]
167
168 >> First of, sorry that it took me so damn long to answer. I didn't lose
169 >> interest but it took a while for me to find the time and motivation
170 >> to address you suggestions. --[[David_Riebenbauer]]
171
172 > * In the refactoring in [f3abeac919c4736429bd3362af6edf51ede8e7fe][],
173 >   you introduced at least 2 bugs, one a possible security hole.
174 >   Now one part of the code tests `if ($file)` and the other
175 >   caller tests `if ($f)`. These two tests both tested `if (! defined $f)`
176 >   before. Notice that the variable needs to be the untainted variable
177 >   for both. Also notice that `if ($f)` fails if `$f` contains `0`,
178 >   which is a very common perl gotcha.
179 > * Your refactored code changes `-l $_ || -d _` to `-l $file || -d $file`.
180 >   The latter makes one more stat system call; note the use of a
181 >   bare `_` in the first to make perl reuse the stat buffer.
182 > * (As a matter of style, could you put a space after the commas in your
183 >   perl?)
184
185 >> The first two points should be addressed in
186 >> [da5d29f95f6e693e8c14be1b896cf25cf4fdb3c0][]. And sure, I can add the
187 >> spaces. --[[David_Riebenbauer]]
188
189 > I'd like to cherry-pick the above commit, once it's in shape, before
190 > looking at the rest in detail. So just a few other things that stood out.
191
192 > * Commit [4af4d26582f0c2b915d7102fb4a604b176385748][] seems unnecessary.
193 >   `srcfile($file, 1)` already is documented to return undef if the
194 >   file does not exist. (But without the second parameter, it throws
195 >   an error.)
196
197 >> You're right. I must have been some confused by some other promplem I
198 >> introduced then. Reverted. --[[David_Riebenbauer]]
199
200 > * Commit [f58f3e1bec41ccf9316f37b014ce0b373c8e49e1][] adds a line
201 >   that is intented by a space, not a tab.
202
203 >> Sorry, That one was reverted anyway. --[[David_Riebenbauer]]
204
205 > * Commit [f58f3e1bec41ccf9316f37b014ce0b373c8e49e1][] says that auto-added
206 >   files will be recreated if the user deletes them. That seems bad.
207 >   `autoindex` goes to some trouble to not recreate deleted files.
208
209 >> I reverted the commit and addressed the issue in
210 >> [a358d74bef51dae31332ff27e897fe04834571e6][] and
211 >> [981400177d68a279f485727be3f013e68f0bf691][].
212  --[[David_Riebenbauer]]
213
214 >>> This doesn't seem to have all the refinements that autoindex has:
215 >>>
216 >>> * `autoindex` attaches the record of deletions to the `index` page, which
217 >>>   is (nearly) guaranteed to exist; this one attaches the record of
218 >>>   deletions to the deleted page's page state. Won't that tend to result
219 >>>   in losing the record along with the deleted page?
220
221 >>>> This is probably on of the harder things to do, 'cause there are (most of the
222 >>>> time) several pages that are responsible for the creation of a single tag page.
223 >>>> Of course I could attach the info to all of them.
224
225 >>>> With current behaviour I think the information in `%pagestate` is kept around
226 >>>> regardless whether the corresponding page exists or not.
227 >>>> --[[David_Riebenbauer]]
228
229 >>>>> Sorry, I'll try to be clearer: `autoindex` hard-codes that the [[index]] page
230 >>>>> of the entire wiki is the one responsible for storing the page state. That
231 >>>>> page isn't responsible for the creation of the tag page, it's just an
232 >>>>> arbitrary page that's (more or less) guaranteed to exist. --[[smcv]]
233
234 >>>>> I don't like that [[plugins/autoindex]] has to do that,
235 >>>>> but `%pagestate` values are only stored for pages that exist,
236 >>>>> so it was necessary. (Another way to look at this is that
237 >>>>> `%pagestate` is not the ideal data structure.) --[[Joey]]
238
239 >>>>>> Aha! Having looked at [[plugins/write]] again, it turns out that what this
240 >>>>>> feature should really use is `%wikistate`, I think? :-) --[[smcv]]
241
242 >>>>>>> Ah, indeed, that came after I wrote autoindex. I've fixed autoindex to
243 >>>>>>> use it. --[[Joey]]
244
245 >>>>> Ok, now I know what you mean. --[[David_Riebenbauer]]
246
247 >>> * `autoindex` forgets that a page was deleted when that page is
248 >>>   re-created
249
250 >>>> Yes, I forgot about that and that is a bug. I'll fix that.
251 >>>> --[[David_Riebenbauer]]
252
253 >>> * `autoindex` forgets that a page was deleted when it's no longer needed
254 >>>   anyway (this may be harder for `autotag`?)
255
256 >>>> I don't think so. AFAIK ikiwiki can detect whether there are taglinks to a page
257 >>>> anyway, so it should be quite easy. I'll try to implement that too.
258 >>>> --[[David_Riebenbauer]]
259
260 >>> It'd probably be an interesting test of the core change to port
261 >>> `autoindex` to use it? (Adding the file to the RCS would be
262 >>> necessary to get parity with `autoindex`.) --[[smcv]]
263
264 >>>> Good suggestion. Adding the files to RCS is on my todo list anyway.
265 >>>> --[[David_Riebenbauer]]
266
267 > Regarding the call from `IkiWiki.pm` to `Render.pm`, wouldn't this be
268 > quite easy to solve by moving `verify_src_file` to IkiWiki.pm? --[[smcv]]
269
270 >> True. I'll do that. --[[David_Riebenbauer]]
271
272 >> I've pushed an autotag branch of my own, which refactors
273 >> things a bit. It is untested so far though. --[[Joey]]
274 >>
275 >> * `verify_src_file` only called from Render.pm
276 >> * Gets rid of `%del_files`.
277 >> * Uses `%wikistate`.
278
279 [f3abeac919c4736429bd3362af6edf51ede8e7fe]: http://git.liegesta.at/?p=ikiwiki.git;a=commitdiff;h=f3abeac919c4736429bd3362af6edf51ede8e7fe (commitdiff for f3abeac919c4736429bd3362af6edf51ede8e7fe)
280 [4af4d26582f0c2b915d7102fb4a604b176385748]: http://git.liegesta.at/?p=ikiwiki.git;a=commitdiff;h=4af4d26582f0c2b915d7102fb4a604b176385748 (commitdiff for 4af4d26582f0c2b915d7102fb4a604b176385748)
281 [f58f3e1bec41ccf9316f37b014ce0b373c8e49e1]: http://git.liegesta.at/?p=ikiwiki.git;a=commitdiff;h=f58f3e1bec41ccf9316f37b014ce0b373c8e49e1 (commitdiff for f58f3e1bec41ccf9316f37b014ce0b373c8e49e1)
282 [da5d29f95f6e693e8c14be1b896cf25cf4fdb3c0]: http://git.liegesta.at/?p=ikiwiki.git;a=commitdiff;h=da5d29f95f6e693e8c14be1b896cf25cf4fdb3c0 (commitdiff for da5d29f95f6e693e8c14be1b896cf25cf4fdb3c0)
283 [a358d74bef51dae31332ff27e897fe04834571e6]: http://git.liegesta.at/?p=ikiwiki.git;a=commitdiff;h=a358d74bef51dae31332ff27e897fe04834571e6 (commitdiff for a358d74bef51dae31332ff27e897fe04834571e6)
284 [981400177d68a279f485727be3f013e68f0bf691]: http://git.liegesta.at/?p=ikiwiki.git;a=commitdiff;h=981400177d68a279f485727be3f013e68f0bf691 (commitdiff for 981400177d68a279f485727be3f013e68f0bf691)