Whitespace cleanup
[ikiwiki] / IkiWiki / Plugin / tag.pm
1 #!/usr/bin/perl
2 # Ikiwiki tag plugin.
3 package IkiWiki::Plugin::tag;
4
5 use warnings;
6 use strict;
7 use IkiWiki 3.00;
8
9 sub import {
10         hook(type => "getopt", id => "tag", call => \&getopt);
11         hook(type => "getsetup", id => "tag", call => \&getsetup);
12         hook(type => "checkconfig", id => "tag", call => \&checkconfig);
13         hook(type => "preprocess", id => "tag", call => \&preprocess_tag, scan => 1);
14         hook(type => "preprocess", id => "taglink", call => \&preprocess_taglink, scan => 1);
15         hook(type => "pagetemplate", id => "tag", call => \&pagetemplate);
16 }
17
18 sub getopt () {
19         eval q{use Getopt::Long};
20         error($@) if $@;
21         Getopt::Long::Configure('pass_through');
22         GetOptions("tagbase=s" => \$config{tagbase});
23 }
24
25 sub getsetup () {
26         return
27                 plugin => {
28                         safe => 1,
29                         rebuild => undef,
30                 },
31                 tagbase => {
32                         type => "string",
33                         example => "tag",
34                         description => "parent page tags are located under",
35                         safe => 1,
36                         rebuild => 1,
37                 },
38                 # TODO make this a hash so that we can have the user specify the linktype, tagbase and template param for each custom tag type
39                 tagtypes => {
40                         type => "string",
41                         example => ["category", "column"],
42                         description => "extra categorization types: they define a new directive each, with a corresponding template parameter, link type and tag base",
43                         safe => 1,
44                         rebuild => 1,
45                 }
46 }
47
48 sub tagtypes() {
49         if (defined $config{tagtypes}) {
50                 return @{$config{tagtypes}};
51         } else {
52                 return ();
53         }
54 }
55
56 sub checkconfig() {
57         foreach my $tagtype (tagtypes()) {
58                 debug("defining '$tagtype' tagtype (directive, linktype, tagbase, template param)");
59                 hook(type => "preprocess", id => $tagtype, call => sub { preprocess_custom_tag($tagtype, @_) });
60         }
61 }
62
63 sub tagpage ($$) {
64         my $tag=shift;
65         my $type=shift;
66
67         if (defined $type && $type ne "tag") {
68                 $tag="/$type/$tag";
69         } elsif ($tag !~ m{^\.?/} &&
70             defined $config{tagbase}) {
71                 $tag="/".$config{tagbase}."/".$tag;
72                 $tag=~y#/#/#s; # squash dups
73         }
74
75         return $tag;
76 }
77
78 sub taglink ($$$$;@) {
79         my $page=shift;
80         my $destpage=shift;
81         my $tag=shift;
82         my $tagtype=shift;
83         my %opts=@_;
84
85         return htmllink($page, $destpage, tagpage($tag, $tagtype), %opts);
86 }
87
88 sub preprocess_tag (@) {
89         if (! @_) {
90                 return "";
91         }
92         my %params=@_;
93         my $page = $params{page};
94         my $tagtype = defined $params{tagtype} ? $params{tagtype} : 'tag' ;
95         delete $params{page};
96         delete $params{tagtype};
97         delete $params{destpage};
98         delete $params{preview};
99
100         foreach my $tag (keys %params) {
101                 $tag=linkpage($tag);
102                 # hidden WikiLink
103                 add_link($page, tagpage($tag, $tagtype), $tagtype);
104         }
105
106         return "";
107 }
108
109 sub preprocess_custom_tag($;@) {
110         my $tagtype = shift;
111         if (! @_) {
112                 return "";
113         }
114         my %params=@_;
115         $params{tagtype} ||=  $tagtype;
116         preprocess_tag(%params);
117 }
118
119 sub preprocess_taglink (@) {
120         if (! @_) {
121                 return "";
122         }
123         my %params=@_;
124         my $tagtype = 'tag'; # TODO customizable?
125         return join(" ", map {
126                 if (/(.*)\|(.*)/) {
127                         my $tag=linkpage($2);
128                         add_link($params{page}, tagpage($tag, $tagtype), $tagtype);
129                         return taglink($params{page}, $params{destpage}, $tag, $tagtype,
130                                 linktext => pagetitle($1));
131                 }
132                 else {
133                         my $tag=linkpage($_);
134                         add_link($params{page}, tagpage($tag, $tagtype), $tagtype);
135                         return taglink($params{page}, $params{destpage}, $tag, $tagtype);
136                 }
137         }
138         grep {
139                 $_ ne 'page' && $_ ne 'destpage' && $_ ne 'preview'
140         } keys %params);
141 }
142
143 sub pagetemplate (@) {
144         my %params=@_;
145         my $page=$params{page};
146         my $destpage=$params{destpage};
147         my $template=$params{template};
148
149         my $tags = $typedlinks{$page}{tag};
150         my @taglist = ();
151
152         if (defined $tags && %$tags) {
153                 push @taglist, keys %$tags;
154                 $template->param(tags => [
155                         map {
156                                 link => taglink($page, $destpage, $_, 'tag', rel => "tag")
157                         }, sort keys %$tags
158                 ]) if $template->query(name => "tags");
159         }
160
161         # custom tagtypes have a separate treatment for a number of reasons, including the fact that
162         # their template parameter name is different from the tag/link type ('tags' vs 'tag')
163         # notice the use of rel => $tagtype
164         foreach my $tagtype (tagtypes()) {
165                 $tags = $typedlinks{$page}{$tagtype};
166                 if (defined $tags && %$tags) {
167                         push @taglist, keys %$tags;
168                         $template->param($tagtype => [
169                                 map {
170                                         link => taglink($page, $destpage, $_, $tagtype, rel => $tagtype)
171                                 }, sort keys %$tags
172                         ]) if $template->query(name => $tagtype);
173                 }
174         }
175
176         debug("full tag list: " . join(',', @taglist)) if @taglist;
177
178         if ($template->query(name => "categories")) {
179                 # It's an rss/atom template. Add any categories.
180                 if (@taglist) {
181                         $template->param(categories => [map { category => $_ },
182                                 sort @taglist]);
183                 }
184         }
185 }
186
187 package IkiWiki::PageSpec;
188
189 sub match_tagged ($$;@) {
190         my %params = @_;
191         my $tagtype = defined $params{tagtype} ? $params{tagtype} : 'tag' ;
192         return match_link($_[0], IkiWiki::Plugin::tag::tagpage($_[1], $tagtype), linktype => $tagtype);
193 }
194
195 1