*/
// must be run within Dokuwiki
if (!defined('DOKU_INC')) die();
if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
require_once(DOKU_PLUGIN.'syntax.php');
/**
* All DokuWiki plugins to extend the parser/rendering mechanism
* need to inherit from this class
*/
class syntax_plugin_wikistats extends DokuWiki_Syntax_Plugin {
/**
* Get an associative array with plugin info.
*
*
* The returned array holds the following fields:
*
* - author
- Author of the plugin
* - email
- Email address to contact the author
* - date
- Last modified date of the plugin in
* YYYY-MM-DD format
* - name
- Name of the plugin
* - desc
- Short description of the plugin (Text only)
* - url
- Website with more information on the plugin
* (eg. syntax description)
*
* @param none
* @return Array Information about this plugin class.
* @public
* @static
*/
function getInfo(){
return confToHash(dirname(__FILE__).'/plugin.info.txt');
}
/**
* Get the type of syntax this plugin defines.
*
* The type of this plugin is "substition".
*
* @param none
* @return String 'substition'.
* @public
* @static
*/
public function getType() {
return 'substition';
}
/**
* Define how this plugin is handled regarding paragraphs.
*
*
* This method is important for correct XHTML nesting. It returns
* one of the following values:
*
*
* - normal
- The plugin can be used inside paragraphs.
* - block
- Open paragraphs need to be closed before
* plugin output.
* - stack
- Special case: Plugin wraps other paragraphs.
*
* @param none
* @return String 'normal'.
* @public
* @static
*/
public function getPType() {
return 'normal';
}
/**
* Where to sort in?
*
* Sort the plugin in just behind the formating tokens
* Low numbers go before high numbers
*
* @param none
* @return Integer 128.
* @public
* @static
*/
public function getSort() {
return 128;
}
/**
* Connect lookup pattern to lexer.
*
* @param $aMode String The desired rendermode.
* @return none
* @public
* @see render()
*/
public function connectTo($mode) {
$this->Lexer->addSpecialPattern('\{\{wikistats>[^}]*\}\}',$mode,'plugin_wikistats');
}
/**
* Handle matches of the wikistats syntax
*
* @param string $match The match of the syntax
* @param int $state The state of the handler
* @param int $pos The position in the document
* @param Doku_Handler $handler The handler
* @return array Data for the renderer
*/
public function handle($match, $state, $pos, Doku_Handler &$handler){
$match = substr($match, 12, -2);
$data = array(
'ns' => array(),
'type' => array()
);
$match = explode('&', $match);
foreach($match as $m) {
if (preg_match('/(\w+)\s*=(.+)/', $m, $temp) == 1){
$this->handleNamedParameter($temp[1], trim($temp[2]), $data);
} else {
$this->addNamespace($data, trim($m));
}
}
return $data;
}
/**
* Handle parameters that are specified using = syntax
*/
function handleNamedParameter($name, $value, &$data) {
static $types = array('pages', 'medias', 'stats');
switch($name) {
case 'ns':
foreach(preg_split('/\s*,\s*/', $value) as $value) {
$this->addNamespace($data, $value);
}
break;
case 'type':
if (preg_match('/(\w+)/', $value, $match) == 1) {
if (in_array($match[1], $types)) {
$data[$name] = $match[1];
}
}
break;
}
}
/**
* Clean-up the namespace name and add it (if valid) into the $data array
*/
function addNamespace(&$data, $namespace) {
$action = ($namespace{0} == '-') ? 'exclude' : 'include';
$namespace = cleanID(preg_replace('/^[+-]/', '', $namespace));
if (!empty($namespace)) {
$data['ns'][$action][] = $namespace;
}
}
/**
* Render xhtml output or metadata
*
* @param string $mode Renderer mode (supported modes: xhtml)
* @param Doku_Renderer $renderer The renderer
* @param array $data The data from the handler() function
* @return bool If rendering was successful.
*/
public function render($mode, Doku_Renderer &$renderer, $data) {
global $conf;
global $ID;
global $TOC;
if ($mode != 'xhtml') return false;
// prevent caching to ensure the included pages are always fresh
$renderer->info['cache'] = false;
$list = array();
$content = '';
switch ($data['type']) {
case "pages":
search($list, $conf['datadir'], array($this, '_search_count'), array('type' => $data['type'], 'ns' => $data['ns']), '');
$content = $list['pages_count'];
break;
case "medias":
search($list, $conf['mediadir'], array($this, '_search_count'), array('type' => $data['type'], 'ns' => $data['ns']));
$content = $list['medias_count'];
break;
case "stats":
search($list, $conf['datadir'], array($this, '_search_count'), array('type' => $data['type'], 'ns' => $data['ns']), '');
search($list, $conf['mediadir'], array($this, '_search_count'), array('type' => $data['type'], 'ns' => $data['ns']));
if ($this->getConf('display_toc')) {
$TOC = p_get_metadata($ID,'description tableofcontents');
} else {
$TOC = NULL;
}
$content .= $this->displayResourceStats($list);
$content .= $this->displayTagStats($list);
$content .= $this->displayNamespaceStats($list['dir_label']['ns']);
break;
}
$renderer->doc .= $content;
return true;
}
function _search_count(&$data, $base, $file, $type, $lvl, $opts){
if ($type == 'd') {
if ($data['dir_nest'] < $lvl) $data['dir_nest'] = $lvl;
$data['dir_count']++;
return true;
}
$file = str_replace("/", ":", $file);
// filter included namespaces
if (isset($opts['ns']['include'])) {
if (!$this->isInNamespace($opts['ns']['include'], $file)) return false;
}
// filter excluded namespaces
if (isset($opts['ns']['exclude'])) {
if ($this->isInNamespace($opts['ns']['exclude'], $file)) return false;
}
switch ($opts['type']) {
case "pages":
$data['pages_count']++;
break;
case "medias":
$data['medias_count']++;
break;
case "stats":
$path = $file;
$file = substr($file, 1);
$file = substr($file, 0, strrpos($file, ':'));
//if (pathinfo($path, PATHINFO_EXTENSION) != 'txt') $file = substr($file, 0, strrpos($file, ':'));
switch (pathinfo($path, PATHINFO_EXTENSION)) {
case 'txt':
$data['pages_count']++;
$data['dir_label']['ns'][$file]['pages']++;
break;
case 'swf':
$data['flash_count']++;
$data['dir_label']['ns'][$file]['medias']++;
break;
case 'jpg':
case 'jpeg':
case 'png':
case 'gif':
case 'svg':
$data['images_count']++;
$data['dir_label']['ns'][$file]['medias']++;
break;
case 'mov':
case 'avi':
case 'flv':
case 'mp4':
case 'webm':
case 'ogv':
$data['movies_count']++;
$data['dir_label']['ns'][$file]['medias']++;
break;
case 'wav':
case 'mp3':
case 'ogg':
$data['audio_count']++;
$data['dir_label']['ns'][$file]['medias']++;
break;
case 'xls':
case 'xlsm':
case 'xlsx':
case 'ods':
$data['sheets_count']++;
$data['dir_label']['ns'][$file]['medias']++;
break;
case 'doc':
case 'docx':
case 'odt':
$data['writer_count']++;
$data['dir_label']['ns'][$file]['medias']++;
break;
case 'ppt':
case 'pptx':
case 'odp':
$data['presentation_count']++;
$data['dir_label']['ns'][$file]['medias']++;
break;
case 'djvu':
case 'epub':
case 'mobi':
case 'pdf':
$data['documents_count']++;
$data['dir_label']['ns'][$file]['medias']++;
break;
case 'tar':
case 'arj':
case 'zip':
case 'bzip':
case 'rar':
case 'tgz':
case 'gz':
case '7z':
case 'bz2':
$data['archives_count']++;
$data['dir_label']['ns'][$file]['medias']++;
break;
case 'exe':
case 'com':
$data['binaries_count']++;
$data['dir_label']['ns'][$file]['medias']++;
break;
default:
//var_export($file);
$data['others_count']++;
$data['dir_label']['ns'][$file]['medias']++;
break;
}
break;
}
return false;
}
/**
* Check if page belongs to one of namespaces in the list
*/
function isInNamespace($namespaces, $id) {
foreach($namespaces as $ns) {
if ((strpos($id, ':' . $ns . ':') === 0)) {
return true;
}
}
return false;
}
/**
* Display pages and medias stats
*/
private function displayResourceStats($list) {
global $TOC;
$content = '';
if ($this->getConf('display_ressources_stats')) {
if ($this->getConf('display_toc')) {
$TOC[] = Array('hid' => 'resources', 'title' => $this->getLang('resources_title'), 'type' => 'ul', 'level' => '2');
}
$class = $this->getConf('display_type');
$col = "page";
$content .= ''.DOKU_LF;
$content .= ''.$this->getLang('resources_title').'
'.DOKU_LF;
$content .= ''.DOKU_LF;
$content .= '
'.DOKU_LF;
$content .= DOKU_TAB.''.DOKU_LF;
$content .= DOKU_TAB.DOKU_TAB.''.$this->getLang('resources').' | '.DOKU_LF;
$content .= DOKU_TAB.DOKU_TAB.'# | '.DOKU_LF;
$content .= DOKU_TAB.'
'.DOKU_LF;
if(empty($list)) {
// Skip output
$content .= DOKU_TAB.''.DOKU_LF;
$content .= DOKU_TAB.DOKU_TAB.''.$this->getLang('empty_output').' | '.DOKU_LF;
$content .= DOKU_TAB.'
'.DOKU_LF;
} else {
foreach($list as $resname => $count) {
if ($count <= 0) continue; // don't display resources with zero occurrences
if ($resname === "dir_nest") continue;
if ($resname === "dir_count") continue;
if ($resname === "dir_label") continue;
$content .= DOKU_TAB.''.DOKU_LF;
$content .= DOKU_TAB.DOKU_TAB.''.$this->getLang($resname).' | '.DOKU_LF;
$content .= DOKU_TAB.DOKU_TAB.''.$count.' | '.DOKU_LF;
$content .= DOKU_TAB.'
'.DOKU_LF;
}
}
$content .= '
'.DOKU_LF;
$content .= '
'.DOKU_LF.DOKU_LF;
}
return $content;
}
/**
* Display tags related stats
*/
private function displayTagStats($list) {
global $TOC;
$content = '';
if ($this->getConf('display_tags_stats')) {
if (plugin_isdisabled('tag') || (!$tag = plugin_load('helper', 'tag'))) {
msg('The Tag Plugin must be installed to display tag related stats.', -1);
} else {
if ($this->getConf('display_toc')) {
$TOC[] = Array('hid' => 'tags', 'title' => $this->getLang('tags_title'), 'type' => 'ul', 'level' => '2');
}
$occurrences = $tag->tagOccurrences(NULL, NULL, true, NULL);
ksort($occurrences);
$class = $this->getConf('display_type');
$col = "page";
$content .= ''.DOKU_LF;
$content .= ''.DOKU_LF;
$content .= ''.DOKU_LF;
$content .= '
'.DOKU_LF;
$content .= DOKU_TAB.''.DOKU_LF;
$content .= DOKU_TAB.DOKU_TAB.''.$this->getLang('tags').' ('.count($occurrences).') | '.DOKU_LF;
$content .= DOKU_TAB.DOKU_TAB.'# | '.DOKU_LF;
$content .= DOKU_TAB.'
'.DOKU_LF;
if (empty($occurrences)) {
// Skip output
$content .= DOKU_TAB.''.DOKU_LF;
$content .= DOKU_TAB.DOKU_TAB.''.$this->getLang('empty_output').' | '.DOKU_LF;
$content .= DOKU_TAB.'
'.DOKU_LF;
} else {
foreach($occurrences as $tagname => $count) {
if ($count <= 0) continue; // don't display tags with zero occurrences
$content .= DOKU_TAB.''.DOKU_LF;
$content .= DOKU_TAB.DOKU_TAB.''.$tagname.' | '.DOKU_LF;
$content .= DOKU_TAB.DOKU_TAB.''.$count.' | '.DOKU_LF;
$content .= DOKU_TAB.'
'.DOKU_LF;
}
}
$content .= '
'.DOKU_LF;
$content .= '
'.DOKU_LF.DOKU_LF;
}
}
return $content;
}
/**
* Display namespaces related stats
*/
private function displayNamespaceStats($list) {
global $TOC;
$content = '';
if ($this->getConf('display_namespaces_stats')) {
if ($this->getConf('display_toc')) {
$TOC[] = Array('hid' => 'namespaces', 'title' => $this->getLang('namespaces_title'), 'type' => 'ul', 'level' => '2');
}
$class = $this->getConf('display_type');
$col = "page";
$content .= ''.DOKU_LF;
$content .= ''.$this->getLang('namespaces_title').'
'.DOKU_LF;
$content .= ''.DOKU_LF;
$content .= '
'.DOKU_LF;
$content .= DOKU_TAB.''.DOKU_LF;
$content .= DOKU_TAB.DOKU_TAB.''.$this->getLang('namespaces').' ('.count($list).') | '.DOKU_LF;
$content .= DOKU_TAB.DOKU_TAB.'# '.$this->getLang('pages_count').' | '.DOKU_LF;
$content .= DOKU_TAB.DOKU_TAB.'# '.$this->getLang('medias_count').' | '.DOKU_LF;
$content .= DOKU_TAB.'
'.DOKU_LF;
if(empty($list)) {
// Skip output
$content .= DOKU_TAB.''.DOKU_LF;
$content .= DOKU_TAB.DOKU_TAB.''.$this->getLang('empty_output').' | '.DOKU_LF;
$content .= DOKU_TAB.'
'.DOKU_LF;
} else {
ksort($list);
foreach($list as $namespace => $arr) {
if ($namespace == '') $namespace = '[root]';
$content .= DOKU_TAB.''.DOKU_LF;
$content .= DOKU_TAB.DOKU_TAB.''.$namespace.' | '.DOKU_LF;
$content .= DOKU_TAB.DOKU_TAB.''.(isset($arr['pages']) ? $arr['pages'] : '0') .' | '.DOKU_LF;
$content .= DOKU_TAB.DOKU_TAB.''.(isset($arr['medias']) ? $arr['medias'] : '0') .' | '.DOKU_LF;
$content .= DOKU_TAB.'
'.DOKU_LF;
}
}
$content .= '
'.DOKU_LF;
$content .= '
'.DOKU_LF.DOKU_LF;
}
return $content;
}
}
// vim:ts=4:sw=4:et: