Add Frank Hecker plugins to general.
[matthijs/upstream/blosxom-plugins.git] / general / extensionless
1 # Blosxom Plugin: extensionless
2 # Author(s): Frank Hecker <hecker@hecker.org>
3 # Version: 0.4
4 # Documentation: See the bottom of this file or type: perldoc extensionless
5
6 package extensionless;
7
8 use strict;
9
10 use CGI qw/:standard :netscape/; 
11
12
13 # --- Configurable variables -----
14
15 my $hide_category = 0;                  # set to 1 to have an extensionless
16                                         # entry URI hide a category of the
17                                         # same name as the entry
18
19 my $hide_year = 0;                      # set to 1 to have an extensionless
20                                         # URI with 4-digit entry name hide an
21                                         # archive page for a year
22
23 my $debug = 0;                          # set to 1 to print debug messages
24
25 # --------------------------------
26
27 sub start {
28     warn "extensionless: \$path_info: '$blosxom::path_info' (before)\n"
29         if $debug > 0;
30
31     # Recover the original path passed to Blosxom, in order to properly
32     # handle extensionless URIs where the entry name starts with a digit
33     # and was parsed by Blosxom as a date reference, not as part of the path.
34
35     my $path_info = path_info() || param('path');
36     $path_info =~ s!(^/*)|(/*$)!!g;
37
38     # Check for the following situation:
39     #
40     #   * the requested URI does not include a file extension
41     #   * the requested URI does not *exactly* name a category OR
42     #     we want the URI to always resolve to an entry if one exists
43     #   * the requested URI does not appear to refer to an archive for a year
44     #     OR we want the URI to always resolve to an entry if one exists
45     #   * there is an entry corresponding to the URI
46     #
47     # If the above conditions hold then we update the URI (more specifically,
48     # the path component of the URI) to include the flavour's file extension.
49     # (The flavour used will be the default flavour, a flavour specified
50     # using the flav parameter, or a flavour set by another plugin.)
51     #
52     # We also set $path_info_yr to be undefined in order to prevent Blosxom
53     # and other plugins (e.g., storystate) from considering this an archive
54     # page reference in the case where the entry name starts with a digit.
55
56     if ($blosxom::path_info !~ m!\.!
57         and (! -d "$blosxom::datadir/$path_info" || $hide_category)
58         and ($path_info !~ m!(/19[0-9]{2}$)|(/2[0-9]{3}$)! || $hide_year)
59         and (-r "$blosxom::datadir/$path_info.$blosxom::file_extension")) {
60         $blosxom::path_info = "$path_info.$blosxom::flavour";
61         $blosxom::path_info_yr = undef;
62     }
63
64     warn "extensionless: \$path_info: '$blosxom::path_info' (after)\n"
65         if $debug > 0;
66
67     1;
68 }
69
70 1;
71
72 __END__
73
74 =head1 NAME
75
76 Blosxom plugin: extensionless
77
78 =head1 SYNOPSIS
79
80 Make Blosxom recognize extensionless URIs corresponding to individual
81 entries. See http://www.w3.org/Provider/Style/URI for motivation.
82
83 =head1 VERSION
84
85 0.4
86
87 =head1 AUTHOR
88
89 Frank Hecker <hecker@hecker.org>, http://www.hecker.org/
90
91 =head1 DESCRIPTION
92
93 This plugin allows Blosxom to recognize extensionless URIs for
94 entries, as recommended by http://www.w3.org/Provider/Style/URI and
95 elsewhere.  In other words, if you are requesting a page for an
96 individual entry then you can omit the file extension that would
97 normally be required for specifying the flavour, unless the extension
98 is required to disambiguate the URI in the case where the entry has
99 the same name as a Blosxom category or looks like a 4-digit year
100 reference.
101
102 For example, if you have an entry C<foo/bar.txt> in your Blosxom data
103 directory then by using this plugin you can use a URI like
104
105   http://www.example.com/blog/foo/bar
106
107 to request the entry rather than a URI like
108
109   http://www.example.com/blog/foo/bar.html
110
111 unless there is an existing category C<foo/bar> (i.e., there is a
112 subdirectory of that name in the Blosxom data directory). If there is
113 such a name conflict then the plugin will cause Blosxom to display the
114 category and not the individual entry.
115
116 (You can change this behavior using a configurable variable as
117 described below. The plugin's default behavior matches the behavior of
118 Apache when using the MultiViews option, where Apache will display the
119 index for an existing directory C<foo/bar> in preference to displaying
120 a file C<foo/bar.html>.)
121
122 You can also use extensionless URIs like
123
124   http://www.example.com/blog/foo/1st-post
125
126 where the entry name starts with a digit, as long as the entry name
127 doesn't look like a 4-digit year (e.g., "2004") used as part of a
128 request for an archive page for that year. (Again, you can set a
129 configurable variable to override the default behavior and have entry
130 names that look like year references.)
131
132 When an entry is requested using an extensionless URI the existing
133 value of the Blosxom C<$flavour> variable will determine the flavour
134 used for the entry. Normally this value will be either that specified
135 by the C<flav> parameter (if it is present) or the default flavour
136 configured into Blosxom; however another plugin could override this
137 value if desired. For example, a separate plugin could do content
138 negotiation (e.g., using the HTTP Accept header) to determine what
139 flavour should be served for an entry. (However see the discussion
140 below regarding plugin execution order.)
141
142 Thus assuming that C<foo/bar.txt> exists in the Blosxom data directory
143 (and does not conflict with a category C<foo/bar>) and the default
144 flavour is "html" then the following URIs are all equivalent:
145
146   http://www.example.com/blog/foo/bar
147   http://www.example.com/blog/foo/bar.html
148   http://www.example.com/blog/foo/bar?flav=html
149
150 =head1 INSTALLATION AND CONFIGURATION
151
152 Copy this plugin into your Blosxom plugin directory. You do not
153 normally need to rename the plugin; however see the discussion below
154 regarding plugin execution order.
155
156 Change the value of the configurable variable C<$hide_category> to 1
157 if you want an extensionless URI to display an entry instead of a
158 category of the same name. As described above, the default behavior is
159 that in the event of a name conflict the category is displayed in
160 preference to the entry, so that displaying the entry in that case
161 requires explicitly adding a file extension to the URI. If you change
162 the plugin's behavior from the default then the category can be
163 displayed only by using a URI explicitly referencing an index page
164 (e.g., C<.../foo/index.html>) or by referencing an abbreviation for
165 the category.
166
167 Change the value of the configurable variable C<$hide_year> to 1 if
168 you want an extensionless URI to display an entry in the case where
169 the URI otherwise appears to be a reference to an archive page for a
170 given year, for example, with the URI
171
172   http://www.example.com/blog/elections/2004
173
174 where an entry C<2004.txt> exists under the category C<elections>. As
175 described above, the default behavior is that in the event of a
176 conflict the archive page is displayed in preference to the entry, so
177 that displaying the entry in that case requires explicitly adding a
178 file extension to the URI. If you change the plugin's behavior from
179 the default then the archive page for that year can be displayed only
180 by usng a URI explicitly referencing an index page (e.g.,
181 C<.../elections/2004/index.html>).
182
183 You can also change the value of the variable C<$debug> to 1 if you
184 need to debug the plugin's operation; the plugin will print to the web
185 server's error log the original path component of the URI and the
186 value after any modification is done.
187
188 This plugin supplies only a start subroutine and can normally coexist
189 with other plugins with start subroutines; however if the other
190 plugins' start subroutines depend on the standard interpretation of
191 the Blosxom variable C<$path_info> (i.e., that requests for individual
192 entries have a period '.' in C<$path_info>) then you should rename
193 this plugin (e.g., to "00extensionless") to ensure that it is loaded
194 first and can modify C<$path_info> to match the standard
195 interpretation before other plugins reference its value.
196
197 Also, if you have a plugin that overrides the value of the Blosxom
198 C<$flavour> variable then you should ensure that that plugin's code
199 runs prior to this plugin's start subroutine being executed.
200
201 =head1 ACKNOWLEDGEMENTS
202
203 This plugin was inspired by the cooluri plugin by Rob Hague
204 http://www.blosxom.com/plugins/link/cooluri.htm and the cooluri2
205 plugin by Mark Ivey http://www.blosxom.com/plugins/link/cooluri2.htm
206 but has much less ambitious goals; the code is correspondingly much
207 simpler.
208
209 Thanks also go to Stu MacKenzie, who contributed a patch to fix a
210 major bug preventing the use of extensionless URIs having entry names
211 beginning with digits.
212
213 =head1 SEE ALSO
214
215 Blosxom Home/Docs/Licensing: http://www.blosxom.com/
216
217 Blosxom Plugin Docs: http://www.blosxom.com/documentation/users/plugins.html
218
219 =head1 BUGS
220
221 In order to check whether a file extension was originally included in
222 the URI we check the value of C<$blosxom::path_info>. As noted in a
223 comment in the code, this value is not the actual URI path in the case
224 where the URI contains a component starting with a digit; however
225 checking C<$blosxom::path_info> works for the URIs of interest to us
226 and (unlike checking our local C<$path_info> value) does not produce
227 potentially spurious results on invalid Blosxom URIs like
228
229   http://www.example.com/blog/foo/123bar/baz.html
230
231 (Unlike entry names, Blosxom category names can never start with a
232 digit, and allowing them to do so is beyond the scope of this
233 plugin. For this example URI Blosxom would have set C<$path_info> to
234 C<foo> and attempted to parse C<123bar> and C<baz.html> as
235 C<$path_info_yr> and C<$path_info_mo> respectively.)
236
237 Using an entry name that looks like a year reference is problematic
238 because of potential ambiguity regarding what actually is a year
239 reference and what is not. The code as presently written takes a
240 relatively (but not completely) conservative approach: values from
241 1900 through 2999 are considered to be year references, and by default
242 will hide references to entries of the same name (sans extension).
243
244 Also note that versions of this plugin prior to 0.4 are not
245 compatible with Blosxom versions 2.0.1 and higher; in prior versions
246 this plugin executed its code in the filter subroutine, and in Blosxom
247 2.0.1 the execution of plugins' filter subroutines was moved in a way
248 that broke this plugin. This problem was fixed in version 0.4 of this
249 plugin by moving processing to the start subroutine.
250
251 Finally note that this plugin assumes that you are storing Blosxom
252 entries in the standard manner, i.e., as text files in the file system
253 within the Blosxom data directory. It will not work if you are using
254 other plugins like svn-backend that use different storage mechanisms.
255
256 Please address other bug reports and comments to the Blosxom mailing
257 list: http://www.yahoogroups.com/group/blosxom
258
259 =head1 LICENSE
260
261 extensionless Blosxom plugin Copyright 2004-2006 Frank Hecker
262
263 Permission is hereby granted, free of charge, to any person obtaining a
264 copy of this software and associated documentation files (the "Software"),
265 to deal in the Software without restriction, including without limitation
266 the rights to use, copy, modify, merge, publish, distribute, sublicense,
267 and/or sell copies of the Software, and to permit persons to whom the
268 Software is furnished to do so, subject to the following conditions:
269
270 The above copyright notice and this permission notice shall be included
271 in all copies or substantial portions of the Software.
272
273 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
274 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
275 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
276 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
277 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
278 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
279 OTHER DEALINGS IN THE SOFTWARE.