Code:

Printing with BlogIt 1.7.0

BlogIt is an excellent add-on to PmWiki: easy to set up and easy to customize. As with all software that is developed continuously, however, the occasional bug sneaks into the code: in this case, the bug has to do with PmWiki's Print skin (look about halfway down the page); the skin that PmWiki uses to prepare a printer-friendly version of the current page. The problem? When attempting to produce a printer-friendly version of the current page by clicking on the Print link at the top of the page, you get everything except the body of the current page -- which isn't much more than the name of the page and the date and time that it was last modified.

Bear in mind that this bug affects version 1.7.0, and it is listed as a known bug on the project page. Future versions of BlogIt are not likely to have the problem. In the meantime, I have patched the bug in question. You can get the patched files, or you can continue to read if you're interested in the how and why. The patch provides an additional feature for skin template authors, which is covered in greater detail below: a section called print-view that allows you to control the layout of the printer-friendly view.

The How and Why

Once again, this applies only to version 1.7.0 (the July 4, 2011 release) and not necessarily to any later versions. Check your versions!

The relevant code is on lines 291 to 297 of cookbook/blogit/blogit.php, in the function bi_HandleBrowse():

  1. if ($action=='pmform' && $_REQUEST['target']=='blogit-entry'){
  2.                 if (isset($bi_ResetPmFormField))
  3.                         foreach ($bi_ResetPmFormField  as $k => $v) {
  4.                                 $_REQUEST["$k"]=$v;  #Reset form variables that have errors captured outside the PmForms mechanism
  5.                                 $FmtPV['$bi_Default_'.$k]='"'.bi_Clean('alpha',$v).'"';  #Always set, but used where values are stored in formats that don't handle errors (like Unix timestamps).
  6.                         }
  7.         }elseif ($entrytype == 'blog' && $action=='browse'){
  8.                 bi_storeCookie();
  9.                 $bi_EntryStatus = PageTextVar($src,'entrystatus');
  10.                 $bi_AuthEditAdmin = bi_Auth('blog-edit,blog-new,blogit-admin');
  11.                 if ( ($bi_EntryStatus!='draft' && (!bi_FuturePost($Now) || $bi_AuthEditAdmin) ) || ($bi_EntryStatus=='draft' && $bi_AuthEditAdmin) )
  12.                         $GroupHeaderFmt .= '(:includesection "#single-entry-view":)';  #Required for action=browse AND comments when redirected on error (in which case $action=pmform).
  13.         }else
  14.                 bi_storeCookie();
  15.         if ($bi_Group == $CategoryGroup){
  16.                 if (@$entrytype != 'blog')  $GroupHeaderFmt .= '(:title '.$AsSpacedFunction($bi_Name).':)';
  17.                 $GroupFooterFmt .= $bi_GroupFooterFmt;
  18.         }
  19.         if ($action == 'print'){
  20.                 $GroupPrintHeaderFmt .= $GroupHeaderFmt;
  21.                 $GroupPrintFooterFmt .= $GroupFooterFmt;  #Needed if trying to print tag list.
  22.         }
Figure 1: The relevant code

The elseif block highlighted above is part of a larger block that determines how BlogIt responds to various requests. The if statement on line 291 has to do with the way that BlogIt interacts with PmWiki forms, which has nothing to do with this particular bug. The highlighted block has to do with how BlogIt interacts with the request to view a page that may represent a single blog entry, and it is here that our problem occurs.

In this block, we check that the page actually does have a blog entry -- $entrytype == 'blog' -- and that the user wants to see it. We then check to see whether the entry is a draft and whether the user has the right to view the entry before inserting some custom markup that will ensure that PmWiki prints the blog entry as specified by the skin template (or the core template, if no skin template is provided).

Ah, but what if the user wants to print the entry?

The only special thing that we do if action == 'print' is shown on line 303. Here, we ensure that we append the appropriate markup to the page header and page footer so that the tag list associated with the entry renders correctly. Nowhere do we provide instructions on how to render the entry if action is 'print'. In theory, PmWiki should do the same thing for the printer-friendly version of the blog that it does for the one displayed on-screen: namely, render it. Because we only insert the proper markup for this if action is 'browse', PmWiki ends up discarding the text of our post in its entirety.

Why does PmWiki discard it? I don't know. I attempted to trace the text of a blog entry through the somewhat-convoluted rendering process, but all I was able to discover is that the text of the entry disappears inside of MarkupToHTML() as it loops through processing its markup rules.

