Wiki source for PageAdminPrune


Show raw source

=====PageAdminPrune Action=====

This is the development page for the PageAdminPrune action.
>>==See also:==
Documentation: PageAdminPruneActionInfo.>>

This action allows you (the owner of a page or the sysadmin) to choose to keep a number of a page's revisions or page revisions from a certain date onwards.

I have tested this with Firefox 1.5, PHP 5.1, IIS6 and Wikka 1.1.6

**PLEASE USE CAREFULLY!**

**I believe I have guarded against SQL injection attacks, however I'd appreciate any comments on the security of this action!**

It consists of
- pageadmin.php: Modified so it contains hooks to the new action
- Date.class.php: Class that contains date functions.
- Prune.php: Contains the actual code for pruning. This class currently only manages page revisions. A variation of this could also do page comment pruning.


Save this in actions\pageadmin.php
**Please note that I did not write this class. Credit goes to DarTar.** (See PageAdminAction)
I merely added hooks for the Prune aspect.

I have marked the lines I changed with /* > > > */ (with no spaces between the >)

%%(php)
<?php

/**
* Display a module for page management.
*
* This action allows admins to display information and perform operations
* on wiki pages. Pages can be sorted, searched, paged, filtered. Page-related
* statistics are given, displaying the number of comments, revisions, backlinks
* and referrers. Several handlers allow admins to perform specific operation on
* single pages. If the current user is not an administrator, the pageindex action
* is displayed instead.
*
* @package Actions
* @name PageAdmin
*
* @author {@link http://wikka.jsnx.com/DarTar Dario Taraborelli}
* @author {@link http://wikka.jsnx.com/JavaWoman JavaWoman} (using getCount(); minor tweaks)
* @version 0.4
* @since Wikka 1.1.X.X
*
* @input integer $colcolor optional: enables color for statistics columns
* 1: enables colored columns;
* 0: disables colored columns;
* default: 1;
* @input integer $rowcolor optional: enables alternate row colors
* 1: enables colored rows;
* 0: disables colored rows;
* default: 1;
*
* @output A list of pages available on the current server.
*
* @todo
* - mass-operations;
* - handlers: rename handler;
* - statistics: page hits;
* - full-text page search;
* - integrate with other admin modules.
*/

//utilities

/**
* Build an array of numbers consisting of 'ranges' with increasing step size in each 'range'.
*
* A list of numbers like this is useful for instance for a dropdown to choose
* a period expressed in number of days: a difference between 2 and 5 days may
* be significant while that between 92 and 95 may not be.
*
* @author {@link http://wikka.jsnx.com/JavaWoman JavaWoman}
* @copyright Copyright (c) 2005, Marjolein Katsma
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
* @version 1.0
*
* @param mixed $limits required: single integer or array of integers;
* defines the upper limits of the ranges as well as the next step size
* @param int $max required: upper limit for the whole list
* (will be included if smaller than the largest limit)
* @param int $firstinc optional: increment for the first range; default 1
* @return array resulting list of numbers
*/
function optionRanges($limits, $max, $firstinc = 1)
{
// initializations
if (is_int($limits))
{
$limits = array($limits);
}
if ($firstinc < 1)
{
$firstinc = 1;
}

$opts = array();
$inc = $firstinc;

// first element is the first increment
$opts[] = $inc;
// each $limit is the upper limit of a 'range'
foreach ($limits as $limit)
{
for ($i = $inc + $inc; $i <= $limit && $i < $max; $i += $inc)
{
$opts[] = $i;
}
// we quit at $max, even if there are more $limit elements
if ($limit >= $max)
{
// add $max to the list; then break out of the loop
$opts[] = $max;
break;
}
// when $limit is reached, it becomes the new start and increment for the next 'range'
$inc = $limit;
}

return $opts;
}

