<?php if (!defined('PmWiki')) exit();

/*

   KOB.PHP - Kind-of Blog, a simple blog for PmWiki.

   Copyright (C) 2005 Ryan R. Varick <rvarick@indiana.edu>

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2 of
   the License, or (at your option) any later version.


   DESCRIPTION:

   The basic components of KOB are:

   1) Posting     - (:blogpostbox:)
   2) Digest      - (:blogdigest group=GROUP:)
   3) Archive     - (:blogarchive group=GROUP monthsback=AA wrap=B:)
   4) Syndication - ?action=blogrss


   RELEASE HISTORY:

   v1.1.0-beta1 released December 12th, 2005.

   Kind-of Blog is based on two existing PmWiki recipes:

     1) Wiki Calendar, v0.3.22 by John Rank <john.rankin@affinity.co.nz>
     2) RSS Simple, v1.1 by Steffen Burmeister <der@dolph.de>

   See the CHANGELOG for all the gory details.


   TODO:
     - Globalize RSS configuration

     - digest by month
     - limit digest entries by date
     - extract titles only

     - sync. post with PmWiki post
         - ese EditTitle.php to customize the edit form
     - edit directive with GUI

     - name-based storage model
         - seperate pages for every entry
	 - move to attributes-based system

     - Audit the global variable calls
     - Audit the utility functions
     - Audit archive styles

 */







/* =============== Configuration =============== */

SDV($max_blog_entries, 6);

// This configures how weekdays are presented.
// 0 - american style, Sun to Sat
// 1 - european style, Mon to Sun
SDV($calendar_weekstyle, 0); // FIXME: (1, sun does not show up)

// How many months after we should wrap and start a new row.
SDV($calendar_month_wrap, 2);

// How many months altogether to show on the calendar.
SDV($calendar_months_number, 12);

// Whether to display non-existent date entries in the calendar as day or day?
// Enter 'true' to use the wiki '?' convention for non-existent entries
SDV($day_as_wiki, false);

// Set depending on whether you need [[Page?logdate= or [[Page&logdate=
SDV($url_connector,"?");



// This configures how dates are presented. (NOT IN USE RIGHT NOW)
// 0 - american style, 11/17/2002
// 1 - european style, 17.11.2002
// 2 - international style, 2002-11-17
SDV($calendar_datestyle,0);
SDV($space_date_titles,1);
SDV($SpaceDateString,'-');



/* --------------- Blog RSS Configuration --------------- */

SDV($RssTitle,     "Your Feed Title");
SDV($RssLink,      "http://www.yourdomain.com");
SDV($RssDesc,      "Your Feed Description");
SDV($RssCopyright, "Your Copyright Information");
SDV($RssEditor,    "you@yourdomain.com");
SDV($RssWebMaster, "you@yourdomain.com");

SDV($RssLanguage,  "en-US");

// You probably shouldn't have to change these
SDV($RssMaxItems,  0);
SDV($RssItems,     array());

// Function to call when the RSS action is triggered
SDV($HandleActions['blogrss'],'HandleBlogRss');



/* --------------- Posting --------------- */

