Extension:AddCategoryParserFunction

AddCategoryParserFunction Extension
Adds a parser function allowing articles to be added to categories, and for these "category" pages to be automatically created in case they do not exist. The new category pages contain the category name and optionally a parent category, which is also automatically created in case it does not exist.

Usage Example
Subcategories with common parent are separated by semi-colon ";", supercategory is separated from the subcategories by colon ":", and multiple sets are separated by pound/hash "#".

Code
Copy the code below into extensions/AddCategoryParserFunction/AddCategoryParserFunction.php and add to your LocalSettings.php the following: require_once( "$IP/extensions/AddCategoryParserFunction/AddCategoryParserFunction.php");

<?php /** * AddCategoryParserFunction Extension * - Adds a parser function allowing articles to be added to categories, and *  for these category pages to be automatically created in case they do *   not exist. The new categories pages contain the category name and *  optionally a parent category, which is also automatically created in *   case it does not exist. * - Started: version 1.0, 2008-03-13 *           version 1.1, 2008-06-29 changed to split only on semi-colons only *                        (i.e., not on commas anymore), and to separate *                        distinct supercategories sets with pound "#" symbol. *           version 1.2, 2008-10-26 very important fixes to fix recursion *                        when an old revision of a page is viewed and to *                         provide more robustness (only valid titles are *                         accepted, recursion detection, triggered only for *                         latest revision). * - Author: Andrea Matsunaga (http://ammatsun.com) */

if (!defined('MEDIAWIKI')) die('Not an entry point.'); $wgCatHookMagic               = "addcat"; $wgCatHookIfMagic             = "ifcat"; // Define a setup function $wgExtensionFunctions[]       = 'wfAddCategoryParserFunction_Setup'; // Add a hook to initialize the magic word $wgHooks['LanguageGetMagic'][] = 'wfAddCategoryParserFunction_Magic'; $wgExtensionCredits['parserhook'][] = array(   'name'        => 'AddCategoryParserFunction',    'author'      => 'Andrea Matsunaga',    'description' => 'Adds a parser function allowing articles to be added to categories, and for these categories page to be automatically created in case they do not exist. The new categories pages contain the category name and optionally a parent category, which is also automatically created in case it does not exist.',    'url'         => 'http://www.acis.ufl.edu/~ammatsun/wiki/index.php/Extension:AddCategoryParserFunction',    'version'     => '1.2, 2008-10-26'    ); /** * Setup the AddCategoryParserFunction extension. */ function wfAddCategoryParserFunction_Setup { global $wgParser; // Set a function hook associating the "addcat" magic word with our function $wgParser->setFunctionHook('addcat', 'wfAddCategoryParserFunction_RenderAddcat'); }

