diff --git a/CategoryPageSubclass.php b/CategoryPageSubclass.php index acc5c068..37228d6e 100644 --- a/CategoryPageSubclass.php +++ b/CategoryPageSubclass.php @@ -12,7 +12,7 @@ class CategoryTreeCategoryPage extends CategoryPage { } class CategoryTreeCategoryViewer extends CategoryViewer { - var $child_titles; + var $child_cats; function getCategoryTree() { global $wgOut, $wgCategoryTreeCategoryPageOptions; @@ -29,25 +29,29 @@ class CategoryTreeCategoryViewer extends CategoryViewer { /** * Add a subcategory to the internal lists */ - function addSubcategory( $title, $sortkey, $pageLength ) { + function addSubcategoryObject( $cat, $sortkey, $pageLength ) { global $wgContLang, $wgOut, $wgRequest; + $title = $cat->getTitle(); + if ( $wgRequest->getCheck( 'notree' ) ) { - return parent::addSubcategory( $title, $sortkey, $pageLength ); + return parent::addSubcategoryObject( $cat, $sortkey, $pageLength ); } - if ( ! $GLOBALS['wgCategoryTreeUnifiedView'] ) { - $this->child_titles[] = $title; - return parent::addSubcategory( $title, $sortkey, $pageLength ); - } + /*if ( ! $GLOBALS['wgCategoryTreeUnifiedView'] ) { + $this->child_cats[] = $cat; + return parent::addSubcategory( $cat, $sortkey, $pageLength ); + }*/ $tree = $this->getCategoryTree(); - $this->children[] = $tree->renderNode( $title ); + $this->children[] = $tree->renderNodeInfo( $title, $cat ); $this->children_start_char[] = $this->getSubcategorySortChar( $title, $sortkey ); } + /* + # this is a pain to keep this consistent, and no one should be using wgCategoryTreeUnifiedView = false anyway. function getSubcategorySection() { global $wgOut, $wgRequest, $wgCookiePrefix; @@ -107,14 +111,14 @@ class CategoryTreeCategoryViewer extends CategoryViewer { if ( $showAs == 'list' ) { $r .= $this->formatList( $this->children, $this->children_start_char ); } else { - $ct = getCategoryTree(); + $ct = $this->getCategoryTree(); - foreach ( $this->child_titles as $title ) { - $r .= $ct->renderNode( $title ); + foreach ( $this->child_cats as $cat ) { + $r .= $ct->renderNodeInfo( $cat->getTitle(), $cat ); } } return $r; - } + }*/ function makeShowAsLink( $targetValue, $currentValue ) { $msg = htmlspecialchars( CategoryTree::msg( "show-$targetValue" ) ); @@ -127,13 +131,13 @@ class CategoryTreeCategoryViewer extends CategoryViewer { } function clearCategoryState() { - $this->child_titles = array(); + $this->child_cats = array(); parent::clearCategoryState(); } function finaliseCategoryState() { if( $this->flip ) { - $this->child_titles = array_reverse( $this->child_titles ); + $this->child_cats = array_reverse( $this->child_cats ); } parent::finaliseCategoryState(); } diff --git a/CategoryTree.i18n.php b/CategoryTree.i18n.php index 46db931c..bcdd019f 100644 --- a/CategoryTree.i18n.php +++ b/CategoryTree.i18n.php @@ -34,8 +34,11 @@ If you have a very old browser, or have JavaScript disabled, it will not work.', 'categorytree-expand' => 'expand', 'categorytree-collapse-bullet' => '[]', # do not translate or duplicate this message to other languages 'categorytree-expand-bullet' => '[+]', # do not translate or duplicate this message to other languages + 'categorytree-empty-bullet' => '[×]', # do not translate or duplicate this message to other languages 'categorytree-page-bullet' => ' ', # do not translate or duplicate this message to other languages + 'categorytree-member-counts' => 'contains $1 subcategories, $2 pages, and $3 files', + 'categorytree-load' => 'load', 'categorytree-loading' => 'loading…', 'categorytree-nothing-found' => 'nothing found', @@ -596,6 +599,7 @@ Diese Seite benötigt bestimmte JavaScript-Funktionen (Ajax) und funktioniert m 'categorytree-category' => 'Kategorie:', 'categorytree-go' => 'Laden', 'categorytree-parents' => 'Oberkategorien', + 'categorytree-member-counts' => 'enthält $1 Unterkategorien, $2 Seiten und $3 Dateien', 'categorytree-mode-categories' => 'nur Kategorien', 'categorytree-mode-pages' => 'Seiten außer Bilder', 'categorytree-mode-all' => 'alle Seiten', diff --git a/CategoryTree.php b/CategoryTree.php index ea9c4e39..3edcd390 100644 --- a/CategoryTree.php +++ b/CategoryTree.php @@ -32,12 +32,13 @@ define('CT_MODE_ALL', 20); * This way, the cache does not need to be disabled. Default is false. * $wgCategoryTreeDisableCache - disabled the parser cache for pages with a tag. Default is true. * $wgCategoryTreeUseCache - enable HTTP cache for anon users. Default is false. - * $wgCategoryTreeUnifiedView - use unified view on category pages, instead of "tree" or "traditional list". Default is true. - * $wgCategoryTreeOmitNamespace - never show namespace prefix. Default is false * $wgCategoryTreeMaxDepth - maximum value for depth argument; An array that maps mode values to * 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 tag. + * $wgCategoryTreeCategoryPageOptions - options to apply on category pages. + * $wgCategoryTreeSpecialPageOptions - options to apply on Special:CategoryTree. */ $wgCategoryTreeMaxChildren = 200; @@ -45,7 +46,7 @@ $wgCategoryTreeAllowTag = true; $wgCategoryTreeDisableCache = true; $wgCategoryTreeDynamicTag = false; $wgCategoryTreeHTTPCache = false; -$wgCategoryTreeUnifiedView = true; +#$wgCategoryTreeUnifiedView = true; $wgCategoryTreeMaxDepth = array(CT_MODE_PAGES => 1, CT_MODE_ALL => 1, CT_MODE_CATEGORIES => 2); $wgCategoryTreeExtPath = '/extensions/CategoryTree'; @@ -56,11 +57,16 @@ $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 diff --git a/CategoryTreeFunctions.php b/CategoryTreeFunctions.php index 7b26e9a2..a69ff1cc 100644 --- a/CategoryTreeFunctions.php +++ b/CategoryTreeFunctions.php @@ -32,6 +32,7 @@ class CategoryTree { $this->mOptions['mode'] = self::decodeMode( $this->mOptions['mode'] ); $this->mOptions['hideprefix'] = self::decodeBoolean( $this->mOptions['hideprefix'] ); + $this->mOptions['showcount'] = self::decodeBoolean( $this->mOptions['showcount'] ); } function getOption( $name ) { @@ -66,7 +67,7 @@ class CategoryTree { if ( is_int( $value ) ) return ( $value > 0 ); $value = trim( strtolower( $value ) ); - if ( is_numeric( $value ) ) return ( (int)$mode > 0 ); + if ( is_numeric( $value ) ) return ( (int)$value > 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; @@ -306,7 +307,7 @@ class CategoryTree { * $title must be a Title object */ function renderChildren( &$title, $depth=1 ) { - global $wgCategoryTreeMaxChildren; + global $wgCategoryTreeMaxChildren, $wgVersion; if( $title->getNamespace() != NS_CATEGORY ) { // Non-categories can't have children. :) @@ -315,10 +316,6 @@ class CategoryTree { $dbr =& wfGetDB( DB_SLAVE ); - #additional stuff to be used if "transaltion" by interwiki-links is desired - $transFields = ''; - $transJoin = ''; - $transWhere = ''; $mode = $this->getOption('mode'); @@ -327,14 +324,33 @@ class CategoryTree { else if ( $mode == CT_MODE_PAGES ) $nsmatch = ' AND cat.page_namespace != ' . NS_IMAGE; else $nsmatch = ' AND cat.page_namespace = ' . NS_CATEGORY; + #additional stuff to be used if "transaltion" by interwiki-links is desired + $transFields = ''; + $transJoin = ''; + $transWhere = ''; + + # fetch member count if possible + $doCount = version_compare( $wgVersion, "1.12", '>' ); + + $countFields = ''; + $countJoin = ''; + + if ( $doCount ) { + $cat = $dbr->tableName( 'category' ); + $countJoin = " LEFT JOIN $cat ON cat_title = page_title AND page_namespace = " . NS_CATEGORY; + $countFields = ', cat_id, cat_title, cat_subcats, cat_pages, cat_files'; + } + $page = $dbr->tableName( 'page' ); $categorylinks = $dbr->tableName( 'categorylinks' ); $sql = "SELECT cat.page_namespace, cat.page_title $transFields + $countFields FROM $page as cat JOIN $categorylinks ON cl_from = cat.page_id $transJoin + $countJoin WHERE cl_to = " . $dbr->addQuotes( $title->getDBkey() ) . " $nsmatch "./*AND cat.page_is_redirect = 0*/" @@ -348,14 +364,20 @@ class CategoryTree { $categories= ''; $other= ''; - while ( $row = $dbr->fetchRow( $res ) ) { + while ( $row = $dbr->fetchObject( $res ) ) { #TODO: translation support; ideally added to Title object - $t = Title::makeTitle( $row['page_namespace'], $row['page_title'] ); + $t = Title::newFromRow( $row ); - $s = $this->renderNode( $t, $depth-1, false ); + $cat = NULL; + + if ( $doCount && $row->page_namespace == NS_CATEGORY ) { + $cat = Category::newFromRow( $row, $t ); + } + + $s = $this->renderNodeInfo( $t, $cat, $depth-1, false ); $s .= "\n\t\t"; - if ($row['page_namespace'] == NS_CATEGORY) $categories .= $s; + if ($row->page_namespace == NS_CATEGORY) $categories .= $s; else $other .= $s; } @@ -394,9 +416,9 @@ class CategoryTree { $s= ''; - while ( $row = $dbr->fetchRow( $res ) ) { + while ( $row = $dbr->fetchObject( $res ) ) { #TODO: translation support; ideally added to Title object - $t = Title::makeTitle( $row['page_namespace'], $row['page_title'] ); + $t = Title::newFromRow( $row ); #$trans = $title->getLocalizedText(); $trans = ''; #place holder for when translated titles are available @@ -424,8 +446,18 @@ class CategoryTree { * Returns a string with a HTML represenation of the given page. * $title must be a Title object */ - function renderNode( &$title, $children = 0, $loadchildren = false ) { - global $wgCategoryTreeDefaultMode; + function renderNode( $title, $children = 0, $loadchildren = false ) { + if ( $title->getNamespace() == NS_CATEGORY ) $cat = Category::newFromTitle( $title ); + else $cat = NULL; + + return $this->renderNodeInfo( $title, $cat, $children, $loadchildren ); + } + + /** + * 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. + */ + function renderNodeInfo( $title, $cat, $children = 0, $loadchildren = false ) { static $uniq = 0; $mode = $this->getOption('mode'); @@ -462,6 +494,7 @@ class CategoryTree { if ( ( $ns % 2 ) > 0 ) $labelClass .= ' CategoryTreeLabelTalk'; + $count = false; $s = ''; #NOTE: things in CategoryTree.js rely on the exact order of tags! @@ -475,25 +508,37 @@ class CategoryTree { $s .= Xml::openElement( 'span', $attr ); if ( $ns == NS_CATEGORY ) { + if ( $cat ) { + if ( $mode == CT_MODE_CATEGORIES ) $count = $cat->getSubcatCount(); + else if ( $mode == CT_MODE_PAGES ) $count = $cat->getPageCount() - $cat->getFileCount(); + else $count = $cat->getPageCount(); + } + $linkattr= array( 'href' => $wikiLink ); if ( $load ) $linkattr[ 'id' ] = $load; $linkattr[ 'class' ] = "CategoryTreeToggle"; - if ( $children == 0 || $loadchildren ) { + if ( $count === 0 ) { + $tag = 'span'; + $txt = $this->msg('empty-bullet'); + } + else if ( $children == 0 || $loadchildren ) { + $tag = 'a'; $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 { + $tag = 'a'; $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' ) . ' '; + $s .= Xml::openElement( $tag, $linkattr ) . $txt . Xml::closeElement( $tag ) . ' '; } else { $s .= $this->msg('page-bullet'); } @@ -501,6 +546,17 @@ class CategoryTree { $s .= Xml::closeElement( 'span' ); $s .= Xml::openElement( 'a', array( 'class' => $labelClass, 'href' => $wikiLink ) ) . $label . Xml::closeElement( 'a' ); + + if ( $count !== false && $this->getOption( 'showcount' ) ) { + $pages = $cat->getPageCount() - $cat->getSubcatCount() - $cat->getFileCount(); + + $attr = array( + 'title' => $this->msg( 'member-counts', $cat->getSubcatCount(), $pages , $cat->getFileCount() ) + ); + + $s .= Xml::element( 'span', $attr, ' (' . $count . ')' ); + } + $s .= Xml::closeElement( 'div' ); $s .= "\n\t\t"; $s .= Xml::openElement( 'div', array( 'class' => 'CategoryTreeChildren', 'style' => $children > 0 ? "display:block" : "display:none" ) ); diff --git a/CategoryTreePage.php b/CategoryTreePage.php index a44544f1..30a61e1e 100644 --- a/CategoryTreePage.php +++ b/CategoryTreePage.php @@ -40,7 +40,7 @@ class CategoryTreePage extends SpecialPage { * @param $par Parameters passed to the page */ function execute( $par ) { - global $wgRequest, $wgOut, $wgMakeBotPrivileged, $wgUser, $wgCategoryTreeDefaultOptions; + global $wgRequest, $wgOut, $wgCategoryTreeDefaultOptions, $wgCategoryTreeSpecialPageOptions; $this->setHeaders(); @@ -56,6 +56,9 @@ class CategoryTreePage extends SpecialPage { # grab all known options from the request. Normalization is done by the CategoryTree class foreach ( $wgCategoryTreeDefaultOptions as $option => $default ) { + if ( isset( $wgCategoryTreeSpecialPageOptions[$option] ) ) + $default = $wgCategoryTreeSpecialPageOptions[$option]; + $options[$option] = $wgRequest->getVal( $option, $default ); }