// restrict access to admins
if ($this->IsAdmin($this->GetUser())) {

// -------------------------------------
// set default values as constants
define('DEFAULT_RECORDS_LIMIT', '20'); # number of records per page
define('DEFAULT_MIN_RECORDS_DISPLAY', '5'); # min number of records
define('DEFAULT_RECORDS_RANGE',serialize(array('10','50','100','500','1000'))); #range array for records pager
define('DEFAULT_SORT_FIELD', 'time'); # sort field
define('DEFAULT_SORT_ORDER', 'desc'); # sort order, ascendant or descendant
define('DEFAULT_START', '0'); # start record
define('DEFAULT_SEARCH', ''); # keyword to restrict page search
define('DEFAULT_TAG_LENGTH', '12'); # max. length of displayed pagename
define('DEFAULT_URL_LENGTH', '15'); # max. length of displayed user host
define('DEFAULT_TERMINATOR', '…'); # standard symbol replacing truncated text (ellipsis) JW 2005-07-19
define('ALTERNATE_ROW_COLOR', '1'); # switch alternate row color
define('STAT_COLUMN_COLOR', '1'); # switch color for statistics columns

// -------------------------------------
// User-interface: icons

define('HITS_ICON', 'images/icons/16x16/stock_about.png');
define('REVISIONS_ICON', 'images/icons/16x16/stock_book_open.png');
define('COMMENTS_ICON', 'images/icons/16x16/stock_help-agent.png');
define('BACKLINKS_ICON', 'images/icons/16x16/stock_link.png');
define('REFERRERS_ICON', 'images/icons/16x16/stock_internet.png');


// -------------------------------------
// User-interface: strings

define('PAGE_TITLE','Page Administration');
define('FORM_LEGEND','Filter view:');
define('FORM_SEARCH_STRING_LABEL','Search page:');
define('FORM_SEARCH_STRING_TITLE','Enter a search string');
define('FORM_SEARCH_SUBMIT','Submit');
define('FORM_PAGER_LABEL_BEFORE','Show');
define('FORM_PAGER_TITLE','Select records-per-page limit');
define('FORM_PAGER_LABEL_AFTER','records per page');
define('FORM_PAGER_SUBMIT','Apply');
define('FORM_PAGER_LINK','Show records from %d to %d');
define('FORM_RESULT_INFO','Records');
define('FORM_RESULT_SORTED_BY','Sorted by:');
define('TABLE_HEADING_PAGENAME','Page Name');
define('TABLE_HEADING_PAGENAME_TITLE','Sort by page name');
define('TABLE_HEADING_OWNER','Owner');
define('TABLE_HEADING_OWNER_TITLE','Sort by page owner');
define('TABLE_HEADING_LASTAUTHOR','Last Author');
define('TABLE_HEADING_LASTAUTHOR_TITLE','Sort by last author');
define('TABLE_HEADING_LASTEDIT','Last Edit');
define('TABLE_HEADING_LASTEDIT_TITLE','Sort by edit time');
define('TABLE_SUMMARY','List of pages on this server');
define('TABLE_HEADING_HITS_TITLE','Hits');
define('TABLE_HEADING_REVISIONS_TITLE','Sort by number of revisions (DEBUG ONLY)');
define('TABLE_HEADING_COMMENTS_TITLE','Comments');
define('TABLE_HEADING_BACKLINKS_TITLE','Backlinks');
define('TABLE_HEADING_REFERRERS_TITLE','Referrers');
define('TABLE_HEADING_HITS_ALT','Hits');
define('TABLE_HEADING_REVISIONS_ALT','Revisions');
define('TABLE_HEADING_COMMENTS_ALT','Comments');
define('TABLE_HEADING_BACKLINKS_ALT','Backlinks');
define('TABLE_HEADING_REFERRERS_ALT','Referrers');
define('TABLE_HEADING_ACTIONS','Actions');
define('ACTION_EDIT_LINK_TITLE','Edit %s');
define('ACTION_DELETE_LINK_TITLE','Delete %s');
define('ACTION_CLONE_LINK_TITLE','Clone %s');
define('ACTION_RENAME_LINK_TITLE','Rename %s (DISABLED)');
define('ACTION_ACL_LINK_TITLE','Change Access Control List for %s');
define('ACTION_INFO_LINK_TITLE','Display information and statistics for %s');
/*>>>*/ define('ACTION_PRUNE_LINK_TITLE','Display misc options for %s');
define('ACTION_EDIT_LINK','edit');
define('ACTION_DELETE_LINK','delete');
define('ACTION_CLONE_LINK','clone');
define('ACTION_RENAME_LINK','rename');
define('ACTION_ACL_LINK','acl');
define('ACTION_INFO_LINK','info');
/*>>>*/ define('ACTION_PRUNE_LINK','prune');
define('TAKE_OWNERSHIP_LINK','Take ownership of');
define('NO_OWNER','(Nobody)');
define('TABLE_CELL_HITS_TITLE','Hits for %s (%d)');
define('TABLE_CELL_REVISIONS_TITLE','Display revisions for %s (%d)');
define('TABLE_CELL_COMMENTS_TITLE','Display comments for %s (%d)');
define('TABLE_CELL_BACKLINKS_TITLE','Display pages linking to %s (%d)');
define('TABLE_CELL_REFERRERS_TITLE','Display external sites linking to %s (%d)');
define('SELECT_RECORD_TITLE','Select %s');
define('NO_EDIT_NOTE','[No edit note]');
define('CHECK_ALL_TITLE','Check all records');
define('CHECK_ALL','Check all');
define('UNCHECK_ALL_TITLE','Uncheck all records');
define('UNCHECK_ALL','Uncheck all');
define('FORM_MASSACTION_LEGEND','Mass-action');
define('FORM_MASSACTION_LABEL','With selected');
define('FORM_MASSACTION_SELECT_TITLE','Choose action to apply to selected records (DISABLED)');
define('FORM_MASSACTION_OPT_DELETE','Delete all');
define('FORM_MASSACTION_OPT_CLONE','Clone all');
define('FORM_MASSACTION_OPT_RENAME','Rename all');
define('FORM_MASSACTION_OPT_ACL','Change Access Control List');
define('FORM_MASSACTION_SUBMIT','Submit');
define('ERROR_NO_MATCHES','Sorry, there are no pages matching "%s"');


// -------------------------------------
// Initialize variables

$r = 1; #initialize row counter
$r_color = ALTERNATE_ROW_COLOR; #get alternate row color option
$c_color = STAT_COLUMN_COLOR; #get column color option
// record dropdown
$page_limits = unserialize(DEFAULT_RECORDS_RANGE);
// pager
$prev = '';
$next = '';

//override defaults with action parameters
if (is_array($vars))
{
foreach ($vars as $param => $value)
{
switch ($param)
{
case 'colcolor':
$c_color = (preg_match('/[01]/',$value))? $value : STAT_COLUMN_COLOR;
break;

case 'rowcolor':
$r_color = (preg_match('/[01]/',$value))? $value : ALTERNATE_ROW_COLOR;
break;
}
}
}

//perform mass-operations if required (forthcoming)
if (isset($_GET['action']))
{
if ($_GET['action'] == 'massdelete')
{
echo $this->Action('massdelete');
}
elseif ($_GET['action'] == 'massrename')
{
echo $this->Action('massrename');
}
elseif ($_GET['action'] == 'massacls')
{
echo $this->Action('massacls');
}
}
else
{
// process URL variables
# JW 2005-07-19 some modifications to avoid notices but these are still not actually secure

// number of records per page
if (isset($_POST['l']))
{
$l = $_POST['l'];
}
elseif (isset($_GET['l']))
{
$l = $_GET['l'];
}
else
{
$l = DEFAULT_RECORDS_LIMIT;
}

// sort field
$sort = (isset($_GET['sort'])) ? $_GET['sort'] : DEFAULT_SORT_FIELD;
// sort order
$d = (isset($_GET['d'])) ? $_GET['d'] : DEFAULT_SORT_ORDER;
// start record
$s = (isset($_GET['s'])) ? $_GET['s'] : DEFAULT_START;

// search string
if (isset($_POST['q']))
{
$q = $_POST['q'];
}
elseif (isset($_GET['q']))
{
$q = $_GET['q'];
}
else
{
$q = DEFAULT_SEARCH;
}

// select all added JW 2005-07-19
$checked = '';
if (isset($_GET['selectall']))
{
$checked = (1 == $_GET['selectall']) ? ' checked="checked"' : '';
}

// restrict MySQL query by search string modified JW 2005-07-19
$where = ('' == $q) ? "`latest` = 'Y'" : "`tag` LIKE '%".$q."%' AND `latest` = 'Y'";
// get total number of pages
$numpages = $this->getCount('pages',$where);

// print page header
echo $this->Format('==== '.PAGE_TITLE.' ==== --- ');

// build pager form
$form1 = $this->FormOpen('','','post','page_admin_panel');
$form1 .= '<fieldset><legend>'.FORM_LEGEND.'</legend>'."\n";
$form1 .= '<label for="q">'.FORM_SEARCH_STRING_LABEL.'</label> <input type ="text" id="q" name="q" title="'.FORM_SEARCH_STRING_TITLE.'" size="20" maxlength="50" value="'.$q.'"/> <input type="submit" value="'.FORM_SEARCH_SUBMIT.'" /><br />'."\n";
// ranged drop-down
$pages_opts = optionRanges($page_limits,$numpages,DEFAULT_MIN_RECORDS_DISPLAY);
$form1 .= '<label for="l">'.FORM_PAGER_LABEL_BEFORE.'</label> '."\n";
$form1 .= '<select name="l" id="l" title="'.FORM_PAGER_TITLE.'">'."\n";

// build drop-down
foreach ($pages_opts as $opt)
{
$selected = ($opt == $l) ? ' selected="selected"' : '';
$form1 .= '<option value="'.$opt.'"'.$selected.'>'.$opt.'</option>'."\n";
}

$form1 .= '</select> <label for="l">'.FORM_PAGER_LABEL_AFTER.'</label> <input type="submit" value="'.FORM_PAGER_SUBMIT.'" /><br />'."\n";

// build pager links
if ($s > 0)
{
$prev = '<a href="' .$this->Href('','','l='.$l.'&sort='.$sort.'&d='.$d.'&s='.($s-$l)).'&q='.$q.'" title="'.sprintf(FORM_PAGER_LINK, ($s-$l+1), $s).'">'.($s-$l+1).'-'.$s.'</a> | '."\n";
}

if ($numpages > ($s + $l))
{
$next = ' | <a href="'.$this->Href('','','l='.$l.'&sort='.$sort.'&d='.$d.'&s='.($s+$l)).'&q='.$q.'" title="'.sprintf(FORM_PAGER_LINK, ($s+$l+1), ($s+2*$l)).'">'.($s+$l+1).'-'.($s+2*$l).'</a>'."\n";
}

$form1 .= FORM_RESULT_INFO.' ('.$numpages.'): '.$prev.($s+1).'-'.($s+$l).$next.'<br />'."\n";
$form1 .= '('.FORM_RESULT_SORTED_BY.'<em>'.$sort.', '.$d.'</em>)'."\n";
$form1 .= '</fieldset>'.$this->FormClose()."\n";

// print form
echo $form1;

// sort by counted values
switch($sort)
{
case 'edits': #alpha --- 'latest' needs to be disabled
//sample query:
//SELECT *, COUNT(*) as edits FROM `wikka1160_pages` GROUP BY tag ORDER BY edits DESC
$count = ', COUNT(*) as edits';
$group = 'GROUP BY tag';
$where = '1';
//$where = ('' == $q) ? "1" : "`tag` LIKE '%".$q."%'";
$table = 'pages';
break;

case 'comments': #to implement
/*
// SELECT wikka1160_pages.tag, COUNT( * ) AS comments FROM wikka1160_pages, wikka1160_comments WHERE wikka1160_pages.tag = wikka1160_comments.page_tag GROUP BY wikka1160_pages.tag ORDER BY comments DESC
$count = ', COUNT(*) as edits';
$group = 'GROUP BY tag';
$where = '1';
*/
break;
default:
$table = 'pages';
}

// get page list
$pagedata = $this->LoadAll("SELECT *".$count." FROM ".$this->config["table_prefix"].$table." WHERE ".
$where." ".$group." ORDER BY ".$sort." ".$d." LIMIT ".$s.", ".$l);

if ($pagedata)
{
// build table headers
$tagheader = '<a href="'.$this->Href('','', (($sort == 'tag' && $d == 'asc')? 'l='.$l.'&sort=tag&d=desc&q='.$q : 'l='.$l.'&sort=tag&d=asc&q='.$q)).'" title="'.TABLE_HEADING_PAGENAME_TITLE.'">'.TABLE_HEADING_PAGENAME.'</a>';
$ownerheader = '<a href="'.$this->Href('','', (($sort == 'owner' && $d == 'asc')? 'l='.$l.'&sort=owner&d=desc&q='.$q : 'l='.$l.'&sort=owner&d=asc&q='.$q)).'" title="'.TABLE_HEADING_OWNER_TITLE.'">'.TABLE_HEADING_OWNER.'</a>';
$userheader = '<a href="'.$this->Href('','', (($sort == 'user' && $d == 'asc')? 'l='.$l.'&sort=user&d=desc&q='.$q : 'l='.$l.'&sort=user&d=asc&q='.$q)).'" title="'.TABLE_HEADING_LASTAUTHOR_TITLE.'">'.TABLE_HEADING_LASTAUTHOR.'</a>';
$lasteditheader = '<a href="'.$this->Href('','', (($sort == 'time' && $d == 'desc')? 'l='.$l.'&sort=time&d=asc&q='.$q : 'l='.$l.'&sort=time&d=desc&q='.$q)).'" title="'.TABLE_HEADING_LASTEDIT_TITLE.'">'.TABLE_HEADING_LASTEDIT.'</a>';
$revisionsheader = '<a href="'.$this->Href('','', (($sort == 'edits' && $d == 'desc')? 'l='.$l.'&sort=edits&d=asc&q='.$q : 'l='.$l.'&sort=edits&d=desc&q='.$q)).'" title="'.TABLE_HEADING_REVISIONS_TITLE.'"><img src="'.REVISIONS_ICON.'" alt="'.TABLE_HEADING_REVISIONS_ALT.'"/></a>';

$htmlout = "<table summary=\"".TABLE_SUMMARY."\" border=\"1px\" id=\"admin_table\">\n".
"<thead>\n<tr>\n".
" <th> </th>\n".
" <th>".$tagheader."</th>\n".
" <th>".$ownerheader."</th>\n".
" <th>".$userheader."</th>\n".
" <th>".$lasteditheader."</th>\n".
" <th class=\"number ".(($c_color == 1)? ' c1' : '')."\" title=\"".TABLE_HEADING_HITS_TITLE."\"><img src=\"".HITS_ICON."\" alt=\"".TABLE_HEADING_HITS_ALT."\"/></th>\n".
" <th class=\"number ".(($c_color == 1)? ' c2' : '')."\" title=\"".TABLE_HEADING_REVISIONS_TITLE."\">".$revisionsheader."</th>\n".
" <th class=\"number ".(($c_color == 1)? ' c3' : '')."\" title=\"".TABLE_HEADING_COMMENTS_TITLE."\"><img src=\"".COMMENTS_ICON."\" alt=\"".TABLE_HEADING_COMMENTS_ALT."\"/></th>\n".
" <th class=\"number ".(($c_color == 1)? ' c4' : '')."\" title=\"".TABLE_HEADING_BACKLINKS_TITLE."\"><img src=\"".BACKLINKS_ICON."\" alt=\"".TABLE_HEADING_BACKLINKS_ALT."\"/></th>\n".
" <th class=\"number ".(($c_color == 1)? ' c5' : '')."\" title=\"".TABLE_HEADING_REFERRERS_TITLE."\"><img src=\"".REFERRERS_ICON."\" alt=\"".TABLE_HEADING_REFERRERS_ALT."\"/></th>\n".
" <th class=\"center\">".TABLE_HEADING_ACTIONS."</th>\n".
" </tr>\n</thead>\n";

// feed table with data
foreach($pagedata as $page)
{
// truncate long page names
$pagename = (strlen($page['tag']) > DEFAULT_TAG_LENGTH) ? substr($page['tag'], 0, DEFAULT_TAG_LENGTH).DEFAULT_TERMINATOR : $page['tag'];

// build handler links
$lastedit = $page['time'];
if ($pagename != $page['tag'])
{
$showpage = '<a href="'.$this->Href('',$page['tag'], '').'" title="'.$page['tag'].'">'.$pagename.'</a>';
}
else
{
$showpage = '<a href="'.$this->Href('',$page['tag'], '').'">'.$pagename.'</a>';
}

$editpage = '<a href="'.$this->Href('edit',$page['tag'], '').'" title="'.sprintf(ACTION_EDIT_LINK_TITLE, $page['tag']).'">'.ACTION_EDIT_LINK.'</a>';
$deletepage = '<a href="'.$this->Href('delete',$page['tag'], '').'" title="'.sprintf(ACTION_DELETE_LINK_TITLE, $page['tag']).'">'.ACTION_DELETE_LINK.'</a>';
$clonepage = '<a href="'.$this->Href('clone',$page['tag'], '').'" title="'.sprintf(ACTION_CLONE_LINK_TITLE, $page['tag']).'">'.ACTION_CLONE_LINK.'</a>';
// renaming disabled
$renamepage = '<a href="'.$this->Href('rename',$page['tag'], '').'" title="'.sprintf(ACTION_RENAME_LINK_TITLE, $page['tag']).'">'.ACTION_RENAME_LINK.'</a>';
$aclpage = '<a href="'.$this->Href('acls',$page['tag'], '').'" title="'.sprintf(ACTION_ACL_LINK_TITLE, $page['tag']).'">'.ACTION_ACL_LINK.'</a>';
$infopage = '<a href="'.$this->Href('info',$page['tag'], '').'" title="'.sprintf(ACTION_INFO_LINK_TITLE, $page['tag']).'">'.ACTION_INFO_LINK.'</a>';
/*>>>*/ $prunepage = '<a href="'.$this->Href('prune',$page['tag'], '').'" title="'.sprintf(ACTION_PRUNE_LINK_TITLE, $page['tag']).'">'.ACTION_PRUNE_LINK.'</a>';

// get page owner
if ($page['owner'])
{
// is the owner a registered user?
if ($this->LoadUser($page['owner']))
{
// does user's homepage exist?
if ($this->ExistsPage($page['owner']))
{
$owner = $this->Link($page['owner']);
}
else
{
$owner = $page['owner'];
}
}
else
{
$owner = $page['owner'];
}
}
else
{
// page has empty owner field: print claim link
$owner = $this->Link($page['tag'], 'claim','(Nobody)','','',TAKE_OWNERSHIP_LINK.' '.$page['tag']);
}
// get last author
if ($page['user'])
{
// is the author a registered user?
if ($this->LoadUser($page['user']))
{
// does user's homepage exist?
if ($this->ExistsPage($page['user']))
{
$user = $this->Link($page['user']);
}
else
{
$user = $page['user'];
}
}
else
{
// truncate long host names
$user = (strlen($page['user']) > DEFAULT_URL_LENGTH) ? substr($page['user'], 0, DEFAULT_URL_LENGTH).DEFAULT_TERMINATOR : $page['user'];
# added JW 2005-07-19
if ($user != $page['user'])
{
$user = '<span title="'.$page['user'].'">'.$user.'</span>';
}
}
}
else
{
// page has empty user field
$user = NO_OWNER;
}

// get counts - JW 2005-07-19
$whereTag = "`tag` = '".$page['tag']."'";
$wherePageTag = "`page_tag` = '".$page['tag']."'";
$whereToTag = "`to_tag` = '".$page['tag']."'";
$hn = 0;
$rv = $this->getCount('pages',$whereTag);
$cn = $this->getCount('comments',$wherePageTag);
$bn = $this->getCount('links',$whereToTag);
$rn = $this->getCount('referrers',$wherePageTag);

// get page hits (forthcoming)
$hitspage = ($hn > 0) ? '<a href="'.$this->Href('hits',$page['tag'], '').'" title="'.sprintf(TABLE_CELL_HITS_TITLE, $page['tag'], $hn).'">'.$hn.'</a>' : '0';

// get page revisions and create revision link if needed
$revpage = ($rv > 0) ? '<a href="'.$this->Href('revisions',$page['tag'], '').'" title="'.sprintf(TABLE_CELL_REVISIONS_TITLE, $page['tag'], $rv).'">'.$rv.'</a>' : '0';

// get page comments and create comments link if needed
$commentspage = ($cn > 0) ? '<a href="'.$this->Href('',$page['tag'], 'show_comments=1#comments').'" title="'.sprintf(TABLE_CELL_COMMENTS_TITLE, $page['tag'], $cn).'">'.$cn.'</a>' : '0';

// get page backlinks and create backlinks link
$backlinkpage = ($bn > 0) ? '<a href="'.$this->Href('backlinks',$page['tag'], '').'" title="'.sprintf(TABLE_CELL_BACKLINKS_TITLE, $page['tag'], $bn).'">'.$bn.'</a>' : '0';

// get page referrers and create referrer link
$refpage = ($rn > 0) ? '<a href="'.$this->Href('referrers',$page['tag'], '').'" title="'.sprintf(TABLE_CELL_REFERRERS_TITLE, $page['tag'], $rn).'">'.$rn.'</a>' : '0';

// build table body
$htmlout .= "<tbody>\n";
if ($r_color == 1)
{
$htmlout .= "<tr ".(($r%2)? '' : 'class="alt"').">\n"; #enable alternate row color
} else
{
$htmlout .= "<tr>\n"; #disable alternate row color
}

$htmlout .=" <td><input type=\"checkbox\" name=\"id_".$page['id']."\"".$checked." title=\"".sprintf(SELECT_RECORD_TITLE, $page['tag'])."\"/></td>\n". # modified JW 2005-07-19
" <td>".$showpage."</td>\n".
" <td>".$owner."</td>\n".
" <td>".$user."</td>\n".
" <td class=\"time\" ".((strlen($page['note'])>0)? 'title="['.$page['note'].']"' : 'title="'.NO_EDIT_NOTE.'"').">".$lastedit."</td>\n".
" <td class=\"number ".(($c_color == 1)? ' c1' : '')."\">".$hitspage."</td>\n".
" <td class=\"number ".(($c_color == 1)? ' c2' : '')."\">".$revpage."</td>\n".
" <td class=\"number ".(($c_color == 1)? ' c3' : '')."\">".$commentspage."</td>\n".
" <td class=\"number ".(($c_color == 1)? ' c4' : '')."\">".$backlinkpage."</td>\n".
" <td class=\"number ".(($c_color == 1)? ' c5' : '')."\">".$refpage."</td>\n".
/*>>>*/ " <td class=\"center \">".$editpage." :: ".$deletepage." :: ".$clonepage." :: "./*$renamepage*." :: ".*/$aclpage." :: ".$infopage." :: ".$prunepage."</td>\n".
" </tr>\n</tbody>\n";

//increase row counter ----- alternate row colors
if ($r_color == 1)
$r++;
}

$htmlout .= '</table>'."\n";
// print the table
echo $this->FormOpen('','','get');
echo $htmlout;

// multiple-page operations (forthcoming) JW 2005-07-19 accesskey removed (causes more problems than it solves)
echo '<fieldset><legend>'.FORM_MASSACTION_LEGEND.'</legend>';
echo '[<a href="'.$this->Href('','','l='.$l.'&sort='.$sort.'&d='.$d.'&s='.$s.'&q='.$q.'&selectall=1').'" title="'.CHECK_ALL_TITLE.'">'.CHECK_ALL.'</a> | <a href="'.$this->Href('','','l='.$l.'&sort='.$sort.'&d='.$d.'&s='.$s.'&q='.$q.'&selectall=0').'" title="'.UNCHECK_ALL_TITLE.'">'.UNCHECK_ALL.'</a>]<br />';
echo '<label for="action" >'.FORM_MASSACTION_LABEL.'</label> <select title="'.FORM_MASSACTION_SELECT_TITLE.'" id="action" name="action">';
echo '<option value="" selected="selected">---</option>';
echo '<option value="massdelete">'.FORM_MASSACTION_OPT_DELETE.'</option>';
echo '<option value="massclone">'.FORM_MASSACTION_OPT_CLONE.'</option>';
echo '<option value="massrename">'.FORM_MASSACTION_OPT_RENAME.'</option>';
echo '<option value="massacls">'.FORM_MASSACTION_OPT_ACL.'</option>';
echo '</select> <input type="submit" value="'.FORM_MASSACTION_SUBMIT.'" />';
echo '</fieldset>';
echo $this->FormClose();
}
else
{
// no records matching the search string: print error message
echo '<p><span class="error">'.sprintf(ERROR_NO_MATCHES, $q).'</span></p>';
}
}
}
else
{
// current user is not admin: show plain page index
echo $this->Action('pageindex');
}
?>
%%


Save this as handlers\page\prune.php
%%(php)
<div class="page"
<?php
require 'date.class.php';

/*
Form to delete a number of revisions from a page.
This form operates in one of 3 phases:
1. Initial load: $_POST is not set. Show form to accept revision prune criteria
2. Confirmation: $_POST is set and contains either of the 2 possible prune criteria (# of revs or date)
3. Execution: $_POST is set and contains either a GoAhead or Cancel value


NickDamoulakis: 4dec2005, v0.50, Initial version

Comments are most welcome!

- I was using the $_SESSION var to hold variables between invocations. However, I wasn't sure if that is the best way. Should those vars
stay around till the session end (browser or browser window shut down??).
I've switched to using the POST vars which only last for the lifetime of the page (right?).
- Initially, I had a function at the end of this source file that did the actual deletion. However, I was getting an error about the function
LoadSingle() not being found. $this->LoadSingle() obviously didn't work but neither did LoadSingle(). I know I am missing something :-)
- I am unable to return to the calling page via Redirect. I did have it working but I don't understand what made it break. It now goes to
the page that is being worked on instead. I was forced to specify 'PageAdmin' as the page to return to, which I am not happy with :-(

*/


define('PAGE_TITLE','Prune actions on %s');
define('TABLENAME', $this->config['table_prefix']."pages");