/** * Hook to initialize magic word. */ function wfAddCategoryParserFunction_Magic(&$magicWords, $langCode) { // Add the magic word // The first array element is case sensitive, in this case it is not case sensitive // All remaining elements are synonyms for our parser function $magicWords['addcat'] = array(0, 'addcat'); // Unless we return true, other parser functions extensions won't get loaded. return true; }

/** * The parser function itself, which converts a list of parent/children * categories into category wikitext and automatically create/update * the actual category articles when necessary. * @param string $data Wikitext (with templates expanded) containing a * list of parent/children categories separated by pound characters. * Parent category name comes before the set of children categories * separated by a colon ":" character. Children categories are separated * by a semi-colon ";" character. * @return wikitext with the input parameter formatted as appropriate * category instruction, e.g. "". */ function wfAddCategoryParserFunction_RenderAddCat(&$parser, $data = '') { global $wgUser; // As this extension is not expected to be used with recursion, // detecting this anomaly if (isset($parser->mOutput->addcat_isAlreadyInside) && $parser->mOutput->addcat_isAlreadyInside) { wfDebug(__METHOD__.": Recursion detected!!! [".$data."]\n"); return ""; } else { $parser->mOutput->addcat_isAlreadyInside = TRUE; }   // To avoid creation of categories when an old revision of the article // is viewed or when the article is being previewed before edit, // detect if an old revision of the article is being viewed $currpageLatestRevID = $parser->getTitle->getLatestRevID; $currpageViewedRevID = $parser->getRevisionID; $performChanges = ($currpageLatestRevID == $currpageViewedRevID); wfDebug(__METHOD__.": Parsing [".$data."] for revid [".$currpageViewedRevID           ."], latest revid [".$currpageLatestRevID."] by [".$wgUser->getName."]\n"); $text = ""; $parts = preg_split("/#/", $data); // Separate by hash symbol foreach ($parts as $part) { $part = trim($part); if (0 != strlen($part)) { $text .= createCategory($part, $performChanges); }   }    $parser->mOutput->addcat_isAlreadyInside = FALSE; return "\n\n".$text; }

/** * Generate category wikitext for a set of parent/children categories, * creating/updating the parent/children category articles when necessary. * @param string data Set of parent/children categories, where parent category * name comes before the children category names, separated by a colon ":" * character and multiple subcategories are separated by semi-colons ";". * @param boolean $performChanges Indicates if categories given should * trigger creation/update of corresponding category articles. * @return Wikitext with categories given. */ function createCategory($data, $performChanges) { global $wgContLang; $maxCatNameSize = 100; // Remove all quotes (single and double) and curly braces $data = preg_replace("/['\"{}]/", "", strtolower($data));   $parts = explode(":", $data, 2);    $text = "";    if (1 == count($parts)) {        $parent = "";        $categories = explode(";", trim($parts[0]));    } else {        $parent = trim($parts[0]);        $categories = explode(";", trim($parts[1]));    }    $categorypages = array;    foreach ($categories as $category) {        $category = trim($category);        // Filter empty categories, names longer than maxCatNameSize,        // and cases where there are error messages (e.g., expansion        // limit errors) instead of the user provided information.        if ((0 != strlen($category)) && ($maxCatNameSize >= strlen($category))                && (0 != strncmp($category, "<span", 5))) {            // Note: one should not use makeTitle API, but            // makeTitleSafe to guarantee that the name is valid            // Thanks to Tim Starling for the information $potentialpage = Title::makeTitleSafe(NS_CATEGORY, $category); if (NULL != $potentialpage) { $categorypages[] = $potentialpage; }       }    }    if ((0 != count($categorypages)) && (0 != strlen($parent))) { $parentpage = Title::makeTitleSafe(NS_CATEGORY, $parent); }   // doEdit is not allowed here since it calls parse, leading to possible // recursion. However, the use of doEdit here is inevitable as changes // performed by this extension are done in view mode (not on edits) // because templates may be changed while the article making use of the // template remains unmodified, i.e. if this functionality is moved to   // hooks like ArticleEditUpdates, the extension will only activate when // not only the template but also the actual page has been modified. if ($performChanges && isset($parentpage) && (is_object($parentpage)) && (!$parentpage->exists)) { // Parent Category page does not exist, create it       wfDebug(__METHOD__.": Going to create parent category: [".$parentpage->getText."]\n"); // When creating a new article object it is necessary to set // the revision id to zero to indicate that current revision // should be used. Otherwise, revision id from the request // is retrieved, leading to the load of the article being // viewed instead of the category page that should be created $article = new Article($parentpage, 0); $content = $parentpage->getNsText." ".$parentpage->getText; $article->doEdit($content, $content, EDIT_NEW | EDIT_SUPPRESS_RC); }   for ($i = 0; $i < count($categorypages); $i++) { if (is_object($categorypages[$i])) { $text .= "".$categorypages[$i]->getNsText.":".$categorypages[$i]->getText.""; if ($performChanges) { if (!$categorypages[$i]->exists) { // Category page does not exist, create // it, possibly with a parent $article = new Article($categorypages[$i], 0); $content = $categorypages[$i]->getNsText." ".$categorypages[$i]->getText; $summary = $categorypages[$i]->getNsText." ".$categorypages[$i]->getText; if (isset($parentpage) && (is_object($parentpage)) && ($parentpage->exists)) { wfDebug(__METHOD__.": Going to create category: ["                           .$categorypages[$i]->getText."] with parent: ["                            .$parentpage->getText."]\n"); $content .= "\n".$categorypages[$i]->getNsText.":".$parentpage->getText.""; } else { wfDebug(__METHOD__.": Going to create category: ["                           .$categorypages[$i]->getText."] without parent\n"); }                   $article->doEdit($content, $summary, EDIT_NEW | EDIT_SUPPRESS_RC); } else { // Category page already exists, but // need to check if parent category // is already specified if (isset($parentpage) && (is_object($parentpage)) && ($parentpage->exists)) { $potentialcontent = "".$categorypages[$i]->getNsText.":".$parentpage->getText.""; $article = new Article($categorypages[$i], 0); $content = $article->getContent; if (strstr($content, $potentialcontent) === FALSE) { wfDebug(__METHOD__.": Going to update category: ["                               .$categorypages[$i]->getText."] with parent: ["                                .$parentpage->getText."] as [".$content                                ."] does not have [".$potentialcontent."]\n"); $content .= "\n".$potentialcontent; $article->doEdit($content, $content, EDIT_UPDATE | EDIT_SUPPRESS_RC); }                   }                }            }        }    }    return $text; } ?>