Fixing the Problem

Fortunately, we don't need to know everything about why the problem occurs in order to fix it. If the markup is not being included when action == 'print', then the obvious solution is to simply modify line 291:

  1.         }elseif ($entrytype == 'blog' && ($action=='browse' || $action == 'print')){

Now the markup necessary to ensure that PmWiki can render the text of the blog post will be inserted in both normal and printer-friendly views.

This works, but the section we instruct PmWiki to include on line 296 -- #single-entry-view -- will likely include the "Edit" link if we are using Site.BlogIt-CoreTemplate to structure the layout. This may not be what we want when preparing the entry for printing.

Structuring the Printer-Friendly Output

What if we want finer control over the printer-friendly output?

If you have ever looked at Site.BlogIt-CoreTemplate, you will see that there are already two layouts defined for rendering posts: single-entry-view and multi-entry-view, which control how blog entries are shown singly and in lists. Skin creators can modify how blog entries are shown by modifying copies of Site.BlogIt-CoreTemplate; more details can be found on the BlogIt project page. We are not interested in creating a skin, however; we just want to control how a blog entry is rendered for print.

If there is a section that controls how entries are displayed by themselves, and one to control how they are displayed in lists, it seems only natural to conclude that we can control how they are displayed for printing by adding a third section: print-view. To support this section, we need to modify bi_HandleBrowse() a little bit more:

  1. }elseif ($entrytype == 'blog' && ($action == 'browse' || $action == 'print')) {
  2.         bi_storeCookie();
  3.         $bi_EntryStatus = PageTextVar($src,'entrystatus');
  4.         $bi_AuthEditAdmin = bi_Auth('blog-edit,blog-new,blogit-admin');
  5.         if ( ($bi_EntryStatus!='draft' && (!bi_FuturePost($Now) || $bi_AuthEditAdmin) ) || ($bi_EntryStatus=='draft' && $bi_AuthEditAdmin) )
  6.         if ($action == 'browse')
  7.             $GroupHeaderFmt .= '(:includesection "#single-entry-view":)';  #Required for action=browse AND comments when redirected on error (in which case $action=pmform).
  8.        else
  9.             $GroupHeaderFmt .= '(:includesection "#print-view":)';
  10. }else
  11.     bi_storeCookie();

Here we've added code on line 296 to check whether the user has requested the normal or printer-friendly views of the entry and then ensure a reference to the appropriate template section is included. Now all we need to do is define the print-view section.

The print-view Section

You never want to edit Site.BlogIt-CoreTemplate directly because it will cause problems when it comes time to upgrade to the latest version. Instead, you want to create a page named Site.BlogIt-SkinTemplate-{SkinName}. The default skin for PmWiki is called, appropriately enough, pmwiki; therefore, if you wanted to define the print-view section for the default site layout, you would create a page named Site.BlogIt-SkinTemplate-pmwiki. To create the print-view section, you'll need to include the following text on that page:

!!! #print-view
[@
(:if false:)[[#print-view]](:if:)
(:div9990 class="blogit-post":)
(:div9999 class="blogit-meta-data-head":)
!!!!! (:blogit-skin author pre_text='$[By] ' post_text=', $[on] ':){*$:entryauthor}(:blogit-skinend:)\
%blogit-date%(:blogit-skin date fmt='long':){*$:entrydate}(:blogit-skinend:)
(:div9999end:)
(:div9991 class="blogit-post-body":)
{*$:entrybody}
(:div9991end:)
(:div9999 class="blogit-meta-data-footer":)
(:blogit-skin tags pre_text='!!!!!%block blogit-tags%$[Tags:] ' post_text='%%':){*$:entrytags}(:blogit-skinend:)
(:blogit-skin commentcount status='{*$:entrycomments}' pre_text='!!!!!%block blogit-comment-count%' post_text='%%' group='{*$Group}' name='{*$Name}':) $[comment(s)](:blogit-skinend:)
(:div9999end:)
(:div9990end:)
(:includesection "#comments-pagelist pagename={*$Name} group={*$Group} entrycomments={*$:entrycomments} divid=commentblock":)
[[#print-viewend]]
@]

This is essentially the same layout as that defined by single-entry-view, except that I've removed the "Edit" link, since it serves no purpose in a printer-friendly view that will, presumably, be printed in some fashion. You may find other alterations that you would like to make.

And that's all there is to it, really! Thanks for reading, and if you'd like to have a printed copy of this entry -- well, now you can.