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

# Author:  Martin Fick
# Version: 1.1
# Date:    7/15/2006
#
#
# This recipe defines these new pagelist options:
#
#   groupcount     specify how many groups to iterate through
#   pergroupcount  specify how many pages per group to iterate through
#   blank          specify a page to include if the pagelist is blank
#   targets        specify a page to get targets from (like trail=)
#
#   Note: the new {$$option} markup is available inside the blank page.
#
# This recipe also defines these new pagelist variables:
#
#   {$PageTotal}   the amount of pages that would have been returned
#                  without a count, groupcount, or pergroupcount
#   {$GroupTotal}  the amount of groups that would have been returned
#                  without a count, groupcount, or pergroupcount
#
# Finally, this recipe defines a special markup {$$option}.  This new
#  markup makes the options used in the pagelist directive available
#  inside of pagelist templates.  The special case {$$} returns the
#  entire option string.  Some examples:
#
#  {$$count} displays the value of the count option
#
#  A custom option: (:pagelist lyric='Hello My Darling' ...:)
#  {$$lyric} displays: Hello My Darling
#
#
# Intalation Notes:
# -----------------
# This recipe surplants the FPLTemplate in pagelist.php.  Be careful
#  with your include ordering if you have other recipes which also
#  alter/extend pagelists.
#

## $FPLFormatOpt is a list of options associated with fmt=
## values.  'default' is used for any undefined values of fmt=.
SDVA($FPLFormatOpt, array(
  'default' => array('fn' => 'FPLTemplateExtensions',
   'fmt' => '#default', 'class' => 'fpltemplate'),
  'bygroup' => array('fn' => 'FPLTemplateExtensions',
   'template' => '#bygroup', 'class' => 'fplbygroup'),
  'simple'  => array('fn' => 'FPLTemplateExtensions',
   'template' => '#simple', 'class' => 'fplsimple'),
  'group'   => array('fn' => 'FPLTemplateExtensions',
   'template' => '#group', 'class' => 'fplgroup'),
  'title'   => array('fn' => 'FPLTemplateExtensions',
   'template' => '#title', 'class' => 'fpltitle', 'order' => 'title'),
  ));

########################################################################
## The function is a modified FPLTemplate from pagelist.php
########################################################################

function FPLTemplateExtensions($pagename, &$matches, $opt) {
  global $Cursor, $FPLFormatOpt, $FPLTemplatePageFmt;
  SDV($FPLTemplatePageFmt, array('{$FullName}',
    '{$SiteGroup}.LocalTemplates','{$SiteGroup}.PageListTemplates'));

  for($i=0; $i< count($opt); $i++) 
    $args[$opt['#'][$i++]] = $opt['#'][$i];
  $template = @$opt['template'];
  if (!$template) $template = @$opt['fmt'];

  list($tname, $qf) = explode('#', $template, 2);
  if ($tname) $tname = array(MakePageName($pagename, $tname));
  else $tname = (array)$FPLTemplatePageFmt;
  foreach ($tname as $t) {
    $t = FmtPageName($t, $pagename);
    if (!PageExists($t)) continue;
    if ($qf) $t .= "#$qf";
    $ttext = IncludeText($pagename, $t, true);
    if (!$qf || strpos($ttext, "[[#$qf]]") !== false) break;
  }

  ##   remove any anchor markups to avoid duplications
  $ttext = preg_replace('/\\[\\[#[A-Za-z][-.:\\w]*\\]\\]/', '', $ttext);

  if (!@$opt['order'] && !@$opt['trail']) $opt['order'] = 'name';
  $matches = array_values(MakePageListExtensions($pagename, $opt, 0));
  $total = count($matches);
  if (@$opt['blank'] && $total ==0) {
    $text = IncludeText($pagename, $opt['blank']);
    $text = preg_replace('/\\{\\$\\$\\}/', $opt['o'], $text);
    $text = preg_replace('/\\{\\$\\$(\\w+)\\}/e',
                '$args["$1"]', $text);
    return $text;
  }

  // Prescan list
  foreach($matches as $i => $pn) {
    $group = PageVar($pn, '$Group');
    if ($group != $lgroup) $grouptotal++;
    $lgroup = $group;
  }
  if (@$opt['count']) array_splice($matches, $opt['count']);
  if (@$opt['groupcount']) $groupmax = $opt['groupcount'];
  if (@$opt['pergroupcount']) $pergroupmax = $opt['pergroupcount'];

  $savecursor = $Cursor;
  $pagecount = 0; $groupcount = 0; $grouppagecount = 0;
  $vk = array('{$PageCount}', '{$GroupCount}', '{$GroupPageCount}',
    '{$PageTotal}', '{$GroupTotal}');
  $vv = array(&$pagecount, &$groupcount, &$grouppagecount, $total,
    $grouptotal);

  $lgroup = ''; $out = '';
  foreach($matches as $i => $pn) {
    $prev = (string)@$matches[$i-1];
    $next = (string)@$matches[$i+1];
    $Cursor['<'] = $Cursor['&lt;'] = $prev;
    $Cursor['='] = $pn;
    $Cursor['>'] = $Cursor['&gt;'] = $next;
    $group = PageVar($pn, '$Group');
    if ($group != $lgroup) { $groupcount++; $grouppagecount = 0; }
    $grouppagecount++; $pagecount++;
    if($groupmax && $groupcount > $groupmax) break;
    if($pergroupmax && $grouppagecount > $pergroupmax) continue;

    $item = str_replace($vk, $vv, $ttext);
    $item = preg_replace('/\\{\\$\\$\\}/', $opt['o'], $item);
    $item = preg_replace('/\\{\\$\\$(\\w+)\\}/e',
                '$args["$1"]', $item);
    $item = preg_replace('/\\{(=|&[lg]t;)(\\$\\w+)\\}/e',
                "PageVar(\$pn, '$2', '$1')", $item);
    $out .= $item;
    $lgroup = $group;
  }
  $class = preg_replace('/[^-a-zA-Z0-9\\x80-\\xff]/', ' ', @$opt['class']);
  $div = ($class) ? "<div class='$class'>" : '<div>';
  return $div.MarkupToHTML($pagename, $out, array('escape' => 0)).'</div>';
}


