User:Robchurch/NCL fixup

Hacked-about version of JohanTheGhost's hacked-about version; this one shouldn't cause problems with the parser chain referencing $wgOut.

<?php

/*
 * Nice Category List extension.
 * Generates a list of all pages in the category, including subcategories.
 * Unlike the default category list, the generated list is flat and shows
 * well, even with long page names.
 *
 * Usage:
 *   <ncl [options]>Category:Some Category</ncl>
 *
 * The following options are available:
 *   maxdepth         Maximum category depth; default 32
 *   style            'bullet' to show category contents as bullet lists
 *                    'compact' for a more compact listing
 *   showcats         Non-0 to display sub-category links in "bottom" (ie.
 *                    maxdepth) categories (default 0).
 *   showarts         Non-0 to display articles in categories (default 1).
 *   headings         'head' to display category headings as Wiki headings;
 *                    'bullet' to display category headings as large bullets
 *                    ('bullet' works well with 'style=compact').
 *   headstart        With 'headings=head', the heading level to list
 *                    top-level categories with (level increases for sub-cats).
 *   sort             With 'sort=1', the list is sorted alphabetically
 *                    else the category index key is used (as usual)
 *
 * Examples:
 * 1. This:
 *      <ncl>Category:Some Category</ncl>
 *    generates a full, recursive listing.
 *
 * 2. Use this in a template:
 *      <ncl style=compact maxdepth=2 headings=bullet headstart=2
 *                            showcats=1 showarts=1>Category:{{PAGENAME}}</ncl>
 *    and include it in major category pages to provide a nice 2-level (or however
 *    many you like) index of the category.
 *
 * 3. This:
 *      <ncl style=compact headings=bullet headstart=2 showcats=1
 *                            showarts=0>Category:Categories</ncl>
 *    generates a full cagtegory listing, with indentation indicating category
 *    containment.
 */

if (!defined('MEDIAWIKI')) die();

$wgExtensionFunctions[] = 'wfNiceCategoryList';
$wgExtensionCredits['parserhook'][] = array(
  'name'=>'NiceCategoryList',
  'author'=>'Kichik',
  'url'=>'http://meta.wikimedia.org/wiki/NiceCategoryList_extension',
  'description'=>'generate a category page showing all pages in a category, including subcategories',
);

/*
 * Global settings for the Nice Category List extension.
 */
$wgNiceCategoryListSettings = array(
    'maxdepth'  => 32,           // Sanity stop level.
    'style'     => 'bullet',     // Default style for "leaf" level.
    'showcats'  => 0,            // Non-0 to display sub-cat links in a category.
    'showarts'  => 1,            // Non-0 to display article links in a category.
    'headings'  => 'head',       // Show category headings as headings.
    'headstart' => 1,            // Heading level to start at.
    'sort' => 0,                 // Non-0 to sort the list alphabetically, else sort the list regarding the index key
);


/*
 * Setup Nice Category List extension.
 * Sets a parser hook for <ncl></ncl>.
 */
function wfNiceCategoryList() {
        global $wgParser;
        $wgParser->setHook('ncl', 'hookNiceCategoryList');
}

/*
 * The hook function. Handles <ncl></ncl>.
 * Receives the category name as a parameter.
 */
function hookNiceCategoryList($category, $argv, &$parser) {
    global $wgNiceCategoryListSettings;
    
    // Get any user-specified parameters.
    foreach (array_keys($argv) as $key) {
        $wgNiceCategoryListSettings[$key] = $argv[$key];
    }

    // Replace variables in cat.  This is still pretty crappy, as template
    // params still won't work.
    $poutput = $parser->parse( $category, $parser->mTitle, $parser->mOptions, false, false );
    $category = $poutput->getText();

    $dbr =& wfGetDB(DB_SLAVE);

    $title = Title::newFromText($category);
    if (!$title) return '';

    $ct = fetchCategoryLinks($dbr, $title, 0);

    $poutput = $parser->parse( outputCategory( $ct ), $parser->mTitle, $parser->mOptions, true, false );
    return $poutput->getText();
}

/*
 * Get all links in a category.
 */
function getCategoryLinks($dbr, $title) {
        return $dbr->select(
                        array( 'page', 'categorylinks' ),
                        array( 'page_title', 'page_namespace', 'page_len', 'cl_sortkey' ),
                        array( 'cl_from          =  page_id',
                               'cl_to'           => $title->getDBKey()),
                               #'page_is_redirect' => 0),
                        #+ $pageCondition,
                        '',
                        array( 'ORDER BY' => 'cl_sortkey' ) );
}

