File Coverage

blib/lib/Blog/Simple/HTMLOnly.pm
Criterion Covered Total %
statement 161 231 69.7
branch 57 122 46.7
condition 12 21 57.1
subroutine 10 15 66.6
pod 4 11 36.3
total 244 400 61.0


line stmt bran cond sub pod time code
1             package Blog::Simple::HTMLOnly;
2            
3             # use 5.6.1;
4 1     1   402313 use strict;
  1         9  
  1         41  
5 1     1   6 use warnings;
  1         2  
  1         38  
6            
7 1     1   20 use vars qw/@ISA $VERSION/;
  1         7  
  1         297  
8             $VERSION = '0.05'; # depends
9            
10 1     1   118 use HTML::TokeParser;
  1         3  
  1         5155  
11            
12             =head1 NAME
13            
14             Blog::Simple::HTMLOnly - Very simple weblog (blogger) with just Core modules.
15            
16             =head1 SYNOPSIS
17            
18             my $blog = Blog::Simple::HTMLOnly->new();
19             $blog->create_index(); # generally only needs to be called once
20             #
21             # ...
22             #
23             my $content="

blah blah blah in XHTM

Better when done in

24             HTML!

";
25             my $title = 'some title';
26             my $author = 'a.n. author';
27             my $email = 'anaouthor@somedomain.net';
28             my $smmry = 'blah blah';
29             my $ctent = '
Twas in the month of Liverpool and the city of July...
',
30             $blog->add($title,$author,$email,$smmry,$ctent);
31             #
32             # ...
33             #
34             my $format = {
35             simple_blog_wrap => '
',
36             simple_blog => '
',
37             title => '
',
38             author => '
',
39             email => '
40             ts => '
',
41             summary => '
',
42             content => '
',
43             };
44             $blog->render_current($format,3);
45             $blog->render_all($format);
46             $blog->remove('08');
47             exit;
48            
49             Please see the *.cgi files included in the tar distribution for examples of simple use.
50            
51             =head1 DEPENDENCIES
52            
53             Nothing outside of the core perl distribution.
54            
55             =head1 EXPORT
56            
57             Nothing.
58            
59             =head1 DESCRIPTION
60            
61             This is a backwards-compatible modification of C
62             by JA Robson , indentical in all but
63             the need for C and Perl 5.6.1. It also includes an additional
64             method to render a specific blog, and the latest C blogs.
65            
66             Instead of C, this module uses C,
67             of the core distribution. Naturally formatting is rather restricted,
68             but it can produce some useful results if you know your way around
69             CSS (L), and is better than
70             a poke in the eye with a sharp stick.
71            
72             =head1 USAGE
73            
74             Please read the documentation for L before continuing,
75             but ignore the documentation for the rendering methods.
76            
77             The rendering methods C and C no longer
78             take a paramter of an XSLT file, but instead a reference to a hash,
79             the keys of which are the names of the nodes in a C
80             XML file, values being HTML to wrap around the named node.
81            
82             Only the opening tags need be supplied: the correct end-tags will
83             supplied in lower-case by this module.
84            
85             For an example, please see the L.
86            
87             =cut
88            
89             #this method takes a predetermined number of blogs from the top of the 'bb.idx' file
90             #and generates an output file (HTML). The $format argument is explained in the POD
91             #
92            
93             =head2 METHOD render_current_by_author
94            
95             As C but accepts a format hash, number of entries to display,
96             an optional B, and optional output file.
97            
98             =cut
99            
100 0     0 1 0 sub render_current_by_author { my ($self, $format, $dispNum, $author, $outFile) = (@_);
101 0         0 $self->{_show_author} = $author;
102 0         0 return $self->render_current($format, $dispNum, $outFile);
103             }
104            
105 1     1 0 20 sub render_current { my ($self, $format, $dispNum, $outFile) = (@_);
106 1         4 local *BB;
107             # make sure we're getting a reasonable number of blogs to print
108 1 50       4 $dispNum = 1 if $dispNum < 1;
109            
110             #read in the blog entries from the 'bb.idx' file
111 1 50       40 unless (open BB, $self->{blog_idx}){
112 0         0 die "No blog index $self->{blog_idx}: $!, caller:" .(join" ",caller);
113             }
114 1 50       12 flock *BB,2 if $^O ne 'MSWin32';
115 1         6 seek BB,0,0; # rewind to the start
116 1         5 truncate BB, 0; # the file might shrink!
117 1         2 my @getFiles;
118 1         3 my $cnt=0;
119 1         23 while () {
120 2 100 66     27 next if (($cnt == $dispNum) || ($_ =~ /^\#/));
121 1         9 my @tmp = split(/\t/, $_);
122 1 50 33     6 next if defined $self->{_show_author} and $tmp[3] ne $self->{_show_author};
123 1         4 push(@getFiles, $tmp[0]);
124 1         9 $cnt++;
125             }
126 1         12 close BB;
127 1 50       41 flock (*BB, 8) if $^O ne 'MSWin32';
128            
129             #open the 'blog.xml' files individually and concatenate into xmlString
130 1         3 my $xmlString = "\n";
131 1         5 foreach my $fil (@getFiles) {
132 1         2 my $preStr;
133 1 50       51 open (GF, "$fil") or die "Error opening $fil - $!";
134 1 50       11 flock *GF,2 if $^O ne 'MSWin32';
135 1         7 seek GF,0,0; # rewind to the start
136 1         5 truncate GF, 0; # the file might shrink!
137 1         30 while () { $preStr .= $_; }
  9         27  
138 1         12 close GF;
139 1 50       36 flock (*GF, 8) if $^O ne 'MSWin32';
140 1         8 $xmlString .= $preStr;
141             }
142 1         3 $xmlString .= "\n";
143            
144             #process the generated Blog file
145 1         9 my $outP = $self->transform ($format,\$xmlString);
146            
147 1 50       4 if (not defined $outFile) { #if output file set to nothing, spit to STDOUT
148 1         63 print $$outP;
149             }
150             else {
151 0         0 open (OF, ">$self->{path}". $outFile);
152 0 0       0 flock *OF,2 if $^O ne 'MSWin32';
153 0         0 seek OF,0,0; # rewind to the start
154 0         0 truncate OF, 0; # the file might shrink!
155 0         0 print OF $$outP;
156 0         0 close OF;
157 0 0       0 flock (*OF, 8) if $^O ne 'MSWin32';
158             }
159 1         8 return $outP;
160             }
161            
162             #this subroutine creates an archive output by opening 'bb.idx' and
163             #concatentating all the files in the
164             #blogbase into a single string, and processing it $format as explained
165             #in the pod. Works nearly identical to gen_Blog_Current,
166             #except it gets all blogs, not just the 'n' most current.
167            
168 1     1 0 546 sub render_all { my ($self, $format, $outFile) = @_;
169             #read in the blog entries from the 'bb.idx' file
170 1 50       59 open(BB, $self->{blog_idx}) or die 'Error opening idx '.$self->{blog_idx}." - $!";
171 1 50       19 flock *BB,2 if $^O ne 'MSWin32';
172 1         6 seek BB,0,0; # rewind to the start
173 1         6 truncate BB, 0; # the file might shrink!
174 1         2 my @getFiles;
175 1         26 while () {
176 2 100       13 next if ($_ =~ /^\#/);
177 1         8 my @tmp = split(/\t/, $_);
178 1 50 33     7 next if defined $self->{_show_author} and $tmp[3] ne $self->{_show_author};
179 1         9 push (@getFiles, $tmp[0]);
180             }
181 1         16 close BB;
182 1 50       39 flock (*BB, 8) if $^O ne 'MSWin32';
183            
184            
185             #open the 'blog.xml' files individually and concatenate into xmlString
186 1         3 my $xmlString = "\n";
187 1         3 foreach my $fil (@getFiles) {
188 1         1 my $preStr;
189 1 50       39 open (GF, $fil) or die "Error opening $fil - $!";
190 1 50       8 flock *GF,2 if $^O ne 'MSWin32';
191 1         5 seek GF,0,0; # rewind to the start
192 1         4 truncate GF, 0; # the file might shrink!
193 1         24 while () { $preStr .= $_; }
  9         24  
194 1         9 close GF;
195 1 50       34 flock (*GF, 8) if $^O ne 'MSWin32';
196 1         5 $xmlString .= $preStr;
197             }
198 1         3 $xmlString .= "\n";
199            
200             #process the generated Blog file
201 1         5 my $outP = $self->transform ($format,\$xmlString);
202            
203 1 50       5 if (not defined($outFile)) { #if output file not defined, spit to STDOUT
204 1         58 print $$outP;
205             }
206             else {
207 0         0 open (OF, ">$self->{path}". $outFile);
208 0 0       0 flock *OF,2 if $^O ne 'MSWin32';
209 0         0 seek OF,0,0; # rewind to the start
210 0         0 truncate OF, 0; # the file might shrink!
211 0         0 print OF $$outP;
212 0         0 close OF;
213 0 0       0 flock (*OF, 8) if $^O ne 'MSWin32';
214             }
215 1         6 return $outP;
216             }
217            
218            
219             =head2 METHOD render_all_by_author
220            
221             Identical to C but takes an additional argument, that is the author ID.
222            
223             =cut
224            
225 0     0 1 0 sub render_all_by_author { my ($self, $format, $author, $outFile) = @_;
226 0         0 $self->{_show_author} = $author;
227 0         0 return $self->render_all($format, $outFile);
228             }
229            
230            
231            
232             # Transform XML to HTML
233             # Accepts: reference to a 'formatting' hash; reference to a string of XML
234             # Returns: reference to a string of HTML
235 2     2 0 5 sub transform { my ($self, $format, $xml) = (shift, shift, shift);
236 2         5 local $_;
237            
238 2 50 33     17 if (not defined $format or ref $format ne 'HASH'){
239 0         0 Carp::confess "transform takes two arguments, the first being a hash reference for formatting";
240             }
241 2 50 33     15 if (not defined $xml or ref $xml ne 'SCALAR'){
242 0         0 Carp::confess "transform takes two arguments, the second being a scalar reference of XML";
243             }
244 2         3 my $open = {};
245 2         2 my $html;
246 2         9 foreach my $node (keys %$format){
247 14         179 my $p = HTML::TokeParser->new(\$format->{$node});
248 14         1709 my $html = "";
249 14         43 while (my $t = $p->get_token){
250 16 50       480 push @{$open->{$node}},"@$t[1]" if @$t[0] eq 'S';
  16         115  
251             }
252             }
253            
254 2         35 my $p = HTML::TokeParser->new($xml);
255 2         294 my @current;
256             # use Data::Dumper; die Dumper $xml,$format; #simple_blog_wrap|simple_blog|ts|
257            
258 2         8 while (my $t = $p->get_token){
259 80 100 100     2096 if (@$t[0] eq 'S' and @$t[1] =~ /^(simple_blog_wrap|simple_blog|ts|title|author|email|summary|content)$/){
    100 100        
    100          
    100          
    50          
260             # warn "Open ",@$t[1],"\n" if $^W;
261 16         23 push @current, @$t[1];
262 16 100       96 $html .= $format->{@$t[1]} if exists $format->{@$t[1]};
263             }
264             elsif (@$t[0] eq 'T'){
265             # warn "Text @$t[1]","\n" if $^W;
266 36         93 $html .= @$t[1] . $p->get_text;
267             }
268             elsif (@$t[0] eq 'E' and @$t[1] =~ /^(simple_blog_wrap|simple_blog|ts|title|author|email|summary|content)$/){
269             # warn "Close @$t[1] with ", join",",@{$open->{$current[$#current]}},"\n" if $^W;
270 16 100       50 $html .= join '',( map {""} reverse @{$open->{$current[$#current]}}) if $open->{$current[$#current]};
  16         45  
  14         27  
271 16         61 pop @current;
272             } elsif (@$t[0] eq 'S') {
273 6         82 $html .= @$t[4];
274             } elsif (@$t[0] =~ /^(E|PI)$/) {
275 6         26 $html .= @$t[2];
276             } else {
277 0         0 $html .= @$t[1];
278             }
279             }
280 2         61 return \$html;
281             }
282            
283            
284             =head2 METHOD: render_these_blogs
285            
286             Alias for C.
287            
288             =head2 METHOD: render_this_blog
289            
290             Renders to C the nominated blog(s).
291            
292             In addition to the method's object reference, accepts
293             a date and an author, and a format hash (see above).
294             The date should be in a C output with spaces
295             turned to underscores (C<_>).
296            
297             On success, returns a reference to the Blog in HTML.
298             On failure returns C, sending a warning to C
299             if you have C on (C<-w>).
300            
301             =cut
302            
303             sub render_these_blogs {
304 0     0 1 0 my $self=shift;
305 0         0 return $self->render_this_blog(@_);
306             }
307            
308 0     0 1 0 sub render_this_blog { my ($self,$date,$author,$format) = (shift,shift,shift,shift);
309 0         0 local (*IN, *DIR);
310 0         0 my ($html);
311 0         0 $date =~ s/[^\w\d_\*]//sg;
312 0         0 $date =~ s/\*/\.\*\?/g;
313 0         0 opendir DIR, $self->{blog_base};
314 0         0 my @dirs = grep {/^$date$/} readdir DIR;
  0         0  
315 0         0 closedir DIR;
316 0         0 foreach my $dir (reverse sort @dirs){
317 0 0       0 unless (open IN, $self->{blog_base}.$dir.'/blog.xml'){
318 0 0       0 warn "Could not find blog,
",
 
319             $self->{blog_base}.$date."_".$author,
320             "" if $^W;
321 0         0 return undef;
322             }
323 0         0 my $xmlString;
324 0         0 read IN,$xmlString,-s IN;
325 0         0 close IN;
326 0         0 $$html .= ${ $self->transform ($format,\$xmlString) };
  0         0  
327             }
328 0         0 print $$html;
329 0         0 return $html;
330             }
331            
332             #################################################################
333             #
334             # Taken almost verbatum from Blog::Simple
335             #
336             #################################################################
337            
338             #instantiate object, create dir/files under path
339             sub new {
340             #get parameters
341 1     1 0 208 my ($obj, $pth) = @_;
342            
343 1 50       4 Carp::croak 'You must supply a path as the sole argument.' if not $pth;
344            
345 1         4 $pth =~ s/\\/\//g; #turn backslashes into forward
346            
347             #add the final slash, if needed
348 1 50       4 $pth .= "/" if $pth !~ /\/$/;
349            
350             #create object data structure
351 1         8 my %sBlog = (
352             path => $pth,
353             blog_idx => $pth . "bb.idx",
354             blog_base => $pth . "b_base/",
355             del_list => ''
356             );
357            
358             #create the paths
359 1         13 mkdir($sBlog{path}); #root path
360 1         12 mkdir($sBlog{blog_base});
361            
362 1         3 my $sBRef = \%sBlog;
363 1         5 bless $sBRef, $obj;
364             }
365            
366             #generate the 'bb.idx' file
367 1     1 0 64 sub create_index { my $obj = shift;
368 1 50       144 open(F, ">$obj->{blog_idx}") or die $obj->{blog_idx}, " ",$!;
369 1 50       16 flock *F,2 if $^O ne 'MSWin32';
370 1         10 seek F,0,0; # rewind to the start
371 1         39 truncate F, 0; # the file might shrink!
372 1         24 print F "#path_to_blog date_stamp title author summary";
373 1         67 close F;
374 1 50       74 flock (*F, 8) if $^O ne 'MSWin32';
375             }
376            
377             #adds a blog to the 'b_base' directory
378 1     1 0 15 sub add { my ($obj, $title, $author, $email, $smmry, $content) = @_;
379 1         5 local (*BF,*BB);
380            
381             #handle undefined variables
382 1 50       8 if (not defined($title)) { $title = ''; }
  0         0  
383 1 50       3 if (not defined($author)) { $author = ''; }
  0         0  
384 1 50       5 if (not defined($email)) { $email = ''; }
  0         0  
385 1 50       3 if (not defined($smmry)) { $smmry = ''; }
  0         0  
386 1 50       14 if (not defined($content)) { $content = ''; }
  0         0  
387            
388 1         50 my $tmp = localtime(time);
389 1         3 my $ts = $tmp; #for 'bb.idx' entry
390            
391 1         4 $content =~ s/\t/ /g; #remove any tabs in the content, summary
392 1         9 $smmry =~ s/\t/ /g;
393            
394            
395             #The core blog XML template
396             #==========================
397 1         7 my $blogTmplt =<
398            
399             $title
400             $author
401             $email
402             $ts
403             $smmry
404             $content
405            
406             END_BT
407             #==========================
408            
409             #prepare the directory to be unique
410 1         13 $tmp =~ s/[\s:]/_/g;
411 1         3 my $tmpA = $author;
412 1         6 $tmpA =~ s/[^a-zA-Z]/_/g;
413 1         5 my $unqDir = $obj->{blog_base} . $tmp . "_" . $tmpA . "/";
414            
415             #create the directory
416 1 50       130 mkdir $unqDir or die 'Could not mkdir '.$unqDir.' - '. $!;
417            
418             #put 'blog.xml' in it
419 1 50       102 open(BF, ">${unqDir}blog.xml") or die "Could not open to write $unqDir/blog.xml - $!";
420 1 50       11 flock *BF,2 if $^O ne 'MSWin32';
421 1         8 seek BF,0,0; # rewind to the start
422 1         28 truncate BF, 0; # the file might shrink!
423 1         21 print BF $blogTmplt;
424 1         44 close BF;
425 1 50       53 flock (*BF, 8) if $^O ne 'MSWin32';
426            
427             #save entry to 'bb.idx'
428 1 50       52 open(BB, $obj->{blog_idx}) or die "Could not open $obj->{blog_idx} - $!";
429 1 50       13 flock *BB,2 if $^O ne 'MSWin32';
430 1         6 seek BB,0,0; # rewind to the start
431 1         6 truncate BB, 0; # the file might shrink!
432 1         2 my $bbIdx;
433 1         125 while () { $bbIdx .= $_; }
  1         7  
434 1         16 close BB;
435 1 50       43 flock (*BB, 8) if $^O ne 'MSWin32';
436            
437 1         18 my $curLine = "${unqDir}blog.xml\t$ts\t$title\t$author\t$smmry\n";
438            
439 1 50       92 open(BB, ">$obj->{blog_idx}") or die "Error writing $obj->{blog_idx} - $!";
440 1 50       11 flock *BB,2 if $^O ne 'MSWin32';
441 1         6 seek BB,0,0; # rewind to the start
442 1         25 truncate BB, 0; # the file might shrink!
443 1         5 print BB $curLine; print BB $bbIdx;
  1         18  
444 1         37 close BB;
445 1 50       48 flock (*BB, 8) if $^O ne 'MSWin32';
446             }
447            
448             #remove entry from bb.idx
449             #the parameter passed is a regular expression. This way, multiple entries
450             #can be removed simultaneously. Only removes entries from the 'bb.idx' file
451             #and returns the paths that need to be removed as an array.
452             sub remove {
453 0     0 0   my ($obj, $rex) = @_;
454 0           local (*RB);
455            
456 0 0         if (defined($rex)) {
457 0           my @bbI;
458             my @delF;
459            
460             #get the index, check for matches, return only those lines
461             #that do not match
462 0 0         open(RB, $obj->{blog_idx}) or die 'Could not open '.$obj->{blog_idx}.' '.$!;
463 0 0         flock *RB,2 if $^O ne 'MSWin32';
464 0           seek RB,0,0; # rewind to the start
465 0           truncate RB, 0; # the file might shrink!
466 0           foreach my $chk () {
467 0 0         if ($chk =~ /$rex/) {
468             #do the removal code
469 0           my @lA = split(/\t/, $chk);
470 0           push(@delF, $lA[0]);
471             }
472 0           else { push(@bbI, $_); }
473             }
474 0           close RB;
475 0 0         flock (*RB, 8) if $^O ne 'MSWin32';
476            
477             #write the new index
478 0 0         open(RB, ">".$obj->{blog_idx}) or die 'Could not open to write to '.$obj->{blog_idx}.' '.$!;
479 0           print RB @bbI;
480 0           close RB;
481            
482 0           $obj->{del_list} = \@delF;
483             } #defined($rex)
484            
485             }
486            
487            
488            
489             1;
490             __END__