2006-07-26 17:12:30 +00:00
|
|
|
<?php
|
|
|
|
/**
|
2008-02-04 09:22:12 +00:00
|
|
|
* Core functions for the CategoryTree extension, an AJAX based gadget
|
2006-07-26 17:12:30 +00:00
|
|
|
* to display the category structure of a wiki
|
|
|
|
*
|
2010-06-06 15:12:22 +00:00
|
|
|
* @file
|
|
|
|
* @ingroup Extensions
|
2007-03-13 11:39:12 +00:00
|
|
|
* @author Daniel Kinzler, brightbyte.de
|
|
|
|
* @copyright © 2006-2007 Daniel Kinzler
|
2008-03-18 17:38:32 +00:00
|
|
|
* @license GNU General Public Licence 2.0 or later
|
2006-07-26 17:12:30 +00:00
|
|
|
*/
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2010-04-20 22:00:34 +00:00
|
|
|
if ( !defined( 'MEDIAWIKI' ) ) {
|
2006-07-26 17:12:30 +00:00
|
|
|
echo( "This file is part of an extension to the MediaWiki software and cannot be used standalone.\n" );
|
|
|
|
die( 1 );
|
|
|
|
}
|
|
|
|
|
2006-08-24 17:12:13 +00:00
|
|
|
class CategoryTree {
|
2014-08-17 21:03:48 +00:00
|
|
|
public $mIsAjaxRequest = false;
|
|
|
|
public $mOptions = array();
|
2008-06-28 20:13:20 +00:00
|
|
|
|
2012-02-09 01:23:31 +00:00
|
|
|
/**
|
|
|
|
* @param $options array
|
|
|
|
* @param $ajax bool
|
|
|
|
*/
|
2008-06-28 20:13:20 +00:00
|
|
|
function __construct( $options, $ajax = false ) {
|
|
|
|
global $wgCategoryTreeDefaultOptions;
|
|
|
|
|
|
|
|
$this->mIsAjaxRequest = $ajax;
|
|
|
|
|
2010-04-20 22:00:34 +00:00
|
|
|
# ensure default values and order of options. Order may become important, it may influence the cache key!
|
2008-06-28 20:13:20 +00:00
|
|
|
foreach ( $wgCategoryTreeDefaultOptions as $option => $default ) {
|
2010-04-20 22:00:34 +00:00
|
|
|
if ( isset( $options[$option] ) && !is_null( $options[$option] ) ) {
|
|
|
|
$this->mOptions[$option] = $options[$option];
|
|
|
|
} else {
|
|
|
|
$this->mOptions[$option] = $default;
|
|
|
|
}
|
2008-06-28 20:13:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
$this->mOptions['mode'] = self::decodeMode( $this->mOptions['mode'] );
|
2008-07-02 09:49:28 +00:00
|
|
|
|
|
|
|
if ( $this->mOptions['mode'] == CT_MODE_PARENTS ) {
|
2010-04-20 22:00:34 +00:00
|
|
|
$this->mOptions['namespaces'] = false; # namespace filter makes no sense with CT_MODE_PARENTS
|
2008-07-02 09:49:28 +00:00
|
|
|
}
|
|
|
|
|
2008-06-30 21:22:03 +00:00
|
|
|
$this->mOptions['hideprefix'] = self::decodeHidePrefix( $this->mOptions['hideprefix'] );
|
2008-06-30 14:09:47 +00:00
|
|
|
$this->mOptions['showcount'] = self::decodeBoolean( $this->mOptions['showcount'] );
|
2008-07-01 21:40:42 +00:00
|
|
|
$this->mOptions['namespaces'] = self::decodeNamespaces( $this->mOptions['namespaces'] );
|
|
|
|
|
|
|
|
if ( $this->mOptions['namespaces'] ) {
|
|
|
|
# automatically adjust mode to match namespace filter
|
2010-04-20 22:00:34 +00:00
|
|
|
if ( sizeof( $this->mOptions['namespaces'] ) === 1
|
2008-07-01 21:40:42 +00:00
|
|
|
&& $this->mOptions['namespaces'][0] == NS_CATEGORY ) {
|
|
|
|
$this->mOptions['mode'] = CT_MODE_CATEGORIES;
|
2011-06-17 16:25:46 +00:00
|
|
|
} elseif ( !in_array( NS_IMAGE, $this->mOptions['namespaces'] ) ) {
|
2008-07-01 21:40:42 +00:00
|
|
|
$this->mOptions['mode'] = CT_MODE_PAGES;
|
|
|
|
} else {
|
|
|
|
$this->mOptions['mode'] = CT_MODE_ALL;
|
|
|
|
}
|
|
|
|
}
|
2008-06-28 20:13:20 +00:00
|
|
|
}
|
|
|
|
|
2012-02-09 01:23:31 +00:00
|
|
|
/**
|
|
|
|
* @param $name string
|
|
|
|
* @return mixed
|
|
|
|
*/
|
2008-06-28 20:13:20 +00:00
|
|
|
function getOption( $name ) {
|
|
|
|
return $this->mOptions[$name];
|
|
|
|
}
|
|
|
|
|
2012-02-09 01:23:31 +00:00
|
|
|
/**
|
|
|
|
* @return bool
|
|
|
|
*/
|
2008-07-02 09:49:28 +00:00
|
|
|
function isInverse( ) {
|
2010-04-20 22:00:34 +00:00
|
|
|
return $this->getOption( 'mode' ) == CT_MODE_PARENTS;
|
2008-07-02 09:49:28 +00:00
|
|
|
}
|
|
|
|
|
2012-02-09 01:23:31 +00:00
|
|
|
/**
|
|
|
|
* @param $nn
|
|
|
|
* @return array|bool
|
|
|
|
*/
|
2008-07-01 21:40:42 +00:00
|
|
|
static function decodeNamespaces( $nn ) {
|
|
|
|
global $wgContLang;
|
|
|
|
|
2012-09-25 14:11:32 +00:00
|
|
|
if ( $nn === false || is_null( $nn ) ) {
|
2008-07-01 21:40:42 +00:00
|
|
|
return false;
|
2012-02-09 01:23:31 +00:00
|
|
|
}
|
2008-07-01 21:40:42 +00:00
|
|
|
|
2012-02-09 01:23:31 +00:00
|
|
|
if ( !is_array( $nn ) ) {
|
2008-07-01 21:40:42 +00:00
|
|
|
$nn = preg_split( '![\s#:|]+!', $nn );
|
2012-02-09 01:23:31 +00:00
|
|
|
}
|
2008-07-01 21:40:42 +00:00
|
|
|
|
|
|
|
$namespaces = array();
|
|
|
|
|
|
|
|
foreach ( $nn as $n ) {
|
|
|
|
if ( is_int( $n ) ) {
|
|
|
|
$ns = $n;
|
2012-02-09 01:23:31 +00:00
|
|
|
} else {
|
2008-07-01 21:40:42 +00:00
|
|
|
$n = trim( $n );
|
2012-02-09 01:23:31 +00:00
|
|
|
if ( $n === '' ) {
|
|
|
|
continue;
|
|
|
|
}
|
2010-04-20 22:00:34 +00:00
|
|
|
|
2008-07-01 21:40:42 +00:00
|
|
|
$lower = strtolower( $n );
|
2010-04-20 22:00:34 +00:00
|
|
|
|
2012-02-09 01:23:31 +00:00
|
|
|
if ( is_numeric( $n ) ) {
|
|
|
|
$ns = (int)$n;
|
|
|
|
} elseif ( $n == '-' || $n == '_' || $n == '*' || $lower == 'main' ) {
|
|
|
|
$ns = NS_MAIN;
|
|
|
|
} else {
|
|
|
|
$ns = $wgContLang->getNsIndex( $n );
|
|
|
|
}
|
2008-07-01 21:40:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ( is_int( $ns ) ) {
|
|
|
|
$namespaces[] = $ns;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sort( $namespaces ); # get elements into canonical order
|
|
|
|
return $namespaces;
|
|
|
|
}
|
|
|
|
|
2012-02-09 01:23:31 +00:00
|
|
|
/**
|
|
|
|
* @param $mode
|
|
|
|
* @return int|string
|
|
|
|
*/
|
2008-06-28 20:13:20 +00:00
|
|
|
static function decodeMode( $mode ) {
|
|
|
|
global $wgCategoryTreeDefaultOptions;
|
|
|
|
|
2012-02-09 01:23:31 +00:00
|
|
|
if ( is_null( $mode ) ) {
|
|
|
|
return $wgCategoryTreeDefaultOptions['mode'];
|
|
|
|
}
|
|
|
|
if ( is_int( $mode ) ) {
|
|
|
|
return $mode;
|
|
|
|
}
|
2008-06-28 20:13:20 +00:00
|
|
|
|
|
|
|
$mode = trim( strtolower( $mode ) );
|
|
|
|
|
2012-02-09 01:23:31 +00:00
|
|
|
if ( is_numeric( $mode ) ) {
|
|
|
|
return (int)$mode;
|
|
|
|
}
|
2008-06-28 20:13:20 +00:00
|
|
|
|
2012-02-09 01:23:31 +00:00
|
|
|
if ( $mode == 'all' ) {
|
|
|
|
$mode = CT_MODE_ALL;
|
|
|
|
} elseif ( $mode == 'pages' ) {
|
|
|
|
$mode = CT_MODE_PAGES;
|
|
|
|
} elseif ( $mode == 'categories' || $mode == 'sub' ) {
|
|
|
|
$mode = CT_MODE_CATEGORIES;
|
|
|
|
} elseif ( $mode == 'parents' || $mode == 'super' || $mode == 'inverse' ) {
|
|
|
|
$mode = CT_MODE_PARENTS;
|
|
|
|
} elseif ( $mode == 'default' ) {
|
|
|
|
$mode = $wgCategoryTreeDefaultOptions['mode'];
|
|
|
|
}
|
2008-06-28 20:13:20 +00:00
|
|
|
|
|
|
|
return (int)$mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2012-02-09 01:23:31 +00:00
|
|
|
* Helper function to convert a string to a boolean value.
|
|
|
|
* Perhaps make this a global function in MediaWiki proper
|
|
|
|
* @param $value
|
|
|
|
* @return bool|null|string
|
|
|
|
*/
|
2008-06-28 20:13:20 +00:00
|
|
|
static function decodeBoolean( $value ) {
|
2012-02-09 01:23:31 +00:00
|
|
|
if ( is_null( $value ) ) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
if ( is_bool( $value ) ) {
|
|
|
|
return $value;
|
|
|
|
}
|
|
|
|
if ( is_int( $value ) ) {
|
|
|
|
return ( $value > 0 );
|
|
|
|
}
|
2008-06-28 20:13:20 +00:00
|
|
|
|
|
|
|
$value = trim( strtolower( $value ) );
|
2012-02-09 01:23:31 +00:00
|
|
|
if ( is_numeric( $value ) ) {
|
|
|
|
return ( (int)$value > 0 );
|
|
|
|
}
|
2008-06-28 20:13:20 +00:00
|
|
|
|
2012-02-09 01:23:31 +00:00
|
|
|
if ( $value == 'yes' || $value == 'y' || $value == 'true' || $value == 't' || $value == 'on' ) {
|
|
|
|
return true;
|
|
|
|
} elseif ( $value == 'no' || $value == 'n' || $value == 'false' || $value == 'f' || $value == 'off' ) {
|
|
|
|
return false;
|
|
|
|
} elseif ( $value == 'null' || $value == 'default' || $value == 'none' || $value == 'x' ) {
|
|
|
|
return null;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
2008-06-28 20:13:20 +00:00
|
|
|
}
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2012-02-09 01:23:31 +00:00
|
|
|
/**
|
|
|
|
* @param $value
|
|
|
|
* @return int|string
|
|
|
|
*/
|
2008-06-30 21:22:03 +00:00
|
|
|
static function decodeHidePrefix( $value ) {
|
|
|
|
global $wgCategoryTreeDefaultOptions;
|
|
|
|
|
2012-02-09 01:23:31 +00:00
|
|
|
if ( is_null( $value ) ) {
|
|
|
|
return $wgCategoryTreeDefaultOptions['hideprefix'];
|
|
|
|
}
|
|
|
|
if ( is_int( $value ) ) {
|
|
|
|
return $value;
|
|
|
|
}
|
|
|
|
if ( $value === true ) {
|
|
|
|
return CT_HIDEPREFIX_ALWAYS;
|
|
|
|
}
|
|
|
|
if ( $value === false ) {
|
|
|
|
return CT_HIDEPREFIX_NEVER;
|
|
|
|
}
|
2008-06-30 21:22:03 +00:00
|
|
|
|
|
|
|
$value = trim( strtolower( $value ) );
|
|
|
|
|
2012-02-09 01:23:31 +00:00
|
|
|
if ( $value == 'yes' || $value == 'y' || $value == 'true' || $value == 't' || $value == 'on' ) {
|
|
|
|
return CT_HIDEPREFIX_ALWAYS;
|
|
|
|
} elseif ( $value == 'no' || $value == 'n' || $value == 'false' || $value == 'f' || $value == 'off' ) {
|
|
|
|
return CT_HIDEPREFIX_NEVER;
|
|
|
|
} elseif ( $value == 'always' ) {
|
|
|
|
return CT_HIDEPREFIX_ALWAYS;
|
|
|
|
} elseif ( $value == 'never' ) {
|
|
|
|
return CT_HIDEPREFIX_NEVER;
|
|
|
|
} elseif ( $value == 'auto' ) {
|
|
|
|
return CT_HIDEPREFIX_AUTO;
|
|
|
|
} elseif ( $value == 'categories' || $value == 'category' || $value == 'smart' ) {
|
|
|
|
return CT_HIDEPREFIX_CATEGORIES;
|
|
|
|
} else {
|
|
|
|
return $wgCategoryTreeDefaultOptions['hideprefix'];
|
|
|
|
}
|
2008-06-30 21:22:03 +00:00
|
|
|
}
|
|
|
|
|
2006-08-24 17:12:13 +00:00
|
|
|
/**
|
|
|
|
* Set the script tags in an OutputPage object
|
2008-02-04 09:22:12 +00:00
|
|
|
* @param OutputPage $outputPage
|
2006-08-24 17:12:13 +00:00
|
|
|
*/
|
2010-06-02 09:10:09 +00:00
|
|
|
static function setHeaders( $outputPage ) {
|
2012-02-08 14:07:10 +00:00
|
|
|
# Add the modules
|
|
|
|
$outputPage->addModuleStyles( 'ext.categoryTree.css' );
|
2011-09-30 02:51:53 +00:00
|
|
|
$outputPage->addModules( 'ext.categoryTree' );
|
2008-02-04 09:22:12 +00:00
|
|
|
}
|
2006-07-29 09:18:34 +00:00
|
|
|
|
2012-02-09 01:23:31 +00:00
|
|
|
/**
|
|
|
|
* @param $options
|
|
|
|
* @param $enc
|
|
|
|
* @return mixed
|
|
|
|
* @throws MWException
|
|
|
|
*/
|
2008-06-28 20:13:20 +00:00
|
|
|
static function encodeOptions( $options, $enc ) {
|
|
|
|
if ( $enc == 'mode' || $enc == '' ) {
|
2010-04-20 22:00:34 +00:00
|
|
|
$opt = $options['mode'];
|
2008-06-28 20:13:20 +00:00
|
|
|
} elseif ( $enc == 'json' ) {
|
2012-03-01 01:17:55 +00:00
|
|
|
$opt = FormatJson::encode( $options );
|
2008-06-28 20:13:20 +00:00
|
|
|
} else {
|
|
|
|
throw new MWException( 'Unknown encoding for CategoryTree options: ' . $enc );
|
|
|
|
}
|
|
|
|
|
|
|
|
return $opt;
|
|
|
|
}
|
|
|
|
|
2012-02-09 01:23:31 +00:00
|
|
|
/**
|
|
|
|
* @param $options
|
|
|
|
* @param $enc
|
|
|
|
* @return array|mixed
|
|
|
|
* @throws MWException
|
|
|
|
*/
|
2008-06-28 20:13:20 +00:00
|
|
|
static function decodeOptions( $options, $enc ) {
|
|
|
|
if ( $enc == 'mode' || $enc == '' ) {
|
|
|
|
$opt = array( "mode" => $options );
|
|
|
|
} elseif ( $enc == 'json' ) {
|
2012-03-01 01:17:55 +00:00
|
|
|
$opt = FormatJson::decode( $options );
|
2012-05-09 19:01:08 +00:00
|
|
|
if ( !$opt ) {
|
|
|
|
throw new MWException( 'JSON cannot decode CategoryTree options' );
|
|
|
|
}
|
2008-06-28 20:13:20 +00:00
|
|
|
$opt = get_object_vars( $opt );
|
2010-04-20 22:00:34 +00:00
|
|
|
} else {
|
2008-06-28 20:13:20 +00:00
|
|
|
throw new MWException( 'Unknown encoding for CategoryTree options: ' . $enc );
|
|
|
|
}
|
|
|
|
|
|
|
|
return $opt;
|
|
|
|
}
|
|
|
|
|
2012-02-09 01:23:31 +00:00
|
|
|
/**
|
|
|
|
* @param $depth null
|
|
|
|
* @return string
|
|
|
|
*/
|
2010-01-06 21:24:10 +00:00
|
|
|
function getOptionsAsCacheKey( $depth = null ) {
|
2008-06-28 20:13:20 +00:00
|
|
|
$key = "";
|
|
|
|
|
|
|
|
foreach ( $this->mOptions as $k => $v ) {
|
2008-07-01 21:40:42 +00:00
|
|
|
if ( is_array( $v ) ) $v = implode( '|', $v );
|
2008-06-28 20:13:20 +00:00
|
|
|
$key .= $k . ':' . $v . ';';
|
|
|
|
}
|
|
|
|
|
2012-02-09 01:23:31 +00:00
|
|
|
if ( !is_null( $depth ) ) {
|
|
|
|
$key .= ";depth=" . $depth;
|
|
|
|
}
|
2008-06-28 20:13:20 +00:00
|
|
|
return $key;
|
|
|
|
}
|
|
|
|
|
2012-02-09 01:23:31 +00:00
|
|
|
/**
|
2012-08-28 22:35:39 +00:00
|
|
|
* @param $depth int|null
|
2012-02-09 01:23:31 +00:00
|
|
|
* @return mixed
|
|
|
|
*/
|
2010-01-06 21:24:10 +00:00
|
|
|
function getOptionsAsJsStructure( $depth = null ) {
|
2008-06-28 20:13:20 +00:00
|
|
|
if ( !is_null( $depth ) ) {
|
|
|
|
$opt = $this->mOptions;
|
|
|
|
$opt['depth'] = $depth;
|
|
|
|
$s = self::encodeOptions( $opt, 'json' );
|
|
|
|
} else {
|
|
|
|
$s = self::encodeOptions( $this->mOptions, 'json' );
|
|
|
|
}
|
|
|
|
|
|
|
|
return $s;
|
|
|
|
}
|
|
|
|
|
2012-02-09 01:23:31 +00:00
|
|
|
/**
|
|
|
|
* @param $depth null
|
|
|
|
* @return String
|
|
|
|
*/
|
|
|
|
function getOptionsAsJsString( $depth = null ) {
|
2011-09-30 21:08:28 +00:00
|
|
|
return Xml::escapeJsString( $this->getOptionsAsJsStructure( $depth ) );
|
2008-06-28 20:13:20 +00:00
|
|
|
}
|
|
|
|
|
2012-02-09 01:23:31 +00:00
|
|
|
/**
|
|
|
|
* @return string
|
|
|
|
*/
|
2008-06-28 20:13:20 +00:00
|
|
|
function getOptionsAsUrlParameters() {
|
2012-11-19 17:21:24 +00:00
|
|
|
return http_build_query( $this->mOptions );
|
2008-06-28 20:13:20 +00:00
|
|
|
}
|
|
|
|
|
2006-08-24 17:12:13 +00:00
|
|
|
/**
|
2012-02-09 01:23:31 +00:00
|
|
|
* Ajax call. This is called by efCategoryTreeAjaxWrapper, which is used to
|
|
|
|
* load CategoryTreeFunctions.php on demand.
|
|
|
|
* @param $category
|
|
|
|
* @param $depth int
|
|
|
|
* @return AjaxResponse|bool
|
|
|
|
*/
|
2008-06-28 20:13:20 +00:00
|
|
|
function ajax( $category, $depth = 1 ) {
|
2011-09-09 21:08:50 +00:00
|
|
|
global $wgLang, $wgContLang, $wgRenderHashAppend;
|
2006-08-24 17:12:13 +00:00
|
|
|
$title = self::makeTitle( $category );
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2012-11-21 08:31:37 +00:00
|
|
|
if ( !$title ) {
|
2010-04-20 22:00:34 +00:00
|
|
|
return false; # TODO: error message?
|
|
|
|
}
|
2006-07-26 17:12:30 +00:00
|
|
|
|
2006-08-24 17:12:13 +00:00
|
|
|
# Retrieve page_touched for the category
|
|
|
|
$dbkey = $title->getDBkey();
|
2010-02-13 23:03:40 +00:00
|
|
|
$dbr = wfGetDB( DB_SLAVE );
|
2008-02-04 09:22:12 +00:00
|
|
|
$touched = $dbr->selectField( 'page', 'page_touched',
|
|
|
|
array(
|
2006-08-24 17:12:13 +00:00
|
|
|
'page_namespace' => NS_CATEGORY,
|
|
|
|
'page_title' => $dbkey,
|
|
|
|
), __METHOD__ );
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2012-02-09 01:23:31 +00:00
|
|
|
$mckey = wfMemcKey(
|
|
|
|
"categorytree(" . $this->getOptionsAsCacheKey( $depth ) . ")",
|
|
|
|
$dbkey, $wgLang->getCode(),
|
|
|
|
$wgContLang->getExtraHashOptions(),
|
|
|
|
$wgRenderHashAppend
|
|
|
|
);
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2006-08-29 15:49:23 +00:00
|
|
|
$response = new AjaxResponse();
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2006-08-29 15:49:23 +00:00
|
|
|
if ( $response->checkLastModified( $touched ) ) {
|
|
|
|
return $response;
|
2006-08-24 17:12:13 +00:00
|
|
|
}
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2006-08-29 15:49:23 +00:00
|
|
|
if ( $response->loadFromMemcached( $mckey, $touched ) ) {
|
|
|
|
return $response;
|
2006-08-24 17:12:13 +00:00
|
|
|
}
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2010-04-20 22:00:34 +00:00
|
|
|
$html = $this->renderChildren( $title, $depth );
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2010-04-20 22:00:34 +00:00
|
|
|
if ( $html == '' ) {
|
|
|
|
# HACK: Safari doesn't like empty responses.
|
|
|
|
# see Bug 7219 and http://bugzilla.opendarwin.org/show_bug.cgi?id=10716
|
2011-06-17 16:25:46 +00:00
|
|
|
$html = ' ';
|
2010-04-20 22:00:34 +00:00
|
|
|
}
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2006-08-29 15:49:23 +00:00
|
|
|
$response->addText( $html );
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2006-08-29 15:49:23 +00:00
|
|
|
$response->storeInMemcached( $mckey, 86400 );
|
|
|
|
|
|
|
|
return $response;
|
2006-08-24 17:12:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2012-01-14 15:27:30 +00:00
|
|
|
* Custom tag implementation. This is called by efCategoryTreeParserHook, which is used to
|
|
|
|
* load CategoryTreeFunctions.php on demand.
|
|
|
|
* @param $parser Parser
|
|
|
|
* @param $category
|
|
|
|
* @param $hideroot bool
|
|
|
|
* @param $attr
|
|
|
|
* @param $depth int
|
|
|
|
* @param $allowMissing bool
|
|
|
|
* @return bool|string
|
|
|
|
*/
|
2010-04-20 22:00:34 +00:00
|
|
|
function getTag( $parser, $category, $hideroot = false, $attr, $depth = 1, $allowMissing = false ) {
|
2014-02-19 13:10:24 +00:00
|
|
|
global $wgCategoryTreeDisableCache;
|
2006-08-24 17:12:13 +00:00
|
|
|
|
|
|
|
$category = trim( $category );
|
|
|
|
if ( $category === '' ) {
|
|
|
|
return false;
|
|
|
|
}
|
2010-04-20 22:00:34 +00:00
|
|
|
|
2014-02-19 13:10:24 +00:00
|
|
|
if ( $parser ) {
|
|
|
|
if ( is_bool( $wgCategoryTreeDisableCache ) && $wgCategoryTreeDisableCache === true ) {
|
|
|
|
$parser->disableCache();
|
|
|
|
} elseif ( is_int( $wgCategoryTreeDisableCache ) ) {
|
|
|
|
$parser->getOutput()->updateCacheExpiry( $wgCategoryTreeDisableCache );
|
|
|
|
}
|
2006-08-24 17:12:13 +00:00
|
|
|
}
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2010-04-20 22:00:34 +00:00
|
|
|
$title = self::makeTitle( $category );
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2010-04-20 22:00:34 +00:00
|
|
|
if ( $title === false || $title === null ) {
|
|
|
|
return false;
|
|
|
|
}
|
2008-06-30 21:35:01 +00:00
|
|
|
|
2010-04-20 22:00:34 +00:00
|
|
|
if ( isset( $attr['class'] ) ) {
|
|
|
|
$attr['class'] .= ' CategoryTreeTag';
|
|
|
|
} else {
|
|
|
|
$attr['class'] = ' CategoryTreeTag';
|
|
|
|
}
|
2008-07-04 20:05:29 +00:00
|
|
|
|
2011-09-30 21:08:28 +00:00
|
|
|
$attr['data-ct-mode'] = $this->mOptions['mode'];
|
2012-03-02 00:18:09 +00:00
|
|
|
$attr['data-ct-options'] = $this->getOptionsAsJsStructure();
|
2011-09-30 21:08:28 +00:00
|
|
|
|
2006-08-24 17:12:13 +00:00
|
|
|
$html = '';
|
2012-03-02 00:18:09 +00:00
|
|
|
$html .= Html::openElement( 'div', $attr );
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2008-07-02 20:19:54 +00:00
|
|
|
if ( !$allowMissing && !$title->getArticleID() ) {
|
2012-03-02 00:18:09 +00:00
|
|
|
$html .= Html::openElement( 'span', array( 'class' => 'CategoryTreeNotice' ) );
|
2010-04-20 22:00:34 +00:00
|
|
|
if ( $parser ) {
|
2012-08-28 22:35:39 +00:00
|
|
|
$html .= $parser->recursiveTagParse( wfMessage( 'categorytree-not-found', $category )->plain() );
|
2009-04-14 17:44:55 +00:00
|
|
|
} else {
|
2012-08-28 22:35:39 +00:00
|
|
|
$html .= wfMessage( 'categorytree-not-found', $category )->parse();
|
2009-04-14 17:44:55 +00:00
|
|
|
}
|
2012-03-02 00:18:09 +00:00
|
|
|
$html .= Html::closeElement( 'span' );
|
2006-08-24 17:12:13 +00:00
|
|
|
}
|
2006-07-29 09:18:34 +00:00
|
|
|
else {
|
2010-04-20 22:00:34 +00:00
|
|
|
if ( !$hideroot ) {
|
2014-02-19 13:10:24 +00:00
|
|
|
$html .= $this->renderNode( $title, $depth, false );
|
2010-04-20 22:00:34 +00:00
|
|
|
} else {
|
2014-02-19 13:10:24 +00:00
|
|
|
$html .= $this->renderChildren( $title, $depth );
|
2006-08-24 17:12:13 +00:00
|
|
|
}
|
2006-07-29 09:18:34 +00:00
|
|
|
}
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2008-03-18 17:38:32 +00:00
|
|
|
$html .= Xml::closeElement( 'div' );
|
2006-08-24 17:12:13 +00:00
|
|
|
$html .= "\n\t\t";
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2006-08-24 17:12:13 +00:00
|
|
|
return $html;
|
2006-07-26 17:12:30 +00:00
|
|
|
}
|
2006-08-24 17:12:13 +00:00
|
|
|
|
|
|
|
/**
|
2012-02-09 01:23:31 +00:00
|
|
|
* Returns a string with an HTML representation of the children of the given category.
|
|
|
|
* @param $title Title
|
|
|
|
* @param $depth int
|
|
|
|
* @return string
|
|
|
|
*/
|
2010-06-02 09:10:09 +00:00
|
|
|
function renderChildren( $title, $depth = 1 ) {
|
2008-07-02 09:49:28 +00:00
|
|
|
global $wgCategoryTreeMaxChildren, $wgCategoryTreeUseCategoryTable;
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2010-04-20 22:00:34 +00:00
|
|
|
if ( $title->getNamespace() != NS_CATEGORY ) {
|
2007-09-18 15:52:30 +00:00
|
|
|
// Non-categories can't have children. :)
|
|
|
|
return '';
|
|
|
|
}
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2010-02-13 23:03:40 +00:00
|
|
|
$dbr = wfGetDB( DB_SLAVE );
|
2006-08-24 17:12:13 +00:00
|
|
|
|
2008-07-02 09:49:28 +00:00
|
|
|
$inverse = $this->isInverse();
|
2010-04-20 22:00:34 +00:00
|
|
|
$mode = $this->getOption( 'mode' );
|
|
|
|
$namespaces = $this->getOption( 'namespaces' );
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2010-08-13 22:38:00 +00:00
|
|
|
$tables = array( 'page', 'categorylinks' );
|
2010-08-16 21:37:26 +00:00
|
|
|
$fields = array( 'page_id', 'page_namespace', 'page_title',
|
|
|
|
'page_is_redirect', 'page_len', 'page_latest', 'cl_to',
|
|
|
|
'cl_from' );
|
2010-08-13 22:38:00 +00:00
|
|
|
$where = array();
|
|
|
|
$joins = array();
|
2011-01-20 18:55:06 +00:00
|
|
|
$options = array( 'ORDER BY' => 'cl_type, cl_sortkey', 'LIMIT' => $wgCategoryTreeMaxChildren );
|
2010-08-13 22:38:00 +00:00
|
|
|
|
2008-07-02 09:49:28 +00:00
|
|
|
if ( $inverse ) {
|
2011-01-20 19:37:22 +00:00
|
|
|
$joins['categorylinks'] = array( 'RIGHT JOIN', array( 'cl_to = page_title', 'page_namespace' => NS_CATEGORY ) );
|
2012-03-11 19:04:37 +00:00
|
|
|
$where['cl_from'] = $title->getArticleID();
|
2010-08-13 22:38:00 +00:00
|
|
|
} else {
|
|
|
|
$joins['categorylinks'] = array( 'JOIN', 'cl_from = page_id' );
|
|
|
|
$where['cl_to'] = $title->getDBkey();
|
2011-01-20 18:55:06 +00:00
|
|
|
$options['USE INDEX']['categorylinks'] = 'cl_sortkey';
|
2008-07-02 09:49:28 +00:00
|
|
|
|
2010-04-20 22:00:34 +00:00
|
|
|
# namespace filter.
|
2008-07-02 09:49:28 +00:00
|
|
|
if ( $namespaces ) {
|
2010-04-20 22:00:34 +00:00
|
|
|
# NOTE: we assume that the $namespaces array contains only integers! decodeNamepsaces makes it so.
|
2010-08-13 22:38:00 +00:00
|
|
|
$where['page_namespace'] = $namespaces;
|
|
|
|
} elseif ( $mode != CT_MODE_ALL ) {
|
|
|
|
if ( $mode == CT_MODE_PAGES ) {
|
Adapt CategoryTree to the new schema
This should obsolete $wgCategoryTreeMaxScanRows, added in r67179, so I
removed it. Note that I only tested with very basic usage, since I
don't quite understand all the complicated things this extension can do,
and some code paths are certainly going to remain inefficient, since
arbitrary namespace filtering seems possible here (at least
renderChildren() has support for it). However, clicking the little plus
sign on category pages should now scan only as many rows as are actually
used, so no limit should be necessary.
Sorting is now by cl_type, cl_sortkey instead of cl_sortkey. This
change has to be made to all users for efficiency, since the old index
was dropped. It means the sort order might be somewhat unexpected in
some cases, but for basic CategoryTree use it makes no difference, since
all the results have cl_type = 'subcat' anyway.
Fixes bug 23682, I think.
2010-08-16 21:57:49 +00:00
|
|
|
$where['cl_type'] = array( 'page', 'subcat' );
|
2010-04-20 22:00:34 +00:00
|
|
|
} else {
|
Adapt CategoryTree to the new schema
This should obsolete $wgCategoryTreeMaxScanRows, added in r67179, so I
removed it. Note that I only tested with very basic usage, since I
don't quite understand all the complicated things this extension can do,
and some code paths are certainly going to remain inefficient, since
arbitrary namespace filtering seems possible here (at least
renderChildren() has support for it). However, clicking the little plus
sign on category pages should now scan only as many rows as are actually
used, so no limit should be necessary.
Sorting is now by cl_type, cl_sortkey instead of cl_sortkey. This
change has to be made to all users for efficiency, since the old index
was dropped. It means the sort order might be somewhat unexpected in
some cases, but for basic CategoryTree use it makes no difference, since
all the results have cl_type = 'subcat' anyway.
Fixes bug 23682, I think.
2010-08-16 21:57:49 +00:00
|
|
|
$where['cl_type'] = 'subcat';
|
2010-04-20 22:00:34 +00:00
|
|
|
}
|
2008-07-02 09:49:28 +00:00
|
|
|
}
|
2008-07-01 21:40:42 +00:00
|
|
|
}
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2008-06-30 14:09:47 +00:00
|
|
|
# fetch member count if possible
|
2008-07-02 09:49:28 +00:00
|
|
|
$doCount = !$inverse && $wgCategoryTreeUseCategoryTable;
|
2008-06-30 14:09:47 +00:00
|
|
|
|
|
|
|
if ( $doCount ) {
|
2010-08-13 22:38:00 +00:00
|
|
|
$tables = array_merge( $tables, array( 'category' ) );
|
|
|
|
$fields = array_merge( $fields, array( 'cat_id', 'cat_title', 'cat_subcats', 'cat_pages', 'cat_files' ) );
|
2011-01-20 19:37:22 +00:00
|
|
|
$joins['category'] = array( 'LEFT JOIN', array( 'cat_title = page_title', 'page_namespace' => NS_CATEGORY ) );
|
2008-06-30 14:09:47 +00:00
|
|
|
}
|
|
|
|
|
2011-01-20 18:55:06 +00:00
|
|
|
$res = $dbr->select( $tables, $fields, $where, __METHOD__, $options, $joins );
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2010-04-20 22:00:34 +00:00
|
|
|
# collect categories separately from other pages
|
|
|
|
$categories = '';
|
|
|
|
$other = '';
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2010-08-13 22:38:00 +00:00
|
|
|
foreach ( $res as $row ) {
|
2010-04-20 22:00:34 +00:00
|
|
|
# NOTE: in inverse mode, the page record may be null, because we use a right join.
|
2008-07-02 20:19:54 +00:00
|
|
|
# happens for categories with no category page (red cat links)
|
2010-01-06 21:24:10 +00:00
|
|
|
if ( $inverse && $row->page_title === null ) {
|
2008-07-02 20:19:54 +00:00
|
|
|
$t = Title::makeTitle( NS_CATEGORY, $row->cl_to );
|
2010-04-20 22:00:34 +00:00
|
|
|
} else {
|
|
|
|
# TODO: translation support; ideally added to Title object
|
2008-07-02 20:19:54 +00:00
|
|
|
$t = Title::newFromRow( $row );
|
|
|
|
}
|
2007-09-01 11:26:00 +00:00
|
|
|
|
2010-01-06 21:24:10 +00:00
|
|
|
$cat = null;
|
2008-06-30 14:09:47 +00:00
|
|
|
|
|
|
|
if ( $doCount && $row->page_namespace == NS_CATEGORY ) {
|
|
|
|
$cat = Category::newFromRow( $row, $t );
|
|
|
|
}
|
|
|
|
|
2014-02-19 13:10:24 +00:00
|
|
|
$s = $this->renderNodeInfo( $t, $cat, $depth - 1 );
|
2007-09-01 11:26:00 +00:00
|
|
|
$s .= "\n\t\t";
|
|
|
|
|
2010-04-20 22:00:34 +00:00
|
|
|
if ( $row->page_namespace == NS_CATEGORY ) {
|
|
|
|
$categories .= $s;
|
|
|
|
} else {
|
|
|
|
$other .= $s;
|
|
|
|
}
|
2006-08-24 17:12:13 +00:00
|
|
|
}
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2007-09-01 11:26:00 +00:00
|
|
|
return $categories . $other;
|
2006-08-24 17:12:13 +00:00
|
|
|
}
|
2006-07-26 17:12:30 +00:00
|
|
|
|
2006-08-24 17:12:13 +00:00
|
|
|
/**
|
2012-02-09 01:23:31 +00:00
|
|
|
* Returns a string with an HTML representation of the parents of the given category.
|
|
|
|
* @param $title Title
|
|
|
|
* @return string
|
|
|
|
*/
|
2010-06-02 09:10:09 +00:00
|
|
|
function renderParents( $title ) {
|
2006-08-24 17:12:13 +00:00
|
|
|
global $wgCategoryTreeMaxChildren;
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2010-02-13 23:03:40 +00:00
|
|
|
$dbr = wfGetDB( DB_SLAVE );
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2012-11-21 08:31:37 +00:00
|
|
|
$res = $dbr->select(
|
|
|
|
'categorylinks',
|
|
|
|
array(
|
2012-12-19 13:59:55 +00:00
|
|
|
'page_namespace' => NS_CATEGORY,
|
|
|
|
'page_title' => 'cl_to',
|
2012-11-21 08:31:37 +00:00
|
|
|
),
|
|
|
|
array( 'cl_from' => $title->getArticleID() ),
|
|
|
|
__METHOD__,
|
|
|
|
array(
|
|
|
|
'LIMIT' => $wgCategoryTreeMaxChildren,
|
|
|
|
'ORDER BY' => 'cl_to'
|
|
|
|
)
|
|
|
|
);
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2010-06-08 19:30:48 +00:00
|
|
|
$special = SpecialPage::getTitleFor( 'CategoryTree' );
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2010-04-20 22:00:34 +00:00
|
|
|
$s = '';
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2010-10-29 21:30:20 +00:00
|
|
|
foreach ( $res as $row ) {
|
2010-04-20 22:00:34 +00:00
|
|
|
# TODO: translation support; ideally added to Title object
|
2008-06-30 14:09:47 +00:00
|
|
|
$t = Title::newFromRow( $row );
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2010-04-20 22:00:34 +00:00
|
|
|
# $trans = $title->getLocalizedText();
|
|
|
|
$trans = ''; # place holder for when translated titles are available
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2006-08-24 17:12:13 +00:00
|
|
|
$label = htmlspecialchars( $t->getText() );
|
2012-11-21 08:31:37 +00:00
|
|
|
if ( $trans && $trans != $label ) {
|
|
|
|
$label .= ' ' . Xml::element( 'i', array( 'class' => 'translation' ), $trans );
|
|
|
|
}
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2012-11-21 08:31:37 +00:00
|
|
|
$wikiLink = $special->getLocalURL( 'target=' . $t->getPartialURL() .
|
|
|
|
'&' . $this->getOptionsAsUrlParameters() );
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2012-08-28 22:35:39 +00:00
|
|
|
if ( $s !== '' ) {
|
|
|
|
$s .= wfMessage( 'pipe-separator' )->escaped();
|
|
|
|
}
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2008-03-18 17:38:32 +00:00
|
|
|
$s .= Xml::openElement( 'span', array( 'class' => 'CategoryTreeItem' ) );
|
2012-11-21 08:31:37 +00:00
|
|
|
$s .= Xml::openElement( 'a', array( 'class' => 'CategoryTreeLabel', 'href' => $wikiLink ) )
|
|
|
|
. $label . Xml::closeElement( 'a' );
|
2008-03-18 17:38:32 +00:00
|
|
|
$s .= Xml::closeElement( 'span' );
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2006-08-24 17:12:13 +00:00
|
|
|
$s .= "\n\t\t";
|
|
|
|
}
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2006-08-24 17:12:13 +00:00
|
|
|
return $s;
|
|
|
|
}
|
2006-07-26 17:12:30 +00:00
|
|
|
|
2006-08-24 17:12:13 +00:00
|
|
|
/**
|
2012-02-09 01:23:31 +00:00
|
|
|
* Returns a string with a HTML represenation of the given page.
|
|
|
|
* @param $title Title
|
|
|
|
* @param int $children
|
|
|
|
* @return string
|
|
|
|
*/
|
2014-02-19 13:10:24 +00:00
|
|
|
function renderNode( $title, $children = 0 ) {
|
2008-07-02 09:49:28 +00:00
|
|
|
global $wgCategoryTreeUseCategoryTable;
|
|
|
|
|
|
|
|
if ( $wgCategoryTreeUseCategoryTable && $title->getNamespace() == NS_CATEGORY && !$this->isInverse() ) {
|
|
|
|
$cat = Category::newFromTitle( $title );
|
2010-04-20 22:00:34 +00:00
|
|
|
} else {
|
|
|
|
$cat = null;
|
2008-07-02 09:49:28 +00:00
|
|
|
}
|
2008-06-30 14:09:47 +00:00
|
|
|
|
2014-02-19 13:10:24 +00:00
|
|
|
return $this->renderNodeInfo( $title, $cat, $children );
|
2008-06-30 14:09:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2012-02-09 01:23:31 +00:00
|
|
|
* Returns a string with a HTML represenation of the given page.
|
|
|
|
* $info must be an associative array, containing at least a Title object under the 'title' key.
|
|
|
|
* @param $title Title
|
|
|
|
* @param $cat Category
|
|
|
|
* @param $children int
|
|
|
|
* @return string
|
|
|
|
*/
|
2014-02-19 13:10:24 +00:00
|
|
|
function renderNodeInfo( $title, $cat, $children = 0 ) {
|
2010-04-20 22:00:34 +00:00
|
|
|
$mode = $this->getOption( 'mode' );
|
2006-08-24 17:12:13 +00:00
|
|
|
$load = false;
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2006-08-24 17:12:13 +00:00
|
|
|
$ns = $title->getNamespace();
|
2008-01-14 10:09:08 +00:00
|
|
|
$key = $title->getDBkey();
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2010-04-20 22:00:34 +00:00
|
|
|
# $trans = $title->getLocalizedText();
|
|
|
|
$trans = ''; # place holder for when translated titles are available
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2010-04-20 22:00:34 +00:00
|
|
|
$hideprefix = $this->getOption( 'hideprefix' );
|
2008-06-30 21:22:03 +00:00
|
|
|
|
2010-04-20 22:00:34 +00:00
|
|
|
if ( $hideprefix == CT_HIDEPREFIX_ALWAYS ) {
|
|
|
|
$hideprefix = true;
|
2011-06-17 16:25:46 +00:00
|
|
|
} elseif ( $hideprefix == CT_HIDEPREFIX_AUTO ) {
|
2010-04-20 22:00:34 +00:00
|
|
|
$hideprefix = ( $mode == CT_MODE_CATEGORIES );
|
2011-06-17 16:25:46 +00:00
|
|
|
} elseif ( $hideprefix == CT_HIDEPREFIX_CATEGORIES ) {
|
2010-04-20 22:00:34 +00:00
|
|
|
$hideprefix = ( $ns == NS_CATEGORY );
|
|
|
|
} else {
|
|
|
|
$hideprefix = true;
|
|
|
|
}
|
2008-06-30 21:22:03 +00:00
|
|
|
|
2010-04-20 22:00:34 +00:00
|
|
|
# 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 ( $hideprefix ) {
|
|
|
|
$label = htmlspecialchars( $title->getText() );
|
|
|
|
} else {
|
|
|
|
$label = htmlspecialchars( $title->getPrefixedText() );
|
|
|
|
}
|
2009-05-27 06:17:56 +00:00
|
|
|
|
2010-04-20 22:00:34 +00:00
|
|
|
if ( $trans && $trans != $label ) {
|
|
|
|
$label .= ' ' . Xml::element( 'i', array( 'class' => 'translation' ), $trans );
|
|
|
|
}
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2006-08-24 17:12:13 +00:00
|
|
|
$labelClass = 'CategoryTreeLabel ' . ' CategoryTreeLabelNs' . $ns;
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2012-03-11 19:04:37 +00:00
|
|
|
if ( !$title->getArticleID() ) {
|
2008-07-02 20:19:54 +00:00
|
|
|
$labelClass .= ' new';
|
|
|
|
$wikiLink = $title->getLocalURL( 'action=edit&redlink=1' );
|
|
|
|
} else {
|
|
|
|
$wikiLink = $title->getLocalURL();
|
|
|
|
}
|
|
|
|
|
2006-08-24 17:12:13 +00:00
|
|
|
if ( $ns == NS_CATEGORY ) {
|
|
|
|
$labelClass .= ' CategoryTreeLabelCategory';
|
|
|
|
} else {
|
|
|
|
$labelClass .= ' CategoryTreeLabelPage';
|
|
|
|
}
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2010-04-20 22:00:34 +00:00
|
|
|
if ( ( $ns % 2 ) > 0 ) {
|
|
|
|
$labelClass .= ' CategoryTreeLabelTalk';
|
|
|
|
}
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2008-06-30 14:09:47 +00:00
|
|
|
$count = false;
|
2006-08-24 17:12:13 +00:00
|
|
|
$s = '';
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2010-04-20 22:00:34 +00:00
|
|
|
# NOTE: things in CategoryTree.js rely on the exact order of tags!
|
2006-08-24 17:12:13 +00:00
|
|
|
# Specifically, the CategoryTreeChildren div must be the first
|
|
|
|
# sibling with nodeName = DIV of the grandparent of the expland link.
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2008-03-18 17:38:32 +00:00
|
|
|
$s .= Xml::openElement( 'div', array( 'class' => 'CategoryTreeSection' ) );
|
|
|
|
$s .= Xml::openElement( 'div', array( 'class' => 'CategoryTreeItem' ) );
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2008-06-28 20:13:20 +00:00
|
|
|
$attr = array( 'class' => 'CategoryTreeBullet' );
|
|
|
|
|
2010-06-01 08:58:04 +00:00
|
|
|
# Get counts, with conversion to integer so === works
|
2011-11-16 12:35:40 +00:00
|
|
|
# Note: $allCount is the total number of cat members,
|
2011-11-15 23:35:52 +00:00
|
|
|
# not the count of how many members are normal pages.
|
2011-11-16 12:35:40 +00:00
|
|
|
$allCount = $cat ? intval( $cat->getPageCount() ) : 0;
|
2010-06-01 14:28:08 +00:00
|
|
|
$subcatCount = $cat ? intval( $cat->getSubcatCount() ) : 0;
|
|
|
|
$fileCount = $cat ? intval( $cat->getFileCount() ) : 0;
|
2010-06-01 08:58:04 +00:00
|
|
|
|
2006-08-24 17:12:13 +00:00
|
|
|
if ( $ns == NS_CATEGORY ) {
|
2010-06-01 08:58:04 +00:00
|
|
|
|
2008-06-30 14:09:47 +00:00
|
|
|
if ( $cat ) {
|
2010-04-20 22:00:34 +00:00
|
|
|
if ( $mode == CT_MODE_CATEGORIES ) {
|
2010-06-01 08:58:04 +00:00
|
|
|
$count = $subcatCount;
|
2011-06-17 16:25:46 +00:00
|
|
|
} elseif ( $mode == CT_MODE_PAGES ) {
|
2011-11-16 12:35:40 +00:00
|
|
|
$count = $allCount - $fileCount;
|
2010-04-20 22:00:34 +00:00
|
|
|
} else {
|
2011-11-16 12:35:40 +00:00
|
|
|
$count = $allCount;
|
2010-04-20 22:00:34 +00:00
|
|
|
}
|
|
|
|
}
|
Adapt CategoryTree to the new schema
This should obsolete $wgCategoryTreeMaxScanRows, added in r67179, so I
removed it. Note that I only tested with very basic usage, since I
don't quite understand all the complicated things this extension can do,
and some code paths are certainly going to remain inefficient, since
arbitrary namespace filtering seems possible here (at least
renderChildren() has support for it). However, clicking the little plus
sign on category pages should now scan only as many rows as are actually
used, so no limit should be necessary.
Sorting is now by cl_type, cl_sortkey instead of cl_sortkey. This
change has to be made to all users for efficiency, since the old index
was dropped. It means the sort order might be somewhat unexpected in
some cases, but for basic CategoryTree use it makes no difference, since
all the results have cl_type = 'subcat' anyway.
Fixes bug 23682, I think.
2010-08-16 21:57:49 +00:00
|
|
|
if ( $count === 0 ) {
|
2012-08-28 22:35:39 +00:00
|
|
|
$bullet = wfMessage( 'categorytree-empty-bullet' )->plain() . ' ';
|
2010-05-28 02:15:44 +00:00
|
|
|
$attr['class'] = 'CategoryTreeEmptyBullet';
|
|
|
|
} else {
|
|
|
|
$linkattr = array( );
|
|
|
|
if ( $load ) {
|
|
|
|
$linkattr[ 'id' ] = $load;
|
|
|
|
}
|
2010-04-20 22:00:34 +00:00
|
|
|
|
2010-05-28 02:15:44 +00:00
|
|
|
$linkattr[ 'class' ] = "CategoryTreeToggle";
|
|
|
|
$linkattr['style'] = 'display: none;'; // Unhidden by JS
|
2011-09-30 21:08:28 +00:00
|
|
|
$linkattr['data-ct-title'] = $key;
|
2008-06-28 20:13:20 +00:00
|
|
|
|
2014-03-30 19:31:50 +00:00
|
|
|
$tag = 'span';
|
2014-02-19 13:10:24 +00:00
|
|
|
if ( $children == 0 ) {
|
2012-08-28 22:35:39 +00:00
|
|
|
$txt = wfMessage( 'categorytree-expand-bullet' )->plain();
|
2010-05-28 02:15:44 +00:00
|
|
|
# Don't load this message for ajax requests, so that we don't have to initialise $wgLang
|
2012-08-28 22:35:39 +00:00
|
|
|
$linkattr[ 'title' ] = $this->mIsAjaxRequest ? '##LOAD##' : wfMessage( 'categorytree-expand' )->plain();
|
2011-09-30 21:08:28 +00:00
|
|
|
$linkattr[ 'data-ct-state' ] = 'collapsed';
|
2010-05-28 02:15:44 +00:00
|
|
|
} else {
|
2012-08-28 22:35:39 +00:00
|
|
|
$txt = wfMessage( 'categorytree-collapse-bullet' )->plain();
|
|
|
|
$linkattr[ 'title' ] = wfMessage( 'categorytree-collapse' )->plain();
|
2011-09-30 21:08:28 +00:00
|
|
|
$linkattr[ 'data-ct-loaded' ] = true;
|
|
|
|
$linkattr[ 'data-ct-state' ] = 'expanded';
|
2010-04-20 22:00:34 +00:00
|
|
|
}
|
2008-06-28 20:13:20 +00:00
|
|
|
|
2010-05-28 02:15:44 +00:00
|
|
|
$bullet = Xml::openElement( $tag, $linkattr ) . $txt . Xml::closeElement( $tag ) . ' ';
|
2010-04-20 22:00:34 +00:00
|
|
|
}
|
2006-08-24 17:12:13 +00:00
|
|
|
} else {
|
2012-08-28 22:35:39 +00:00
|
|
|
$bullet = wfMessage( 'categorytree-page-bullet' )->plain();
|
2006-08-24 17:12:13 +00:00
|
|
|
}
|
2010-06-01 08:58:04 +00:00
|
|
|
$s .= Xml::tags( 'span', $attr, $bullet ) . ' ';
|
2008-06-28 20:13:20 +00:00
|
|
|
|
2012-11-21 08:31:37 +00:00
|
|
|
$s .= Xml::openElement( 'a', array( 'class' => $labelClass, 'href' => $wikiLink ) )
|
|
|
|
. $label . Xml::closeElement( 'a' );
|
2008-06-30 14:09:47 +00:00
|
|
|
|
|
|
|
if ( $count !== false && $this->getOption( 'showcount' ) ) {
|
2011-11-16 12:35:40 +00:00
|
|
|
$pages = $allCount - $subcatCount - $fileCount;
|
2008-06-30 14:09:47 +00:00
|
|
|
|
2011-10-17 11:35:57 +00:00
|
|
|
global $wgContLang, $wgLang;
|
2008-06-30 14:09:47 +00:00
|
|
|
$attr = array(
|
2012-08-28 22:35:39 +00:00
|
|
|
'title' => wfMessage( 'categorytree-member-counts' )
|
|
|
|
->numParams( $subcatCount, $pages , $fileCount, $allCount, $count )->text(),
|
2011-10-17 11:35:57 +00:00
|
|
|
'dir' => $wgLang->getDir() # numbers and commas get messed up in a mixed dir env
|
2008-06-30 14:09:47 +00:00
|
|
|
);
|
|
|
|
|
2011-06-24 19:53:15 +00:00
|
|
|
$s .= $wgContLang->getDirMark() . ' ';
|
2011-10-17 11:35:57 +00:00
|
|
|
|
|
|
|
# Create a list of category members with only non-zero member counts
|
|
|
|
$memberNums = array();
|
|
|
|
if ( $subcatCount ) {
|
2012-08-28 22:35:39 +00:00
|
|
|
$memberNums[] = wfMessage( 'categorytree-num-categories' )
|
|
|
|
->numParams( $subcatCount )->text();
|
2011-10-17 11:35:57 +00:00
|
|
|
}
|
2011-11-16 12:35:40 +00:00
|
|
|
if ( $pages ) {
|
2012-08-28 22:35:39 +00:00
|
|
|
$memberNums[] = wfMessage( 'categorytree-num-pages' )->numParams( $pages )->text();
|
2011-10-17 11:35:57 +00:00
|
|
|
}
|
|
|
|
if ( $fileCount ) {
|
2012-08-28 22:35:39 +00:00
|
|
|
$memberNums[] = wfMessage( 'categorytree-num-files' )
|
|
|
|
->numParams( $fileCount )->text();
|
2011-10-17 11:35:57 +00:00
|
|
|
}
|
|
|
|
$memberNumsShort = $memberNums
|
|
|
|
? $wgLang->commaList( $memberNums )
|
|
|
|
: wfMessage( 'categorytree-num-empty' )->text();
|
|
|
|
|
|
|
|
# Only $5 is actually used in the default message.
|
|
|
|
# Other arguments can be used in a customized message.
|
2012-08-28 22:35:39 +00:00
|
|
|
$s .= Xml::tags(
|
|
|
|
'span',
|
|
|
|
$attr,
|
|
|
|
wfMessage( 'categorytree-member-num' )
|
2012-09-11 22:30:39 +00:00
|
|
|
// Do not use numParams on params 1-4, as they are only used for customisation.
|
2012-09-19 19:25:42 +00:00
|
|
|
->params( $subcatCount, $pages, $fileCount, $allCount, $memberNumsShort )
|
2012-09-11 22:30:39 +00:00
|
|
|
->escaped()
|
2012-08-28 22:35:39 +00:00
|
|
|
);
|
2008-06-30 14:09:47 +00:00
|
|
|
}
|
|
|
|
|
2008-03-18 17:38:32 +00:00
|
|
|
$s .= Xml::closeElement( 'div' );
|
2006-08-24 17:12:13 +00:00
|
|
|
$s .= "\n\t\t";
|
2012-08-28 22:35:39 +00:00
|
|
|
$s .= Xml::openElement(
|
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'class' => 'CategoryTreeChildren',
|
|
|
|
'style' => $children > 0 ? "display:block" : "display:none"
|
|
|
|
)
|
|
|
|
);
|
2010-04-20 22:00:34 +00:00
|
|
|
|
2014-02-19 13:10:24 +00:00
|
|
|
if ( $ns == NS_CATEGORY && $children > 0 ) {
|
2008-07-01 21:40:42 +00:00
|
|
|
$children = $this->renderChildren( $title, $children );
|
|
|
|
if ( $children == '' ) {
|
|
|
|
$s .= Xml::openElement( 'i', array( 'class' => 'CategoryTreeNotice' ) );
|
2010-04-20 22:00:34 +00:00
|
|
|
if ( $mode == CT_MODE_CATEGORIES ) {
|
2012-08-28 22:35:39 +00:00
|
|
|
$s .= wfMessage( 'categorytree-no-subcategories' )->text();
|
2011-06-17 16:25:46 +00:00
|
|
|
} elseif ( $mode == CT_MODE_PAGES ) {
|
2012-08-28 22:35:39 +00:00
|
|
|
$s .= wfMessage( 'categorytree-no-pages' )->text();
|
2011-06-17 16:25:46 +00:00
|
|
|
} elseif ( $mode == CT_MODE_PARENTS ) {
|
2012-08-28 22:35:39 +00:00
|
|
|
$s .= wfMessage( 'categorytree-no-parent-categories' )->text();
|
2010-04-20 22:00:34 +00:00
|
|
|
} else {
|
2012-08-28 22:35:39 +00:00
|
|
|
$s .= wfMessage( 'categorytree-nothing-found' )->text();
|
2010-04-20 22:00:34 +00:00
|
|
|
}
|
2008-07-01 21:40:42 +00:00
|
|
|
$s .= Xml::closeElement( 'i' );
|
|
|
|
} else {
|
|
|
|
$s .= $children;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-18 17:38:32 +00:00
|
|
|
$s .= Xml::closeElement( 'div' );
|
|
|
|
$s .= Xml::closeElement( 'div' );
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2006-08-24 17:12:13 +00:00
|
|
|
if ( $load ) {
|
|
|
|
$s .= "\n\t\t";
|
2008-03-18 17:38:32 +00:00
|
|
|
$s .= Xml::openElement( 'script', array( 'type' => 'text/javascript' ) );
|
2012-11-21 08:31:37 +00:00
|
|
|
$s .= 'categoryTreeExpandNode("' . Xml::escapeJsString( $key ) . '", '
|
|
|
|
. $this->getOptionsAsJsStructure( $children ) . ', document.getElementById("' . $load . '"));';
|
2008-03-18 17:38:32 +00:00
|
|
|
$s .= Xml::closeElement( 'script' );
|
2006-08-24 17:12:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
$s .= "\n\t\t";
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2006-08-24 17:12:13 +00:00
|
|
|
return $s;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2012-02-09 01:23:31 +00:00
|
|
|
* Creates a Title object from a user provided (and thus unsafe) string
|
|
|
|
* @param $title string
|
|
|
|
* @return null|Title
|
|
|
|
*/
|
2006-08-24 17:12:13 +00:00
|
|
|
static function makeTitle( $title ) {
|
2010-04-20 22:00:34 +00:00
|
|
|
$title = trim( $title );
|
2008-02-04 09:22:12 +00:00
|
|
|
|
2012-12-13 20:36:50 +00:00
|
|
|
if ( strval( $title ) === '' ) {
|
2010-01-06 21:24:10 +00:00
|
|
|
return null;
|
2006-08-24 17:12:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
# The title must be in the category namespace
|
|
|
|
# Ignore a leading Category: if there is one
|
|
|
|
$t = Title::newFromText( $title, NS_CATEGORY );
|
2014-01-23 16:20:15 +00:00
|
|
|
if ( !$t || $t->getNamespace() != NS_CATEGORY || $t->getInterwiki() != '' ) {
|
2011-02-13 22:19:18 +00:00
|
|
|
// If we were given something like "Wikipedia:Foo" or "Template:",
|
|
|
|
// try it again but forced.
|
2006-08-24 17:12:13 +00:00
|
|
|
$title = "Category:$title";
|
|
|
|
$t = Title::newFromText( $title );
|
|
|
|
}
|
|
|
|
return $t;
|
|
|
|
}
|
|
|
|
}
|