mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/CategoryTree
synced 2024-11-27 09:43:06 +00:00
Restructuring and revamping; more messages, more options, better config, more flexible ajax interface. Should be fully backwards compatible. It will hopefully even work for people with old JS code cached.
This commit is contained in:
parent
0bffdb6902
commit
dbfd3e4833
|
@ -14,11 +14,23 @@ class CategoryTreeCategoryPage extends CategoryPage {
|
|||
class CategoryTreeCategoryViewer extends CategoryViewer {
|
||||
var $child_titles;
|
||||
|
||||
function getCategoryTree() {
|
||||
global $wgOut, $wgCategoryTreeCategoryPageOptions;
|
||||
|
||||
if ( ! isset($this->categorytree) ) {
|
||||
CategoryTree::setHeaders( $wgOut );
|
||||
|
||||
$this->categorytree = new CategoryTree( $wgCategoryTreeCategoryPageOptions );
|
||||
}
|
||||
|
||||
return $this->categorytree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a subcategory to the internal lists
|
||||
*/
|
||||
function addSubcategory( $title, $sortkey, $pageLength ) {
|
||||
global $wgContLang, $wgOut, $wgRequest, $wgCategoryTreeCategoryPageMode;
|
||||
global $wgContLang, $wgOut, $wgRequest;
|
||||
|
||||
if ( $wgRequest->getCheck( 'notree' ) ) {
|
||||
return parent::addSubcategory( $title, $sortkey, $pageLength );
|
||||
|
@ -29,18 +41,15 @@ class CategoryTreeCategoryViewer extends CategoryViewer {
|
|||
return parent::addSubcategory( $title, $sortkey, $pageLength );
|
||||
}
|
||||
|
||||
if ( ! isset($this->categorytree) ) {
|
||||
CategoryTree::setHeaders( $wgOut );
|
||||
$this->categorytree = new CategoryTree;
|
||||
}
|
||||
$tree = $this->getCategoryTree();
|
||||
|
||||
$this->children[] = $this->categorytree->renderNode( $title, $wgCategoryTreeCategoryPageMode );
|
||||
$this->children[] = $tree->renderNode( $title );
|
||||
|
||||
$this->children_start_char[] = $this->getSubcategorySortChar( $title, $sortkey );
|
||||
}
|
||||
|
||||
function getSubcategorySection() {
|
||||
global $wgOut, $wgRequest, $wgCookiePrefix, $wgCategoryTreeCategoryPageMode;
|
||||
global $wgOut, $wgRequest, $wgCookiePrefix;
|
||||
|
||||
if ( $wgRequest->getCheck( 'notree' ) ) {
|
||||
return parent::getSubcategorySection();
|
||||
|
@ -98,11 +107,10 @@ class CategoryTreeCategoryViewer extends CategoryViewer {
|
|||
if ( $showAs == 'list' ) {
|
||||
$r .= $this->formatList( $this->children, $this->children_start_char );
|
||||
} else {
|
||||
CategoryTree::setHeaders( $wgOut );
|
||||
$ct = new CategoryTree;
|
||||
$ct = getCategoryTree();
|
||||
|
||||
foreach ( $this->child_titles as $title ) {
|
||||
$r .= $ct->renderNode( $title, $wgCategoryTreeCategoryPageMode );
|
||||
$r .= $ct->renderNode( $title );
|
||||
}
|
||||
}
|
||||
return $r;
|
||||
|
|
|
@ -16,11 +16,13 @@
|
|||
margin-left: 1.5ex;
|
||||
}
|
||||
|
||||
.CategoryTreeBullet a:link {
|
||||
.CategoryTreeBullet a,
|
||||
.CategoryTreeBullet a:link,
|
||||
.CategoryTreeBullet a:active,
|
||||
.CategoryTreeBullet a:visited {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
font-weight: bold;
|
||||
speak: none;
|
||||
speak: none;
|
||||
}
|
||||
|
||||
.CategoryTreeLabelPage {
|
||||
|
|
|
@ -17,7 +17,7 @@ $messages['en'] = array(
|
|||
'categorytree' => 'CategoryTree',
|
||||
'categorytree-tab' => 'Tree',
|
||||
'categorytree-legend' => 'Show category tree',
|
||||
'categorytree-desc' => 'AJAX based gadget to display the [[Special:CategoryTree|category structure]] of a wiki',
|
||||
'categorytree-desc' => 'Dynamically navigate the [[Special:CategoryTree|category structure]]',
|
||||
'categorytree-header' => 'Enter a category name to see its contents as a tree structure.
|
||||
Note that this requires advanced JavaScript functionality known as AJAX.
|
||||
If you have a very old browser, or have JavaScript disabled, it will not work.',
|
||||
|
@ -32,6 +32,10 @@ If you have a very old browser, or have JavaScript disabled, it will not work.',
|
|||
|
||||
'categorytree-collapse' => 'collapse',
|
||||
'categorytree-expand' => 'expand',
|
||||
'categorytree-collapse-bullet' => '[<b>−</b>]',
|
||||
'categorytree-expand-bullet' => '[<b>+</b>]',
|
||||
'categorytree-page-bullet' => ' ',
|
||||
|
||||
'categorytree-load' => 'load',
|
||||
'categorytree-loading' => 'loading…',
|
||||
'categorytree-nothing-found' => 'nothing found',
|
||||
|
@ -586,7 +590,7 @@ $messages['de'] = array(
|
|||
'categorytree' => 'Kategorienbaum',
|
||||
'categorytree-tab' => 'Baum',
|
||||
'categorytree-legend' => 'Zeige Kategorienbaum',
|
||||
'categorytree-desc' => 'Ajax-basiertes Gadget, um die [[Special:CategoryTree|Kategorien-Struktur]] eines Wikis anzuzeigen',
|
||||
'categorytree-desc' => 'Dynamische Navigation für die [[Special:CategoryTree|Kategorien-Struktur]]',
|
||||
'categorytree-header' => 'Zeigt für die angegebene Kategorie die Unterkategorien in einer Baumstruktur.
|
||||
Diese Seite benötigt bestimmte JavaScript-Funktionen (Ajax) und funktioniert möglicherweise nicht, wenn JavaScript ausgeschaltet ist oder ein sehr alter Browser verwendet wird.',
|
||||
'categorytree-category' => 'Kategorie:',
|
||||
|
|
|
@ -26,48 +26,88 @@ var categoryTreeRetryMsg = "Please wait a moment and try again.";
|
|||
return n;
|
||||
}
|
||||
|
||||
function categoryTreeExpandNode(cat, mode, lnk) {
|
||||
function categoryTreeExpandNode(cat, options, lnk) {
|
||||
var div= categoryTreeNextDiv( lnk.parentNode.parentNode );
|
||||
|
||||
div.style.display= 'block';
|
||||
lnk.innerHTML= '–';
|
||||
lnk.innerHTML= categoryTreeCollapseBulletMsg;
|
||||
lnk.title= categoryTreeCollapseMsg;
|
||||
lnk.onclick= function() { categoryTreeCollapseNode(cat, mode, lnk) }
|
||||
lnk.onclick= function() { categoryTreeCollapseNode(cat, options, lnk) }
|
||||
|
||||
if (lnk.className != "CategoryTreeLoaded") {
|
||||
categoryTreeLoadNode(cat, mode, lnk, div);
|
||||
categoryTreeLoadNode(cat, options, lnk, div);
|
||||
}
|
||||
}
|
||||
|
||||
function categoryTreeCollapseNode(cat, mode, lnk) {
|
||||
function categoryTreeCollapseNode(cat, options, lnk) {
|
||||
var div= categoryTreeNextDiv( lnk.parentNode.parentNode );
|
||||
|
||||
div.style.display= 'none';
|
||||
lnk.innerHTML= '+';
|
||||
lnk.innerHTML= categoryTreeExpandBulletMsg;
|
||||
lnk.title= categoryTreeExpandMsg;
|
||||
lnk.onclick= function() { categoryTreeExpandNode(cat, mode, lnk) }
|
||||
lnk.onclick= function() { categoryTreeExpandNode(cat, options, lnk) }
|
||||
}
|
||||
|
||||
function categoryTreeLoadNode(cat, mode, lnk, div) {
|
||||
function categoryTreeLoadNode(cat, options, lnk, div) {
|
||||
div.style.display= 'block';
|
||||
lnk.className= 'CategoryTreeLoaded';
|
||||
lnk.innerHTML= '–';
|
||||
lnk.innerHTML= categoryTreeCollapseBulletMsg;
|
||||
lnk.title= categoryTreeCollapseMsg;
|
||||
lnk.onclick= function() { categoryTreeCollapseNode(cat, mode, lnk) }
|
||||
lnk.onclick= function() { categoryTreeCollapseNode(cat, options, lnk) }
|
||||
|
||||
categoryTreeLoadChildren(cat, mode, div)
|
||||
categoryTreeLoadChildren(cat, options, div)
|
||||
}
|
||||
|
||||
function categoryTreeLoadChildren(cat, mode, div) {
|
||||
function categoryTreeEncodeOptions(options) {
|
||||
var opt = "";
|
||||
|
||||
for (k in options) {
|
||||
v = options[k];
|
||||
|
||||
switch (typeof v) {
|
||||
case 'string':
|
||||
v = '"' + v.replace(/([\\"'])/g, "\\$1") + '"';
|
||||
break;
|
||||
|
||||
case 'number':
|
||||
case 'boolean':
|
||||
case 'null':
|
||||
v = String(v);
|
||||
break;
|
||||
|
||||
case 'object':
|
||||
if ( !v ) v = 'null';
|
||||
else throw new Error("categoryTreeLoadChildren can not encode complex types");
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error("categoryTreeLoadChildren encountered strange variable type " + (typeof v));
|
||||
}
|
||||
|
||||
if ( opt != "" ) opt += ", ";
|
||||
opt += k;
|
||||
opt += ":";
|
||||
opt += v;
|
||||
}
|
||||
|
||||
opt = "{"+opt+"}";
|
||||
return opt;
|
||||
}
|
||||
|
||||
function categoryTreeLoadChildren(cat, options, div) {
|
||||
div.innerHTML= '<i class="CategoryTreeNotice">' + categoryTreeLoadingMsg + '</i>';
|
||||
|
||||
if ( typeof options == "string" ) { //hack for backward compatibility
|
||||
options = { mode : options };
|
||||
}
|
||||
|
||||
function f( request ) {
|
||||
if (request.status != 200) {
|
||||
div.innerHTML = '<i class="CategoryTreeNotice">' + categoryTreeErrorMsg + ' </i>';
|
||||
var retryLink = document.createElement('a');
|
||||
retryLink.innerHTML = categoryTreeRetryMsg;
|
||||
retryLink.onclick = function() {
|
||||
categoryTreeLoadChildren(cat, mode, div);
|
||||
categoryTreeLoadChildren(cat, options, div, enc);
|
||||
}
|
||||
div.appendChild(retryLink);
|
||||
return;
|
||||
|
@ -79,8 +119,8 @@ var categoryTreeRetryMsg = "Please wait a moment and try again.";
|
|||
if ( result == '' ) {
|
||||
result= '<i class="CategoryTreeNotice">';
|
||||
|
||||
if ( mode == 0 ) result= categoryTreeNoSubcategoriesMsg;
|
||||
else if ( mode == 10 ) result= categoryTreeNoPagesMsg;
|
||||
if ( options.mode == 0 ) result= categoryTreeNoSubcategoriesMsg;
|
||||
else if ( options.mode == 10 ) result= categoryTreeNoPagesMsg;
|
||||
else result= categoryTreeNothingFoundMsg;
|
||||
|
||||
result+= '</i>';
|
||||
|
@ -90,5 +130,6 @@ var categoryTreeRetryMsg = "Please wait a moment and try again.";
|
|||
div.innerHTML= result;
|
||||
}
|
||||
|
||||
sajax_do_call( "efCategoryTreeAjaxWrapper", [cat, mode] , f );
|
||||
var opt = categoryTreeEncodeOptions(options);
|
||||
sajax_do_call( "efCategoryTreeAjaxWrapper", [cat, opt, 'json'] , f );
|
||||
}
|
||||
|
|
111
CategoryTree.php
111
CategoryTree.php
|
@ -6,7 +6,7 @@
|
|||
*
|
||||
* @addtogroup Extensions
|
||||
* @author Daniel Kinzler, brightbyte.de
|
||||
* @copyright © 2006-2007 Daniel Kinzler
|
||||
* @copyright © 2006-2008 Daniel Kinzler and others
|
||||
* @license GNU General Public Licence 2.0 or later
|
||||
*/
|
||||
|
||||
|
@ -46,12 +46,21 @@ $wgCategoryTreeDisableCache = true;
|
|||
$wgCategoryTreeDynamicTag = false;
|
||||
$wgCategoryTreeHTTPCache = false;
|
||||
$wgCategoryTreeUnifiedView = true;
|
||||
$wgCategoryTreeOmitNamespace = false;
|
||||
$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
|
||||
#TODO: hideprefix: always, never, catonly, catonly_if_onlycat
|
||||
|
||||
$wgCategoryTreeCategoryPageMode = CT_MODE_CATEGORIES;
|
||||
$wgCategoryTreeVersion = '1'; #NOTE: bump this when you change the CSS or JS files!
|
||||
$wgCategoryTreeCategoryPageOptions = array(); #Options to be used for category pages
|
||||
$wgCategoryTreeCategoryPageOptions['mode'] = NULL; # will be set to $wgCategoryTreeDefaultMode in efCategoryTree(); compatibility quirk
|
||||
|
||||
/**
|
||||
* Register extension setup hook and credits
|
||||
|
@ -63,7 +72,7 @@ $wgExtensionCredits['specialpage'][] = array(
|
|||
'svn-revision' => '$LastChangedRevision$',
|
||||
'author' => 'Daniel Kinzler',
|
||||
'url' => 'http://www.mediawiki.org/wiki/Extension:CategoryTree',
|
||||
'description' => 'AJAX based gadget to display the category structure of a wiki',
|
||||
'description' => 'Dynamically navigate the category structure',
|
||||
'descriptionmsg' => 'categorytree-desc',
|
||||
);
|
||||
$wgExtensionCredits['parserhook'][] = array(
|
||||
|
@ -72,7 +81,7 @@ $wgExtensionCredits['parserhook'][] = array(
|
|||
'svn-revision' => '$LastChangedRevision$',
|
||||
'author' => 'Daniel Kinzler',
|
||||
'url' => 'http://www.mediawiki.org/wiki/Extension:CategoryTree',
|
||||
'description' => 'AJAX based gadget to display the category structure of a wiki',
|
||||
'description' => 'Dynamically navigate the category structure',
|
||||
'descriptionmsg' => 'categorytree-desc',
|
||||
);
|
||||
|
||||
|
@ -90,6 +99,8 @@ $wgSpecialPageGroups['CategoryTree'] = 'pages';
|
|||
$wgHooks['OutputPageParserOutput'][] = 'efCategoryTreeParserOutput';
|
||||
$wgHooks['ArticleFromTitle'][] = 'efCategoryTreeArticleFromTitle';
|
||||
$wgHooks['LanguageGetMagic'][] = 'efCategoryTreeGetMagic';
|
||||
|
||||
|
||||
/**
|
||||
* register Ajax function
|
||||
*/
|
||||
|
@ -100,6 +111,8 @@ $wgAjaxExportList[] = 'efCategoryTreeAjaxWrapper';
|
|||
*/
|
||||
function efCategoryTree() {
|
||||
global $wgUseAjax, $wgHooks;
|
||||
global $wgCategoryTreeDefaultOptions, $wgCategoryTreeDefaultMode, $wgCategoryTreeOmitNamespace;
|
||||
global $wgCategoryTreeCategoryPageOptions, $wgCategoryTreeCategoryPageMode;
|
||||
|
||||
# Abort if AJAX is not enabled
|
||||
if ( !$wgUseAjax ) {
|
||||
|
@ -113,6 +126,17 @@ function efCategoryTree() {
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
||||
function efCategoryTreeSetHooks() {
|
||||
|
@ -140,13 +164,23 @@ function efCategoryTreeGetMagic( &$magicWords, $langCode ) {
|
|||
|
||||
/**
|
||||
* 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, $mode ) {
|
||||
function efCategoryTreeAjaxWrapper( $category, $options, $enc = '' ) {
|
||||
global $wgCategoryTreeHTTPCache, $wgSquidMaxAge, $wgUseSquid;
|
||||
|
||||
$ct = new CategoryTree;
|
||||
$response = $ct->ajax( $category, $mode ); //FIXME: would need to pass on depth parameter here.
|
||||
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 );
|
||||
|
||||
if ( $wgCategoryTreeHTTPCache && $wgSquidMaxAge && $wgUseSquid ) {
|
||||
$response->setCacheDuration( $wgSquidMaxAge );
|
||||
|
@ -180,25 +214,6 @@ function efCategoryTreeCapDepth( $mode, $depth ) {
|
|||
return min($depth, $max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to convert a string to a boolean value.
|
||||
* Perhaps make this a global function in MediaWiki proper
|
||||
*/
|
||||
function efCategoryTreeAsBool( $s ) {
|
||||
if ( is_null( $s ) || is_bool( $s ) ) return $s;
|
||||
$s = trim( strtolower( $s ) );
|
||||
|
||||
if ( $s === '1' || $s === 'yes' || $s === 'on' || $s === 'true' ) {
|
||||
return true;
|
||||
}
|
||||
else if ( $s === '0' || $s === 'no' || $s === 'off' || $s === 'false' ) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point for the {{#categorytree}} tag parser function.
|
||||
* This is a wrapper around efCategoryTreeParserHook
|
||||
|
@ -242,51 +257,21 @@ function efCategoryTreeParserHook( $cat, $argv, &$parser ) {
|
|||
|
||||
static $initialized = false;
|
||||
|
||||
$ct = new CategoryTree( $argv );
|
||||
|
||||
$divAttribs = Sanitizer::validateTagAttributes( $argv, 'div' );
|
||||
$style = isset( $divAttribs['style'] ) ? $divAttribs['style'] : null;
|
||||
|
||||
$mode = isset( $argv[ 'mode' ] ) ? $argv[ 'mode' ] : null;
|
||||
if ( $mode !== NULL ) {
|
||||
$mode= trim( strtolower( $mode ) );
|
||||
|
||||
if ( $mode == 'all' ) $mode = CT_MODE_ALL;
|
||||
else if ( $mode == 'pages' ) $mode = CT_MODE_PAGES;
|
||||
else if ( $mode == 'categories' ) $mode = CT_MODE_CATEGORIES;
|
||||
}
|
||||
else {
|
||||
$mode = $wgCategoryTreeDefaultMode;
|
||||
}
|
||||
|
||||
$hideroot = isset( $argv[ 'hideroot' ] ) ? efCategoryTreeAsBool( $argv[ 'hideroot' ] ) : null;
|
||||
$onlyroot = isset( $argv[ 'onlyroot' ] ) ? efCategoryTreeAsBool( $argv[ 'onlyroot' ] ) : null;
|
||||
$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($mode, $depthArg);
|
||||
|
||||
$depth = efCategoryTreeCapDepth( $ct->getOption( 'mode' ), $depthArg );
|
||||
if ( $onlyroot ) $depth = 0;
|
||||
|
||||
$ct = new CategoryTree;
|
||||
return $ct->getTag( $parser, $cat, $mode, $hideroot, $style, $depth );
|
||||
return $ct->getTag( $parser, $cat, $hideroot, $style, $depth );
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook callback that installs a tab for CategoryTree on Category pages
|
||||
*/
|
||||
/*
|
||||
function efCategoryTreeInstallTabs( &$skin, &$content_actions ) {
|
||||
global $wgTitle;
|
||||
|
||||
if ( $wgTitle->getNamespace() != NS_CATEGORY ) return true;
|
||||
|
||||
$special = Title::makeTitle( NS_SPECIAL, 'CategoryTree' );
|
||||
|
||||
$content_actions['categorytree'] = array(
|
||||
'class' => false,
|
||||
'text' => htmlspecialchars( CategoryTree::msg( 'tab' ) ),
|
||||
'href' => $special->getLocalUrl() . '/' . $wgTitle->getPartialURL() );
|
||||
return true;
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Hook callback that injects messages and things into the <head> tag
|
||||
* Does nothing if $parserOutput->mCategoryTreeTag is not set
|
||||
|
|
|
@ -17,6 +17,62 @@ if( !defined( 'MEDIAWIKI' ) ) {
|
|||
|
||||
class CategoryTree {
|
||||
var $mIsAjaxRequest = false;
|
||||
var $mOptions = array();
|
||||
|
||||
function __construct( $options, $ajax = false ) {
|
||||
global $wgCategoryTreeDefaultOptions;
|
||||
|
||||
$this->mIsAjaxRequest = $ajax;
|
||||
|
||||
#ensure default values and order of options. Order may become important, it may influence the cache key!
|
||||
foreach ( $wgCategoryTreeDefaultOptions as $option => $default ) {
|
||||
if ( isset( $options[$option] ) && !is_null( $options[$option] ) ) $this->mOptions[$option] = $options[$option];
|
||||
else $this->mOptions[$option] = $default;
|
||||
}
|
||||
|
||||
$this->mOptions['mode'] = self::decodeMode( $this->mOptions['mode'] );
|
||||
$this->mOptions['hideprefix'] = self::decodeBoolean( $this->mOptions['hideprefix'] );
|
||||
}
|
||||
|
||||
function getOption( $name ) {
|
||||
return $this->mOptions[$name];
|
||||
}
|
||||
|
||||
static function decodeMode( $mode ) {
|
||||
global $wgCategoryTreeDefaultOptions;
|
||||
|
||||
if ( is_null( $mode ) ) return $wgCategoryTreeDefaultOptions['mode'];
|
||||
if ( is_int( $mode ) ) return $mode;
|
||||
|
||||
$mode = trim( strtolower( $mode ) );
|
||||
|
||||
if ( is_numeric( $mode ) ) return (int)$mode;
|
||||
|
||||
if ( $mode == 'all' ) $mode = CT_MODE_ALL;
|
||||
else if ( $mode == 'pages' ) $mode = CT_MODE_PAGES;
|
||||
else if ( $mode == 'categories' ) $mode = CT_MODE_CATEGORIES;
|
||||
else if ( $mode == 'default' ) $mode = $wgCategoryTreeDefaultOptions['mode'];
|
||||
|
||||
return (int)$mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to convert a string to a boolean value.
|
||||
* Perhaps make this a global function in MediaWiki proper
|
||||
*/
|
||||
static function decodeBoolean( $value ) {
|
||||
if ( is_null( $value ) ) return NULL;
|
||||
if ( is_bool( $value ) ) return $value;
|
||||
if ( is_int( $value ) ) return ( $value > 0 );
|
||||
|
||||
$value = trim( strtolower( $value ) );
|
||||
if ( is_numeric( $value ) ) return ( (int)$mode > 0 );
|
||||
|
||||
if ( $value == 'yes' || $value == 'y' || $value == 'true' || $value == 't' || $value == 'on' ) return true;
|
||||
else if ( $value == 'no' || $value == 'n' || $value == 'false' || $value == 'f' || $value == 'off' ) return false;
|
||||
else if ( $value == 'null' || $value == 'default' || $value == 'none' || $value == 'x' ) return NULL;
|
||||
else return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the script tags in an OutputPage object
|
||||
|
@ -57,6 +113,8 @@ class CategoryTree {
|
|||
" <script type=\"{$wgJsMimeType}\">
|
||||
var categoryTreeCollapseMsg = \"".Xml::escapeJsString(self::msg('collapse'))."\";
|
||||
var categoryTreeExpandMsg = \"".Xml::escapeJsString(self::msg('expand'))."\";
|
||||
var categoryTreeCollapseBulletMsg = \"".Xml::escapeJsString(self::msg('collapse-bullet'))."\";
|
||||
var categoryTreeExpandBulletMsg = \"".Xml::escapeJsString(self::msg('expand-bullet'))."\";
|
||||
var categoryTreeLoadMsg = \"".Xml::escapeJsString(self::msg('load'))."\";
|
||||
var categoryTreeLoadingMsg = \"".Xml::escapeJsString(self::msg('loading'))."\";
|
||||
var categoryTreeNothingFoundMsg = \"".Xml::escapeJsString(self::msg('nothing-found'))."\";
|
||||
|
@ -68,16 +126,101 @@ class CategoryTree {
|
|||
);
|
||||
}
|
||||
|
||||
static function getJsonCodec() {
|
||||
static $json = NULL;
|
||||
|
||||
if (!$json) {
|
||||
$json = new Services_JSON(); #recycle API's JSON codec implementation
|
||||
}
|
||||
|
||||
return $json;
|
||||
}
|
||||
|
||||
static function encodeOptions( $options, $enc ) {
|
||||
if ( $enc == 'mode' || $enc == '' ) {
|
||||
$opt =$options['mode'];
|
||||
} elseif ( $enc == 'json' ) {
|
||||
$json = self::getJsonCodec(); //XXX: this may be a bit heavy...
|
||||
$opt = $json->encode( $options );
|
||||
} else {
|
||||
throw new MWException( 'Unknown encoding for CategoryTree options: ' . $enc );
|
||||
}
|
||||
|
||||
return $opt;
|
||||
}
|
||||
|
||||
static function decodeOptions( $options, $enc ) {
|
||||
if ( $enc == 'mode' || $enc == '' ) {
|
||||
$opt = array( "mode" => $options );
|
||||
} elseif ( $enc == 'json' ) {
|
||||
$json = self::getJsonCodec(); //XXX: this may be a bit heavy...
|
||||
$opt = $json->decode( $options );
|
||||
$opt = get_object_vars( $opt );
|
||||
} /* elseif () {
|
||||
foreach ( $oo as $o ) {
|
||||
if ($o === "") continue;
|
||||
|
||||
if ( preg_match( '!^(.*?)=(.*)$!', $o, $m ) {
|
||||
$n = $m[1];
|
||||
$opt[$n] = $m[2];
|
||||
} else {
|
||||
$opt[$o] = true;
|
||||
}
|
||||
}
|
||||
} */ else {
|
||||
throw new MWException( 'Unknown encoding for CategoryTree options: ' . $enc );
|
||||
}
|
||||
|
||||
return $opt;
|
||||
}
|
||||
|
||||
function getOptionsAsCacheKey( $depth = NULL ) {
|
||||
$key = "";
|
||||
|
||||
foreach ( $this->mOptions as $k => $v ) {
|
||||
$key .= $k . ':' . $v . ';';
|
||||
}
|
||||
|
||||
if ( !is_null( $depth ) ) $key .= ";depth=" . $depth;
|
||||
return $key;
|
||||
}
|
||||
|
||||
function getOptionsAsJsStructure( $depth = NULL ) {
|
||||
if ( !is_null( $depth ) ) {
|
||||
$opt = $this->mOptions;
|
||||
$opt['depth'] = $depth;
|
||||
$s = self::encodeOptions( $opt, 'json' );
|
||||
} else {
|
||||
$s = self::encodeOptions( $this->mOptions, 'json' );
|
||||
}
|
||||
|
||||
return $s;
|
||||
}
|
||||
|
||||
function getOptionsAsJsString( $depth = NULL ) {
|
||||
return Xml::escapeJsString( $s );
|
||||
}
|
||||
|
||||
function getOptionsAsUrlParameters() {
|
||||
$u = '';
|
||||
|
||||
foreach ( $this->mOptions as $k => $v ) {
|
||||
if ( $u != '' ) $u .= '&';
|
||||
$u .= $k . '=' . urlencode($v) ;
|
||||
}
|
||||
|
||||
return $u;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax call. This is called by efCategoryTreeAjaxWrapper, which is used to
|
||||
* load CategoryTreeFunctions.php on demand.
|
||||
*/
|
||||
function ajax( $category, $mode ) {
|
||||
function ajax( $category, $depth = 1 ) {
|
||||
global $wgDBname;
|
||||
$title = self::makeTitle( $category );
|
||||
|
||||
if ( ! $title ) return false; #TODO: error message?
|
||||
$this->mIsAjaxRequest = true;
|
||||
|
||||
# Retrieve page_touched for the category
|
||||
$dbkey = $title->getDBkey();
|
||||
|
@ -88,7 +231,7 @@ class CategoryTree {
|
|||
'page_title' => $dbkey,
|
||||
), __METHOD__ );
|
||||
|
||||
$mckey = "$wgDBname:categorytree($mode):$dbkey"; //FIXME: would need to add depth parameter.
|
||||
$mckey = "$wgDBname:categorytree(" . $this->getOptionsAsCacheKey( $depth ) . "):$dbkey";
|
||||
|
||||
$response = new AjaxResponse();
|
||||
|
||||
|
@ -100,7 +243,7 @@ class CategoryTree {
|
|||
return $response;
|
||||
}
|
||||
|
||||
$html = $this->renderChildren( $title, $mode ); //FIXME: would need to pass depth parameter.
|
||||
$html = $this->renderChildren( $title, $depth );
|
||||
|
||||
if ( $html == '' ) $html = ' '; #HACK: Safari doesn't like empty responses.
|
||||
#see Bug 7219 and http://bugzilla.opendarwin.org/show_bug.cgi?id=10716
|
||||
|
@ -116,11 +259,10 @@ class CategoryTree {
|
|||
* Custom tag implementation. This is called by efCategoryTreeParserHook, which is used to
|
||||
* load CategoryTreeFunctions.php on demand.
|
||||
*/
|
||||
function getTag( &$parser, $category, $mode, $hideroot = false, $style = '', $depth=1 ) {
|
||||
function getTag( &$parser, $category, $hideroot = false, $style = '', $depth=1 ) {
|
||||
global $wgCategoryTreeDisableCache, $wgCategoryTreeDynamicTag;
|
||||
static $uniq = 0;
|
||||
|
||||
$this->mIsAjaxRequest = false;
|
||||
$category = trim( $category );
|
||||
if ( $category === '' ) {
|
||||
return false;
|
||||
|
@ -141,14 +283,14 @@ class CategoryTree {
|
|||
$html .= Xml::closeElement( 'span' );
|
||||
}
|
||||
else {
|
||||
if ( !$hideroot ) $html .= CategoryTree::renderNode( $title, $mode, $depth>0, $wgCategoryTreeDynamicTag, $depth-1 );
|
||||
else if ( !$wgCategoryTreeDynamicTag ) $html .= $this->renderChildren( $title, $mode, $depth-1 );
|
||||
else { //FIXME: depth would need to be propagated here. this would imact the cache key, too
|
||||
if ( !$hideroot ) $html .= CategoryTree::renderNode( $title, $depth, $wgCategoryTreeDynamicTag );
|
||||
else if ( !$wgCategoryTreeDynamicTag ) $html .= $this->renderChildren( $title, $depth );
|
||||
else {
|
||||
$uniq += 1;
|
||||
$load = 'ct-' . $uniq . '-' . mt_rand( 1, 100000 );
|
||||
|
||||
$html .= Xml::openElement( 'script', array( 'type' => 'text/javascript', 'id' => $load ) );
|
||||
$html .= 'categoryTreeLoadChildren("' . Xml::escapeJsString( $title->getDBkey() ) . '", "' . $mode . '", document.getElementById("' . $load . '").parentNode );';
|
||||
$html .= 'categoryTreeLoadChildren("' . Xml::escapeJsString( $title->getDBkey() ) . '", ' . $this->getOptionsAsJsStructure( $depth ) . ', document.getElementById("' . $load . '").parentNode);';
|
||||
$html .= Xml::closeElement( 'script' );
|
||||
}
|
||||
}
|
||||
|
@ -163,8 +305,8 @@ class CategoryTree {
|
|||
* Returns a string with an HTML representation of the children of the given category.
|
||||
* $title must be a Title object
|
||||
*/
|
||||
function renderChildren( &$title, $mode = NULL, $depth=0 ) {
|
||||
global $wgCategoryTreeMaxChildren, $wgCategoryTreeDefaultMode;
|
||||
function renderChildren( &$title, $depth=1 ) {
|
||||
global $wgCategoryTreeMaxChildren;
|
||||
|
||||
if( $title->getNamespace() != NS_CATEGORY ) {
|
||||
// Non-categories can't have children. :)
|
||||
|
@ -178,7 +320,7 @@ class CategoryTree {
|
|||
$transJoin = '';
|
||||
$transWhere = '';
|
||||
|
||||
if ( $mode === NULL ) $wgCategoryTreeDefaultMode;
|
||||
$mode = $this->getOption('mode');
|
||||
|
||||
#namespace filter. Should be configurable
|
||||
if ( $mode == CT_MODE_ALL ) $nsmatch = '';
|
||||
|
@ -210,7 +352,7 @@ class CategoryTree {
|
|||
#TODO: translation support; ideally added to Title object
|
||||
$t = Title::makeTitle( $row['page_namespace'], $row['page_title'] );
|
||||
|
||||
$s = $this->renderNode( $t, $mode, $depth>0, false, $depth-1 );
|
||||
$s = $this->renderNode( $t, $depth-1, false );
|
||||
$s .= "\n\t\t";
|
||||
|
||||
if ($row['page_namespace'] == NS_CATEGORY) $categories .= $s;
|
||||
|
@ -226,7 +368,7 @@ class CategoryTree {
|
|||
* Returns a string with an HTML representation of the parents of the given category.
|
||||
* $title must be a Title object
|
||||
*/
|
||||
static function renderParents( &$title, $mode ) {
|
||||
function renderParents( &$title ) {
|
||||
global $wgCategoryTreeMaxChildren;
|
||||
|
||||
$dbr =& wfGetDB( DB_SLAVE );
|
||||
|
@ -262,7 +404,7 @@ class CategoryTree {
|
|||
$label = htmlspecialchars( $t->getText() );
|
||||
if ( $trans && $trans!=$label ) $label.= ' ' . Xml::element( 'i', array( 'class' => 'translation'), $trans );
|
||||
|
||||
$wikiLink = $special->getLocalURL( 'target=' . $t->getPartialURL() . '&mode=' . $mode );
|
||||
$wikiLink = $special->getLocalURL( 'target=' . $t->getPartialURL() . '&' . $this->getOptionsAsUrlParameters() );
|
||||
|
||||
if ( $s !== '' ) $s .= ' | ';
|
||||
|
||||
|
@ -282,18 +424,17 @@ class CategoryTree {
|
|||
* Returns a string with a HTML represenation of the given page.
|
||||
* $title must be a Title object
|
||||
*/
|
||||
function renderNode( &$title, $mode = NULL, $children = false, $loadchildren = false, $depth = 1 ) {
|
||||
global $wgCategoryTreeOmitNamespace, $wgCategoryTreeDefaultMode;
|
||||
function renderNode( &$title, $children = 0, $loadchildren = false ) {
|
||||
global $wgCategoryTreeDefaultMode;
|
||||
static $uniq = 0;
|
||||
|
||||
if ( $mode === NULL ) $wgCategoryTreeDefaultMode;
|
||||
$mode = $this->getOption('mode');
|
||||
$load = false;
|
||||
|
||||
if ( $children && $loadchildren ) {
|
||||
if ( $children > 0 && $loadchildren ) {
|
||||
$uniq += 1;
|
||||
|
||||
$load = 'ct-' . $uniq . '-' . mt_rand( 1, 100000 );
|
||||
$children = false;
|
||||
}
|
||||
|
||||
$ns = $title->getNamespace();
|
||||
|
@ -304,7 +445,7 @@ class CategoryTree {
|
|||
|
||||
#when showing only categories, omit namespace in label unless we explicitely defined the configuration setting
|
||||
#patch contributed by Manuel Schneider <manuel.schneider@wikimedia.ch>, Bug 8011
|
||||
if ( $wgCategoryTreeOmitNamespace || $mode == CT_MODE_CATEGORIES ) $label = htmlspecialchars( $title->getText() );
|
||||
if ( $this->getOption('hideprefix') || $mode == CT_MODE_CATEGORIES ) $label = htmlspecialchars( $title->getText() );
|
||||
else $label = htmlspecialchars( $title->getPrefixedText() );
|
||||
|
||||
if ( $trans && $trans!=$label ) $label.= ' ' . Xml::element( 'i', array( 'class' => 'translation'), $trans );
|
||||
|
@ -321,23 +462,6 @@ class CategoryTree {
|
|||
|
||||
if ( ( $ns % 2 ) > 0 ) $labelClass .= ' CategoryTreeLabelTalk';
|
||||
|
||||
$linkattr= array( 'href' => '#' );
|
||||
|
||||
if ( $load ) $linkattr[ 'id' ] = $load;
|
||||
|
||||
if ( !$children ) {
|
||||
$txt = '+';
|
||||
$linkattr[ 'onclick' ] = "this.href='javascript:void(0)'; categoryTreeExpandNode('".Xml::escapeJsString($key)."','".$mode."',this);";
|
||||
# Don't load this message for ajax requests, so that we don't have to initialise $wgLang
|
||||
$linkattr[ 'title' ] = $this->mIsAjaxRequest ? '##LOAD##' : self::msg('expand');
|
||||
}
|
||||
else {
|
||||
$txt = '–'; #NOTE: that's not a minus but a unicode ndash!
|
||||
$linkattr[ 'onclick' ] = "this.href='javascript:void(0)'; categoryTreeCollapseNode('".Xml::escapeJsString($key)."','".$mode."',this);";
|
||||
$linkattr[ 'title' ] = self::msg('collapse');
|
||||
$linkattr[ 'class' ] = 'CategoryTreeLoaded';
|
||||
}
|
||||
|
||||
$s = '';
|
||||
|
||||
#NOTE: things in CategoryTree.js rely on the exact order of tags!
|
||||
|
@ -347,27 +471,48 @@ class CategoryTree {
|
|||
$s .= Xml::openElement( 'div', array( 'class' => 'CategoryTreeSection' ) );
|
||||
$s .= Xml::openElement( 'div', array( 'class' => 'CategoryTreeItem' ) );
|
||||
|
||||
$attr = array( 'class' => 'CategoryTreeBullet' );
|
||||
$s .= Xml::openElement( 'span', $attr );
|
||||
|
||||
if ( $ns == NS_CATEGORY ) {
|
||||
$s .= Xml::openElement( 'span', array( 'class' => 'CategoryTreeBullet' ) );
|
||||
$s .= '[' . Xml::element( 'a', $linkattr, $txt ) . '] ';
|
||||
$s .= Xml::closeElement( 'span' );
|
||||
$linkattr= array( 'href' => '#' );
|
||||
if ( $load ) $linkattr[ 'id' ] = $load;
|
||||
|
||||
$linkattr[ 'class' ] = "CategoryTreeToggle";
|
||||
|
||||
if ( $children == 0 || $loadchildren ) {
|
||||
$txt = $this->msg('expand-bullet');
|
||||
$linkattr[ 'onclick' ] = "this.href='javascript:void(0)'; categoryTreeExpandNode('".Xml::escapeJsString($key)."',".$this->getOptionsAsJsStructure().",this);";
|
||||
# Don't load this message for ajax requests, so that we don't have to initialise $wgLang
|
||||
$linkattr[ 'title' ] = $this->mIsAjaxRequest ? '##LOAD##' : self::msg('expand');
|
||||
}
|
||||
else {
|
||||
$txt = $this->msg('collapse-bullet');
|
||||
$linkattr[ 'onclick' ] = "this.href='javascript:void(0)'; categoryTreeCollapseNode('".Xml::escapeJsString($key)."',".$this->getOptionsAsJsStructure().",this);";
|
||||
$linkattr[ 'title' ] = self::msg('collapse');
|
||||
$linkattr[ 'class' ] .= ' CategoryTreeLoaded';
|
||||
}
|
||||
|
||||
$s .= Xml::openElement( 'a', $linkattr ) . $txt . Xml::closeElement( 'a' ) . ' ';
|
||||
} else {
|
||||
$s .= ' ';
|
||||
$s .= $this->msg('page-bullet');
|
||||
}
|
||||
|
||||
$s .= Xml::closeElement( 'span' );
|
||||
|
||||
$s .= Xml::openElement( 'a', array( 'class' => $labelClass, 'href' => $wikiLink ) ) . $label . Xml::closeElement( 'a' );
|
||||
$s .= Xml::closeElement( 'div' );
|
||||
$s .= "\n\t\t";
|
||||
$s .= Xml::openElement( 'div', array( 'class' => 'CategoryTreeChildren', 'style' => $children ? "display:block" : "display:none" ) );
|
||||
//HACK here?
|
||||
if ( $children ) $s .= $this->renderChildren( $title, $mode, $depth );
|
||||
$s .= Xml::openElement( 'div', array( 'class' => 'CategoryTreeChildren', 'style' => $children > 0 ? "display:block" : "display:none" ) );
|
||||
|
||||
if ( $children > 0 && !$loadchildren) $s .= $this->renderChildren( $title, $children );
|
||||
$s .= Xml::closeElement( 'div' );
|
||||
$s .= Xml::closeElement( 'div' );
|
||||
|
||||
if ( $load ) {
|
||||
$s .= "\n\t\t";
|
||||
$s .= Xml::openElement( 'script', array( 'type' => 'text/javascript' ) );
|
||||
$s .= 'categoryTreeExpandNode("'.Xml::escapeJsString($key).'", "'.$mode.'", document.getElementById("'.$load.'") );';
|
||||
$s .= 'categoryTreeExpandNode("'.Xml::escapeJsString($key).'", '.$this->getOptionsAsJsStructure($children).', document.getElementById("'.$load.'"));';
|
||||
$s .= Xml::closeElement( 'script' );
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ if( !defined( 'MEDIAWIKI' ) ) {
|
|||
class CategoryTreePage extends SpecialPage {
|
||||
|
||||
var $target = '';
|
||||
var $mode = CT_MODE_CATEGORIES;
|
||||
var $tree = NULL;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -28,12 +28,19 @@ class CategoryTreePage extends SpecialPage {
|
|||
wfLoadExtensionMessages( 'CategoryTree' );
|
||||
}
|
||||
|
||||
function getOption( $name ) {
|
||||
global $wgCategoryTreeDefaultOptions;
|
||||
|
||||
if ( $this->tree ) return $this->tree->getOption( $name );
|
||||
else return $wgCategoryTreeDefaultOptions[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Main execution function
|
||||
* @param $par Parameters passed to the page
|
||||
*/
|
||||
function execute( $par ) {
|
||||
global $wgRequest, $wgOut, $wgMakeBotPrivileged, $wgUser;
|
||||
global $wgRequest, $wgOut, $wgMakeBotPrivileged, $wgUser, $wgCategoryTreeDefaultOptions;
|
||||
|
||||
$this->setHeaders();
|
||||
|
||||
|
@ -45,13 +52,14 @@ class CategoryTreePage extends SpecialPage {
|
|||
#HACK for undefined root category
|
||||
if ( $this->target == '<rootcategory>' || $this->target == '<rootcategory>' ) $this->target = NULL;
|
||||
|
||||
$this->mode = $wgRequest->getVal( 'mode', CT_MODE_CATEGORIES );
|
||||
$options = array();
|
||||
|
||||
if ( $this->mode == 'all' ) $this->mode = CT_MODE_ALL;
|
||||
else if ( $this->mode == 'pages' ) $this->mode = CT_MODE_PAGES;
|
||||
else if ( $this->mode == 'categories' ) $this->mode = CT_MODE_CATEGORIES;
|
||||
# grab all known options from the request. Normalization is done by the CategoryTree class
|
||||
foreach ( $wgCategoryTreeDefaultOptions as $option => $default ) {
|
||||
$options[$option] = $wgRequest->getVal( $option, $default );
|
||||
}
|
||||
|
||||
$this->mode = (int)$this->mode;
|
||||
$this->tree = new CategoryTree( $options );
|
||||
|
||||
$wgOut->addWikiText( wfMsgNoTrans( 'categorytree-header' ) );
|
||||
|
||||
|
@ -64,12 +72,9 @@ class CategoryTreePage extends SpecialPage {
|
|||
|
||||
if ( $title && $title->getArticleID() ) {
|
||||
$html = Xml::openElement( 'div', array( 'class' => 'CategoryTreeParents' ) );
|
||||
Xml::element( 'span',
|
||||
array( 'class' => 'CategoryTreeParents' ),
|
||||
wfMsg( 'categorytree-parents' ) ) . ': ';
|
||||
$html .= wfMsg( 'categorytree-parents' ) . ': ';
|
||||
|
||||
$ct = new CategoryTree;
|
||||
$parents = $ct->renderParents( $title, $this->mode );
|
||||
$parents = $this->tree->renderParents( $title );
|
||||
|
||||
if ( $parents == '' ) {
|
||||
$html .= wfMsg( 'categorytree-nothing-found' );
|
||||
|
@ -77,10 +82,12 @@ class CategoryTreePage extends SpecialPage {
|
|||
$html .= $parents;
|
||||
}
|
||||
|
||||
$html .= Xml::closeElement( 'div' ) .
|
||||
Xml::openElement( 'div', array( 'class' => 'CategoryTreeResult' ) ) .
|
||||
$ct->renderNode( $title, $this->mode, true, false ) .
|
||||
Xml::closeElement( 'div' );
|
||||
$html .= Xml::closeElement( 'div' );
|
||||
|
||||
$html .= Xml::openElement( 'div', array( 'class' => 'CategoryTreeResult' ) );
|
||||
$html .= $this->tree->renderNode( $title, 1 ) .
|
||||
$html .= Xml::closeElement( 'div' );
|
||||
|
||||
$wgOut->addHtml( $html );
|
||||
}
|
||||
else {
|
||||
|
@ -98,6 +105,7 @@ class CategoryTreePage extends SpecialPage {
|
|||
function makeInputForm() {
|
||||
global $wgScript;
|
||||
$thisTitle = Title::makeTitle( NS_SPECIAL, $this->getName() );
|
||||
$mode = $this->getOption('mode');
|
||||
|
||||
$form = Xml::openElement( 'form', array( 'name' => 'categorytree', 'method' => 'get', 'action' => $wgScript, 'id' => 'mw-categorytree-form' ) ) .
|
||||
Xml::openElement( 'fieldset' ) .
|
||||
|
@ -105,9 +113,9 @@ class CategoryTreePage extends SpecialPage {
|
|||
Xml::hidden( 'title', $thisTitle->getPrefixedDbKey() ) .
|
||||
Xml::inputLabel( wfMsg( 'categorytree-category' ), 'target', 'target', 20, $this->target ) . ' ' .
|
||||
Xml::openElement( 'select', array( 'name' => 'mode' ) ) .
|
||||
Xml::option( wfMsg( 'categorytree-mode-categories' ), 'categories', $this->mode == CT_MODE_CATEGORIES ? true : false ) .
|
||||
Xml::option( wfMsg( 'categorytree-mode-pages' ), 'pages', $this->mode == CT_MODE_PAGES ? true : false ) .
|
||||
Xml::option( wfMsg( 'categorytree-mode-all' ), 'all', $this->mode == CT_MODE_ALL ? true : false ) .
|
||||
Xml::option( wfMsg( 'categorytree-mode-categories' ), 'categories', $mode == CT_MODE_CATEGORIES ? true : false ) .
|
||||
Xml::option( wfMsg( 'categorytree-mode-pages' ), 'pages', $mode == CT_MODE_PAGES ? true : false ) .
|
||||
Xml::option( wfMsg( 'categorytree-mode-all' ), 'all', $mode == CT_MODE_ALL ? true : false ) .
|
||||
Xml::closeElement( 'select' ) . ' ' .
|
||||
Xml::submitButton( wfMsg( 'categorytree-go' ), array( 'name' => 'dotree' ) ) .
|
||||
Xml::closeElement( 'fieldset' ) .
|
||||
|
|
119
README
119
README
|
@ -1,6 +1,6 @@
|
|||
--------------------------------------------------------------------------
|
||||
README for the CategoryTree extension
|
||||
Copyright © 2006-2007 Daniel Kinzler
|
||||
Copyright © 2006-2008 Daniel Kinzler and others
|
||||
Licenses: GNU General Public Licence (GPL)
|
||||
GNU Free Documentation License (GFDL)
|
||||
--------------------------------------------------------------------------
|
||||
|
@ -8,125 +8,12 @@ Licenses: GNU General Public Licence (GPL)
|
|||
The CategoryTree extension provides a dynamic view of the wiki's category
|
||||
structure as a tree. It uses AJAX to load parts of the tree on demand.
|
||||
|
||||
<http://meta.wikimedia.org/wiki/CategoryTree_extension>
|
||||
|
||||
The CategoryTree extension was originally written by Daniel Kinzler in
|
||||
2006 and is released under the GNU General Public Licence (GPL). The
|
||||
internationalization files contain contributions by several people;
|
||||
they are mentioned in each file individually. Also thanks to Tim Starling
|
||||
for his contributions.
|
||||
|
||||
Instructions on installing and using this extension are available at
|
||||
<http://www.mediawiki.org/wiki/Extension:CategoryTree>
|
||||
|
||||
INSTALLING
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
Copy the CategoryTree directory into the extensions folder of your
|
||||
MediaWiki installation. Then add the following lines to your
|
||||
LocalSettings.php file (near the end):
|
||||
|
||||
$wgUseAjax = true;
|
||||
require_once( "{$IP}/extensions/CategoryTree/CategoryTree.php" );
|
||||
|
||||
Note that $wgUseAjax = true; will enable the Ajax framework in MediaWiki,
|
||||
which is required by the CategoryTree extension. Ajax is a term for using
|
||||
JavaScript to load parts of a page on demand. It is supported by all
|
||||
recent graphic web browsers. For more information about Ajax see
|
||||
<http://en.wikipedia.org/wiki/Ajax_%28programming%29>.
|
||||
|
||||
|
||||
USAGE
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
CategoryTree can be used in three ways: directly on the category pages,
|
||||
as a "custom tag" to show a category structure inline on a wiki page,
|
||||
and as a special page.
|
||||
|
||||
The CategoryTree extension replaces the subcategory section of category
|
||||
pages with a dynamic tree view. If Javascript is disabled, this appears
|
||||
as a plain list. The dynamic subcategory entries can be disabled using
|
||||
the URL parameter "notree" - this is intended for bots that rely on
|
||||
parsing the HTML of category pages.
|
||||
|
||||
The custom tag is called <categorytree>. For example, if you put
|
||||
<categorytree mode="pages">Foo</categorytree> on a wiki page, it will show
|
||||
the contents of category Foo as a dynamic tree on that page. The tag accepts
|
||||
the following attributes, using a HTML-like syntax:
|
||||
|
||||
* hideroot - set this to "on" to hide the "root" node of the tree, i.e.
|
||||
the mention of category Foo from the example.
|
||||
|
||||
* onlyroot - set this to "on" show only the "root" node of the tree initially
|
||||
|
||||
* mode - can be "categories", "pages" or "all". See the Modes section below.
|
||||
The default for this attribute is controlled by
|
||||
$wgCategoryTreeDefaultMode, and is initially set to CT_MODE_CATEGORIES,
|
||||
the equivalent of setting the mode attribute to "categories".
|
||||
|
||||
* style - can be used to specify any CSS styles you would like for the
|
||||
tree.
|
||||
|
||||
Alternatively to the <categorytree> tag, parser-function syntax can also be
|
||||
used, e.g {{#categorytree:Foo|hideroot=yes}}. This syntax allows the use of
|
||||
magic variables, templates and template parameters for the category name.
|
||||
|
||||
The special page is called Special:CategoryTree; there you can enter the
|
||||
name of a category and then browse it's content. The CategoryTree
|
||||
extension also adds a tab for this special page to every category page.
|
||||
|
||||
|
||||
MODES
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
The category tree can be shown in different modes, determining what types of
|
||||
"leaves" the "tree" has:
|
||||
* categories (constant CT_MODE_CATEGORIES): show subcategories only
|
||||
* pages (constant CT_MODE_PAGES): show subcategories and pages, except images
|
||||
* all (constant CT_MODE_ALL): show all pages, subcategories, images, etc
|
||||
|
||||
The CT_MODE_XXX constants can be used with configuration optiosn (see below).
|
||||
|
||||
OPTIONS
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
There are some options you can specify in your LocalSettings.php file:
|
||||
|
||||
$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.
|
||||
|
||||
$wgCategoryTreeHTTPCache - enable HTTP cache for anon users. Default is
|
||||
false.
|
||||
|
||||
$wgCategoryTreeOmitNamespace - never show namespace prefix. Default is
|
||||
false. Patch contributed by Manuel Schneider
|
||||
<manuel.schneider@wikimedia.ch>, Bug 8011
|
||||
|
||||
$wgCategoryMaxDepth - maximum value for depth argument; can be an integer,
|
||||
or an associative array, mapping CT_MODE_XXX constants
|
||||
to the maximum depth for that mode.
|
||||
Ignored if $wgCategoryTreeDynamicTag is true. Introduced
|
||||
by Steve Sanbeg.
|
||||
|
||||
$wgCategoryTreeExtPath - the (URL-) path where the extension is installed,
|
||||
relative to $wgScriptPath, with leading "/". Default is
|
||||
"/extensions/CategoryTree".
|
||||
|
||||
$wgCategoryTreeDefaultMode - the default mode to use when no mode attribute
|
||||
is specified in a <categorytree> tag. May be
|
||||
CT_MODE_CATEGORIES (the default), CT_MODE_PAGES, or
|
||||
CT_MODE_ALL.
|
||||
|
||||
$wgCategoryTreeCategoryPageMode - the mode to use when rendering trees on
|
||||
category pages. May be CT_MODE_CATEGORIES (the default),
|
||||
CT_MODE_PAGES, or CT_MODE_ALL.
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
EOF
|
||||
|
|
Loading…
Reference in a new issue