DokuWiki

It's better when it's simple

User Tools

Site Tools


plugin:content

Content plugin

Compatible with DokuWiki

2006-03-09

plugin Derivative of Blog plugin, with certain enhancement and extension

Last updated on
2005-09-27
Provides
Syntax

The missing download url means that this extension cannot be installed via the Extension Manager. Please see Publishing a Plugin on dokuwiki.org. Recommended are public repository hosts like GitHub, GitLab or Bitbucket.

This extension has not been updated in over 2 years. It may no longer be maintained or supported and may have compatibility issues.

Similar to blog

Tagged with !broken, !experimental, blog, news

:!: This plugin is highly experimental :!:

By Adrian Sai-wah Tam

Description

I tried out the Blog plugin by Esther Brunner and it is a wonderful product. However, there is one shortcoming: it looks up the data from the change log.

I know it is needed for the performance reason. However, if the namespace is small, it is also reasonable to get the list of new files from the file system directly. And, sometimes, I love this way more! So I wrote this plugin.

This Plugin get a list of recently changed wiki pages of a given namespace as the Blog plugin does. However, it has the following syntax:

{{content>[namespace]?[number[&options[&options..]]]}}
[namespace] is a namespace in your wiki; defaults to the top namespace
[number] is the number of entries to show; default is 5
[options] is the option applied. Separated by the & character

All the three parameters are optional. The valid list of options are:

recursive - Include the pages recursively under the namespace
showpage - Show the page content, up to the first horizontal rule
noshowpage - Do not show the page content, show only the title
filesystem - Scan for the recently changed file from the file system
changelog - Scan for the recently changed file from the changelog

The title that used by the “showpage” or “noshowpage” options are defined as the first <h1> enclosed string. Similar to the Blog plugin and table of contents tags will be stripped and below the entry the username and the date of the last change is displayed if “showpage” option is given.

If the “noshowpage” option is given, however, the recently changed wiki pages will be shown as unordered lists. With the title, file name, and change date shown.

Code

Create a directory lib/plugins/content/ and put the following file as syntax.php to use.

<?php
/**
 * Content Plugin: displays a number of recent entries from either the
 * changelog or the filesystem
 * modified from the blog plugin by Esther Brunner and bin/wantedpages.php
 *
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
 * @author     Adrian Sai-wah Tam <adrian [at] ieaa [dot] org>
 */
 
if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
require_once(DOKU_PLUGIN.'syntax.php');
 
define ('DW_DIR_CONTINUE',1);
define ('DW_DIR_NS',2);
define ('DW_DIR_PAGE',3);
 
// Reverse sort the pages by date (recents first)
function _content_cmp_pages($a,$b) {
    if ($a[date] == $b[date]) {
        return 0;
    }
    return ($a[date] < $b[date]) ? 1 : -1;
}
 
/**
 * All DokuWiki plugins to extend the parser/rendering mechanism
 * need to inherit from this class
 */
class syntax_plugin_content extends DokuWiki_Syntax_Plugin {
    /**
     * return some info
     */
    function getInfo(){
        return array(
            'author' => 'Adrian Sai-wha Tam',
            'email'  => 'adrian@ieaa.org',
            'date'   => '2005-09-27',
            'name'   => 'Content Plugin',
            'desc'   => 'Displays a number of recent entries from a given namespace, from filesystem or changelog',
            'url'    => 'http://aipl.ie.cuhk.edu.hk/~adrian/doku.php/project:dokuwiki:content',
        );
    }
 
    function getType(){ return 'substition'; }
    function getPType(){ return 'block'; }
    function getSort(){ return 307; }
    function connectTo($mode) { $this->Lexer->addSpecialPattern('\{\{content>.+?\}\}',$mode,'plugin_content'); }
 
    /**
     * Handle the match, syntax: content>[namespace][?num[&options[&options..]]]
     */
    function handle($match, $state, $pos, &$handler){
        global $conf;
        $match = substr($match,10,-2); // strip {{content> from start and }} from end
        list($ns,$options) = explode('?',$match,2);
        // Processing the options
        $options = explode('&',$options);
        $showpage = $conf['content']['showpage'];
        $scan = $conf['content']['scanmode'];
        $recursive = (in_array("recursive",$options))?true:false;
        $showpage = (in_array("showpage",$options))?true:$showpage;
        $showpage = (in_array("noshowpage",$options))?false:$showpage;
        $scan = (in_array("filesystem",$options))?true:$scan;
        $scan = (in_array("changelog",$options))?false:$scan;
        $num = $options[0];     // Suppose the first option is the number
        if (!is_numeric($num)){
            // if (is_integer($ns)){
            if (is_numeric($ns)){ // reset number of returned entries fix 
                $num = $ns;
                $ns  = '';
            } else {
                $num = 5;
            }
        }
        return array($ns,$num,$recursive,$showpage,$scan);
    }
 
