Extension:Article Class Extended

From MediaWiki.org
Jump to: navigation, search
MediaWiki extensions manual - list
Crystal Clear action run.png
ArticleEx

Release status: stable

Implementation Hook
Description Extends the Mediawiki Article Class to provide serving pages without skin and addressed through explicit 'type' information.
Author(s) Jean-Lou Dupont (jlduponttalk)
Latest version 1.7
MediaWiki tested on 1.8.2 and 1.9.3
Database changes no
License No license specified
Download No link
Hooks used
ArticleFromTitle

Translate the Article Class Extended extension if possible

Check usage and version matrix; code metrics

This extension extends the Mediawiki Article class with the following features:

  • Serves XML / XSL data sources stored in MW pages with proper MIME content type (requires using Extension:XML Class)
  • Serves 'raw' data stored in MW pages, the said data must be enclosed in <tag> whereby <tag> defines the title name extension that will be used to retrieve the said data.
  • Adds a new hook ArticleViewEx which can be leveraged to modify content before it is added to the output page.

Usage[edit | edit source]

Installation[edit | edit source]

Parameters[edit | edit source]

Changes to LocalSettings.php[edit | edit source]

Code[edit | edit source]

<?php
/*
 * ArticleEx.php
 * 
 * MediaWiki extension
 * @author: Jean-Lou Dupont (http://www.bluecortex.com)
 *
 * Purpose:  Extends the MW Article class to handle
 *           the serving of pages without skin and addressed
 *           through explicit type information.
 *           Example:
 *           /index.php?title=Main:somexmldatapage.xml
 *           The actual MW stored article is titled:
 *           "Main:somexmldatapage" and the "raw" form
 *           is addressed with the above URL.
 *
 * Supported types:  all.  Data is retrieved from
 *                   a section delimited with an XML style
 *                   tag.  E.g. for an XSL XML stylesheet,
 *                   the page data would be enclosed in
 *                   <xsl> stylesheet data here </xsl>  
 *
 * Tested Compatibility: MW 1.8.2
 *
 * History:
 * v1.0		Initial availability
 * v1.1		- Added "ArticleViewEx" hook
 * v1.2		- Added "ArticleViewExBegin" hook
 *          - Added a general purpose "attributes" array.
 *          - Added the capability to flag is the said article
 *            is the first loaded in a transaction.
 * v1.3		- Corrected major bug: if article did not exists
 *			  would cause a fatal error.
 * v1.4		- Corrected maor bug: if an non-existing page from
 *            the following namespaces is requested (NS_MEDIA,
 *            NS_CATEGORY, NS_IMAGE), the standard processing
 *            flow should have been continued.
 * v1.5     - Corrected bug: initialised attributes array AFTER
 *            setting some variables causing loss of variables...
 * v1.6     - Added support for retrieving the 'categorylinks'
 *            when a valid article is loaded. The data can be found
 *            in '$this->categories'.
 *            This functionality is especially useful when templates
 *            require 'categorisation' level information.
 *          - Added initialisation of the global variable 'wgArticle'.
 *            Normally, this global variable is initialised a little bit
 *            too late to be useful i.e. after the action on the page is
 *            actually performed (see Wiki.php 'initialize' function).
 *
 * v1.7     - (a) Added chaining capability to 'ArticleViewExBegin' hook.
 *
 */
$wgExtensionCredits['other'][] = array( 
	'name'    => 'ArticleEx',
	'version' => '1.7',
	'author'  => 'Jean-Lou Dupont', 
	'url'     => 'http://www.bluecortex.com',
);
 
require_once(dirname( __FILE__ ) . '/ArticleExClass.php');
 
$wgExtensionFunctions[] = "wfArticleExSetup";
 
function wfArticleExSetup()
{
	global $wgHooks;
	$wgHooks['ArticleFromTitle'][] = 'wfArticleExInit';	
}
 
function wfArticleExInit( &$title, &$article )
{
	// What really counts is what is returned in $article.
	$GLOBALS['wgArticle'] = new ArticleExClass( $title, $article, true );
	return true;	
} 
?>
<?php
/*
 * ArticleExClass.php
 * 
 * MediaWiki extension
 * @author: Jean-Lou Dupont (http://www.bluecortex.com)
*/
 
require_once("$IP/includes/Article.php");
 
class ArticleExClass extends Article
{
	static $qType = array(
							'js'  => array( 'js', 'javascript' ),
							'xsl' => array( 'xsl', 'xml' ),
							'xml' => array( 'xml', 'xsl' ),
						);
	var $noskin;
	var $extractSection;
	var $type;
	var $bname;
	var $fname;
	var $section;
	var $attributes;
	var $foundDot;
 
	public function ArticleExClass( &$title, &$article, $firstArticleInTransation=false )
	{
		$this->init();	
		$this->setA("first", $firstArticleInTransation );  // new in v1.2
		$this->create( $title, $article );
	}
 
	public function init()
	{
		$this->noskin = false;
		$this->type   = null;
		$this->bname  = "";
		$this->extractSection = false;
		$this->section = null;
		$this->attributes = array();
		$this->foundDot = false;
	}
 
	// Attributes table 'getter/setter' interface.
	// v1.2 change.
	public function setA($key,$value) { $this->attributes[$key]=$value; }
	public function getA($key)		  { return @$this->attributes[$key]; }
 
	public function create( &$title, &$article )
	/*
	 *   Hook function handler.
	 *   Substitutes an ArticleEx object instance in place
	 *   of the usual MW Article class object.
	 *
	 */
	{
		// Check the special cases
		$ns = $title->getNamespace();
		if ($ns == NS_MEDIA || $ns == NS_IMAGE || $ns == NS_CATEGORY)
			return null; // let the normal flow proceed.
 
		parent::__construct( $title );
		$article = $this;
 
		// Let's check the database if a page matching the title name
		// actually exists... if one does, then abort our special hook.
		if ($this->getID()!=0)
			return true;  // continue the hook chain.
 
		// First, extract base name from MW Title Class object.
		// The base name takes the form:
		//    somemwprefixeddbkey.type
		// Where only the right most '.' delimits the 'type' information
		$n = $this->fname = $this->getTitle()->getPrefixedDBkey();
		$p = strrpos( $n, "." );
 
		if ($p===false) 	// no '.' found, bail out.
			return true;
 
		// Raise a flag to simplify our lives in the view() method.
		$this->foundDot = true;
 
		// We are anyhow dealing with a valid MW title name,
		// so spare the security checks.
		$this->bname = substr($n, 0, $p);
		$this->type  = substr($n, $p+1 );
 
		return true; // continue the hook chain.
	}
 
	public function view()
	{
		// V1.2 change
		// Don't forget to check user rights (if applicable) 
		// in the extensions that attach here!
		if (!wfRunHooks( 'ArticleViewExBegin', array( &$this ) ))
			return; // v1.7 feature (a)
 
		if (!$this->foundDot)
			return parent::view();				
 
		// if the article does not exists (probably given the 'type' extension
		// appended to the title name) & an actual 'type' extension is 
		// found in the title name, then let's check if an article actually
		// exists with a title name that EXCLUDES the 'type' extension.
		if ($this->getID()!=0)
			return parent::view();
 
		// at this point, we know that the article does not exist.
		// Let's check for an article with for title one without
		// the 'type' extension.
		$title = Title::newFromText($this->bname);
		$this->mTitle = $title; 
		$this->clear();
		$this->oldid= null;
 
		// nope...
		if ($this->getID()==0)
		{
			// good thing we kept the full title name just in case!
			$title = Title::newFromText($this->fname);
			$this->mTitle = $title;
			$this->clear();
			$this->oldid = null;
			return parent::view(); // go with default behavior
		}
 
		// At this point, we know we have a valid article
 
		// Check User Rights...
		if (!$title->userCanRead()) 
			return parent::view(); // let the normal flow handle this one...
 
		$this->loadContent();
 
		$this->extractSection = empty($this->type) ? false:true;	
 
		#echo "type = $this->type, extract= $this->extractSection <br/>";
 
		if ($this->extractSection)
			$this->section = $this->extractSection( $this->type, $this->mContent ) ;			
 
		#echo $this->section;
 
		// DEFAULT BEHAVIOR:
		// If we have detected a "type" in the page name,
		// then serve the page without the skin.
		$this->noskin = empty($this->type) ? false:true;
		$content = $this->section;
 
		// The other parameters can be fetched from the object itself.
		// Parameters passed:
		// - ArticleEx object instance
		// - type e.g. xml, xsl etc.
		// - content  (what is about to be returned if nothing is done)
		// 
		// NOTE: at this point, the variable $content does not include the
		//       Mediawiki content enclosing tags e.g. <xml> content </xml>
		wfRunHooks( 'ArticleViewEx', array( &$this, &$this->type, &$content ) );
 
		global $wgOut;
		$wgOut->clearHTML();
		$wgOut->addHTML( $content );				
 
		// next, let's see if we need to apply special treatment.
		if ($this->noskin)
			$wgOut->setArticleBodyOnly(true);
 
	}
	private function extractSection( &$type, &$content )
	{
		// check if we have multiple choices
		if (isset(self::$qType[$type]))
		{
			$tags = self::$qType[$type];
			foreach( $tags as $tag )
			{
				$pattern = "/<".$tag."(?:.*)\>(.*)(?:\<.?".$tag.">)/siU";
				$r = preg_match_all( $pattern, $content, $m );
				if ($r>0)
					break;
			}
		}
		else
		{
			$pattern = "/<".$type."(?:.*)\>(.*)(?:\<.?".$type.">)/siU";
			preg_match_all( $pattern, $content, $m );
		}
 
		return trim( $m[1][0] ); // just the first submatch
	}
 
// =================================================================================
 
	public function loadPageData( $data = 'fromdb' )
	/*
	 *   This hook is used to retrieve all the categories associated with an article.
	 */
	{
		$ret = parent::loadPageData( $data ); // get return code just in case.
 
		// At this point, we have a valid 'page id' in a local variable.
		// Use it to query the database for 'categorylinks'
		$dbr      =& wfGetDB( DB_SLAVE );
		$page     = $dbr->tableName( 'page' );
        $catlinks = $dbr->tableName( 'categorylinks' ); 
 
		$id = $this->getID();
 
		$query = "SELECT cl_to FROM {$catlinks} WHERE {$catlinks}.cl_from={$id}";
 
		$results = $dbr->query( $query );
		$count   = $dbr->numRows( $results );
 
		if ($count>=1)
			while( $row = $dbr->fetchObject( $results ) )
				$this->categories[] = $row->cl_to;
 
		$dbr->freeResult( $results );
 
		return $ret;
	}
 
} // End class definition.

See also[edit | edit source]