0byt3m1n1
Path:
/
data
/
applications
/
aps
/
tikiwiki
/
7.0-0
/
standard
/
htdocs
/
lib
/
wiki
/
[
Home
]
File: histlib.php
<?php // (c) Copyright 2002-2011 by authors of the Tiki Wiki CMS Groupware Project // // All Rights Reserved. See copyright.txt for details and a complete list of authors. // Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. // $Id: histlib.php 33195 2011-03-02 17:43:40Z changi67 $ //this script may only be included - so its better to die if called directly. if (strpos($_SERVER["SCRIPT_NAME"],basename(__FILE__)) !== false) { header("location: index.php"); exit; } class HistLib extends TikiLib { /* * Removes a specific version of a page * */ function remove_version($page, $version, $historyId = '') { global $prefs; if ($prefs['feature_contribution'] == 'y') { global $contributionlib; include_once('lib/contribution/contributionlib.php'); if ($historyId == '') { $query = 'select `historyId` from `tiki_history` where `pageName`=? and `version`=?'; $historyId = $this->getOne($query, array($page, $version)); } $contributionlib->remove_history($historyId); } $query = "delete from `tiki_history` where `pageName`=? and `version`=?"; $result = $this->query($query,array($page,$version)); global $logslib; include_once('lib/logs/logslib.php'); $logslib->add_action("Removed version", $page, 'wiki page', "version=$version"); //get_strings tra("Removed version $version") return true; } function use_version($page, $version, $comment = '') { $this->invalidate_cache($page); // Store the current page in tiki_history before rolling back if (strtolower($page) != 'sandbox') { $info = $this->get_page_info($page); $old_version = $this->get_page_latest_version($page) + 1; $lastModif = $info["lastModif"]; $user = $info["user"]; $ip = $info["ip"]; $comment = $info["comment"]; $data = $info["data"]; $description = $info["description"]; $query = "insert into `tiki_history`(`pageName`, `version`, `version_minor`, `lastModif`, `user`, `ip`, `comment`, `data`, `description`,`is_html`) values(?,?,?,?,?,?,?,?,?,?)"; $this->query($query,array($page,(int) $old_version, (int) $info["version_minor"],(int) $lastModif,$user,$ip,$comment,$data,$description, $info["is_html"])); } $query = "select * from `tiki_history` where `pageName`=? and `version`=?"; $result = $this->query($query,array($page,$version)); if (!$result->numRows()) return false; $res = $result->fetchRow(); global $prefs; if ($prefs['feature_wikiapproval'] == 'y') { // for approval and staging feature to work properly, one has to use real commit time of rollbacks //TODO: make this feature to set rollback time as current time as more general optional feature $res["lastModif"] = time(); } // add rollback comment to existing one (after truncating if needed) $ver_comment = " [" . tra("rollback version ") . $version . "]"; $too_long = 200 - strlen($res["comment"] . $ver_comment); if ($too_long < 0) { $too_long -= 4; $res["comment"] = substr($res["comment"], 0, $too_long) . '...'; } $res["comment"] = $res["comment"] . $ver_comment; $query = "update `tiki_pages` set `data`=?,`lastModif`=?,`user`=?,`comment`=?,`version`=`version`+1,`ip`=?, `description`=?, `is_html`=?"; $bindvars = array($res['data'], $res['lastModif'], $res['user'], $res['comment'], $res['ip'], $res['description'], $res['is_html']); // handle rolling back once page has been edited in a different editor (wiki or wysiwyg) based on is_html in history if ($prefs['feature_wysiwyg'] == 'y' && $prefs['wysiwyg_optional'] == 'y' && $prefs['wysiwyg_memo'] == 'y') { if ($res['is_html'] == 1) { $bindvars[] = 'y'; } else { $bindvars[] = 'n'; } $query .= ', `wysiwyg`=?'; } $query .= ' where `pageName`=?'; $bindvars[] = $page; $result = $this->query($query, $bindvars); $query = "delete from `tiki_links` where `fromPage` = ?"; $result = $this->query($query,array($page)); $this->clear_links($page); $pages = $this->get_pages($res["data"]); foreach ($pages as $a_page) { $this->replace_link($page, $a_page); } global $prefs; if ($prefs['feature_actionlog'] == 'y') { global $logslib; include_once('lib/logs/logslib.php'); $logslib->add_action("Rollback", $page, 'wiki page', "version=$version"); } //get_strings tra("Changed actual version to $version"); return true; } // Used to see a specific version of the page function get_view_date($date_str) { global $tikilib; if (!$date_str) { // Date is undefined throw new Exception(); } $view_date = $date_str; $tsp = explode('-', $date_str); if (count($tsp) == 3) { // Date in YYYY-MM-DD format $view_date = $tikilib->make_time(23, 59, 59, $tsp[1] + 1, $tsp[2], $tsp[0] + 1900); } return $view_date; } function get_user_versions($user) { $query = "select `pageName`,`version`, `lastModif`, `user`, `ip`, `comment` from `tiki_history` where `user`=? order by `lastModif` desc"; $result = $this->query($query,array($user)); $ret = array(); while ($res = $result->fetchRow()) { $aux = array(); $aux["pageName"] = $res["pageName"]; $aux["version"] = $res["version"]; $aux["lastModif"] = $res["lastModif"]; $aux["ip"] = $res["ip"]; $aux["comment"] = $res["comment"]; $ret[] = $aux; } return $ret; } // Returns information about a specific version of a page function get_version($page, $version) { $query = "select * from `tiki_history` where `pageName`=? and `version`=?"; $result = $this->query($query,array($page,$version)); $res = $result->fetchRow(); return $res; } // Get page info for a specified version function get_page_info($pageName, $version) { $info = parent::get_page_info($pageName); if (!isset($version)) { // No version = last version return $info; } if (!$info) { // Page does not exist return false; } $old_info = $this->get_version($pageName, $version); if ($old_info == null) { // History does not exist if ($version == $this->get_page_latest_version($pageName) + 1) { // Last version return $info; } throw new Exception(); } // Override parameters with versioned data $info['data'] = $old_info['data']; $info['version'] = $old_info['version']; $info['last_version'] = $info['version']; $info["user"] = $old_info["user"]; $info["ip"] = $old_info["ip"]; $info["description"] = $old_info["description"]; $info["comment"] = $old_info["comment"]; $info["is_html"] = $old_info["is_html"]; $info['lastModif'] = $old_info["lastModif"]; $info['page_size'] = strlen($old_info['data']); return $info; } // Returns all the versions for this page // without the data itself function get_page_history($page, $fetchdata=true, $offset = 0, $limit = -1) { global $prefs; $query = "select * from `tiki_history` where `pageName`=? order by `version` desc"; $result = $this->query($query,array($page), $limit, $offset); $ret = array(); while ($res = $result->fetchRow()) { $aux = array(); $aux["version"] = $res["version"]; $aux["lastModif"] = $res["lastModif"]; $aux["user"] = $res["user"]; $aux["ip"] = $res["ip"]; if ($fetchdata==true) $aux["data"] = $res["data"]; $aux["pageName"] = $res["pageName"]; $aux["description"] = $res["description"]; $aux["comment"] = $res["comment"]; $aux["is_html"] = $res["is_html"]; //$aux["percent"] = levenshtein($res["data"],$actual); if ($prefs['feature_contribution'] == 'y') { global $contributionlib; include_once('lib/contribution/contributionlib.php'); $aux['contributions'] = $contributionlib->get_assigned_contributions($res['historyId'], 'history'); global $logslib; include_once('lib/logs/logslib.php'); $aux['contributors'] = $logslib->get_wiki_contributors($aux); } $ret[] = $aux; } return $ret; } // Returns one version of the page from the history // without the data itself (version = 0 now returns data from current version) function get_page_from_history($page,$version,$fetchdata=false) { if ($fetchdata==true) { if ($version > 0) $query = "select `pageName`, `description`, `version`, `lastModif`, `user`, `ip`, `data`, `comment`, `is_html` from `tiki_history` where `pageName`=? and `version`=?"; else $query = "select `pageName`, `description`, `version`, `lastModif`, `user`, `ip`, `data`, `comment`, `is_html` from `tiki_pages` where `pageName`=?"; } else { if ($version > 0) $query = "select `pageName`, `description`, `version`, `lastModif`, `user`, `ip`, `comment`, `is_html` from `tiki_history` where `pageName`=? and `version`=?"; else $query = "select `pageName`, `description`, `version`, `lastModif`, `user`, `ip`, `comment`, `is_html` from `tiki_pages` where `pageName`=?"; } if ($version > 0) $result = $this->query($query,array($page,$version)); else $result = $this->query($query,array($page)); $ret = array(); while ($res = $result->fetchRow()) { $aux = array(); $aux["version"] = $res["version"]; $aux["lastModif"] = $res["lastModif"]; $aux["user"] = $res["user"]; $aux["ip"] = $res["ip"]; if ($fetchdata==true) $aux["data"] = $res["data"]; $aux["pageName"] = $res["pageName"]; $aux["description"] = $res["description"]; $aux["comment"] = $res["comment"]; $aux["is_html"] = $res["is_html"]; //$aux["percent"] = levenshtein($res["data"],$actual); $ret[] = $aux; } return empty($ret)?$ret: $ret[0]; } // note that this function returns the latest version in the // history db table, which is one less than the current version function get_page_latest_version($page, $sort_mode='version_desc') { $query = "select `version` from `tiki_history` where `pageName`=? order by ".$this->convertSortMode($sort_mode); $result = $this->query($query,array($page),1); $ret = array(); if ($res = $result->fetchRow()) { $ret = $res['version']; } else { $ret = FALSE; } return $ret; } function version_exists($pageName, $version) { $query = "select `pageName` from `tiki_history` where `pageName` = ? and `version`=?"; $result = $this->query($query,array($pageName,$version)); return $result->numRows(); } // This function get the last changes from pages from the last $days days // if days is 0 this gets all the registers // function parameters modified by ramiro_v on 11/03/2002 function get_last_changes($days, $offset = 0, $limit = -1, $sort_mode = 'lastModif_desc', $findwhat = '') { global $user; $bindvars = array(); $categories = $this->get_jail(); if (!isset($categjoin)) $categjoin = ''; if ($categories) { $categjoin .= "inner join `tiki_objects` as tob on (tob.`itemId`= ta.`object` and tob.`type`= ?) inner join `tiki_category_objects` as tc on (tc.`catObjectId`=tob.`objectId` and tc.`categId` IN(" . implode(', ', array_fill(0, count($categories), '?')) . ")) "; $bindvars = array_merge(array('wiki page'), $categories); } $where = "where true "; if ($findwhat) { $findstr='%' . $findwhat . '%'; $where.= " and ta.`object` like ? or ta.`user` like ? or ta.`comment` like ?"; $bindvars = array_merge($bindvars, array($findstr,$findstr,$findstr)); } if ($days) { $toTime = $this->make_time(23, 59, 59, $this->date_format("%m"), $this->date_format("%d"), $this->date_format("%Y")); $fromTime = $toTime - (24 * 60 * 60 * $days); $where .= " and ta.`lastModif`>=? and ta.`lastModif`<=? "; $bindvars[] = $fromTime; $bindvars[] = $toTime; } $query = "select distinct ta.`action`, ta.`lastModif`, ta.`user`, ta.`ip`, ta.`object`, thf.`comment`, thf.`version`, thf.`versionlast` from `tiki_actionlog` ta inner join (select NULL as version, `comment`, `pageName`, `lastModif`, '1' as versionlast from tiki_pages union select `version`, `comment`, `pageName`, `lastModif`, '0' as versionlast from `tiki_history`) as thf on ta.`object`=thf.`pageName` and ta.`lastModif`=thf.`lastModif` and ta.`objectType`='wiki page' " . $categjoin . $where . " order by ta.".$this->convertSortMode($sort_mode); $query_cant = "select count(distinct ta.`action`, ta.`lastModif`, ta.`user`, ta.`object`, thf.`versionlast`) from `tiki_actionlog` ta inner join (select `pageName`, `lastModif`, '1' as versionlast from tiki_pages union select `pageName`, `lastModif`, '0' as versionlast from `tiki_history`) as thf on ta.`object`=thf.`pageName` and ta.`lastModif`=thf.`lastModif` and ta.`objectType`='wiki page' " . $categjoin . $where; $result = $this->fetchAll($query,$bindvars,$limit,$offset); $result = Perms::filter( array( 'type' => 'wiki page' ), 'object', $result, array( 'object' => 'object' ), 'view' ); $cant = $this->getOne($query_cant,$bindvars); $ret = array(); $retval = array(); foreach( $result as $res ) { $res['pageName'] = $res['object']; $ret[] = $res; } $retval["data"] = $ret; $retval["cant"] = $cant; return $retval; } function get_nb_history($page) { $query_cant = "select count(*) from `tiki_history` where `pageName` = ?"; $cant = $this->getOne($query_cant, array($page)); return $cant; } // This function gets the version number of the version before or after the time specified // (note that current version is not included in search) function get_version_by_time($page, $unixtimestamp, $before_or_after = 'before', $include_minor = true) { $query = "select `version`, `version_minor`, `lastModif` from `tiki_history` where `pageName`=? order by `version` desc"; $result = $this->query($query,array($page)); $ret = array(); $version = 0; while ($res = $result->fetchRow()) { $aux = array(); $aux["version"] = $res["version"]; $aux["version_minor"] = $res["version_minor"]; $aux["lastModif"] = $res["lastModif"]; $ret[] = $aux; } foreach ($ret as $ver) { if ($ver["lastModif"] <= $unixtimestamp && ($include_minor || $ver["version_minor"] == 0)) { if ($before_or_after == 'before') { $version = (int) $ver["version"]; break; } elseif ($before_or_after == 'after') { break; } } if ($before_or_after == 'after' && ($include_minor || $ver["version_minor"] == 0)) { $version = (int) $ver["version"]; } } return max(0, $version); } } /** * * This class represents a structured view (per word) on a document. Feeding it with additional references, it can be used to generate a * complete view of the document including changes made over time (like the "Track changes" in some word processing programs). A statistics * of the different authors contributions can be generated as well * * @author cdrwhite * @since 6.0 */ class Document { /** * @var array a list of words and whitespaces represented by an array(word,author,deleted,diffid,[deleted_by]) */ private $_document; /** * @var array array of statistical data grouped by author each represented by an array(words,deleted_words,whitespaces,deleted_whitespaces,characters,deleted_characters,printables,deleted_printables) * @see getStatistics */ private $_statistics; /** * @var array sum of all statistics for all authors, generated by getStatistics, retrieved by getTotal() * @see getTotal; */ private $_total; /** * @var string filter used in getStatistics to distinguish between characters and printable characters * @see getStatistics */ private $_filter; /** * @var int processing settings */ private $_process=1; /** * @var bool should the page contents be parsed (HTML instead of WIKI text) */ private $_parsed; /** * @var bool should the html tags be stripped from the parsed contents */ private $_nohtml; /** * @var string start marker. If set, text before this marker (including the marker itself) will be removed */ var $startmarker=''; /** * @var string end marker. If set, text after this marker (including the marker itself) will be removed */ var $endmarker=''; /** * @var string regex for splitting page text into an array of words; */ private $_search="#(\[[^\[].*?\]|\(\(.*?\)\)|(~np~\{.*?\}~/np~)|<[^>]+>|[,\"':\s]+|[^\s,\"':<]+|</[^>]+>)#"; /** * @var array Page info */ private $_info; /** * @var array complete page history */ private $_data; /** * * Initializing Internal variables for getStatistics and getTotals and adding the first page to the document * @param string $page Name of the page to include * @param int $lastversion >0 uses the version specified (or last page, if this is greater than the version of the last page) =0 uses the latest(current) version, <0 means a timestamp (lastModif has to be before that) * @param int $process 0 = don't parse (take original wiki text and count wiki tags/plugins), 1 = parse (take html as base), 2 = parse and strip html tags * @param string $start start marker (all text will be skipped, including this marker which must be at the beginning of a line) * @param string $end end marker (all text will be skipped from this marker on, including this marker which must be at the beginning of a line) */ function __construct($page, $lastversion=0, $process=1, $showpopups=true, $startmarker='', $endmarker='') { global $histlib; $this->_document=array(); $this->_history=false; $this->_filter='/([[:blank:]]|[[:cntrl:]]|[[:punct:]]|[[:space:]])/'; $this->_parsed=true; $this->_nohtml=false; $this->_showpopups=$showpopups; switch($process) { case 0: $this->_parsed=false; $this->_process=0; break; case 2: $this->_nohtml=true; $this->_process=2; break; } $this->startmarker=$startmarker; $this->endmarker=$endmarker; $this->_info=$histlib->get_page_info($page,true); if($lastversion==0) { $lastversion=$this->_info['version']; } $this->_data=array(); $this->_data=array(array( 'version' => $this->_info['version'], 'lastModif' => $this->_info['lastModif'], 'user' => $this->_info['user'], 'ip' => $this->_info['ip'], 'pageName' => $page, 'description' => $this->_info['description'], 'comment' => $this->_info['comment'], 'data' => $this->_info['data'], )); $this->_data=array_merge($this->_data,$histlib->get_page_history($page, true, 0, -1)); $next=count($this->_data)-1; $author=$this->_data[$next]['user']; $next=$this->getLastAuthorText($author, $next, $lastversion); if ($next==-1) { // all pages from the same author, no need to diff $index=$this->getIndex($lastversion); } else { $index=$next; } $source=$this->removeText($this->_data[$index]['data']); $source=preg_replace(array('/\{AUTHOR\(.+?\)\}/','/{AUTHOR\}/','/\{INCLUDE\(.+?\)\}\{INCLUDE\}/'), ' ~np~$0~/np~', $source); if ($this->_parsed) { $source=$histlib->parse_data($source, array('suppress_icons'=>true)); } if ($this->_nohtml) { $source=strip_tags($source); } preg_match_all($this->_search,$source,$out,PREG_PATTERN_ORDER); $words=$out[0]; $this->_document=$this->addWords($this->_document,$words, $author); if ($next==-1) { return; } do { $author=$this->_data[$next-1]['user']; $next=$this->getLastAuthorText($author, $next-1, $lastversion); if ($next==-1) { $index=$this->getIndex($lastversion); } else { $index=$next; } $newpage=$this->removeText($this->_data[$index]['data']); $this->mergeDiff($newpage, $author); } while($next>0); $this->parseAuthorAndInclude(); } /** * * Removes all text before the first occurrence of start marker and after the last occurrence of the end marker * This copies the original behaviour of the wikiplugin_include even though it could be done with a regex in fewer lines * @param string $text contains the whole text * @return string returns the text inside the markers */ private function removeText($text) { $start=($this->startmarker!=''); $stop=($this->endmarker!=''); if ($start || $stop) { $explText = explode("\n", $text); if ($start && $stop) { $state = 0; foreach ($explText as $i => $line) { if ($state == 0) { // Searching for start marker, dropping lines until found unset($explText[$i]); // Drop the line if (0 == strcmp($this->startmarker, trim($line))) { $state = 1; // Start retaining lines and searching for stop marker } } else { // Searching for stop marker, retaining lines until found if (0 == strcmp($this->endmarker, trim($line))) { unset($explText[$i]); // Stop marker, drop the line $state = 0; // Go back to looking for start marker } } } } elseif ($start) { // Only start marker is set. Search for it, dropping all lines until it is found. foreach ($explText as $i => $line) { unset($explText[$i]); // Drop the line if (0 == strcmp($this->startmarker, trim($line))) { break; } } } else { // Only stop marker is set. Search for it, dropping all lines after it is found. $state = 1; foreach ($explText as $i => $line) { if ($state == 0) { // Dropping lines unset($explText[$i]); } else { // Searching for stop marker, retaining lines until found if (0 == strcmp($this->endmarker, trim($line))) { unset($explText[$i]); // Stop marker, drop the line $state = 0; // Start dropping lines } } } } $text = implode("\n", $explText); } return $text; } /** * * get the id of the last text of the given author * @param string $author name of the current author * @param int $start start index * @param int $lastversion last version to check, assuming all versions, if none is provided * @return int id of the first text of a different author or -1 if there is none * @see get_page_history_all */ private function getLastAuthorText($author, $start=-1, $lastversion=-1) { if($start==-1) { return $start; } if($start<0) { $start=count($this->_data)-1; } if ($lastversion==-1) { $lastversion=$this->_data[0]['version']; } $i=$start; while ($i>=0 and $this->_data[$i]['user']==$author and $this->_data[$i]['version']<=$lastversion) { $i--; } $i++; if ($this->_data[$i]['version']>=$lastversion) { $i=-1; } return $i; } /** * * gets the index position of the requested version in the data array * @param int $version */ private function getIndex($version) { for($i=count($this->_data)-1;$i>=0;$i--) { if ($this->_data[$i]['version']==$version) { return $i; } } return -1; } /** * * returns the history (identical to $histlib->get_page_history, but saves another fetch from database as we already have the info */ function getHistory() { return array_slice($this->_data,1); } /** * * returns the page info history (identical to $tikilib->get_page_info, but saves another fetch from database as we already have the info */ function getInfo() { return $this->_info; } /** * * Generates an array of words from the internal document structure, which can be used by the diff class. * The internal document structure will be modified to allow mergeDiff to integrate a new page with the current page without losing any information * @see mergeDiff * @return array list of words in the document (no author etc.) */ function getDiffArray() { $diffarray=array(); foreach($this->_document as &$word) { if (!$word['deleted']) { $word['diffid']=count($diffarray); $diffarray[]=$word['word']; } else { $word['diffid']=-1; } } return $diffarray; } /** * * Generates a statistics per author, the totals can be retrieved via getTotal * @see getTotal * @param string $filter regex to filter out non printable characters (difference between characters and printables) * @return array array indexed by author containing arrays with statistics (words, deleted_words, whitespaces, deleted_whitespaces, characters, deleted_characters, printables, deleted_printables) */ function getStatistics($filter='/([[:blank:]]|[[:cntrl:]]|[[:punct:]]|[[:space:]])/') { $style=0; if ($this->_filter!=$filter) { //a new filter invalidates the statistics $this->_statistics=false; $this->_filter=$filter; } if ($this->_statistics!=false) return $this->_statistics; //there is already a history for the current state $this->_statistics=array(); $this->_total=array( 'words' => 0, 'deleted_words' => 0, 'whitespaces' => 0, 'deleted_whitespaces' => 0, 'characters' => 0, 'deleted_characters' => 0, 'printables' =>0, 'deleted_printables' => 0, ); foreach ($this->_document as $word) { $author=$word['author']; if(!isset($this->_statistics[$author])) { $this->_statistics[$author]=array( 'words' => 0, 'words_percent' => 0, 'deleted_words' => 0, 'deleted_words_percent' => 0, 'whitespaces' => 0, 'whitespaces_percent' => 0, 'deleted_whitespaces' => 0, 'deleted_whitespaces_percent' => 0, 'characters' => 0, 'characters_percent' => 0, 'deleted_characters' => 0, 'deleted_characters_percent' => 0, 'printables' =>0, 'printables_percent' => 0, 'deleted_printables' => 0, 'deleted_printables_percent' => 0, 'style' => "author$style", ); $style++; if ($style>15) $style=0; } //isset author if ($word['deleted']) { $prefix='deleted_'; } else { $prefix=''; } $w=$word['word']; if($this->_nohtml) { $w=strip_tags($w); } if (trim($w)=='') { $this->_statistics[$author][$prefix.'whitespaces']++; $this->_total[$prefix.'whitespaces']++; } else { $this->_statistics[$author][$prefix.'words']++; $this->_total[$prefix.'words']++; } $l=mb_strlen($w); $this->_statistics[$author][$prefix.'characters']+=$l; $this->_total[$prefix.'characters']+=$l; $l=mb_strlen(preg_replace($this->_filter,'',$w)); $this->_statistics[$author][$prefix.'printables']+=$l; $this->_total[$prefix.'printables']+=$l; } //foreach //calculate percentages foreach($this->_statistics as &$author) { $author['words_percent']=$author['words']/$this->_total['words']; $author['deleted_words_percent']=($this->_total['deleted_words']!=0?$author['deleted_words']/$this->_total['deleted_words']:0); $author['whitespaces_percent']=$author['whitespaces']/$this->_total['whitespaces']; $author['deleted_whitespaces_percent']=($this->_total['deleted_whitespaces']!=0?$author['deleted_whitespaces']/$this->_total['deleted_whitespaces']:0); $author['characters_percent']=$author['characters']/$this->_total['characters']; $author['deleted_characters_percent']=($this->_total['deleted_characters']!=0?$author['deleted_characters']/$this->_total['deleted_characters']:0); $author['printables_percent']=$author['printables']/$this->_total['printables']; $author['deleted_printables_percent']=($this->_total['deleted_printables']!=0?$author['deleted_printables']/$this->_total['deleted_printables']:0); } return $this->_statistics; } /** * * gets the totals from a previous getStatistics call * @see getStatistics * @return array with statistics (words, deleted_words, whitespaces, deleted_whitespaces, characters, deleted_characters, printables, deleted_printables) */ function getTotal() { return $this->_total; } /** * * Retrieves the document data in different formats, * @param string $type can be one of 'words' (array of words/whitespaces), 'text' (unformatted string), 'wiki' (string with wikiplugin AUTHOR tags to show the authors) or the default empty string '' (returns the internal document structure) * @param array $options array containing the filter specific options: * <table> * <tr><th>Type</th><th>Name</th><th>Applicable for</th><th>Purpose</th></tr> * <tr><td>bool</td><td>showpopups</td><td>wiki</td><td>renders popups, defaults to true</td></tr> * <tr><td>bool</td><td>escape</td><td>text/wiki</td><td>Escapes brackets and htmlspecialchars</td></tr> * </table> * @return array|string depending on the parameter $type, a string or array containing the documents words */ function get($type='',$options=array()) { switch($type) { case 'words': $words=array(); foreach ($this->_document as $word) { $words[]=$word['word']; } return $words; break; case 'text': $text=''; foreach ($this->_document as $word) { $text.=$word['word']; } return $text; if ($options['escape']) { if (!$this->_parsed) { $text='~np~' . preg_replace(array('/\~np\~/', '//\~\/np\~/'), array('~np~','~/np~;'), $text) . '~/np~'; } $text=preg_replace(array('/</','/>/'), array('<','>'), $text); } break; case 'wiki': $text=''; $author=''; $deleted=0; $deleted_by=''; if (isset($options['showpopups'])) { $showpopups=$options['showpopups']; } else { $showpopups=true; } foreach ($this->_document as $word) { $skip=false; $d=isset($word['deleted_by'])?$word['deleted_by']:''; $w=$word['word']; if($author!=$word['author'] or $deleted!=$word['deleted'] or $deleted_by!=$d) { if ($text!='') { if ($options['escape']) { $text.='~/np~'; } $text.='{AUTHOR}'; } $author=$word['author']; $deleted=$word['deleted']; $deleted_by=$d; $text.="{AUTHOR(author=\"$author\"" . ($deleted?",deleted_by=\"$deleted_by\"":'') . ',visible="1"' . ($showpopups?', popup="1"':'') . ')}'; if ($options['escape']) { $text.="~np~"; } } if (!$options['escape']) { if ($this->_parsed and !$this->_nohtml) { // skipping popups for links if (substr($w,0,3)=='<a ') { $text.='{AUTHOR}'; } if (substr($w,-4)=='</a>') { $text.=$w . "{AUTHOR(author=\"$author\"" . ($deleted?",deleted_by=\"$deleted_by\"":'') . ',visible="1", ' . ($showpopups?', popup="1"':'') . ')}'; $skip=true; } } } else { //escape existing tags if (!$this->_parsed) { $w=preg_replace(array('/\~np\~/', '/\~\/np\~/'), array('~np~','~/np~'), $w); } $w=preg_replace(array('/</','/>/'), array('&lt;','&gt;'), $w); //double encode! } if (strlen($w)==0 and !$this->_parsed) { $text.="\n"; } else { if (!$skip) { $text.=$w; } } } // foreach if ($options['escape']) { $text.="~/np~"; } $text.="{AUTHOR}"; return $text; break; default: return $this->_document; } } /** * * Adds the supplied list of words to the provided document structure * @param array $doc a list of words (arrays containing word, author, deleted, diffid, optionally deleted_by and statistical data) where the new words will be added to * @param array $list array of words/whitespaces to add to the document * @param string $author name of the author to credit * @return provided document structure $doc with the words from $list appended */ private function addWords($doc, $list, $author, $deleted=false, $deleted_by='') { $newdoc=$doc; foreach($list as $word) { $newword=array( 'word' => $word, 'author' => $author, 'deleted' => $deleted, 'diffid' => -1, ); if($deleted) { $newword['deleted_by']=$deleted_by; } $newdoc[]=$newword; } return $newdoc; } /** * * moves a nuber of words from the b eginning of this document to the provided document structure * @param array $doc a list of words (arrays containing word, author, deleted, diffid, optionally deleted_by and statistical data) where the new words will be appended to * @param int $pos number of characters to move from the current documents beginning to the new list, deleted words which have a negative diff id wille be moved but not counted * @param array $list list of words to move * @param bool $setDeleted mark the moved words as deleted, if not already deleted * @param string $deletedBy name of the author who deleted the words */ private function moveWords(&$doc, &$pos, $list, $deleted=false, $deleted_by='') { $pos+=count($list); // get the words from the old document $i=0; while ($i<count($this->_document) and $this->_document[$i]['diffid']<$pos) { $word=$this->_document[$i]; if($deleted) { if(!$word['deleted']) { $word['deleted']=true; $word['deleted_by']=$deleted_by; } } $doc[]=$word; $i++; } //take care of deleted words while ($i<count($this->_document) and $this->_document[$i]['diffid']<0) { $word=$this->_document[$i]; $doc[]=$word; $i++; } $this->_document=array_slice($this->_document, $i); } /** * * Returns an indexed array containing the plugins parameters indexed by key name * @param string $pluginstr Complete Plugin tag including brackets () containing the parameters * @return array|bool Array containing the parameters or false if none are given */ function retrieveParams($pluginstr) { $params=array(); $start=strpos($pluginstr,'('); if ($start===false) return false; $end=strrpos($pluginstr, ')'); if ($end===false) return false; $pstr=substr($pluginstr,$start+1,$end-$start-1); $plist=explode(',',$pstr); foreach($plist as $paramstr) { $p=explode('=',trim($paramstr)); $params[strtolower(trim($p[0]))]=preg_replace('/^"|^\"|"$|\"$/', '', trim($p[1])); } return $params; } /** * * merges a newer version of a page into the current document * @param string $newpage a string with a later version of the page * @param string $newauthor name of the author of the new version */ function mergeDiff($newpage, $newauthor) { global $tikilib; $this->_history=false; $author=$newauthor; $deleted=false; $deleted_by=''; $newdoc=array(); $page=preg_replace(array('/\{AUTHOR\(.+?\)\}/','/{AUTHOR\}/','/\{INCLUDE\(.+?\)\}\{INCLUDE\}/'), ' ~np~$0~/np~', $newpage); if ($this->_parsed) { $page=$tikilib->parse_data($page, array('suppress_icons'=>true)); $page=preg_replace(array('/\{AUTHOR\(.+?\)\}/','/{AUTHOR\}/','/\{INCLUDE\(.+?\)\}\{INCLUDE\}/'), ' ~np~$0~/np~', $page); } if ($this->_nohtml) { $page=strip_tags($page); } preg_match_all($this->_search,$page,$out,PREG_PATTERN_ORDER); $new=$out[0]; $z = new Text_Diff($this->getDiffArray(),$new); $pos=0; foreach ($z->getDiff() as $element) { if (is_a($element, 'Text_Diff_Op_copy')) { $this->moveWords($newdoc,$pos,$element->orig, $deleted, $deleted_by); } else { if (is_a($element, 'Text_Diff_Op_add')){ $newdoc=$this->addWords($newdoc, $element->final, $author, $deleted, $deleted_by); } else { if (is_a($element, 'Text_Diff_Op_delete')) { $this->moveWords($newdoc,$pos,$element->orig, $deleted, $author); } else { //change $newdoc=$this->addWords($newdoc, $element->final, $author, $deleted, $deleted_by); $this->moveWords($newdoc, $pos, $element->orig, true, $author); } //delete } // add } // copy } // foreach diff $this->_document=$newdoc; } /** * * Kills double whitespaces in parseAuthor before/after {author} tags * @param array $newdoc array containing the new document * @param int $index position in the old document */ private function killDoubleWhitespaces(&$newdoc, &$index) { if(count($newdoc)>2) { $w1=$newdoc[count($newdoc)-1]['word']; $w2=$newdoc[count($newdoc)-2]['word']; if($this->_nohtml) { $w1=strip_tags($w1); $w2=strip_tags($w2); } if(trim($w1)=='' and trim($w2)=='') { array_pop($newdoc); // kill one of the whitespaces } } if($index<count($this->_document)-2) { $w1=$this->_document[$index+1]['word']; $w2=$this->_document[$index+2]['word']; if($this->_nohtml) { $w1=strip_tags($w1); $w2=strip_tags($w2); } if(trim($w1)=='' and trim($w2)=='') { $index++; // jump over one of the whitespaces } } } /** * * parses the left over author/include tags and sets the author accordingly */ function parseAuthorAndInclude() { $newdoc=array(); $author=''; $deleted_by=''; for($index=0, $cdoc = count($this->_document); $index < $cdoc; $index++) { $word=$this->_document[$index]; if (preg_match('/\{AUTHOR\(.+?\)\}/',$word['word'])) { $params=$this->retrieveParams($word['word']); $author=$params['author']; if(isset($params['deleted_by'])) { $deleted_by=$params['deleted_by']; } // manage double whitespace before and after $this->killDoubleWhitespaces($newdoc, $index); } elseif (preg_match('/\{AUTHOR\}/',$word['word'])) { $author=''; $deleted_by=''; $this->killDoubleWhitespaces($newdoc, $index); } elseif (preg_match('/\{INCLUDE\(.+?\)\}\{INCLUDE\}/',$word['word'])) { $params=$this->retrieveParams($word['word']); $start=''; $stop=''; if (isset($params['start'])) { $start=$params['start']; } if (isset($params['stop'])) { $stop=$params['stop']; } $subdoc=new Document($params['page'], 0, $this->_process, $this->_showpopups, $start, $stop); $newdoc=array_merge($newdoc,$subdoc->get()); } else { //normal word if ($author!='') { $word['author']=$author; } if ($deleted_by!='') { $word['deleted']=true; $word['deleted_by']=$deleted_by; } $newdoc[]=$word; } } //foreach $this->_document=$newdoc; } } $histlib = new HistLib; function histlib_helper_setup_diff( $page, $oldver, $newver ) { global $smarty, $histlib, $tikilib, $prefs; $prefs['wiki_edit_section'] = 'n'; $info = $tikilib->get_page_info( $page ); if ($oldver == 0 || $oldver == $info["version"]) { $old = & $info; $smarty->assign_by_ref('old', $info); } else { // fetch the required page from history, including its content while( $oldver > 0 && ! ($exists = $histlib->version_exists($page, $oldver) ) ) --$oldver; if ( $exists ) { $old = $histlib->get_page_from_history($page,$oldver,true); $smarty->assign_by_ref('old', $old); } } if ($newver == 0 || $newver >= $info["version"]) { $new =& $info; $smarty->assign_by_ref('new', $info); } else { // fetch the required page from history, including its content while( $newver > 0 && ! ($exists = $histlib->version_exists($page, $newver) ) ) --$newver; if ( $exists ) { $new = $histlib->get_page_from_history($page,$newver,true); $smarty->assign_by_ref('new', $new); } } $oldver_mod = $oldver; if ($oldver == 0) { $oldver_mod = 1; } $query = "SELECT `comment`, `version` from `tiki_history` WHERE `pageName`=? and `version` BETWEEN ? AND ? ORDER BY `version` DESC"; $result = $histlib->query($query,array($page,$oldver_mod,$newver)); $diff_summaries = array(); if ($oldver == 0) { $diff_summaries[] = $old['comment']; } while ($res = $result->fetchRow()) { $aux = array(); $aux["comment"] = $res["comment"]; $aux["version"] = $res["version"]; $diff_summaries[] = $aux; } $smarty->assign('diff_summaries', $diff_summaries); if (!isset($_REQUEST["diff_style"]) || $_REQUEST["diff_style"] == "old") { $_REQUEST["diff_style"] = 'unidiff'; } $smarty->assign('diff_style', $_REQUEST["diff_style"]); if ($_REQUEST["diff_style"] == "sideview") { $old["data"] = $tikilib->parse_data($old["data"], array('preview_mode' => true)); $new["data"] = $tikilib->parse_data($new["data"], array('preview_mode' => true)); } else { require_once('lib/diff/difflib.php'); if ($info['is_html'] == 1 and $_REQUEST["diff_style"] != "htmldiff") { $search[] = "~</(table|td|th|div|p)>~"; $replace[] = "\n"; $search[] = "~<(hr|br) />~"; $replace[] = "\n"; $old['data'] = strip_tags(preg_replace($search,$replace,$old['data']),'<h1><h2><h3><h4><b><i><u><span>'); $new['data'] = strip_tags(preg_replace($search,$replace,$new['data']),'<h1><h2><h3><h4><b><i><u><span>'); } if ($_REQUEST["diff_style"] == "htmldiff") { $parse_options = array('is_html' => ($old['is_html'] == 1), 'noheadinc' => true, 'suppress_icons' => true); $old["data"] = $tikilib->parse_data($old["data"], $parse_options); $new["data"] = $tikilib->parse_data($new["data"], $parse_options); $old['data'] = histlib_strip_irrelevant( $old['data'] ); $new['data'] = histlib_strip_irrelevant( $new['data'] ); } # If the user doesn't have permission to view # source, strip out all tiki-source-based comments global $tiki_p_wiki_view_source; if ($tiki_p_wiki_view_source != 'y' && $_REQUEST["diff_style"] != "htmldiff") { $old["data"] = preg_replace(';~tc~(.*?)~/tc~;s', '', $old["data"]); $new["data"] = preg_replace(';~tc~(.*?)~/tc~;s', '', $new["data"]); } $html = diff2($old["data"], $new["data"], $_REQUEST["diff_style"]); $smarty->assign_by_ref('diffdata', $html); } } function histlib_strip_irrelevant( $data ) { $data = preg_replace( "/<(h1|h2|h3|h4|h5|h6|h7)\s+([^\\\\>]+)>/i", '<$1>', $data ); return $data; } function rollback_page_to_version($page, $version, $check_key = true, $keep_lastModif = false) { global $prefs, $histlib, $tikilib, $categlib, $access; if ($check_key) { $access->check_authenticity(); } $histlib->use_version($page, $version, '', $keep_lastModif); if ( ($approved = $tikilib->get_approved_page($page)) && $prefs['wikiapproval_outofsync_category'] > 0) { $approved_page = $histlib->get_page_from_history($approved, 0, true); $staging_page = $histlib->get_page_from_history($page, $version, true); $cat_type='wiki page'; $staging_cats = $categlib->get_object_categories($cat_type, $page); $s_cat_desc = ($prefs['feature_wiki_description'] == 'y') ? substr($staging_info["description"],0,200) : ''; $s_cat_objid = $page; $s_cat_name = $page; $s_cat_href="tiki-index.php?page=".urlencode($s_cat_objid); //Instead of firing up diff, just check if the pages share the same exact data, drop the staging //copy out of the review category if so if ( $approved_page["data"] != $staging_page["data"] ) //compare these only once $pages_diff = true; if ( in_array($prefs['wikiapproval_outofsync_category'], $staging_cats) ) $in_staging_cat = true; if ( !$pages_diff && $in_staging_cat ) { $staging_cats = array_diff($staging_cats,Array($prefs['wikiapproval_outofsync_category'])); $categlib->update_object_categories($staging_cats, $s_cat_objid, $cat_type, $s_cat_desc, $s_cat_name, $s_cat_href); } elseif ( $pages_diff && !$in_staging_cat ) { $staging_cats[] = $prefs['wikiapproval_outofsync_category']; $categlib->update_object_categories($staging_cats, $s_cat_objid, $cat_type, $s_cat_desc, $s_cat_name, $s_cat_href); } } $tikilib->invalidate_cache( $page ); }