// TODO: add option for GUI controls
// TODO: ^ tie this to the default PmWiki edit mechanism
SDV($BlogFormFmt, "
  <div id='wikiedit'>
    <a id='top' name='top'></a>
    <h2>\$BlogTitle</h2>
    <form action='\$PageUrl' method='post'>
	<input type='hidden' name='n' value='\$FullName' />
	<input type='hidden' name='action' value='postentry' />
	<input type='hidden' name='order' value='\$Chrono' />
	<select name='storydate'>\$StoryDate</select><br />
	<input type='text' name='headline' value='' size='50' /><br />
      <textarea id='text' name='text' rows='30' cols='60'></textarea><br />
      <input type='submit' name='preview' class='butn' value='$[Preview]' accesskey='p' />
      <input type='submit' name='post' class='butn' value=' $[Save] ' accesskey='s' />
    </form>
  </div>");



/* --------------- Stylesheet and Appearance --------------- */

// NOTE: To change the way blog entries are formatted, look at
//  the format_kob_entrystart() function -- you can do whatever you
//  want in there.  In the future, I plan to support the most popular
//  options as here.

// bugfix - v1.1.0 - $FarmPubDirUrl -> $PubDirUrl
$HTMLHeaderFmt[] = 
  "<link rel='stylesheet' href='\$PubDirUrl/css/kob.css' type='text/css' />";

// Blog entry style rules
$blogTimestampStyle = 'blog-timestamp';      // Class used by timestamps
$blogPermalinkStyle = 'blog-permalink';      // Class used by permalinks

// Alternating classes seperating entries
$blogEntry1 = 'blog-entry1';
$blogEntry2 = 'blog-entry2';

// Archive style rules (not yet in use)
$blogTodayEntryStyle      = 'blog-today-entry';       // Current date, with entries
$blogTodayNoentryStyle    = 'blog-today-noentry';     // Current date, no entry
$blogDateEntryStyle       = 'blog-date-entry';        // Other date w/an entry
$blogDateNoentryStyle     = 'blog-date-noentry';      // Other date w/out an entry
$blogBlankCellStyle       = 'blog-blank-cell';        // Empty cells at the start/end of months
$blogNonexistentDateStyle = 'blog-nonexistent-date';  // Wikilink w/out an entry

// This is where we format the way the blog entry appears --
//  Move things around, add text, etc, just make sure the variables
//  appear in there somewhwere
//
// For date formatting, arrange the variables, as outlined here:  
//  http://us3.php.net/strftime
//
// NOTE: The heading expects wiki markup, while the date and permalink
//  are mixes of HTML and markup.  I'm having trouble with divs in
//  markup, so this will have to do for now.  It'll be fixed in a
//  later version.
//
// NOTE: If you want the permalink before the timestamp, just reorder
//  the array elements!
//
$kobEntryFmt = array(
  title     => '!!! $title',
  timestamp => '<div class="$blogTimestampStyle">Posted on %b. %d, %Y at %I:%M %p</div>',
  permalink => '<div class="$blogPermalinkStyle">[[$permalink | Permanent link]]</div>' );



/* --------------- Translations --------------- */

// Month and day names in chosen language
$shortdaynames = array('Sun','Mon','Tue','Wed','Thu','Fri','Sat');
$longdaynames  = array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday');

$monthnames = array('January',
		    'February',
		    'March',
		    'April',
		    'May',
		    'June',
		    'July',
		    'August',
		    'September',
		    'October',
		    'November',
		    'December'); 

// TODO: Figure out which of these is in use
$period            = 'period';
$default_date_text = 'From the wiki calendar';
$WikilogHomeText   = 'Calendar';
$DefaultBlogTitle  = 'New Blog Entry';

// Submit form text
$DateText     = 'Date';
$HeadlineText = 'Headline';
$StoryText    = 'Story';
$AuthorText   = 'Author';
$PostText     = ' Post ';
$ResetText    = 'Reset';







/* ==================== CODE STARTS HERE ===================== */



/* --------------- Viewing --------------- */

// NOTE: Viewing blog entries involves these two directives.
//  The first opens a containing div, parses the entry's arguments 
//  and formats them accordingly.  The second closes the containing
//  div.  The actual blog entry is handled by PmWiki as normal.
//  So to edit how timestamps and permalinks appear, you'll want
//  to play around with the format_kob_entrystart() function below.

// NOTE: See notes in the configuration section above.
Markup('blogentry', 'directives',
       "/\(:blogentry\s+(.*):\)/e",
       "format_kob_entrystart(PSS('$1'))");

Markup('blogentryend', 'directives',
       "/\(:blogentryend:\)/e",
       "format_kob_entryend()");



/**
 * FORMAT_KOB_ENTRYSTART - Formats blog entry metadata.
 *
 *  args:    raw arguments
 *  returns: formatted string of HTML
 *
 */
function format_kob_entrystart($match)
{
  global $even_entry, $blogEntry1, $blogEntry2;
  global $kobEntryFmt;
  global $blogTimestampStyle, $blogPermalinkStyle;

  // Parse the raw arguments
  $args = ParseArgs($match);

  // Make a copy of the entry format
  $entryFmt = $kobEntryFmt;



  // Alternate between container div classes
  if($even_entry == false) {
    $num = 1;
    $even_entry = true;
  }
  else {
    $num = 2;
    $even_entry = false;
  }
  $class = 'blogEntry' . $num;

  // Start building the string here (wrap the entry in
  //  a div; this will be closed by format_kob_entryend())
  $string = "<div class=\"${$class}\">\n";



  // These statements look pretty complicated -- we have some generic
  //  formats in $entryFmt.  All we're doing is replacing the
  //  variables with the real values from the entry (in $args).
  //
  // TODO: I think $FmtV[] would be useful here... maybe next time ;-)
  //

  $entryFmt['title']     = MarkupToHTML('', str_replace('$title', $args['title'], $entryFmt['title']));

  $entryFmt['timestamp'] = strftime($entryFmt['timestamp'], strtotime($args['time']));
  $entryFmt['timestamp'] = str_replace('$blogTimestampStyle', $blogTimestampStyle, $entryFmt['timestamp']);

  $entryFmt['permalink'] = str_replace('$permalink', $args['permalink'], $entryFmt['permalink']);
  $entryFmt['permalink'] = str_replace('$blogPermalinkStyle', $blogPermalinkStyle, $entryFmt['permalink']);



  // Add all the formatted pieces to the string -- since they're in
  //  an associative array, we can just reorder array elements to
  //  automagically reorder the print order... pretty cool!
  foreach($entryFmt as $entry => $value)
  {
    $string .= $value . "\n";
  }

  return $string;
}



/**
 * FORMAT_KOB_ENTRYEND - Adds any closing formatting.
 *
 *  args:    none
 *  returns: formatted string of wiki markup
 *
 *  NOTE: This usually returns just the closing div tag.
 *
 */
function format_kob_entryend()
{
  return "</div>\n";
}







/* ----------------- Posting ----------------*/

// Handle incoming entries from the post form
if($action == 'postentry')
{
  SDV($HandleActions['postentry'],'HandleBlogPost');
}

// Handle the original form
else
{
  // This expands the (:blogpostbox:) directive to the actual wiki-code
  Markup('wbox', '>links',
	 '/\(:blogpostbox(chrono)?\s*(.*?):\)/e',
	 "'<:block>'.Keep(str_replace('\$BlogTitle',SetBlogTitle('$2'),
            str_replace('\$StoryDate',select_calendar_date(\$pagename),
            str_replace('\$Chrono','$1',
            FmtPageName(\$GLOBALS['BlogFormFmt'],\$pagename)))))");

  // TODO: When the markup conversion is triggered, we should also
  //  assert edit mode, so skins can adjust the skin accordingly
}