if (!($this->UserIsOwner() || $this->IsAdmin()))
{
$this->redirect($this->Href(), "Only the owner of this page can make these changes.");
}
else
{
// If we are in phase 1, show initial form
if (! $_POST)
{
echo $this->FormOpen("prune");
echo $this->Format('=== '.sprintf(PAGE_TITLE,'[['.$this->tag.']]').' ===----');
?>

<!-- use this to pass the tag name across to the next phases -->
<input type="hidden" name="tag" value=<?php echo "\"$this->tag\""; ?> >
<label for="NumOfRevs2Keep">
<br>
</label>
<table height="64" width="600" border="1">
<tr><!-- Row 1 -->
<td width="275">
No. of revisions to keep for this page
</td><!-- Col 1 -->
<td width="115">
<input maxlength="5" size="5" type="text" name="NumOfRevs2Keep">
</td><!-- Col 2 -->
<td>
</td><!-- Col 3 -->
</tr>
<tr><!-- Row 2 -->
<td width="5">
Or
</td><!-- Col 1 -->
<tr><!-- Row 3 -->
<td width="275">
Delete revisions prior to date (inclusive)
</td><!-- Col 1 -->
<td width="120">
<input type="text" maxlength="20" size="20" name="PurgeRevisionsBeforeDate">
</td><!-- Col 2 -->
<td>
<input style="WIDTH: 145px; HEIGHT: 24px" type="submit" size="15" name="Phase2" value="Purge old revisions">
</td><!-- Col 3 --></tr>
</table>
<br><br>
<br>

<?php
echo $this->FormClose();
}
// ---------------------------------------------------------------------------------------
// Phase 2: show what we are about to do and ask for ok to do so.
elseif ($_POST['Phase2'])
{
echo $this->FormOpen("prune");

$pageRevisions2Keep = trim($_POST["NumOfRevs2Keep"]);
$purgeRevisionsBeforeDate = trim($_POST["PurgeRevisionsBeforeDate"]);

// Dummy DO loop that simply allows multiple exits to a single point.
$ok = false;
do
{
// If both date and # of revs have been specified, use the date in preference of # of revs

// Has a date been specified?
if ($purgeRevisionsBeforeDate)
{
$dateError = "";
// Check data, record errors in $dateError and return db-friendly formatted date
$purgeRevisionsBeforeDate = IsValidDate($purgeRevisionsBeforeDate, $dateError);
if ($dateError != "")
{
echo "<br>".$dateError;
break;
}

echo "<br>Keeping page revisions from ".$purgeRevisionsBeforeDate." onwards";

$ok = true;
break;
}

// User specified (only!) # of revs to keep. Validate number, find what date that number corresponds to
// in the PAGES table and act as if he specified a date rather than # of revs.
if ($pageRevisions2Keep)
{
if (! is_numeric($pageRevisions2Keep))
{
echo "This is not a valid number (".$pageRevisions2Keep.")";
break;
}

if ($pageRevisions2Keep < 1)
{
echo "Hmmm. You want me to keep less than 1 revisions, eh? I refuse!";
break;
}

echo "<br>Keeping only the ".$pageRevisions2Keep." most recent revisions";

$tag = $_POST["tag"];
$tag = addslashes($tag);

$sql = "SELECT time FROM ".TABLENAME." WHERE tag='".$tag."' ORDER BY time DESC LIMIT ".$pageRevisions2Keep.",1";
if (! $tablepgname = $this->LoadSingle($sql))
{
echo "<br><strong>There are less page revisions than what you specified. I've got nothing to do.</strong>";
break;
}

// Pickup the timestamp of the record corresponding to the # of revisions we requested.
// ie, if we speficied 2, we need to look at the timestamp of the 3rd record and delete
// from there on backwords in time (including that record)
$purgeRevisionsBeforeDate = $tablepgname['time'];
$purgeRevisionsBeforeDate = addslashes($purgeRevisionsBeforeDate);

echo "<br>Deleting revisions dated ".$purgeRevisionsBeforeDate." and older";

$ok = true;
break;
}

echo "That's funny, that is! Am I meant to guess your intentions then? Please provide <em>some</em> input!";
break;

} while (false);

// this is the exit point for the above DO loop. When we get here, $ok will be true or false
// and it will be used below to enable/disable to GoAhead button.
?>

<br>
<!-- nonsense input so form submission works with rewrite mode -->
<input type="hidden" name="pageRevisions2Keep" value=<?php echo "\"".$pageRevisions2Keep."\""; ?> >
<input type="hidden" name="purgeRevisionsBeforeDate" value=<?php echo "\"".$purgeRevisionsBeforeDate."\""; ?> >
<input type="hidden" name="tag" value=<?php echo "\"".$_POST['tag']."\""; ?> >

<input style="WIDTH: 77px; HEIGHT: 24px" type="submit" name="Phase3" value="Go Ahead" <?php echo $ok ? "" : "disabled"; ?> >
<input style="WIDTH: 67px; HEIGHT: 24px" type="submit" name="Cancel" value="Cancel">

<?php
echo $this->FormClose();
}
elseif ($_POST['Phase3'])
// ---------------------------------------------------------------------------------------
// Phase 3? User has pressed the GoAhead button
{
echo $this->FormOpen("prune");
do
{
if (! $tag = $_POST["tag"])
{
echo "Tag variable was not set!???";
break;
}

$tag = addslashes($tag);
if (! $purgeRevisionsBeforeDate = $_POST["purgeRevisionsBeforeDate"])
{
$dateError = "";
$purgeRevisionsBeforeDate = IsValidDate($purgeRevisionsBeforeDate, $dateError);
if ($dateError != "")
{
echo $dateError;
break;
}
}

echo $sql = "DELETE from ".TABLENAME." WHERE tag = '".$tag."' AND time <= '".$purgeRevisionsBeforeDate."'";
$this->Query($sql);
$msg = "Deleted revisions dated ".$purgeRevisionsBeforeDate." and older";

} while (false);
echo $this->FormClose();

// redirect back to page
//************************************************************************************
//************************************************************************************
//************************************************************************************
//************************************************************************************
$this->redirect($this->config['base_url'].'PageAdmin', $msg);
//************************************************************************************
//************************************************************************************
//************************************************************************************
//************************************************************************************
}
elseif ($_POST["Cancel"])
// ---------------------------------------------------------------------------------------
// Phase 3? User chickened out
{
//************************************************************************************
//************************************************************************************
//************************************************************************************
//************************************************************************************
// Is there a better way to go back to the page that called me (actually, 2 pages back!)
$this->redirect($this->config['base_url'].'PageAdmin');
//************************************************************************************
//************************************************************************************
//************************************************************************************
//************************************************************************************
}
}

