All my plugins in the released stable versions, so the repository starts
[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 This plugin is now maintained by the Blosxom Sourceforge Team,
92 <blosxom-devel@lists.sourceforge.net>.
93
94 =head1 DESCRIPTION
95
96 This plugin allows Blosxom to recognize extensionless URIs for
97 entries, as recommended by http://www.w3.org/Provider/Style/URI and
98 elsewhere.  In other words, if you are requesting a page for an
99 individual entry then you can omit the file extension that would
100 normally be required for specifying the flavour, unless the extension
101 is required to disambiguate the URI in the case where the entry has
102 the same name as a Blosxom category or looks like a 4-digit year
103 reference.
104
105 For example, if you have an entry C<foo/bar.txt> in your Blosxom data
106 directory then by using this plugin you can use a URI like
107
108   http://www.example.com/blog/foo/bar
109
110 to request the entry rather than a URI like
111
112   http://www.example.com/blog/foo/bar.html
113
114 unless there is an existing category C<foo/bar> (i.e., there is a
115 subdirectory of that name in the Blosxom data directory). If there is
116 such a name conflict then the plugin will cause Blosxom to display the
117 category and not the individual entry.
118
119 (You can change this behavior using a configurable variable as
120 described below. The plugin's default behavior matches the behavior of
121 Apache when using the MultiViews option, where Apache will display the
122 index for an existing directory C<foo/bar> in preference to displaying
123 a file C<foo/bar.html>.)
124
125 You can also use extensionless URIs like
126
127   http://www.example.com/blog/foo/1st-post
128
129 where the entry name starts with a digit, as long as the entry name
130 doesn't look like a 4-digit year (e.g., "2004") used as part of a
131 request for an archive page for that year. (Again, you can set a
132 configurable variable to override the default behavior and have entry
133 names that look like year references.)
134
135 When an entry is requested using an extensionless URI the existing
136 value of the Blosxom C<$flavour> variable will determine the flavour
137 used for the entry. Normally this value will be either that specified
138 by the C<flav> parameter (if it is present) or the default flavour
139 configured into Blosxom; however another plugin could override this
140 value if desired. For example, a separate plugin could do content
141 negotiation (e.g., using the HTTP Accept header) to determine what
142 flavour should be served for an entry. (However see the discussion
143 below regarding plugin execution order.)
144
145 Thus assuming that C<foo/bar.txt> exists in the Blosxom data directory
146 (and does not conflict with a category C<foo/bar>) and the default
147 flavour is "html" then the following URIs are all equivalent:
148
149   http://www.example.com/blog/foo/bar
150   http://www.example.com/blog/foo/bar.html
151   http://www.example.com/blog/foo/bar?flav=html
152
153 =head1 INSTALLATION AND CONFIGURATION
154
155 Copy this plugin into your Blosxom plugin directory. You do not
156 normally need to rename the plugin; however see the discussion below
157 regarding plugin execution order.
158
159 Change the value of the configurable variable C<$hide_category> to 1
160 if you want an extensionless URI to display an entry instead of a
161 category of the same name. As described above, the default behavior is
162 that in the event of a name conflict the category is displayed in
163 preference to the entry, so that displaying the entry in that case
164 requires explicitly adding a file extension to the URI. If you change
165 the plugin's behavior from the default then the category can be
166 displayed only by using a URI explicitly referencing an index page
167 (e.g., C<.../foo/index.html>) or by referencing an abbreviation for
168 the category.
169
170 Change the value of the configurable variable C<$hide_year> to 1 if
171 you want an extensionless URI to display an entry in the case where
172 the URI otherwise appears to be a reference to an archive page for a
173 given year, for example, with the URI
174
175   http://www.example.com/blog/elections/2004
176
177 where an entry C<2004.txt> exists under the category C<elections>. As
178 described above, the default behavior is that in the event of a
179 conflict the archive page is displayed in preference to the entry, so
180 that displaying the entry in that case requires explicitly adding a
181 file extension to the URI. If you change the plugin's behavior from
182 the default then the archive page for that year can be displayed only
183 by usng a URI explicitly referencing an index page (e.g.,
184 C<.../elections/2004/index.html>).
185
186 You can also change the value of the variable C<$debug> to 1 if you
187 need to debug the plugin's operation; the plugin will print to the web
188 server's error log the original path component of the URI and the
189 value after any modification is done.
190
191 This plugin supplies only a start subroutine and can normally coexist
192 with other plugins with start subroutines; however if the other
193 plugins' start subroutines depend on the standard interpretation of
194 the Blosxom variable C<$path_info> (i.e., that requests for individual
195 entries have a period '.' in C<$path_info>) then you should rename
196 this plugin (e.g., to "00extensionless") to ensure that it is loaded
197 first and can modify C<$path_info> to match the standard
198 interpretation before other plugins reference its value.
199
200 Also, if you have a plugin that overrides the value of the Blosxom
201 C<$flavour> variable then you should ensure that that plugin's code
202 runs prior to this plugin's start subroutine being executed.
203
204 =head1 ACKNOWLEDGEMENTS
205
206 This plugin was inspired by the cooluri plugin by Rob Hague
207 http://www.blosxom.com/plugins/link/cooluri.htm and the cooluri2
208 plugin by Mark Ivey http://www.blosxom.com/plugins/link/cooluri2.htm
209 but has much less ambitious goals; the code is correspondingly much
210 simpler.
211
212 Thanks also go to Stu MacKenzie, who contributed a patch to fix a
213 major bug preventing the use of extensionless URIs having entry names
214 beginning with digits.
215
216 =head1 SEE ALSO
217
218 Blosxom Home/Docs/Licensing: http://blosxom.sourceforge.net/
219
220 Blosxom Plugin Docs: http://blosxom.sourceforge.net/documentation/users/plugins.html
221
222 =head1 BUGS
223
224 In order to check whether a file extension was originally included in
225 the URI we check the value of C<$blosxom::path_info>. As noted in a
226 comment in the code, this value is not the actual URI path in the case
227 where the URI contains a component starting with a digit; however
228 checking C<$blosxom::path_info> works for the URIs of interest to us
229 and (unlike checking our local C<$path_info> value) does not produce
230 potentially spurious results on invalid Blosxom URIs like
231
232   http://www.example.com/blog/foo/123bar/baz.html
233
234 (Unlike entry names, Blosxom category names can never start with a
235 digit, and allowing them to do so is beyond the scope of this
236 plugin. For this example URI Blosxom would have set C<$path_info> to
237 C<foo> and attempted to parse C<123bar> and C<baz.html> as
238 C<$path_info_yr> and C<$path_info_mo> respectively.)
239
240 Using an entry name that looks like a year reference is problematic
241 because of potential ambiguity regarding what actually is a year
242 reference and what is not. The code as presently written takes a
243 relatively (but not completely) conservative approach: values from
244 1900 through 2999 are considered to be year references, and by default
245 will hide references to entries of the same name (sans extension).
246
247 Also note that versions of this plugin prior to 0.4 are not
248 compatible with Blosxom versions 2.0.1 and higher; in prior versions
249 this plugin executed its code in the filter subroutine, and in Blosxom
250 2.0.1 the execution of plugins' filter subroutines was moved in a way
251 that broke this plugin. This problem was fixed in version 0.4 of this
252 plugin by moving processing to the start subroutine.
253
254 Finally note that this plugin assumes that you are storing Blosxom
255 entries in the standard manner, i.e., as text files in the file system
256 within the Blosxom data directory. It will not work if you are using
257 other plugins like svn-backend that use different storage mechanisms.
258
259 Please send other bug reports and feedback to the Blosxom development 
260 mailing list <blosxom-devel@lists.sourceforge.net>.
261
262 =head1 LICENSE
263
264 extensionless Blosxom plugin Copyright 2004-2006 Frank Hecker
265
266 Permission is hereby granted, free of charge, to any person obtaining a
267 copy of this software and associated documentation files (the "Software"),
268 to deal in the Software without restriction, including without limitation
269 the rights to use, copy, modify, merge, publish, distribute, sublicense,
270 and/or sell copies of the Software, and to permit persons to whom the
271 Software is furnished to do so, subject to the following conditions:
272
273 The above copyright notice and this permission notice shall be included
274 in all copies or substantial portions of the Software.
275
276 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
277 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
278 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
279 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
280 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
281 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
282 OTHER DEALINGS IN THE SOFTWARE.