    /**
     * Create output
     */
    function render($mode, &$renderer, $data) {
        global $conf;
        global $ID;
        global $filechain;
        global $conf;
 
        if($mode == 'xhtml'){
            if (!isset($filechain)) $filechain[] = $ID;
 
            // list here pages which should not be included in the blog
            $exclude = array('start','sidebar');
            $exclude = array_merge($filechain,$exclude);
 
            $comments   = 'Comments'; // @todo: localize!
 
            // prevent caching to ensure the included page is always fresh
            // $renderer->info['cache'] = FALSE; // without caching it's to slow
 
            $num        = $data[1];
            $recents = ($data[4])?  // Scan filesystem or changelog?
                          $this->_get_pages($data[0],$data[2]) :
                          getRecents(0,$num,$data[0], $data[2]?0:RECENTS_SKIP_SUBSPACES );
            if ($num > count($recents)) $num = count($recents);
            if ($num < count($recents)) $recents = array_slice($recents, 0, $num); // show recent using filesystem method fix
 
            if (! $data[3]) {    // If showpage is not set, the pages are expressed as lists
                $renderer->doc .= '<ul>';
            };
 
            foreach($recents as $recent){
                $id = $recent['id'];
                if (in_array($id, $exclude)) continue;
                $filechain[] = $id;
 
                $date     = date($conf['dformat'],$recent['date']);
                if ($recent['user']){       // Available only if we obtained the list from changelog
                    $userInfo = auth_getUserData($recent['user']);
                    $nametag  = $userInfo['name'];
                } else {                    // If it is from filesystem scan, we should know the filename
                    $nametag  = $recent['file'];
                }
                $content  = p_cached_xhtml(wikiFN($id));
                if ($data[3]) {    // showpage ?
                    $patterns = array('!<div class="toc">.*?(</div>\n</div>)!s', // remove toc
                                      '!<h1>(.*?)</h1>!s',                       // link h1 (permalink)
                                      '#<!-- SECTION \[(\d*-\d*)\] -->#e',       // remove section edit buttons
                                      '!<div class="category">.*?</div>!s');     // remove category tags
                    $replace  = array('',
                                      '<h1>'.$renderer->internallink($id,"\\1",'',true).'</h1>',
                                      '',
                                      '');
                    $content  = preg_replace($patterns,$replace,$content);
                    $parts = preg_split('/<hr.*>/i', $content);
                    if (count($parts)>1) $content = $parts[0].'</div>';
                    $renderer->doc .= '<div class="include">';
                    $renderer->doc .= $content;
                    $renderer->doc .= '</div>';
                    $renderer->doc .= '<div class="meta">' . $nametag . ' &middot; ' . $date;
                    $dFN = wikiFN($this->_addDiscussionNS($id));
                    if ($dFN && @file_exists($dFN) && $data[3]){
                        $text = io_readFile($dFN);
                        $noc  = count(explode('----',$text)) - 1;
 
                        $renderer->doc .= ' &middot; '.
                            $renderer->internallink($id.'#discussion',$comments.' ('.$noc.')','',true);
                    }
                    $renderer->doc .= '</div><br /><br />';
                } else {    // no showpage
                    if (preg_match('!<h1>.*</h1>!',$content) == 0) {
                        $content = $renderer->internallink($id,$id,"",true);
                    } else {
                        // Bug fix reported by Johannes Zarl, for release 2006-03-09b
                        $content  = preg_replace('!.*?<h1>(<a [^>]*>)?(.*?)(</a>)?</h1>.*!s',
                                                 $renderer->internallink($id,"\\2",'',true),
                                                 $content);
                    };
                    $renderer->doc .= "<li>$content<br/>&nbsp;&nbsp;&nbsp;&nbsp;".$recent['file']." &middot; $date</li>";
                };
            }
            if (! $data[3]) {    // If showpage is not set, the pages are expressed as lists
                $renderer->doc .= '</ul>';
            };
            return true;
        }
        return false;
    }
 
    /**
     * Convert a normal page ID to a discussion page ID.
     *
     * @author Dave Lawson <dlawson@masterytech.com>
     * @author Esther Brunner <esther@kaffeehaus.ch>
     */
    function _addDiscussionNS($id) {
        global $conf;
        $dNS = $conf['discussion']['namespace'];
        if (!$dNS) return false;
        if ($this->_isDiscussionID($ID)) return $id;
        $dID = getNS($id);
        return $dID.($dID ? ':' : '').$dNS.':'.noNS($id);;
    }
 