/**
 * HANDLE_BLOG_POST - Take post data and create a new blog entry.
 *
 *  args:    pagename
 *  returns: nothing
 *
 *  entry format:
 *
 *   (:blogentry title="TITLE" time="ISO" permalink="GROUP.PAGE":)
 *    WIKI MARKUP
 *   (:blogentryend:)
 *
 */
function HandleBlogPost($pagename) 
{
   global $_POST, $TimeFmt, $SpaceDateString, $blogTimestampStyle, $blogPermalinkStyle;

   // Use the date to determine the blog entry's page name
   $date = str_replace($SpaceDateString, '', $_POST['storydate']);
   $blog_entry_name = FmtPageName('$Group', $pagename) . ".$date";

   // Get the entry title
   $blog_entry_title = $_POST['headline'];

   // Use the date from the post form to build the timestamp
   $blog_entry_date = $_POST['storydate'];
   $blog_entry_time = strftime("%H:%M", time());
   $blog_entry_unix_time = strtotime("$blog_entry_date $blog_entry_time");
   $blog_entry_timestamp = strftime("%Y-%m-%d %H:%M", $blog_entry_unix_time);


   // Stitch the components together to form the complete blog entry
   $blog_entry = 
     "(:blogentry title=\"$blog_entry_title\" time=\"$blog_entry_timestamp\" permalink=\"$blog_entry_name\":)\n" . 
     $_POST['text'] . "\n" . 
     "(:blogentryend:)\n\n\n\n";


   // If a blog entry already exists, append the latest post
   //  TODO: We could create seperate pages for each entry
   //   here, if we so desired
   if(PageExists($blog_entry_name)) 
   {
     $page = RetrieveAuthPage($blog_entry_name, "edit");

     // TODO: If we decide we want to offer order flexibility, here's
     // where we could reorder the append mechanism
     $_POST['text'] = $blog_entry . $page['text'];
   }
   else 
   {
     $_POST['text'] = $blog_entry;
   }


   // Now that we have the updated blog entry in the POST variable,
   //  kick over to PmWiki for the actual file I/O.
   HandleEdit($blog_entry_name);
}







