tagging: Allow using titles in for related stories.
[matthijs/upstream/blosxom-plugins.git] / general / moreentries
1 # Blosxom Plugin: moreentries
2 # Author(s): Jason Clark
3 # Version: 0+1i
4 # Blosxom Home/Docs/Licensing: http://blosxom.sourceforge.net/
5 # moreentries plugin Home/Docs/Licensing: 
6 #     http://jclark.org/weblog/WebDev/Blosxom/plugins/moreentries
7
8 package moreentries;
9
10 use strict;
11 use vars qw/ $active $totalposts $start $end $prevlink $nextlink $nextcount $links $selfstyle /;
12
13 # --- Configurable variables -----
14
15 # If set to 1, the html created by $moreentries::links will have simple style attribs
16 $selfstyle =1;
17
18 # --------------------------------
19
20 sub start {
21   return 1;
22 }
23
24 sub filter {
25   # moreentries has some special requirements we're going to
26   # address here:
27   #  * must be last filter to run - we'll check our pos in 
28   #    @blosxom::plugins.  If we're not last, move us and
29   #    exit, otherwise procede
30   #  * need to operate on sorted entries - since sort()
31   #    hasn't run yet, and isn't even defined yet, we'll
32   #    have to mimic blosxom's behavior, i.e, find and run
33   #    the apropriate plugin's sort()
34     my ($self, $files, $others) = @_;
35     $active = undef;
36     $start = $end = $prevlink = $nextlink = undef;
37     # $num_entries is ignored for date-based urls, or if num_entries not in use
38     return 1 if $blosxom::path_info_yr =~ /\d/ or !defined($blosxom::num_entries) or $blosxom::num_entries < 1;
39     my $pos;
40     for(my $i=0;$i<@blosxom::plugins;$i++) { 
41         if($blosxom::plugins[$i] eq 'moreentries') {
42             $pos=$i;
43             last;
44         }
45     }
46     return 0 unless $pos;
47     unless ($pos==$#blosxom::plugins) {
48         #we're not last plugin, so move us
49         splice @blosxom::plugins, $pos, 1;
50         push @blosxom::plugins, 'moreentries';
51         return 1;
52     }
53     #made it this far, time to do our stuff.  First, do we need to?
54     #filter out posts outside of requested path
55     my $currentdir = $blosxom::path_info;
56     if ( $currentdir =~ /(.*?)([^\/]+)\.(.+)$/ and $2 ne 'index' ) {
57       $currentdir = "$1$2.$blosxom::file_extension";
58     } 
59     else { 
60       $currentdir =~ s!/index\..+$!!;
61     }
62
63     foreach my $path_file (keys %{$files}) {
64         my ($path,$fn) = $path_file =~ m!^$blosxom::datadir/(?:(.*)/)?(.*)\.$blosxom::file_extension!;
65         # Only stories in the right hierarchy
66         ($path =~ /^$currentdir/ or $path_file eq "$blosxom::datadir/$currentdir") and next;
67 #print STDERR "path='$path', curdir='$currentdir', pathfile='$path_file', ddir='$datadir'\n";
68         delete $files->{$path_file};
69     }
70
71     $totalposts = scalar(keys %{$files});
72     print STDERR "TOTALPOSTS='$totalposts'\n";    
73
74     return 1 if $totalposts <= $blosxom::num_entries;
75
76     #guess we do.
77     $active = 1;
78     
79     # Define a default sort subroutine
80     my $sort = sub {
81         my($files_ref) = @_;
82         return sort { $files_ref->{$b} <=> $files_ref->{$a} } keys %$files_ref;
83     };
84     
85     # Allow for the first encountered plugin::sort subroutine to override the
86     # default built-in sort subroutine
87     my $tmp; 
88     foreach my $plugin ( @plugins ) { 
89         if ($plugins{$plugin} > 0 and $plugin->can('sort')) {
90             if (defined($tmp = $plugin->sort())) {
91                 $sort = $tmp and last; 
92              }
93         }
94     }
95   
96     my @sorted = &$sort($files, $others);
97     $start = (blosxom::param("_start") or 1);
98     $end = $start + $blosxom::num_entries -1;
99     $end = $totalposts if $end > $totalposts;
100     
101     #trim %files
102     for (my $i=0;$i<$start-1;$i++) {
103         delete $files->{$sorted[$i]};
104     }
105     for (my $i=$end; $i<$totalposts; $i++) {
106         delete $files->{$sorted[$i]};
107     }
108     
109     #setup prevlink, nextlink
110     my $url = blosxom::self_url();
111     $url =~ s/[&\?;]_start=\d+//g;
112     $url =~ s/[&?;]-\w+=\d+//g;
113     $url =~ s/\?\s*$//;
114
115     my $appendchar = $url =~ /\?/ ? "&" : "?";
116     my $prev = $start - $blosxom::num_entries;
117     $prev = 1 if $prev < 0;
118     my $next = $end + 1;
119     $prevlink = "$url${appendchar}_start=$prev" if $start > 1;
120     $nextlink = "$url${appendchar}_start=$next" if $end < $totalposts;
121     $nextcount = $totalposts - $end;
122     $nextcount = $blosxom::num_entries if $nextcount > $blosxom::num_entries;
123     my $style = $selfstyle? "style='padding:0 15px'" : '';
124     $links = "<div class='moreentries'><span class='prevlink' $style>";
125     if (defined($prevlink)) { $links .= "<a href='$prevlink'>Previous $blosxom::num_entries entries</a>"; }
126     $links .= "</span><span class='nextlink' $style>";
127     if (defined($nextlink)) { $links .= "<a href='$nextlink'>Next $nextcount entries</a>"; }
128     $links .= "</span></div>";
129     return 1;  
130 }
131
132 sub sort() {
133     return undef;
134 }
135
136 1;
137
138 __END__
139
140 =head1 Name
141
142 Blosxom Plug-In: moreentries
143
144 =head1 Synopsis
145
146 When the Blosxom variable $num_entries is set to a non-zero number,
147 Any non-date style request(category or root url) will only display
148 $num_entries entries on the page.  No method is provided to see
149 subsequent entries (aside from using date-urls to browse back in time,
150 and this can't be combined with categories).
151
152 moreentries enables these addional entries to be viewed.  moreentries adds
153 the ability for blosxom urls to accept an _start parameter, for example:
154
155   http://jclark.org/weblog/index.html?_start=11
156
157 Will show you all the weblog entries *after* the first 10.  moreentries also 
158 offers several variable to allow you to add Next and Previous links to your
159 templates, and to show the current range of posts, and total posts.
160
161 PLEASE NOTE: Probably doens't work with static rendering.  Untested.
162
163 =head2 Quick Start
164
165 Just drop it in your $plugin_dir.  There is only one config variable, which 
166 we will ignore for the quick start; see next section for more info.  You'll
167 need to modify your head or foot template to see the links.  For a quick
168 start, add $moreentries::links to your head or foot template.  And of course,
169 your blosxom.cgi must have a (nonzero) value set for $num_entries.
170
171 That's it; you should now have next & previous functionality.
172
173 =head1 More Info
174
175 moreentries exposes the following variables for use in your templates.  None of 
176 these are per-story, and are generally suited for use in your head or foot template.
177
178 $moreentries::active - true if the plugin is 'in use'.  This will be false if
179 the url is a date url (blosxom ignores $num_entries), or if there are not at 
180 least $num_entries entries matching the request, or if $num_entries is not set.
181 $moreentries::totalposts - total number of entries that match the request
182 $moreentries::start - the number of the first entry being displayed
183 $moreentries::end  - the number of the last entry being displayed
184 $moreentries::links - inserts (x)HTML containing links to the previous and
185 next batch of entries.  If one (or both) of these links does not apply ( for example,
186 when viewing the first batch of entries, there are no previous entries) there
187 is no link.
188
189 The output of $moreentries::links will look like this:
190  <div class='moreentries'>
191    <span class='prevlink' style='padding:0 15px'><a href='http://127.0.0.1/weblog/index.html?_start=1'>Previous 10 entries</a></span>
192    <span class='nextlink' style='padding:0 15px'><a href='http://127.0.0.1/weblog/index.html?_start=21'>Next 10 entries</a></span>
193  </div>
194  
195 The embedded style prevents the links from being right next to each other.  This output
196 is sufficient for a quick drop-in.  If you'd like to control CSS styles yourself, 
197 there is a confi variable ( $selfstyle ) that controls this, just set it to 0.
198
199 If you want even more control over your output, there are some additional variables
200 you can use:
201
202
203 $moreentries::prevlink - if $moreentries::start is not 1, this will contain an
204 URL to move to the previous batch of entries.  Will be undef if no Previous batch
205 (i.e., $moreentries::start =1)
206
207 $moreentries::nextlink - if $moreentries::end < $morrentries::totalposts, this will
208 contain an URL to the next batch of entries.  Will be undef if no Next batch
209 (i.e., $moreentries::end = $moreentries::totalposts)
210
211 $moreentries::nextcount - If there is a next batch, this contains the number of posts
212 in it.  This is normally $num_entries, except when there arent enough entries left.  For
213 example, if there are 12 posts in the Rants category, and your $num_entries is 10, then
214 $morrentries::nextcount will be 2.
215
216 There is one config variable, explained above in the discussion of $moreentries::links:
217
218 $moreentries::selfstyle - when set, $moreentries::links will contain simple style attribs.
219
220 =head1 Internals
221
222 This is a big ugly hack.  The only entrypoint that worked for this is the filter() hook;
223 the problem is that when filter() is called, the sort() routine hasn't run (isn't even
224 decided on yet!), and %files contains all posts, not just the ones matching the request.
225 Even running as filter, it has to be the last plugin to run, so that any other filtering 
226 happens before we decide the 'numbering' of the posts.
227
228 The way it works:  when filter() is called, we check @plugins to see if moreentries is
229 the last plugin.  If not, we munge @plugins, making us the last plugin, and return.  If
230 we are the last plugin (as we eventually will be), then we proceed.  
231
232 The next step is rather inefficient:  we have to sort %files to number them, so we
233 check all plugins for a sort() routine, providing a default if none is found.  This
234 code is stolen wholesale from blosxom.cgi.  Once they are sorted, we get rid of all
235 files that don't match the request url (anything in the wrong category, essentially).
236 This code is also stolen from blosxom.cgi, with a couple of adjustments.
237
238 Finally, we delete all the posts before our start (as provided by the param _start), and
239 all posts after our ending post.  Thus, when filter is done, %files only contains the posts
240 that will be displayed.  This makes redundant much of the code that runs in blosxom after this.
241
242
243 =head1 Version
244
245 0+1i
246
247 =head1 Author
248
249 Jason Clark  (jason@jclark.org; http://jclark.org/weblog)
250
251 =head1 Acknowledgements
252
253 Thanks to Fletcher T. Penny (http://fletcher.freeshell.org/) for helping test the plugin.
254
255 =head1 See Also
256
257 Blosxom Home/Docs/Licensing: http://blosxom.sourceforge.net
258
259 =head1 Bugs
260
261 This whole thing is a big hack, see the Internals section above for some info.
262 This should probably be implemented as part of the core Blosxom application.
263
264 Not tested at all with static rendering, probably won't work.
265
266 =head1 License
267
268 This Blosxom Plug-in Copyright 2003, Jason Clark
269
270 (This license is the same as Blosxom's)
271 Permission is hereby granted, free of charge, to any person obtaining a copy 
272 of this software and associated documentation files (the "Software"), to deal 
273 in the Software without restriction, including without limitation the rights 
274 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
275 copies of the Software, and to permit persons to whom the Software is furnished 
276 to do so, subject to the following conditions:
277
278
279 The above copyright notice and this permission notice shall be included in all 
280 copies or substantial portions of the Software.
281
282 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
283 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 
284 FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 
285 COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 
286 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
287 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
288