    /**
     * Is specified page ID in a discussion namespace?
     *
     * @author Dave Lawson <dlawson@masterytech.com>
     */
    function _isDiscussionID($id) {
        global $conf;
        $dNS = $conf['discussion']['namespace'];
        $pID = noNS($id);
        return strpos($id, $dNS.':'.$pID) !== false;
    }
 
    // Check for the type of directory entry
    // copied from bin/wantedpages.php
    function _dir_filter($entry, $basepath) {
        if ($entry == '.' || $entry == '..' ) {
            return DW_DIR_CONTINUE;
        }
        if ( is_dir($basepath . '/' . $entry) ) {
            if ( strpos($entry, '_') === 0 ) {
                return DW_DIR_CONTINUE;
            }
            return DW_DIR_NS;
        }
        if ( preg_match('/\.txt$/',$entry) ) {
            return DW_DIR_PAGE;
        }
        return DW_DIR_CONTINUE;
    }
 
    // Get a list of pages, without sorting
    // copied from bin/wantedpages.php with modification
    function _get_pages($dir,$recursive) {
        global $conf;
        static $trunclen = NULL;
        $fullpath = $conf['datadir'].'/'.$dir;
        if ( !$trunclen ) {
            global $conf;
            $trunclen = strlen($conf['datadir'].':');
        };
        if ( !is_dir($fullpath) ) {
            echo "Unable to read directory $dir\n";
            exit(1);
        };
        $pages = array();
        $dh = opendir($fullpath);
        while ( FALSE !== ( $entry = readdir($dh) ) ) {
            $status = $this->_dir_filter($entry, $fullpath);
            if ( $status == DW_DIR_CONTINUE ) {
                continue;
            } else if ( $status == DW_DIR_NS ) {
                if ($recursive) {
                  $pages = array_merge($pages, $this->_get_pages($dir . '/' . $entry,$recursive));
                };
            } else {
                $page = array(
                    'id'=>$this->pathID($dir . '/' . $entry),
                    'file'=>$dir . '/' . $entry,
                    'date'=>filemtime($fullpath . '/' . $entry),
                    );
                $pages[] = $page;
            }
        }
        closedir($dh);
        usort($pages,"_content_cmp_pages");
        return $pages;
    }
 
    // copied from inc/search.php
    function pathID($path,$keeptxt=false){
        $id = utf8_decodeFN($path);
        $id = str_replace('/',':',$id);
        if(!$keeptxt) $id = preg_replace('#\.txt$#','',$id);
        $id = preg_replace('#^:+#','',$id);
        $id = preg_replace('#:+$#','',$id);
        return $id;
    }
}
 
//Setup VIM: ex: et ts=4 enc=utf-8 :

Change History

  • 2005-09-27:
    • Initial release.
  • 2006-08-07:
    • Updated to include the fixes by Markus Aittola and Johannes Zarl. Now works for the most recent release (2006-03-09b)
  • 2006-08-08:
    • Minor bug fix: Changed line 104 (getRecents call) to reflect the change in the function prototype in the recent releases.

Options

After your creation of lib/plugins/content/syntax.php, make sure you include the following lines into conf/local.php as defaults options:

// Content plugin
$conf['content']['showpage']    = true;  // By default, show the page
$conf['content']['scanmode']    = true;  // By default, scan the filesystem for the list

Discussion

A usable plugin, thank you. I think I found a bugs, though, from handling the number of shown entries. It returns the specified number of entries only if you are using changelog option. Fixed that adding following into the render() function:

if ($num < count($recents)) $recents = array_slice($recents, 0, $num);

Also using is_integer on line 68 (handle() function) always reset the number of returned entries. Using is_numeric worked better. — Markus Aittola 2005-10-31 09:15 —

I confirm this bug fix: I changed the line in the script code to use is_numeric in line 68. I modified the code of this page for the fix. — JF L.

Listed inline

By default, the plugin list the pages using ul and li. I changed the behaviour of the plugin for an inline list:

Modifications:

  • Line 109:
$renderer->doc .= '<ul>';
$renderer->doc .= ' ';
  • Line 158:
$renderer->doc .= <li>$content<br/>&nbsp;&nbsp;&nbsp;&nbsp;".$recent['file']." &middot $date</        li>";
  $renderer->doc .= "$content ";
  • Line 162:
$renderer->doc .= '</ul>';
  $renderer->doc .= ' ';

Maybe it could be of some use to add a parameter to choose between inline or ul/li list… — JF L.

What's the Advantage

You said the reason for creating this plugin is to get the list of new files from the file system directly. What is the advantage of this?

plugin/content.txt · Last modified: 2023-10-30 22:40 by Klap-in

Except where otherwise noted, content on this wiki is licensed under the following license: CC Attribution-Share Alike 4.0 International
CC Attribution-Share Alike 4.0 International Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki