mediawiki-extensions-Catego.../CategoryTree.php

301 lines
10 KiB
PHP
Raw Normal View History

<?php
/**
2008-02-04 09:22:12 +00:00
* Setup and Hooks for the CategoryTree extension, an AJAX based gadget
* to display the category structure of a wiki
*
* @addtogroup Extensions
* @author Daniel Kinzler, brightbyte.de
* @copyright © 2006-2008 Daniel Kinzler and others
* @license GNU General Public Licence 2.0 or later
*/
2008-02-04 09:22:12 +00:00
if( !defined( 'MEDIAWIKI' ) ) {
echo( "This file is an extension to the MediaWiki software and cannot be used standalone.\n" );
die( 1 );
}
/**
* Constants for use with efCategoryTreeRenderChildren,
* defining what should be shown in the tree
*/
define('CT_MODE_CATEGORIES', 0);
define('CT_MODE_PAGES', 10);
define('CT_MODE_ALL', 20);
/**
* Options:
*
* $wgCategoryTreeMaxChildren - maximum number of children shown in a tree node. Default is 200
* $wgCategoryTreeAllowTag - enable <categorytree> tag. Default is true.
* $wgCategoryTreeDynamicTag - loads the first level of the tree in a <categorytag> dynamically.
* This way, the cache does not need to be disabled. Default is false.
* $wgCategoryTreeDisableCache - disabled the parser cache for pages with a <categorytree> tag. Default is true.
* $wgCategoryTreeUseCache - enable HTTP cache for anon users. Default is false.
* $wgCategoryTreeMaxDepth - maximum value for depth argument; An array that maps mode values to
2008-02-04 09:22:12 +00:00
* the maximum depth acceptable for the depth option.
* Per default, the "categories" mode has a max depth of 2,
* all other modes have a max depth of 1.
* $wgCategoryTreeDefaultOptions - default options for the <categorytree> tag.
* $wgCategoryTreeCategoryPageOptions - options to apply on category pages.
* $wgCategoryTreeSpecialPageOptions - options to apply on Special:CategoryTree.
2008-02-04 09:22:12 +00:00
*/
$wgCategoryTreeMaxChildren = 200;
$wgCategoryTreeAllowTag = true;
$wgCategoryTreeDisableCache = true;
$wgCategoryTreeDynamicTag = false;
$wgCategoryTreeHTTPCache = false;
#$wgCategoryTreeUnifiedView = true;
$wgCategoryTreeMaxDepth = array(CT_MODE_PAGES => 1, CT_MODE_ALL => 1, CT_MODE_CATEGORIES => 2);
$wgCategoryTreeExtPath = '/extensions/CategoryTree';
$wgCategoryTreeVersion = '2'; #NOTE: bump this when you change the CSS or JS files!
$wgCategoryTreeOmitNamespace = false;
$wgCategoryTreeDefaultMode = CT_MODE_CATEGORIES;
$wgCategoryTreeDefaultOptions = array(); #Default values for most options. ADD NEW OPTIONS HERE!
$wgCategoryTreeDefaultOptions['mode'] = NULL; # will be set to $wgCategoryTreeDefaultMode in efCategoryTree(); compatibility quirk
$wgCategoryTreeDefaultOptions['hideprefix'] = NULL; # will be set to $wgCategoryTreeDefaultMode in efCategoryTree(); compatibility quirk
$wgCategoryTreeDefaultOptions['showcount'] = false;
#TODO: hideprefix: always, never, catonly, catonly_if_onlycat
$wgCategoryTreeCategoryPageMode = CT_MODE_CATEGORIES;
$wgCategoryTreeCategoryPageOptions = array(); #Options to be used for category pages
$wgCategoryTreeCategoryPageOptions['mode'] = NULL; # will be set to $wgCategoryTreeDefaultMode in efCategoryTree(); compatibility quirk
$wgCategoryTreeCategoryPageOptions['showcount'] = true;
$wgCategoryTreeSpecialPageOptions = array(); #Options to be used for Special:CategoryTree
$wgCategoryTreeSpecialPageOptions['showcount'] = true;
/**
* Register extension setup hook and credits
*/
$wgExtensionFunctions[] = 'efCategoryTree';
2008-02-04 09:22:12 +00:00
$wgExtensionCredits['specialpage'][] = array(
'name' => 'CategoryTree',
'svn-date' => '$LastChangedDate$',
'svn-revision' => '$LastChangedRevision$',
2008-02-04 09:22:12 +00:00
'author' => 'Daniel Kinzler',
'url' => 'http://www.mediawiki.org/wiki/Extension:CategoryTree',
'description' => 'Dynamically navigate the category structure',
'descriptionmsg' => 'categorytree-desc',
);
2008-02-04 09:22:12 +00:00
$wgExtensionCredits['parserhook'][] = array(
'name' => 'CategoryTree',
'svn-date' => '$LastChangedDate$',
'svn-revision' => '$LastChangedRevision$',
2008-02-04 09:22:12 +00:00
'author' => 'Daniel Kinzler',
'url' => 'http://www.mediawiki.org/wiki/Extension:CategoryTree',
'description' => 'Dynamically navigate the category structure',
'descriptionmsg' => 'categorytree-desc',
);
/**
* Register the special page
*/
$dir = dirname(__FILE__) . '/';
$wgExtensionMessagesFiles['CategoryTree'] = $dir . 'CategoryTree.i18n.php';
$wgAutoloadClasses['CategoryTreePage'] = $dir . 'CategoryTreePage.php';
$wgAutoloadClasses['CategoryTree'] = $dir . 'CategoryTreeFunctions.php';
$wgAutoloadClasses['CategoryTreeCategoryPage'] = $dir . 'CategoryPageSubclass.php';
$wgSpecialPages['CategoryTree'] = 'CategoryTreePage';
$wgSpecialPageGroups['CategoryTree'] = 'pages';
#$wgHooks['SkinTemplateTabs'][] = 'efCategoryTreeInstallTabs';
$wgHooks['OutputPageParserOutput'][] = 'efCategoryTreeParserOutput';
$wgHooks['ArticleFromTitle'][] = 'efCategoryTreeArticleFromTitle';
2007-09-13 10:23:45 +00:00
$wgHooks['LanguageGetMagic'][] = 'efCategoryTreeGetMagic';
/**
* register Ajax function
*/
$wgAjaxExportList[] = 'efCategoryTreeAjaxWrapper';
/**
* Hook it up
*/
function efCategoryTree() {
2008-03-02 13:37:26 +00:00
global $wgUseAjax, $wgHooks;
global $wgCategoryTreeDefaultOptions, $wgCategoryTreeDefaultMode, $wgCategoryTreeOmitNamespace;
global $wgCategoryTreeCategoryPageOptions, $wgCategoryTreeCategoryPageMode;
# Abort if AJAX is not enabled
if ( !$wgUseAjax ) {
wfDebug( 'efCategoryTree: $wgUseAjax is not enabled, aborting extension setup.' );
return;
}
2008-03-02 13:37:26 +00:00
if ( defined( 'MW_SUPPORTS_PARSERFIRSTCALLINIT' ) ) {
$wgHooks['ParserFirstCallInit'][] = 'efCategoryTreeSetHooks';
} else {
efCategoryTreeSetHooks();
}
if ( !isset( $wgCategoryTreeDefaultOptions['mode'] ) || is_null( $wgCategoryTreeDefaultOptions['mode'] ) ) {
$wgCategoryTreeDefaultOptions['mode'] = $wgCategoryTreeDefaultMode;
}
if ( !isset( $wgCategoryTreeDefaultOptions['hideprefix'] ) || is_null( $wgCategoryTreeDefaultOptions['hideprefix'] ) ) {
$wgCategoryTreeDefaultOptions['hideprefix'] = $wgCategoryTreeOmitNamespace;
}
if ( !isset( $wgCategoryTreeCategoryPageOptions['mode'] ) || is_null( $wgCategoryTreeCategoryPageOptions['mode'] ) ) {
$wgCategoryTreeCategoryPageOptions['mode'] = $wgCategoryTreeCategoryPageMode;
}
2008-03-02 13:37:26 +00:00
}
function efCategoryTreeSetHooks() {
global $wgParser, $wgCategoryTreeAllowTag;
2007-09-13 10:23:45 +00:00
if ( $wgCategoryTreeAllowTag ) {
$wgParser->setHook( 'categorytree' , 'efCategoryTreeParserHook' );
$wgParser->setFunctionHook( 'categorytree' , 'efCategoryTreeParserFunction' );
}
2008-03-02 13:37:26 +00:00
return true;
2007-09-13 10:23:45 +00:00
}
/**
* Hook magic word
*/
function efCategoryTreeGetMagic( &$magicWords, $langCode ) {
global $wgUseAjax, $wgCategoryTreeAllowTag;
if ( $wgUseAjax && $wgCategoryTreeAllowTag ) {
//XXX: should we allow a local alias?
$magicWords['categorytree'] = array( 0, 'categorytree' );
}
return true;
}
/**
* Entry point for Ajax, registered in $wgAjaxExportList.
* The $enc parameter determins how the $options is decoded into a PHP array.
* If $enc is not given, '' is asumed, which simulates the old call interface,
* namely, only providing the mode name or number.
* This loads CategoryTreeFunctions.php and calls CategoryTree::ajax()
*/
function efCategoryTreeAjaxWrapper( $category, $options, $enc = '' ) {
global $wgCategoryTreeHTTPCache, $wgSquidMaxAge, $wgUseSquid;
2008-02-04 09:22:12 +00:00
if ( is_string( $options ) ) {
$options = CategoryTree::decodeOptions( $options, $enc );
}
$depth = isset( $options['depth'] ) ? (int)$options['depth'] : 1;
$ct = new CategoryTree( $options, true );
$depth = efCategoryTreeCapDepth( $ct->getOption('mode'), $depth );
$response = $ct->ajax( $category, $depth );
2008-02-04 09:22:12 +00:00
if ( $wgCategoryTreeHTTPCache && $wgSquidMaxAge && $wgUseSquid ) {
$response->setCacheDuration( $wgSquidMaxAge );
$response->setVary( 'Accept-Encoding, Cookie' ); #cache for anons only
#TODO: purge the squid cache when a category page is invalidated
}
return $response;
}
/**
* Internal function to cap depth
*/
function efCategoryTreeCapDepth( $mode, $depth ) {
global $wgCategoryTreeMaxDepth;
if (is_numeric($depth))
$depth = intval($depth);
else return 1;
if (is_array($wgCategoryTreeMaxDepth)) {
$max = isset($wgCategoryTreeMaxDepth[$mode]) ? $wgCategoryTreeMaxDepth[$mode] : 1;
} elseif (is_numeric($wgCategoryTreeMaxDepth)) {
$max = $wgCategoryTreeMaxDepth;
} else {
wfDebug( 'efCategoryTreeCapDepth: $wgCategoryTreeMaxDepth is invalid.' );
$max = 1;
}
2008-02-04 09:22:12 +00:00
return min($depth, $max);
}
2007-09-13 10:23:45 +00:00
/**
* Entry point for the {{#categorytree}} tag parser function.
* This is a wrapper around efCategoryTreeParserHook
*/
function efCategoryTreeParserFunction( &$parser ) {
$params = func_get_args();
array_shift( $params ); //first is &$parser, strip it
//first user-supplied parameter must be category name
if ( !$params ) return ''; //no category specified, return nothing
$cat = array_shift( $params );
//build associative arguments from flat parameter list
$argv = array();
foreach ( $params as $p ) {
if ( preg_match('/^\s*(\S.*?)\s*=\s*(.*?)\s*$/', $p, $m) ) {
$k = $m[1];
$v = $m[2];
}
else {
$k = trim($p);
$v = true;
}
$argv[$k] = $v;
}
//now handle just like a <categorytree> tag
$html = efCategoryTreeParserHook( $cat, $argv, $parser );
return array( $html, 'isHTML' => true );
2007-09-13 10:23:45 +00:00
}
/**
* Entry point for the <categorytree> tag parser hook.
* This loads CategoryTreeFunctions.php and calls CategoryTree::getTag()
*/
function efCategoryTreeParserHook( $cat, $argv, &$parser ) {
2007-04-30 21:18:56 +00:00
global $wgCategoryTreeDefaultMode;
$parser->mOutput->mCategoryTreeTag = true; # flag for use by efCategoryTreeParserOutput
2008-02-04 09:22:12 +00:00
static $initialized = false;
2008-02-04 09:22:12 +00:00
$ct = new CategoryTree( $argv );
$divAttribs = Sanitizer::validateTagAttributes( $argv, 'div' );
$style = isset( $divAttribs['style'] ) ? $divAttribs['style'] : null;
2008-02-04 09:22:12 +00:00
$hideroot = isset( $argv[ 'hideroot' ] ) ? CategoryTree::decodeBoolean( $argv[ 'hideroot' ] ) : null;
$onlyroot = isset( $argv[ 'onlyroot' ] ) ? CategoryTree::decodeBoolean( $argv[ 'onlyroot' ] ) : null;
$depthArg = isset( $argv[ 'depth' ] ) ? $argv[ 'depth' ] : null;
$depth = efCategoryTreeCapDepth( $ct->getOption( 'mode' ), $depthArg );
if ( $onlyroot ) $depth = 0;
return $ct->getTag( $parser, $cat, $hideroot, $style, $depth );
}
/**
* Hook callback that injects messages and things into the <head> tag
* Does nothing if $parserOutput->mCategoryTreeTag is not set
*/
function efCategoryTreeParserOutput( &$outputPage, &$parserOutput ) {
if ( !empty( $parserOutput->mCategoryTreeTag ) ) {
CategoryTree::setHeaders( $outputPage );
}
2006-08-28 17:13:16 +00:00
return true;
}
/**
* ArticleFromTitle hook, override category page handling
*/
function efCategoryTreeArticleFromTitle( &$title, &$article ) {
if ( $title->getNamespace() == NS_CATEGORY ) {
$article = new CategoryTreeCategoryPage( $title );
}
return true;
}