/* --------------- Digest --------------- */

// Handle the digest much like the archive above
Markup('blogdigest', 'directives',
       "/^\(:blogdigest?(\s+group=($GroupPattern)){0,1}+(\s+startdate=([0-9]{8})){0,1}+(\s+weeksback=([0-9]{1,3})){0,1}:\)\\s*$/e",
       "Keep('<:block>' . view_blog_digest('$2','$4','$6'),'L')");



/**
 * VIEW_BLOG_DIGEST - 
 *
 *
 */
//function view_blog_digest($group, $start, $months)
function view_blog_digest($group)
{
  global $pagename;
  
  // Check for (:blogdigest:) with no GROUP
  if($group == '')
  {
    $pieces = explode('.', $pagename);
    $group  = $pieces[0];
  }

  $digest = get_raw_digest($group);
  $html   = MarkupToHTML($pagename, $digest);

  return $html;
}







/* --------------- Archive --------------- */


// Handle the archive with a markup replacement:
//
// 1.0.3 - This will match (:blogarchive:), or (:blogarchive GROUP:),
//  with (:blogarchive GROUP[.\]PAGE:) for legacy support
//
// 1.1.0 - Removed [.\]PAGE support to simplify the regexp, added
//  months= and wrap=; GROUP now uses group=
//
// Regexp matches:
//  0 -> full match
//  1 -> group=GROUP
//  2 -> GROUP
//  3 -> months=AA
//  4 -> AA
//  5 -> wrap=B
//  6 -> B
//
Markup('blogarchive', 'directives',
       "/^\(:blogarchive?(\s+group=($GroupPattern)){0,1}+(\s+monthsback=([0-9]{1,2})){0,1}+(\s+wrap=([0-9]{1})){0,1}:\)\\s*$/e",
       "view_blog_archive('$2','$4','$6')");



/**
 * VIEW_BLOG_ARCHIVE - Returns a graphical view of recent blog entries
 *
 *  args:    group to scan, months to show, wrapping
 *  returns: string of html markup containing the graphical view
 *
 */
function view_blog_archive($group, $months, $wrap) {

  global $calendar_months_start, $calendar_months_number,$calendar_month_wrap;
  global $wikilog_home_page, $summary_only, $logdate, $PublishCalendarFmt;
  global $newest_first, $display_log_entries, $enable_publish, $pagename;


  // Check for a group parameter
  if($group == '')
  {
    $pieces = explode('.', $pagename);
    $group  = $pieces[0];
  }

  // Check for a month parameter
  $num_months = 0;
  if($months == '')
  {
    $num_months = $calendar_months_number;
  }
  else
  {
    $num_months = $months;
  }

  // Check for the wrap parameter
  $wrap_entries = 0;
  if($wrap == '')
  {
    $wrap_entries = $calendar_month_wrap;
  }
  else
  {
    $wrap_entries = $wrap;
  }


  // Something about date formatting
  $title = FmtPageName('$Name', $pagename);
  $startingdate = $logdate."16";
  if(IsDate($startingdate))
  {
    $year = substr($startingdate,0,4);
    $month= substr($startingdate,4,2);
  } 
  else 
  {
    if (IsDate($title))
    {
      $year = substr($title,0,4);
      $month= substr($title,4,2);
    } 
    else
    {
      $year = date("Y");
      $month= date("m");
    }
  }
  $startingtime = mktime(0,0,0,$month,16,$year);

  // generate the calendar navigation bar
  // TODO: add conditional here
  // TODO: this needs to be made aware of the number of months too
  // TODO: this doesn't even work right now (navigation)
  //  $r = calendar_nav_bar($pagename,$startingtime,$num_months);

  // Start the calendar
  $r .= "<table><tr>\n";

  $itime = $startingtime;
  $i = 0;
  $r_list = '';

  // Build each month
  while($i < $num_months) 
  {
    $r_mon  = "<td class='calendar-outer'>";  // re-init the month string
    $r_mon .= calendar_month(strftime("%m",$itime),strftime("%Y",$itime), $group);
    $r_mon .= "</td>";

    $itime -= 2592000;
    $i++;

    // Check if we should wrap and start a new row
    if(($i % $wrap_entries) == 0)
    {
      $r_mon = $r_mon . "</tr><tr>";
    }

    // Append the month to the list
    $r_list .= $r_mon;
  }

  // Finish the calendar and return
  $r .= $r_list . "</tr></table>";
  return $r;
}







/* --------------- Syndication --------------- */



/**
 * HANDLE_BLOG_RSS - Build an RSS 2.0 feed of a digest page.
 *
 *  args:     ???
 *  returns:  XML string (RSS 2.0)
 *
 */
function HandleBlogRss($pagename, $auth='read') {

  global $RssMaxItems,$RssEditor, $RssDesc,
    $RssLink, $RssTitle, $RssLanguage, $RssCopyright, $RssEditor,
    $RssWebMaster, $RssChannelFmt, $RssChannelDesc,
    $RssItems,$RssItemFmt, $RssChannelBuildDate, 
    $blogPermalinkStyle, $blogTimestampStyle,
    $HandleRssFmt,$FmtV,$ScriptUrl,$Group,$Name;

  // RSS 2.0 definitions
  SDV($RssChannelFmt, '<?xml version="1.0"?>
  <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
    <channel>
      <title>' . $RssTitle . '</title>
      <link>' . $RssLink . '</link>
      <description>' . $RssDesc . '</description>
      <language>' . $RssLanguage . '</language>
      <copyright>' . $RssCopyright . '</copyright>
      <managingEditor>' . $RssEditor . '</managingEditor>
      <webMaster>' . $RssWebMaster . '</webMaster>
      <lastBuildDate>$RssChannelBuildDate</lastBuildDate>
      <generator>Kind-of Blog</generator>');

  SDV($RssItemFmt,'
      <item>
        <title>$RssItemTitle</title>
        <link>$RssItemLink</link>
        <pubDate>$RssItemPubDate</pubDate>
        <description>$RssItemDesc</description>
      </item>');

  SDV($HandleRssFmt, array(&$RssChannelFmt, &$RssItems, "\n    </channel>\n  </rss>"));



  // Security -- don't try to build a feed for restricted pages
  $page = RetrieveAuthPage($pagename, $auth, false, READPAGE_CURRENT);
  if (!$page) Abort("?cannot read $pagename");

  // Get the page and group
  $parts = explode('.', $pagename);
  $blog_group = $parts[0];
  $blog_page  = $parts[1];


  // The timestamp for the RSS feed
  $cbgmt = $page['time'];
  SDV($RssChannelBuildDate, gmdate('D, d M Y H:i:s \G\M\T', $cbgmt));

  // Get the raw digest for RSS transformation
  $source = get_raw_digest($blog_group);

  // Taken together, the regex parses the digest into:
  //  match[0] = selection (entire string, not very useful)
  //  match[1] = raw args
  //  match[2] = entry
  $entryRegEx = "/\(:blogentry\s+(.*?):\)(.*?)(?=\(:blogentryend:\))/s";
  $num_matches = preg_match_all($entryRegEx, $source, $match, PREG_SET_ORDER);

  // DEBUG: It's always helpful to see what your regex is:
  //  print "<p>$entryRegEx</p>";
  //  print "<pre>Found $num_matches matches " . $match[0][2] . "</pre>";


  // Go through each match and format the pieces for RSS
  for($i = 0; $i < $num_matches; $i++) {

    // Parse the raw arguments
    $args = ParseArgs($match[$i][1]);

    $title = $args['title'];
    $time  = $args['time'];
    $link  = $args['permalink'];
    $description = MarkupToHTML($pagename, ltrim(rtrim($match[$i][2])));

    // Parse the permanent link for the pagename (numeric) only
    $cnt   = preg_match('/.*?([0-9]{8})/', $link, $lm);
    $cnt   = preg_match('/(.*?)\./', $pagename, $pm);
    $page  = $lm[1];
    $group = $pm[1];
    $link  = $ScriptUrl ."/". $group . "/" . $page;

    // Build the item's RSS tags
    $FmtV['$RssItemTitle']  = $title;
    $FmtV['$RssItemLink']   = $link;
    $FmtV['$RssItemDesc']   = $description;
    $FmtV['$RssItemAuthor'] = $RssEditor;

    // Use the timestamp and the pagename to get the time components
    //  FIXME: this is really shaky, verify time is working properly
    $cnt   = preg_match('/.*?([0-9]{2}):([0-9]{2}).*?/', $time, $rm);
    $hour  = $rm[1];
    $min   = $rm[2];
    $year  = substr($page, 0, 4);
    $month = substr($page, 4, 2);
    $day   = substr($page, 6, 2);

    $gmt   = gmdate("r", mktime($hour, $min, 0, $month, $day, $year));
    $FmtV['$RssItemPubDate'] = $gmt;



    // Encoding (optional)
    //    $RssItems[] = entityencode(FmtPageName($RssItemFmt, $title));
    $RssItems[] = FmtPageName($RssItemFmt, $title);
  }


  // Here is where the RSS feed is sent to the client
  header("Content-type: text/xml");
  PrintFmt($pagename, $HandleRssFmt);
}







/* --------------- Miscellaneous Stuff --------------- */

// Allow page names to be all numerics.
$PageNamePattern = "[0-9]{8}";

// process date links
Markup('datelink','>inline',
       "/\[\[($GroupPattern(?:[\/.])$PageNamePattern)\|(.*?)\]\]/e",
       "Keep(MakeDateLink(\$pagename,'$1','$2'),'L')");

// bugfix - 1.0.3 - Fixed function call (londate -> longdate)
Markup('daylink','>inline',
       "/\[\[((?:$GroupPattern\/)?($PageNamePattern))\]\]/e",
       "Keep(MakeDateLink(\$pagename,'$1',((IsDate('$2')) ? longdate('$2') : '$2')),'L')");

//
//  bugfix 1.1.1 - delayed execution to prevent links from being
//                 processed as dates
//
SDV($DateSeparatorPattern,'[-.\/]');
Markup('wdate','block',"/(\d\d\d\d)($DateSeparatorPattern)(\d\d)\\2(\d\d)/e",
       "((IsDate('$1$3$4')) ? longdate('$1$3$4') : '$0')");







/* =============== UTILITY FUNCTIONS =============== */

// These functions support the high-level functions above, 
// for the most part, they are copied verbatim from my original
// hack, and the original authors' code.

/**
 * GET_RAW_DIGEST - Returns digest in wiki markup format
 *
 *  args:    group to scan
 *  returns: string of wiki markup
 *
 */
function get_raw_digest($group)
{
  global $calendar_months_number, $max_blog_entries;

  $digest = "";
  $entries_found = 0;

  // Get the current time, then loop backward by month
  //  looking for entries
  $itime = mktime(0, 0, 0, date('m'), 16, date('Y'));
  $i = 0;
  for($i = $calendar_months_number; $i > 0; $i--) {

    $month = strftime("%m", $itime);
    $year  = strftime("%Y", $itime);

    // This decriments the time, I don't really know how
    $itime = $itime - 2592000;

    // Start at the last day of the month and work backward
    //  looking for blog entries
    $j = 0;
    $prefix = $group . "/" . $year . $month;
    $last_day_of_month = strftime("%d", mktime(0, 0, 0, $month+1, 0, $year));
    for($j = $last_day_of_month; $j > 0; $j--) {

      // Format the date a bit
      if ($j < 10) { $day ="0".$j; } else { $day=$j; }

      $entryname= $prefix . $day;

      // See if there is a blog entry for the current date, and, if so
      //  pluck it out and append it to the list
      if(PageExists($entryname) && ($entries_found < $max_blog_entries)) {

	// Read the page content
	$snippet  = ReadPage($entryname);
	$stuffing = $snippet['text'];

	// Count and match the individual entries
	//  FIXME: globalize this pattern
	$entryRegEx = "/\(:blogentry\s+(.*?):\)(.*?)(?=\(:blogentryend:\))/s";
	$num_matches = preg_match_all($entryRegEx, $stuffing, $match, PREG_SET_ORDER);

	for($k = 0; $k < $num_matches; $k++) {

	  if($entries_found < $max_blog_entries) {
	    $digest .= $match[$k][0] . "(:blogentryend:)\n\n";
	    $entries_found++;
	  }
	  
	}



	// We want a dividing line between entries.  But we don't want one
	// after the last entry, and we don't want a hanging divider if we can't
	// find max_entries.  So I figure we can safely add a divider after we've
	// at least one entry
	//	if($entries_found > 0) {
	//	  $digest .= "\n----\n";
	//	}
	//	$entries_found++;


	//	$digest  .= $stuffing;
      }
    }
  }

  return $digest;
}







/* --------------- ... --------------- */

function home_link($wikiloghome) {
  global $WikilogHomeText, $HTMLVSpace;
  $WikilogHomeLink= ($WikilogHomeText=='') ? "[[$wikiloghome]]" :
    "[[$wikiloghome | $WikilogHomeText]]";
  return "$HTMLVSpace<p class='wikiloghome'>$WikilogHomeLink:</p>";
}



function calendar_month($month,$year,$group) {
  global $pagename, $monthnames, $shortdaynames, $calendar_weekstyle;

  $prefix = $group . "/" . $year . $month;
  $todays_time=mktime(0,0,0,date("m"),date("d"),date("Y"));
  $last_day_of_month=strftime("%d",mktime(0,0,0,$month+1,0,$year));
  $r="<table class='calendar-inner'>";

  // header with month and year
  // TODO: monthly link to digest here
  $my_date = $year . ($month - 1);
  //  $r.="<caption><a href=\"n=$Group.$Page?action=kobdigest&month=$my_date\">". $monthnames[$month-1] . " ". $year . "</a></caption>";
  $r.="<caption>" . $monthnames[$month-1] . " ". $year . "</caption>";

  // weekday names
  $r.="<tr>";
  for ($i=$calendar_weekstyle;$i<7+$calendar_weekstyle;$i++)
    {
      $r.="<th>" . $shortdaynames[$i] . "</th>";
    }
  $r.="</tr>";

  $count=0;
  // pre-padding
  $r.="<tr>";
  for ($i=0;$i<(strftime("%w",mktime(0,0,0,$month,1,$year))-$calendar_weekstyle+7)%7;$i++)
    {
      $r.="<td class='calendar-blank'>&nbsp;</td>";
      $count++;
    }

  // days
  for ($i=1;$i<=$last_day_of_month;$i++) {

    if ($count%7==0 && $count>0) $r.="</tr><tr>";
    if ($i<10) { $day="0".$i; } else { $day=$i; }
    $entryname= $prefix . $day;
    $r.="<td class='calendar-";
    if (abs($todays_time - mktime(0,0,0,$month,$i,$year)) < 86400) {
      $r.="today-";
    }
    if (PageExists($entryname)) {
      $r.="entry'><b>[[". $entryname . "|" . $day . "]]</b>";
    } else {
      $r.="noentry'>[[". $entryname . "|" . $day . "]]";
    }
    $r.="</td>";
    $count++;
  }

  // post-padding
  while ($count%7!=0) {
    $r.="<td class='calendar-blank'>&nbsp;</td>";
    $count++;
  }
  return $r."</tr></table>\n";
}

function SetBlogTitle($text) {
  global $DefaultBlogTitle;
  return ($text=='') ? $DefaultBlogTitle : $text;
}


function IsDate($title) {
  if (is_numeric($title) && strlen($title) == 8) {
    $year = substr($title,0,4);
    $month= substr($title,4,2);
    $day  = substr($title,6,2);
    return checkdate($month, $day, $year);
  } else {
    return false;
  }
}


function longdate($title) {
  global $calendar_datestyle, $monthnames, $longdaynames;
  $year = substr($title,0,4);
  $month= substr($title,4,2);
  $day  = substr($title,6,2);
  if ($day[0] == "0") { $day = $day[1]; }
  $longmonth = $monthnames[$month-1];
  $r = ($calendar_datestyle == 0) ? ", $longmonth $day, " : 
    ", $day $longmonth ";	
  $dayname=$longdaynames[strftime("%w",mktime(0,0,0,$month,$day,$year))];
	return "$dayname$r$year";
}

function calendar_nav_bar($pagename,$startingtime,$months) {
  //  global $calendar_months_number;
  $calendar_months_number = $months;



  $currentmonth= date("Y") . date("m");
  $lasttime =$startingtime-$calendar_months_number*2592000;
  $lastmonth= strftime("%Y",$lasttime) . strftime("%m",$lasttime);
  $nexttime =$startingtime+$calendar_months_number*2592000;
  $nextmonth=strftime("%Y",$nexttime) . strftime("%m",$nexttime);
  $r = "<p class='datetrail'>&laquo; ";
  if ($currentmonth < $lastmonth)
    $r.=calendar_nav($pagename,$currentmonth,true) . " &middot; ";
  $r.=calendar_nav($pagename,$lastmonth,false) . " &middot; " . 
    calendar_nav($pagename,$nextmonth,false);
  if ($currentmonth > $nextmonth)
    $r.=" &middot; " . calendar_nav($pagename,$currentmonth,true);
  return $r . " &raquo;</p>\n";
}

function calendar_nav($pagename,$yearmo,$highlight) {
  global $calendar_months_number, $monthnames, $period, $url_connector;
  $year = substr($yearmo,0,4);
  $r = "[[$pagename$url_connector"."logdate=$yearmo | ";
  $mname = $monthnames[substr($yearmo,4,2)-1] . " " . $year;
  if ($calendar_months_number == 1) $mname .= " " . $period;
  if ($highlight) $mname = highlight($mname);
  return "$r$mname]]";
}

function highlight($text) {
  return "<span class='currentmonth'>$text</span>";
}

function shortdate($year, $month, $day) {
  global $calendar_datestyle;
  switch ($calendar_datestyle) {
  case 0:
    return "$month/$day/$year";
  case 1:
    return "$day.$month.$year";
  case 2:
    return "$year&ndash;$month&ndash;$day";
  }
}

function show_date($page) {
  global $default_date_text;
  $title = FmtPageName('$Name',$page);
  return (IsDate($title)) ? "$default_date_text: ".longdate($title) : $default_date_text;
}

function MakeDateLink($pagename,$ref,$btext) {
  global $LinkPageCreateFmt,$day_as_wiki;

  $WikiDateCreateFmt = 
    "<a class='nonexistent-date' href='\$PageUrl?action=edit'>\$LinkText</a>";


  if ($day_as_wiki==true) 
    return MakeLink($pagename,$ref,$btext);
  $hold = $LinkPageCreateFmt;
  $LinkPageCreateFmt = $WikiDateCreateFmt;
  $r = MakeLink($pagename,$ref,$btext);
  $LinkPageCreateFmt = $hold;
  return $r;
}


function select_calendar_date($pagename) {
  global $calendar_months_start, $calendar_months_number,$calendar_month_wrap;
  global $logdate;

  // start configured number of months before/ahead
  $startingdate = $logdate."16";
  if (IsDate($startingdate)) {
    $year = substr($startingdate,0,4);
    $month= substr($startingdate,4,2);
  } else {
    $year = date("Y");
    $month= date("m");
  }
  $startingtime = mktime(0,0,0,$month,16,$year);

  // generate the calendar
  $r="";
  $itime=$startingtime;
  $i=0;
  while ($i<$calendar_months_number) {
    $r.=calendar_days(strftime("%m",$itime),strftime("%Y",$itime));
    // +1 month
    // It's actually 30 days which could break if displaying a lot of months
    // but it should be ok when displaying only one or two years at a time
    $itime=$itime+2592000;
    $i++;
  }

  return $r;
}

function calendar_days($month,$year) {
  global $SpaceDateString;
  $todays_time=mktime(0,0,0,date("m"),date("d"),date("Y"));
  $last_day_of_month=strftime("%d",mktime(0,0,0,$month+1,0,$year));
  $r='';
  // days
  for ($i=1;$i<=$last_day_of_month;$i++) {
    if ($i<10) { $day="0".$i; } else { $day=$i; }
    $entry= "$year$SpaceDateString$month$SpaceDateString$day";
    $s = (abs($todays_time - mktime(0,0,0,$month,$i,$year)) < 86400) ?
      " selected='selected'" : '';
    $r.="<option value='$entry'$s>$entry</option>";
  }
  return $r;
}