Extension:VirtualPage

From MediaWiki.org

Jump to: navigation, search
Manual on MediaWiki Extensions
List of MediaWiki Extensions
VirtualPage

Release status: beta

Implementation Page action
Description
Author(s) Jean-Lou Dupont (jldupont Talk)
Version See SVN ($Id: VirtualPage.php 619 2007-08-08 18:20:01Z jeanlou.dupont $)
MediaWiki tested on 1.10 but probably works with a earlier versions
Download SVN
Hooks used

ArticleViewHeader

Contents

[edit] Purpose

Provides regex based virtual page serving functionality based on a virtual directory.

[edit] Theory of Operation

When a user requests a non-existing page (i.e. not in the database) from namespace X, this extension looks up the virtual directory page 'X:Virtual Directory', parses it and performs a regular expression match to find a 'target template' for the requested page. If a template can not be found, the transaction reverts to the standard MediaWiki one.

[edit] Features

  • No patches
  • Fast - stub functionality for usual queries
  • Virtual Directory page can be in wikitext format

[edit] Usage

[edit] Basics

Create a page titled 'Virtual Directory' for each namespace where this extension should execute.

[edit] Format of the Virtual Directory

  • One 'regex' expression per '\n' (i.e. newline) terminated line.
  • Each line must contain 1 regex pattern + 1 link
    • Regex pattern format: <regex>/..regex expression here../</regex>
    • Link format: [[namespace:page]]

The 'link' corresponds to the page 'template' which will be served upon a successful regex match.

[edit] Example

  • Create a page called 'Virtual Directory' in the main namespace and place the following:
{| border=1
| Pattern 1 || <regex>/Log(.*)/</regex>  || [[Log:LogTemplate]]
|-
| Pattern 2 || <regex>/Blog(.*)/</regex> || [[Blog:BlogTemplate]]
|}
  • All the non-existing pages following 'Pattern1' will get served using 'Log:LogTemplate'
  • All the non-existing pages following 'Pattern2' will get served using 'Blog:BlogTemplate'

[edit] Dependancy

[edit] Installation

To install independantly from BizzWiki:

  • Download 'StubManager' extension
  • Download the two files of this extension (VirtualPageSwitch.php & VirtualPage.php)
  • Apply the following changes to 'LocalSettings.php'
require('extensions/StubManager.php');
require('extensions/VirtualPage/VirtualPage.php');
  • Make sure to have 'VirtualPage.php' and 'VirtualPageSwitch.php' in the same directory.

[edit] History

[edit] See Also

This extension is part of the BizzWiki Platform.

[edit] Code

$wgExtensionCredits[VirtualPage::thisType][] = array( 
        'name'                 => VirtualPage::thisName, 
        'version'              => StubManager::getRevisionId( '$Id: VirtualPage.php 619 2007-08-08 18:20:01Z jeanlou.dupont $' ),
        'author'               => 'Jean-Lou Dupont', 
        'description'  =>  'Provides configurable per-namespace virtual pages',
        'url'                  => StubManager::getFullUrl(__FILE__),                    
);
 
class VirtualArticle extends Article
{
        var $virtualTitle;
        var $mDbkeyform;              // variable to undo THE ugly hack.
 
        /**
                The situation is the following:
                - $this contains the 'template page' content
                - $this->virtualTitle contains the virtual title (duh!)
 
                What we need to do here: display the template page
                with:
                - correct title
                - correct skin 
                - correct actions etc.
                as if the page was really the 'virtual' one.
         */
        function view()
        {
                global $wgOut;
                global $wgNamespaceRobotPolicies;
                global $wgEnableParserCache;
                global $wgUser;
 
                // undo our little hack.
                $this->virtualTitle->mDbkeyform = $this->mDbkeyform;
 
//{{
                # Discourage indexing of printable versions, but encourage following
                if( $wgOut->isPrintable() ) {
                        $policy = 'noindex,follow';
                } elseif( isset( $wgNamespaceRobotPolicies[$ns] ) ) {
                        # Honour customised robot policies for this namespace
                        $policy = $wgNamespaceRobotPolicies[$ns];
                } else {
                        # Default to encourage indexing and following links
                        $policy = 'index,follow';
                }
                $wgOut->setRobotPolicy( $policy );
//}}
 
                //case #1: the content is available in the parser cache.
                $parserCache =& ParserCache::singleton();
 
                # Should the parser cache be used?
                $pcache = $wgEnableParserCache &&
                        intval( $wgUser->getOption( 'stubthreshold' ) ) == 0 &&
                        $this->exists() &&
                        empty( $oldid );
 
                $outputDone = false;
                wfRunHooks( 'ArticleViewHeader', array( &$this ) );
                if ( $pcache ) 
                {
                        if ( $wgOut->tryParserCache( $this, $wgUser ) ) 
                        {
                                $outputDone = true;
                        }
                }
 
                // case #2: the content is available in the database
                // We need to feed the correct 'title' object
 
                $this->mTitle = $this->templateTitle;
//{{
                if ( !$outputDone ) 
                {
                        $text = $this->getContent();
                        if ( $text === false ) {
                                # Failed to load, replace text with error message
                                $t = $this->mTitle->getPrefixedText();
                                if( $oldid ) {
                                        $t .= ',oldid='.$oldid;
                                        $text = wfMsg( 'missingarticle', $t );
                                } else {
                                        $text = wfMsg( 'noarticletext', $t );
                                }
                        }
                }
//}}
                // Put in place the necessary title corresponding
                // to the virtual page requested.
                $this->mTitle = $this->virtualTitle;
                global $wgTitle;
                $wgTitle = $this->mTitle;
 
                $wgOut->mTitle = $this->mTitle;
 
//{{  
                # Another whitelist check in case oldid is altering the title
                if ( !$this->mTitle->userCanRead() ) 
                {
                        $wgOut->loginToUse();
                        $wgOut->output();
                        exit;
                }
//}}          
                # Display content and save to parser cache
                if ( $pcache )
                        $this->outputWikiText( $text );
                else
                        $this->outputWikiText( $text, false );
 
                $wgOut->setPageTitle( $this->mTitle->getPrefixedText() );             
 
 
                $this->viewUpdates();                    
        } // view method
 
}//end class VirtualArticle
 