/*
 * Title comparison function
 */
function titleCmp($a, $b) {
        return $a->getText() > $b->getText();
}

/*
 * CategoryLinks comparison function
 */
function categoryCmp($a, $b) {
        return titleCmp($a->title, $b->title);
}

/*
 * Simple class to hold category's title, links list,
 * and categories list.
 */
class CategoryLinks {
        var $title;
        var $links = array();
        var $categories = array();
        var $subcats = array();

        function CategoryLinks($title) {
                $this->title = $title;
        }

        /*
         * Sort links and categories alphabetically.
         */
        function sort() {
                global $wgNiceCategoryListSettings;
                if ($wgNiceCategoryListSettings['sort']) {
                    usort($this->links, "titleCmp");
                    usort($this->categories, "categoryCmp");
                }
        }
}

/*
 *
 */
function fetchCategoryLinks($dbr, $category_title, $depth, $processed = array()) {
    global $wgNiceCategoryListSettings;
    
    // avoid recursion
    if (in_array($category_title->getText(), $processed))
        return NULL;
    $processed[] = $category_title->getText();

    $cl = new CategoryLinks($category_title);

    // get category links from db
    $res = getCategoryLinks($dbr, $category_title);

    // process all category links
    while ($x = $dbr->fetchObject($res)) {
        $title = Title::makeTitle($x->page_namespace, $x->page_title);

        if ($title->getNamespace() == NS_CATEGORY) {
            // if category, recurse.  Stop at max depth - but record the cat.
            if ($depth + 1 < $wgNiceCategoryListSettings['maxdepth']) {
                $fc = fetchCategoryLinks($dbr, $title, $depth + 1, $processed);
                if ($fc) $cl->categories[] = $fc;
            }
            $cl->subcats[] = $title;
        } else {
            // if regular page, add to list
            $cl->links[] = $title;
        }
    }

    // sort
    $cl->sort();

    return $cl;
}

/*
 * Generate output for the list.
 */
function outputCategory($category, $level = 0) {
    global $wgContLang, $wgUser;
    global $wgNiceCategoryListSettings;
    
    $sk =& $wgUser->getSkin();

    if ($level == 0) {
        // no need for TOC
        $output = "__NOTOC__\n";
    } else {
        // second level and onwards, has a heading.
        // the heading gets smaller as the level grows.

        $title = $category->title;
        $ptitle = $title->getPrefixedText();
        $title = $wgContLang->convert($title->getText());
        $link = "[[:" . $ptitle . "|'''" . $title . "''']]";
        if ($wgNiceCategoryListSettings['headings'] == 'head') {
            $heading = str_repeat('=', $level + $wgNiceCategoryListSettings['headstart']);
            $output = $heading . $title . $heading . "\n";
        } else {
            $stars = str_repeat('*', $level);
            if ($level <= 1)
                $output = "<big>\n" . $stars . " " . $link . "</big>\n";
            else
                $output = $stars . " " . $link . "\n";
        }
    }

    $pieces = array();
    
    // output each subcategory
    if ($wgNiceCategoryListSettings['showcats'] && !$category->categories) {
        foreach ($category->subcats as $title) {
            $ptitle = $title->getPrefixedText();
            $title = $wgContLang->convert($title->getText());
            $disp = "[[:" . $ptitle . "|'''" . $title . "''']]";
            $pieces[] = $disp;
        }
    }

    // output each link
    if ($wgNiceCategoryListSettings['showarts']) {
        foreach ($category->links as $link) {
            $ptitle = $link->getPrefixedText();
            $title = $link->getText();
            $disp = "[[:" . $ptitle . "|" . $title . "]]";
            $pieces[] = $disp;
        }
    }

    if (count($pieces) > 0) {
        if ($wgNiceCategoryListSettings['style'] == 'bullet')
            $output .= "* " . implode("\n* ", $pieces) . "\n";
        else
            $output .= ":<div style=\"font-size: small\">" . implode(" ¤ ", $pieces) . "</div><br>\n";
    }

    // recurse into each subcategory
    foreach ($category->categories as $cat) {
        $output .= outputCategory($cat, $level + 1);
    }

    return $output;
}

?>