function IsValidDate($theDate, &$err)
{
// Do date validation
$dateobj = new DateClass;

// Validate date and convert to database-friendly format
if (! $internaldate = $dateobj->getInternalDate($theDate))
{
$err = $dateobj->errors." (".$theDate.")";
$result = null;
}
else
{
$result = $dateobj->getExternalDate($internaldate);
}

return $result;
}

?>
</div>

%%


Save this in date.class.php (same dir as wikka.php)
**Please note that I did not write this class. Credit goes to A J Marston**

%%(php)
<?php
//*****************************************************************************
// Copyright 2003 by A J Marston <http://www.tonymarston.net>
// Distributed under the GNU General Public Licence
//*****************************************************************************

class DateClass
{
// member variables
var $monthalpha; // array of 3-character month names
var $internaldate; // date as held in the database (yyyymmdd)
var $externaldate; // date as shown to the user (dd Mmm yyyy)
var $errors; // error messages

// ****************************************************************************
// class constructor
// ****************************************************************************
function DateClass ()
{

$this->monthalpha = array(1=>'Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');

} // DateClass

// ****************************************************************************
// accessor functions
// ****************************************************************************
function getInternalDate ($input)
// convert date from external format (as input by user)
// to internal format (as used in the database)
{

// look for d(d)?m(m)?y(yyy) format
$pattern = '(^[0-9]{1,2})' // 1 or 2 digits
.'([^0-9a-zA-Z])' // not alpha or numeric
.'([0-9]{1,2})' // 1 or 2 digits
.'([^0-9a-zA-Z])' // not alpha or numeric
.'([0-9]{1,4}$)'; // 1 to 4 digits

if (ereg($pattern, $input, $regs)) {
$result = $this->verifyDate($regs[1],$regs[3],$regs[5]);
return $result;
} // if

// look for d(d)?MMM?y(yyy) format
$pattern = '(^[0-9]{1,2})' // 1 or 2 digits
.'([^0-9a-zA-Z])' // not alpha or numeric
.'([a-zA-Z]{1,})' // 1 or more alpha
.'([^0-9a-zA-Z])' // not alpha or numeric
.'([0-9]{1,4}$)'; // 1 to 4 digits

if (ereg($pattern, $input, $regs)) {
$result = $this->verifyDate($regs[1],$regs[3],$regs[5]);
return $result;
} // if

// look for d(d)MMMy(yyy) format
$pattern = '(^[0-9]{1,2})' // 1 or 2 digits
.'([a-zA-Z]{1,})' // 1 or more alpha
.'([0-9]{1,4}$)'; // 1 to 4 digits

if (ereg($pattern, $input, $regs)) {
$result = $this->verifyDate($regs[1],$regs[2],$regs[3]);
return $result;
} // if

// look for MMM?d(d)?y(yyy) format
$pattern = '(^[a-zA-Z]{1,})' // 1 or more alpha
.'([^0-9a-zA-Z])' // not alpha or numeric
.'([0-9]{1,2})' // 1 or 2 digits
.'([^0-9a-zA-Z])' // not alpha or numeric
.'([0-9]{1,4}$)'; // 1 to 4 digits

if (ereg($pattern, $input, $regs)) {
$result = $this->verifyDate($regs[3],$regs[1],$regs[5]);
return $result;
} // if

// look for MMMddyyyy format
$pattern = '(^[a-zA-Z]{1,})' // 1 or more alpha
.'([0-9]{2})' // 2 digits
.'([0-9]{4}$)'; // 4 digits

if (ereg($pattern, $input, $regs)) {
$result = $this->verifyDate($regs[2],$regs[1],$regs[3]);
return $result;
} // if

// look for yyyy?m(m)?d(d) format
$pattern = '(^[0-9]{4})' // 4 digits
.'([^0-9a-zA-Z])' // not alpha or numeric
.'([0-9]{1,2})' // 1 or 2 digits
.'([^0-9a-zA-Z])' // not alpha or numeric
.'([0-9]{1,2}$)'; // 1 to 2 digits

if (ereg($pattern, $input, $regs)) {
$result = $this->verifyDate($regs[5],$regs[3],$regs[1]);
return $result;
} // if

// look for ddmmyyyy format
$pattern = '(^[0-9]{2})' // 2 digits
.'([0-9]{2})' // 2 digits
.'([0-9]{4}$)'; // 4 digits

if (ereg($pattern, $input, $regs)) {
$result = $this->verifyDate($regs[1],$regs[2],$regs[3]);
return $result;
} // if

// look for yyyy?MMM?d(d) format
$pattern = '(^[0-9]{4})' // 4 digits
.'([^0-9a-zA-Z])' // not alpha or numeric
.'([a-zA-Z]{1,})' // 1 or more alpha
.'([^0-9a-zA-Z])' // not alpha or numeric
.'([0-9]{1,2}$)'; // 1 to 2 digits

if (ereg($pattern, $input, $regs)) {
$result = $this->verifyDate($regs[5],$regs[3],$regs[1]);
return $result;
} // if

$this->errors = 'This is not a valid date';
return FALSE;

} // getInternalDate

// ****************************************************************************
function getInternalTime ($input)
// convert time from external format (as input by user)
// to internal format (as used in the database)
{
// look for HH?MM?SS format
$pattern = '(^[0-9]{2})' // 2 digits
.'([^0-9a-zA-Z])' // not alpha or numeric
.'([0-9]{2})' // 2 digits
.'([^0-9a-zA-Z])' // not alpha or numeric
.'([0-9]{2}$)'; // 2 digits

if (ereg($pattern, $input, $regs)) {
$result = $this->verifyTime($regs[1],$regs[3],$regs[5]);
return $result;
} // if

// look for HHMMSS format
$pattern = '(^[0-9]{2})' // 2 digits
.'([0-9]{2})' // 2 digits
.'([0-9]{2}$)'; // 2 digits

if (ereg($pattern, $input, $regs)) {
$result = $this->verifyTime($regs[1],$regs[2],$regs[3]);
return $result;
} // if

$this->errors = 'This is not a valid time';
return FALSE;

} // getInternalTime

// ****************************************************************************
function verifyDate($day, $month, $year)
{

// convert alpha month to digits
if (eregi('([a-z]{3})', $month)) {
$month = ucfirst(strtolower($month));
if (!$month = array_search($month, $this->monthalpha)) {
$this->errors = 'Month name is invalid';
return FALSE;
} // if
} // if

// ensure that year has 4 digits
if (strlen($year) == 1) {
$year = '200' .$year;
} // if
if (strlen($year) == 2) {
$year = '20' .$year;
} // if
if (strlen($year) == 3) {
$year = '2' .$year;
} // if

if (!checkdate($month, $day, $year)) {
$this->errors = 'This is not a valid date';
return FALSE;
} else {
if (strlen($day) < 2) {
$day = '0' .$day; // add leading zero
} // if
if (strlen($month) < 2) {
$month = '0' .$month; // add leading zero
} // if
$this->internaldate = $year .'-' .$month .'-' .$day;
return $this->internaldate;
} // if

return;

} // verifyDate

// ****************************************************************************
function verifyTime($hours, $minutes, $seconds)
{

if ($hours > 24) {
$this->errors = 'Invalid HOURS';
return FALSE;
} // if

if ($minutes > 59) {
$this->errors = 'Invalid MINUTES';
return FALSE;
} // if

if ($minutes > 59) {
$this->errors = 'Invalid MINUTES';
return FALSE;
} // if

return "$hours:$minutes:$seconds";

} // verifyTime

// ****************************************************************************
function getExternalDate ($input)
// convert date from internal format (as used in the database)
// to external format (as shown to the user))
{

// input may be 'yyyy-mm-dd' or 'yyyymmdd', so
// check the length and process accordingly

if (strlen($input) == 8) {
// test for 'yyyymmdd'
$pattern = '(^[0-9]{4})' // 4 digits (yyyy)
.'([0-9]{2})' // 2 digits (mm)
.'([0-9]{2}$)'; // 2 digits (dd)
if (ereg($pattern, $input, $regs)) {
if (!checkdate($regs[2], $regs[3], $regs[1])) {
$this->errors = 'This is not a valid date';
return FALSE;
} else {
$monthnum = (int)$regs[2];
$this->externaldate = "$regs[3] " .$this->monthalpha[$monthnum] ." $regs[1]";
return $this->externaldate;
} // if
} // if
$this->errors = "Invalid date format: expected 'yyyymmdd'";
return FALSE;
} // if

if (strlen($input) == 10) {
// test for 'yyyy-mm-dd'
$pattern = '(^[0-9]{4})' // 4 digits (yyyy)
.'([^0-9])' // not a digit
.'([0-9]{2})' // 2 digits (mm)
.'([^0-9])' // not a digit
.'([0-9]{2}$)'; // 2 digits (dd)
if (ereg($pattern, $input, $regs)) {
if (!checkdate($regs[3], $regs[5], $regs[1])) {
$this->errors = 'This is not a valid date';
return FALSE;
} else {
$monthnum = (int)$regs[3];
$this->externaldate = "$regs[5] " .$this->monthalpha[$monthnum] ." $regs[1]";
return $this->externaldate;
} // if
} // if
$this->errors = "Invalid date format: expected 'dd-mm-yyyy'";
return FALSE;
} // if

$this->errors = 'This is not a valid date';
return $input;

} // getExternalDate

// ****************************************************************************
function addDays ($internaldate, $days)
// add a number of days (may be negative) to $internaldate (YYYY-MM-DD)
// and return the result in the same format
{

// ensure date is in internal format
$internaldate = $this->getInternalDate($internaldate);

// convert to the number of days since basedate (4714 BC)
$julian = GregoriantoJD(substr($internaldate,5,2)
,substr($internaldate,8,2)
,substr($internaldate,0,4));

$days = (int)$days;
$julian = $julian + $days;

// convert from Julian to Gregorian (format m/d/y)
$gregorian = JDtoGregorian($julian);

// split date into its component parts
list ($month, $day, $year) = split ('[/]', $gregorian);

// convert back into standard format
$result = $this->getInternaldate("$day/$month/$year");

return $result;

} // addDays

// ****************************************************************************
function getErrors ()
{
return $this->errors;

} // getErrMsg

// ****************************************************************************
} // end DateClass
// ****************************************************************************

?>

%%

~Add prune link to footer:
Open templates/footer.php

find the following:
%%(php)
echo $this->HasAccess('write') ? '<a href="'.$this->Href('edit').'" title="Click to edit this page">Edit</a> ::'."\n" : '';
%%

add after:
%%(php)
echo $this->HasAccess('write') ? '<a href="'.$this->Href('prune').'" title="Click to prune old revisions of this page">Prune</a> ::'."\n" : '';
%%
Addition by [[http://grez868.info/sniper/wiki/HomePage | GrahamKelly]]


====Categories====
CategoryUserContributions
Valid XHTML :: Valid CSS: :: Powered by WikkaWiki