class VirtualPage
{
        const thisName = 'VirtualPage';
        const thisType = 'other';  // must use this type in order to display useful info in Special:Version
 
        const vdName   = 'VirtualDirectory';
 
        public function __construct() {       }
 
        /**
                This hook gets called when a title does not have
                a corresponding article in the database.
 
                The method retrieves the 'VirtualDirectory' page
                on the target namespace, parses it in order to
                derive which 'template' page should be served.
         */
        public function hVirtualPage( &$title, &$article )
        {
                $ns = $title->getNamespace();
 
                $tvd= Title::makeTitle( $ns, self::vdName );
                $vd = new Article( $tvd );
 
                // the VirtualDirectory page might not exist;
                // in this case, just bail-out graciously
                if ($vd->getID() == 0 )
                        return true;
 
                // from this point, we have a valid article
                // that serves as 'Virtual Directory'
                $vdPage = $vd->getContent();
 
                $target = $this->getTarget( $title, $vdPage );
 
                // no match? return silently
                if ($target === null)
                        return true;
 
                // We have a match!  Let's try to load the
                // template article.
                $nsT  = $target['linkNs'];
                $page = $target['linkPage'];
                if (empty($nsT)) 
                        $ns = NS_MAIN;
                else
                        $ns    = Namespace::getCanonicalIndex( $nsT );
 
                $tmplTitle   = Title::makeTitle( $ns, $page );
                $tmplArticle = new VirtualArticle( $tmplTitle );
 
                // did we find satisfaction?
                if ( $tmplArticle->getID() == 0 )
                        return true;
 
                // yes we did find a template article!
                $article = $tmplArticle;
                $article->virtualTitle   = $title;
                $article->templateTitle  = $tmplTitle;
 
                // prepare an undo for our hack below.
                $article->mDbkeyform     = $title->getDBkey();
 
                // ugly, ugly hack to get us pass 'wiki.php' hurdles          
                $title->mDbkeyform = $tmplTitle->getDBkey();
 
                return true;  
        }
        /**
                Parses the VirtualDirectory page in search
                of a match to the requested 'title'.
         */
        private function getTarget( &$title, &$vdPage )
        {
                $m = $this->parse( $vdPage );  
 
                $pageTitle = $title->getText();
 
                $result = null;
                foreach( $m as &$e )
                {
                        $regex = $e['regex'];
                        $r = preg_match( $regex, $pageTitle );    
                        if ( ( false === $r ) || (0 == $r) )
                                continue;
                        $result = array( 'linkNs' => $e['linkNs'], 'linkPage' => $e['linkPage'] );
                }
 
                return $result;
        }
        /**
                Parses the VirtualDirectory page
         */
        private function parse( $vdPage )
        {
                // one entry per line maximum
                $e = explode( "\n", $vdPage );
 
                $m = array();               
                // some other stuff is allowed on each line;
                // just pick up what we need, namely the pair:
                // <regex> ... </regex>  and  [[NS:PAGE]]
                foreach( $e as &$line )
                {
                        $mr = preg_match( "/<regex\>(.*)(?:\<</span>.?regex)>/siU", $line, $matchRegex );
 
                        // did we find a valid line?
                        if ( (false === $mr) || (0 == $mr) )
                                continue;
 
                        // pick the regex expression
                        $regex = $matchRegex[1];
 
                        // now onto the page link
                        $ml = preg_match( "/\[\[(.*):(.*)\]\]/siU", $line, $matchLink );                   
 
                        // did we find a valid link?
                        if ( (false === $ml) || (0 == $ml) )
                                continue;
 
                        $linkNs = $matchLink[1];
                        $linkP = $matchLink[2];                    
 
                        // at this point, we have a valid regex+link pair; get it.
                        $m[] = array( 'regex' => $regex, 'linkNs' => $linkNs, 'linkPage' => $linkP );
 
                } // end foreach
 
                return $m;
        } // end parse method
 
} // end class
 
//
Personal tools