########################################################################
## The function is a modified MakePageList from pagelist.php
########################################################################

## MakePageListExtensions generates a list of pages using the specifications given
## by $opt.
function MakePageListExtensions($pagename, $opt, $retpages = 1) {
  global $MakePageListOpt, $SearchPatterns, $EnablePageListProtect, $PCache,
    $FmtV;
  StopWatch('MakePageList begin');
  SDVA($MakePageListOpt, array('list' => 'default'));

  $opt = array_merge((array)$MakePageListOpt, $opt);
  $readf = @$opt['readf'];
  # we have to read the page if order= is anything but name
  $order = @$opt['order'];
  $readf |= $order && ($order!='name') && ($order!='-name');

  $pats = @(array)$SearchPatterns[$opt['list']];
  if (@$opt['group']) $pats[] = FixGlob($opt['group'], '$1$2.*');
  if (@$opt['name']) $pats[] = FixGlob($opt['name'], '$1*.$2');

  # inclp/exclp contain words to be included/excluded.  
  $incl = array(); $inclp = array(); $inclx = false;
  $excl = array(); $exclp = ''; 
  foreach((array)@$opt[''] as $i) { $incl[] = $i; }
  foreach((array)@$opt['+'] as $i) { $incl[] = $i; }
  foreach((array)@$opt['-'] as $i) { $excl[] = $i; }
  foreach($incl as $i) {
    $inclp[] = '$'.preg_quote($i).'$i';
    $inclx |= preg_match('[^\\w\\x80-\\xff]', $i);
  }
  if ($excl) $exclp = '$'.implode('|', array_map('preg_quote', $excl)).'$i';

  $searchterms = count($incl) + count($excl);
  $readf += $searchterms;                         # forced read if incl/excl

  if (@$opt['trail']) {
    $trail = ReadTrail($pagename, $opt['trail']);
    $list = array();
    foreach($trail as $tstop) {
      $pn = $tstop['pagename'];
      $list[] = $pn;
      $tstop['parentnames'] = array();
      PCache($pn, $tstop);
    }
    foreach($trail as $tstop) 
      $PCache[$tstop['pagename']]['parentnames'][] =
        @$trail[$tstop['parent']]['pagename'];
  }
  elseif (@$opt['targets']) {
    $pn = $opt['targets'];
    $page = ($readf >= 1000) 
      ? RetrieveAuthPage($pn, 'read', false, READPAGE_CURRENT)
      : ReadPage($pn, READPAGE_CURRENT);
    $list = explode(",", $page['targets']);
  }
  else $list = ListPages($pats);

  if (IsEnabled($EnablePageListProtect, 1)) $readf = 1000;
  $matches = array();
  $FmtV['$MatchSearched'] = count($list);

  $terms = ($incl) ? PageIndexTerms($incl) : array();
  if (@$opt['link']) { 
    $link = MakePageName($pagename, $opt['link']);
    $linkp = "/(^|,)$link(,|$)/i";
    $terms[] = " $link ";
    $readf++;
  }
  if ($terms) {
    $xlist = PageIndexGrep($terms, true);
    $a = count($list);
    $list = array_diff($list, $xlist);
    $a -= count($list);
    StopWatch("MakePageList: PageIndex filtered $a pages");
  }
  $xlist = array();

  StopWatch('MakePageList scanning '.count($list)." pages, readf=$readf");
  foreach((array)$list as $pn) {
    if ($readf) {
      $page = ($readf >= 1000) 
              ? RetrieveAuthPage($pn, 'read', false, READPAGE_CURRENT)
              : ReadPage($pn, READPAGE_CURRENT);
      if (!$page) continue;
      if (@$linkp && !preg_match($linkp, @$page['targets']))
        { $xlist[] = $pn; continue; }
      if ($searchterms) {
        $text = $pn."\n".@$page['targets']."\n".@$page['text'];
        if ($exclp && preg_match($exclp, $text)) continue;
        foreach($inclp as $i) 
          if (!preg_match($i, $text)) 
            { if ($inclx) $xlist[] = $pn; continue 2; }
      }
      $page['size'] = strlen(@$page['text']);
    } else $page = array();
    $page['pagename'] = $page['name'] = $pn;
    PCache($pn, $page);
    $matches[] = $pn;
  }
  StopWatch('MakePageList sort');
  if ($order) SortPageList($matches, $order);
  if ($xlist) {
    register_shutdown_function('flush');
    register_shutdown_function('PageIndexUpdate', $xlist, getcwd());
  }
  StopWatch('MakePageList end');
  if ($retpages) 
    for($i=0; $i<count($matches); $i++)
      $matches[$i] = &$PCache[$matches[$i]];
  return $matches;
}