Kji has submitted this change and it was merged. Change subject: MW extensions dev standards restructuring completed. ......................................................................
MW extensions dev standards restructuring completed. Change-Id: I0a5ac038d4ee774f3797e46656f40340b80f5c0a Refactored hooks into new hooks file. Change-Id: I43047c7147ca473ed050946521face42671181b4 Refactored hierarchyBuilder parser function. Change-Id: Ie7c3cd4f70779eb7fb0a47f31aed380a29d02aa9 HierarchyBreadcrumb cleanup Change-Id: Ib9e549b8445f03f7ee500694d3390e8e06c8457d Finished refactoring all the parser functions out of HierarchyBuilder.hooks.php. Change-Id: Ie610c7d8dbf2911c85becdeb1dfbaa52b9830a8b Fixed the edit form to display the usage error message properly. Change-Id: I98ba9fca6101cc068b71f1f280fad0d27db9e5b1 Refactored parser functions. Updated method visibility. Change-Id: I6867893cf809d828f64ea564d3324a47019daa5a More fixes for the edit form usage error message. Changed the HierarchyBuilder interface for getting titleicons. Change-Id: I4c228ebb9ebffce602d5f511800101346c2af66f Added version checking when using wgLoadExtension function. Improved interface for getting hierarchy titleicons. Change-Id: I1d9e8727fd60ce481cca1dab4d0ed0b86dc7229f --- A HierarchyBuilder.hooks.php M HierarchyBuilder.php R HierarchyBuilder_body.php A extension.json R includes/HierarchyFormInput.php R includes/HierarchySelectFormInput.php R includes/HierarchyTree.php R includes/TreeNode.php R includes/editHierarchy.css R includes/editHierarchy.js R includes/jquery.jstree.js R includes/renderHierarchy.css R includes/renderHierarchy.js R includes/renderHierarchySelected.js R includes/selectFromHierarchy.css R includes/selectFromHierarchy.js 16 files changed, 849 insertions(+), 731 deletions(-) Approvals: Kji: Verified; Looks good to me, approved jenkins-bot: Verified diff --git a/HierarchyBuilder.hooks.php b/HierarchyBuilder.hooks.php new file mode 100644 index 0000000..221ac3f --- /dev/null +++ b/HierarchyBuilder.hooks.php @@ -0,0 +1,49 @@ +<?php + +class HierarchyBuilderHooks { + + public static function onRegistration () { + if ( !defined( 'MEDIAWIKI' ) ) { + die( '<b>Error:</b> This file is part of a MediaWiki extension and cannot' . + ' be run standalone.' ); + } + + global $wgVersion; + if ( version_compare( $wgVersion, '1.21', 'lt' ) ) { + die( '<b>Error:</b> This version of HierarchyBuilder is only compatible ' . + 'with MediaWiki 1.21 or above.' ); + } + + if ( !defined( 'SF_VERSION' ) ) { + die( '<b>Error:</b> HierarchyBuilder is a Semantic Forms extension so must' . + ' be included after Semantic Forms.' ); + } + + if ( version_compare( SF_VERSION, '2.5.2', 'lt' ) ) { + die( '<b>Error:</b> This version of HierarchyBuilder is only compatible with' . + ' Semantic Forms 2.5.2 or above.' ); + } + } + + /** + * @param $parser Parser + * @return bool + */ + public static function efHierarchyBuilderSetup ( & $parser ) { + $parser->setFunctionHook( 'hierarchyBreadcrumb', + 'HierarchyBuilder::hierarchyBreadcrumb' ); + $parser->setFunctionHook( 'hierarchySectionNumber', 'HierarchyBuilder::hierarchySectionNumber' ); + $parser->setFunctionHook( 'hierarchyParent', 'HierarchyBuilder::hierarchyParent' ); + $parser->setFunctionHook( 'hierarchyChildren', 'HierarchyBuilder::hierarchyChildren' ); + $parser->setFunctionHook( 'hierarchySubtree', 'HierarchyBuilder::hierarchySubtree' ); + $parser->setFunctionHook( 'hierarchySelected', 'HierarchyBuilder::hierarchySelected' ); + + $parser->setHook( 'hierarchy', 'HierarchyBuilder::renderHierarchy' ); + $parser->setHook( 'hierarchySelected', 'HierarchyBuilder::renderHierarchySelected' ); + global $sfgFormPrinter; + $sfgFormPrinter->registerInputType( 'HierarchyFormInput' ); + $sfgFormPrinter->registerInputType( 'HierarchySelectFormInput' ); + return true; + } + +} \ No newline at end of file diff --git a/HierarchyBuilder.php b/HierarchyBuilder.php index 6a2e687..f67b502 100644 --- a/HierarchyBuilder.php +++ b/HierarchyBuilder.php @@ -22,6 +22,19 @@ * DEALINGS IN THE SOFTWARE. */ +if ( function_exists( 'wfLoadExtension' ) ) { + wfLoadExtension( 'HierarchyBuilder' ); + // Keep i18n globals so mergeMessageFileList.php doesn't break + $wgMessagesDirs['HierarchyBuilder'] = __DIR__ . "/i18n"; + $wgExtensionMessagesFiles['HierarchyBuilder'] = __DIR__ . '/HierarchyBuilder.i18n.php'; + $wgExtensionMessagesFiles['HierarchyBuilderMagic'] = __DIR__ . '/HierarchyBuilder.i18n.magic.php'; + wfWarn( + 'Deprecated PHP entry point used for HierarchyBuilder extension. Please use wfLoadExtension instead, ' . + 'see https://www.mediawiki.org/wiki/Extension_registration for more details.' + ); + return; +} + if ( !defined( 'MEDIAWIKI' ) ) { die( '<b>Error:</b> This file is part of a MediaWiki extension and cannot' . ' be run standalone.' ); @@ -42,11 +55,13 @@ ' Semantic Forms 2.5.2 or above.' ); } +define( 'HB_VERSION', '3.0.0' ); + # credits $wgExtensionCredits['parserhook'][] = array ( 'path' => __FILE__, 'name' => 'HierarchyBuilder', - 'version' => '2.0.0', + 'version' => HB_VERSION, 'author' => array( '[https://www.mediawiki.org/wiki/User:Cindy.cicalese Cindy Cicalese]', '[https://www.mediawiki.org/wiki/User:Kevin.ji Kevin Ji]' @@ -55,21 +70,17 @@ 'url' => 'https://www.mediawiki.org/wiki/Extension:HierarchyBuilder' ); -$wgExensionMessagesFiles['HierarchyBuilder'] = __DIR__ . '/HierarchyBuilder.i18n.php'; -$wgHooks['ParserFirstCallInit'][] = 'efHierarchyBuilderSetup'; +$wgHooks['ParserFirstCallInit'][] = 'HierarchyBuilderHooks::efHierarchyBuilderSetup'; -$wgAutoloadClasses['HierarchyBuilder'] = __DIR__ . '/HierarchyBuilder.class.php'; -$wgAutoloadClasses['HierarchyFormInput'] = __DIR__ . '/HierarchyFormInput.php'; -$wgAutoloadClasses['HierarchySelectFormInput'] = __DIR__ . '/HierarchySelectFormInput.php'; -$wgAutoloadClasses['HierarchyTree'] = __DIR__ . '/HierarchyTree.php'; -$wgAutoloadClasses['TreeNode'] = __DIR__ . '/TreeNode.php'; - -/* Adding API for retrieving urls of the title icons for pages*/ -$wgAPIModules['hbGetTitleIcons'] = 'ApiHBGetTitleIcons'; -$wgAutoloadClasses['ApiHBGetTitleIcons'] = __DIR__ . '/ApiHBGetTitleIcons.php'; +$wgAutoloadClasses['HierarchyBuilder'] = __DIR__ . '/HierarchyBuilder_body.php'; +$wgAutoloadClasses['HierarchyBuilderHooks'] = __DIR__ . '/HierarchyBuilder.hooks.php'; +$wgAutoloadClasses['HierarchyFormInput'] = __DIR__ . '/includes/HierarchyFormInput.php'; +$wgAutoloadClasses['HierarchySelectFormInput'] = __DIR__ . '/includes/HierarchySelectFormInput.php'; +$wgAutoloadClasses['HierarchyTree'] = __DIR__ . '/includes/HierarchyTree.php'; +$wgAutoloadClasses['TreeNode'] = __DIR__ . '/includes/TreeNode.php'; $wgMessagesDirs['HierarchyBuilder'] = __DIR__ . "/i18n"; - +$wgExtensionMessagesFiles['HierarchyBuilder'] = __DIR__ . '/HierarchyBuilder.i18n.php'; $wgExtensionMessagesFiles['HierarchyBuilderMagic'] = __DIR__ . '/HierarchyBuilder.i18n.magic.php'; @@ -77,14 +88,14 @@ 'localBasePath' => __DIR__, 'remoteExtPath' => 'HierarchyBuilder', 'styles' => 'themes/apple/style.css', - 'scripts' => 'jquery.jstree.js' + 'scripts' => '/includes/jquery.jstree.js' ); $wgResourceModules['ext.HierarchyBuilder.render'] = array( 'localBasePath' => __DIR__, 'remoteExtPath' => 'HierarchyBuilder', - 'scripts' => 'renderHierarchy.js', - 'styles' => 'renderHierarchy.css', + 'scripts' => '/includes/renderHierarchy.js', + 'styles' => '/includes/renderHierarchy.css', 'dependencies' => array( 'ext.HierarchyBuilder.jstree' ) @@ -93,7 +104,7 @@ $wgResourceModules['ext.HierarchyBuilder.renderSelected'] = array( 'localBasePath' => __DIR__, 'remoteExtPath' => 'HierarchyBuilder', - 'scripts' => 'renderHierarchySelected.js', + 'scripts' => '/includes/renderHierarchySelected.js', 'dependencies' => array( 'ext.HierarchyBuilder.jstree' ) @@ -102,8 +113,8 @@ $wgResourceModules['ext.HierarchyBuilder.edit'] = array( 'localBasePath' => __DIR__, 'remoteExtPath' => 'HierarchyBuilder', - 'scripts' => 'editHierarchy.js', - 'styles' => 'editHierarchy.css', + 'scripts' => '/includes/editHierarchy.js', + 'styles' => '/includes/editHierarchy.css', 'dependencies' => array( 'ext.HierarchyBuilder.jstree', 'ext.semanticforms.main' @@ -113,595 +124,10 @@ $wgResourceModules['ext.HierarchyBuilder.select'] = array( 'localBasePath' => __DIR__, 'remoteExtPath' => 'HierarchyBuilder', - 'scripts' => 'selectFromHierarchy.js', - 'styles' => 'selectFromHierarchy.css', + 'scripts' => '/includes/selectFromHierarchy.js', + 'styles' => '/includes/selectFromHierarchy.css', 'dependencies' => array( 'ext.HierarchyBuilder.jstree', 'ext.semanticforms.main' ) ); - -/** - * @param $parser Parser - * @return bool - */ -function efHierarchyBuilderSetup ( & $parser ) { - $parser->setFunctionHook( 'hierarchyBreadcrumb', 'hierarchyBreadcrumb' ); - $parser->setFunctionHook( 'hierarchySectionNumber', 'sectionNumber' ); - $parser->setFunctionHook( 'hierarchyParent', 'parent' ); - $parser->setFunctionHook( 'hierarchyChildren', 'children' ); - $parser->setFunctionHook( 'hierarchySubtree', 'subhierarchy' ); - $parser->setFunctionHook( 'hierarchySelected', 'hierarchySelected' ); - - $parser->setHook( 'hierarchy', 'renderHierarchy' ); - $parser->setHook( 'hierarchySelected', 'renderHierarchySelected' ); - global $sfgFormPrinter; - $sfgFormPrinter->registerInputType( 'HierarchyFormInput' ); - $sfgFormPrinter->registerInputType( 'HierarchySelectFormInput' ); - return true; -} - -/** - * This parser function will return only specific selected rows of a hierarchy - * in addition to any necessary contextual rows. - * - * The returned hierarchy is displayd similarly to the HierarchySelectFormInput, - * with each row preceeded by a checkbox. However, the checkboxes will be inactive. - * - * For a given set of selected rows, only those rows will be provided from the - * hierarchy in addition to the minimal necessary contextual rows needed to display - * the hierarchical relationships. For example, if a single selected row is given, - * but that row is a leaf node which is 5 levels deep within the hierarchy, then - * that row will be given along with each of its ancestors. This is conidered the - * "pruned" behavior. - * - * The "collapsed" behavior will not remove any branches of the hierarchy, even - * when those branches do not contain any of the specified selected rows. Instead, - * these unnecessary branches will be collapsed initially, allowing only the - * selected rows and their siblings to be shown. - * - * @param $parser: Parser - * @return I don't know yet. - * - * Example invokation: - * @code - * {{#hierarchySelected:<list of page names>|<hierarchy page name>|<hierarchy property>}} - * {{#hierarchySelected:<list of page names>|<hierarchy page name>|<hierarchy property>|pruned}} - * {{#hierarchySelected:<list of page names>|<hierarchy page name>|<hierarchy property>|collapsed}} - * @endcode - */ -function hierarchySelected( $parser ) { - $params = func_get_args(); - if ( count( $params ) < 4) { - $output = ''; - } else { - $selectedPages = $params[1]; - $hierarchyPageName = $params[2]; - $hierarchyPropertyName = $params[3]; - // if "pruned" is given, then set the displaymode to pruned. otherwise, "collapsed" - if ( isset( $params[4] ) && $params[4] == 'collapsed' ) { - $displayMode = 'collapsed'; - } else { - $displayMode = 'pruned'; - } - - $wikitextHierarchy = HierarchyBuilder::getPropertyFromPage( $hierarchyPageName, $hierarchyPropertyName ); - // this is where we ask HierarchyBuilder class to actually do the work for us. - $hierarchyTree = HierarchyTree::fromWikitext( $wikitextHierarchy ); - - $normalizedSelectedPages = - array_map( - function( $page ) { - $pagename = HierarchyBuilder::getPageNameFromHierarchyRow( $page ); - if ( $pagename == '' ) { - $pagename = trim( $page ); - } - return trim( $pagename ); - }, - explode( ',', $selectedPages ) - ); - - $mst = $hierarchyTree->getMST( $normalizedSelectedPages ); - - // output formatting - $flatNormalizedSelectedPages = - array_reduce( $normalizedSelectedPages, - function( $carry, $item ) { - if ( $carry == '' ) { - $carry = $item; - } else { - $carry .= ',' . $item; - } - return $carry; - } - ); - - $selected = htmlspecialchars( str_replace( " ", "%20", $flatNormalizedSelectedPages ) ); - - $output = ''; - if ( $displayMode == 'collapsed') { - //$output = "<hierarchySelected collapsed $displaynameattr selected=$selected>" . (string)$mst . '</hierarchySelected>'; - $output = "<hierarchySelected collapsed selected=$selected>" . (string)$mst . '</hierarchySelected>'; - } else { - //$output = "<hierarchySelected $displaynameattr selected=$selected>" . (string)$mst . '</hierarchySelected>'; - $output = "<hierarchySelected selected=$selected>" . (string)$mst . '</hierarchySelected>'; - } - - $output = $parser->recursiveTagParse( $output ); - - } - return $parser->insertStripItem( $output, $parser->mStripState ); -} - -/** - * This parser function will return the subhierarchy that is rooted at the specified - * node within a hierarchy. - * - * The three required arguments are (in order): - * - The root node of the subhierarchy within the overall hierarchy. If this - * argument is empty, then the entire hierarchy is returned. - * - Full page name of the page containing the hierarchy - * - Property name of the property containing the hierarchy data - * - * The optional argument is: - * - Format to specify if the results should be returned as a bulleted list as - * opposed to the default striped format. - * - * Example invokation: - * @code - * {{#hierarchySubtree:|Main Page|Hierarchy Data}} - * {{#hierarchySubtree:Hierarchy Builder|Main Page|Hierarchy Data}} - * {{#hierarchySubtree:Hierarchy Builder|Main Page|Hierarchy Data}} - * {{#hierarchySubtree:Hierarchy Builder|Main Page|Hierarchy Data|format=ul}} - * @endcode - * - * @param $parser: Parser - * - * @return string: The string containing the specified subhierarchy as though - * it were a standalone hierarchy. - */ -function subhierarchy( $parser ) { - $params = func_get_args(); - if ( count( $params ) < 4 ) { - $output = ''; - } else { - $rootNode = $params[1]; - $hierarchyPageName = $params[2]; - $hierarchyPropertyName = $params[3]; - - $optionalParams = array_slice( $params, 4 ); - $optionalParams = parseParams( $optionalParams ); - $format = ''; - if ( isset( $optionalParams[HierarchyBuilder::FORMAT] ) ) { - $format = $optionalParams[HierarchyBuilder::FORMAT]; - } - $titleiconproperty = ''; - if (isset( $optionalParams[HierarchyBuilder::TITLEICONPROPERTY] ) ) { - $titleiconproperty = $optionalParams[HierarchyBuilder::TITLEICONPROPERTY]; - } - $showroot = ''; - if ( isset( $optionalParams[HierarchyBuilder::SHOWROOT] ) ) { - $showroot = $optionalParams[HierarchyBuilder::SHOWROOT]; - } - if ( $rootNode == '' ) { - $showroot = 'showroot'; - } - $collapsed = ''; - if ( isset( $optionalParams[HierarchyBuilder::COLLAPSED] ) ) { - $collapsed = $optionalParams[HierarchyBuilder::COLLAPSED]; - } - - $output = HierarchyBuilder::getSubhierarchy( - $rootNode, - $hierarchyPageName, - $hierarchyPropertyName - ); - - // this is where we have to handle the default mode which is not showroot and not collapsed - if ( $showroot == '' ) { - // fix $output so only the children are given - $hierarchyrows = preg_split( '/\n/', $output ); - $root = $hierarchyrows[0]; - $children = array_slice( $hierarchyrows, 1 ); - - $depth = HierarchyBuilder::getDepthOfHierarchyRow( $root ); - $output = array_reduce( $children, - function( $carry, $item ) use ( $depth ) { - if ( $carry != '' ) { - $carry .= "\n" . substr( $item, strlen( $depth ) ); - } else { - $carry = substr( $item, strlen( $depth ) ); - } - return $carry; - } - ); - } - - // this is the default output display format - if ( $format != 'ul' ) { - if ( $titleiconproperty != '' ) { - $titleiconproperty = "titleiconproperty=\"$titleiconproperty\""; - } - $output = "<hierarchy $collapsed $titleiconproperty>$output</hierarchy>"; - } - // otherwise it's the bulleted format and we don't modify output. - - $output = $parser->recursiveTagParse( PHP_EOL . $output ); - } - return $parser->insertStripItem( $output, $parser->mStripState ); -} - -/** - * This parser function will give the section number of a page in a hierarchy. - * - * The three required arguments are (in order): - * - Full page name - * - Full page name of the page containing the hierarchy - * - Property name of the property containing the hierarchy data - * - * Example invokation: - * @code - * {{#hierarchySectionNumber:{{FULLPAGENAME}}|Table of Contents|Hierarchy Data}} - * @endcode - * - * @param $parser: Parser - * - * @return string: The section number of the specified page name within the - * specified hierarchy. - */ -function sectionNumber( $parser ) { - $params = func_get_args(); - if ( count( $params ) != 4 ) { - $output = ""; - } else { - $pageName = $params[1]; - $hierarchyPageName = $params[2]; - $hierarchyPropertyName = $params[3]; - $output = HierarchyBuilder::getPageSectionNumber( - $pageName, - $hierarchyPageName, - $hierarchyPropertyName - ); - } - return $parser->insertStripItem( $output, $parser->mStripState ); -} - -/** - * This parser function will return a list of the immediate children of a given - * page within a hierarchy on a page. The list of chilren will be delimited by - * a specified character or the ',' character by default if no delimiter is given. - * - * 3 unnamed mandatory args: - * - pagename - * - hierarchy page - * - hierarchy property - * - * 1 named optional arg: - * - link = [none] - * - * Example invokations: - * @code - * {{#hierarchyParent:{{FULLPAGENAME}}|hierarchy page name|hierarchy property}} - * {{#hierarchyParent:{{FULLPAGENAME}}|hierarchy page name|hierarchy property|link=none}} - * @endcode - * - * @param $parser Parser - * - * @return string: The parent of the specified page within the specified hierarchy - * as wikitext for formatted display. - */ -function parent( $parser ) { - $params = func_get_args(); - if ( count( $params ) < 4 ) { - $output = ""; - } else { - // mandatory args - $pageName = $params[1]; - $hierarchyPageName = $params[2]; - $hierarchyPropertyName = $params[3]; - // optional args (just link=none) - $optionalParams = array_slice( $params, 4 ); - $optionalParams = parseParams( $optionalParams ); - // look for the template parameter - if ( isset( $optionalParams[HierarchyBuilder::TEMPLATE] ) ) { - $template = $optionalParams[HierarchyBuilder::TEMPLATE]; - } else { - $template = ''; - } - // look for the introtemplate parameter - if ( isset( $optionalParams[HierarchyBuilder::INTROTEMPLATE] ) ) { - $introTemplate = $optionalParams[HierarchyBuilder::INTROTEMPLATE]; - } else { - $introTemplate = ''; - } - // look for the outrotemplate parameter - if ( isset( $optionalParams[HierarchyBuilder::OUTROTEMPLATE] ) ) { - $outroTemplate = $optionalParams[HierarchyBuilder::OUTROTEMPLATE]; - } else { - $outroTemplate = ''; - } - // look for the link parameter - if ( isset( $optionalParams[HierarchyBuilder::LINK] ) ) { - $link = $optionalParams[HierarchyBuilder::LINK]; - } else { - $link = ''; - } - // look for the delimiter parameter - if ( isset( $optionalParams[HierarchyBuilder::SEPARATOR] ) ) { - $delimiter = $optionalParams[HierarchyBuilder::SEPARATOR]; - } else { - if ( $template != '' ) { - $delimiter = ''; - } else { - $delimiter = ','; - } - } - - // find the parents - $parents = HierarchyBuilder::getPageParent( $pageName, $hierarchyPageName, - $hierarchyPropertyName ); - - // format the parents for return according to the optional arg - // this code is the same as below for children - $output = ''; - if ( count( $parents ) > 0 ) { - if ( $template != '' ) { - $intro = $introTemplate != '' ? "{{{$introTemplate}}}\n" : ''; - $outro = $outroTemplate != '' ? "\n{{{$outroTemplate}}}" : ''; - $templateParentString = implode( - array_map( - function( $parent ) use ( $template, $link ) { - if ( $link == 'none' ) { - return "{{" . $template . "|$parent}}"; - } else { - return "{{" . $template . "|[[$parent]]}}"; - } - } , - $parents - ), - "$delimiter\n" - ); - $output = $intro . $templateParentString . $outro; - } else { - $parentString = implode( - array_map( - function( $parent ) use ( $link ) { - return $link == 'none' ? $parent : "[[$parent]]"; - } , - $parents - ), - $delimiter - ); - - $output = $parentString; - } - } - $output = $parser->recursiveTagParse( $output ); - } - return $parser->insertStripItem( $output, $parser->mStripState ); -} - -/** - * This parser function will return the immediate parent of a given page within - * a hierarchy on a page. - * - * 3 unnamed mandatory args: - * - pagename - * - hierarchy page - * - hierarchy property - * - * 5 named optional args: - * - sep = [, | ; | ... ] - * - template = any template taking a single param - * - introtemplate = any template with no params - * - outrotemplate = any tempalte with no params - * - link = [none] - * - * Example invokations: - * @code - * {{#hierarchyChildren:{{FULLPAGENAME}}|hierarchy page|hierarchy property}} - * {{#hierarchyChildren:{{FULLPAGENAME}}|hierarchy page|hierarchy property|sep=,}} - * {{#hierarchyChildren:{{FULLPAGENAME}}|hierarchy page|hierarchy property|sep=,|link=none}} - * {{#hierarchyChildren:{{FULLPAGENAME}}|hierarchy page|hierarchy property|sep=,|template=X|link=none}} - * {{#hierarchyChildren:{{FULLPAGENAME}}|hierarchy page|hierarchy property|sep=,|template=X|introtemplate=Y|outrotemplate=Z|link=none}} - * @endcode - * - * @param $parser: Parser - * - * @return string: The list of children of the specified page within the specified - * hierarchy. The list is returned as wikitext for formatted display according to - * the various separator, template, and link parameters. - */ -function children( $parser ) { - $params = func_get_args(); - if ( count( $params ) < 4 ) { - $output = ''; - } else { - // mandatory arguments - $pageName = $params[1]; - $hierarchyPageName = $params[2]; - $hierarchyPropertyName = $params[3]; - - // look for all the optional args in any order. We really don't care if - // the right combination of optional parameters appears at this point. - // The logic for handling different parameter combinations will happen - // after pulling children when we attempt to return results. - $optionalParams = array_slice( $params, 4 ); - $optionalParams = parseParams( $optionalParams ); - // look for the template parameter - if ( isset( $optionalParams[HierarchyBuilder::TEMPLATE] ) ) { - $template = $optionalParams[HierarchyBuilder::TEMPLATE]; - } else { - $template = ''; - } - // look for the introtemplate parameter - if ( isset( $optionalParams[HierarchyBuilder::INTROTEMPLATE] ) ) { - $introTemplate = $optionalParams[HierarchyBuilder::INTROTEMPLATE]; - } else { - $introTemplate = ''; - } - // look for the outrotemplate parameter - if ( isset( $optionalParams[HierarchyBuilder::OUTROTEMPLATE] ) ) { - $outroTemplate = $optionalParams[HierarchyBuilder::OUTROTEMPLATE]; - } else { - $outroTemplate = ''; - } - // look for the link parameter - if ( isset( $optionalParams[HierarchyBuilder::LINK] ) ) { - $link = $optionalParams[HierarchyBuilder::LINK]; - } else { - $link = ''; - } - // look for the delimiter parameter - if ( isset( $optionalParams[HierarchyBuilder::SEPARATOR] ) ) { - $delimiter = $optionalParams[HierarchyBuilder::SEPARATOR]; - } else { - if ( $template != '' ) { - $delimiter = ''; - } else { - $delimiter = ','; - } - } - - // find the page children - $children = HierarchyBuilder::getPageChildren( $pageName, $hierarchyPageName, - $hierarchyPropertyName ); - - // format the output according to the optional params - $output = ''; - if ( count( $children ) > 0 ) { - if ( $template != '' ) { - $intro = $introTemplate != '' ? "{{{$introTemplate}}}\n" : ''; - $outro = $outroTemplate != '' ? "\n{{{$outroTemplate}}}" : ''; - $templateChildrenString = implode( - array_map( - function( $child ) use ( $template, $link ) { - if ( $link == 'none' ) { - return "{{" . $template . "|$child}}"; - } else { - return "{{" . $template . "|[[$child]]}}"; - } - } , - $children - ), - "$delimiter\n" - ); - $output = $intro . $templateChildrenString . $outro; - } else { - $childrenString = implode( - array_map( - function( $child ) use ( $link ) { - return $link == 'none' ? $child : "[[$child]]"; - } , - $children - ), - $delimiter - ); - - $output = $childrenString; - } - } - $output = $parser->recursiveTagParse( $output ); - } - return $parser->insertStripItem( $output, $parser->mStripState ); -} - -/** - * This parser function displays a breadcrumb for a page within a hierarchy. - * - * The breadcrumb display consists of three pages: - * - previous page in the hierarchy - * - next page in the hierarchy - * - hierarchical parent page in the hierarchy. - * - * There are 4 required parameters for this parser function: - * - pagename - that page who's breadcrumb you want to display. - * - hierarchy page - the page that has the hierarchy on it. - * - hierarchy property - the property containing the hierarchy data on the - * hierarchy page. - * - display name property - the property for display names when using content - * free page naming. - * - * Example Usage: - * @code - * {{#hierarchyBreadcrumb:{{FULLPAGENAME}}|Table of Contents|Hierarchy Data|Name}} - * @endcode - * - * @param $parser Parser - * - * @return string: Wikitext that displays the breadcrumb on the page. - */ -function hierarchyBreadcrumb( $parser ) { - $params = func_get_args(); - if ( count( $params ) < 4 ) { - $output = ""; - } else { - // $parser is always $params[0] - $currentPage = $params[1]; - $hierarchyPage = $params[2]; - $hierarchyProperty = $params[3]; - - $hierarchyBuilder = new HierarchyBuilder; - $output = $hierarchyBuilder->hierarchyBreadcrumb( $currentPage, - $hierarchyPage, $hierarchyProperty ); - $output = $parser->recursiveTagParse( $output ); - } - $parser->disableCache(); - return array( $parser->insertStripItem( $output, $parser->mStripState ), - 'noparse' => false ); -} - -function renderHierarchy( $input, $attributes, $parser, $frame ) { - $hierarchyBuilder = new HierarchyBuilder; - $output = $hierarchyBuilder->renderHierarchy( $input, $attributes, $parser, $frame ); - $parser->disableCache(); - return array( $parser->insertStripItem( $output, $parser->mStripState ), - 'noparse' => false ); -} - -function renderHierarchySelected( $input, $attributes, $parser, $frame ) { - $hierarchyBuilder = new HierarchyBuilder; - $output = $hierarchyBuilder->renderHierarchySelected( $input, $attributes, $parser, - $frame ); - - $parser->disableCache(); - return array( $parser->insertStripItem( $output, $parser->mStripState ), - 'noparse' => false ); -} - -/** - * Helper function for parsing a list of named parser function parameters. - * - * @param array $params: A list of named parameters (e.g. "array('sep=|', 'link=none')) - * - * @return array: Associative array of named parameters. - */ -function parseParams( $params ) { - $paramsArray = array(); - foreach ( $params as $param ) { - $paramsArray += parseParam( $param ); - } - return $paramsArray; -} - -/** - * Helper function for parsing a single named parser function parameter. - * - * @param string $param: A single named parameter (e.g. 'link=none') - * - * @param array: A single element associative array containing the named parameter. - */ -function parseParam( $param ) { - $paramArray = array(); - $ret = preg_split( '/=/', $param, 2 ); - if ( count( $ret ) > 1 ) { - $paramArray[$ret[0]] = $ret[1]; - } else { - $paramArray[$ret[0]] = $ret[0]; - } - return $paramArray; -} - -function wikiLog($className, $methodName, $message) { - wfErrorLog( "[".date("c")."]" . "[".$className."][".$methodName."] " . $message . "\n", '/home/kji/hierarchyBuilder.log' ); -} diff --git a/HierarchyBuilder.class.php b/HierarchyBuilder_body.php similarity index 65% rename from HierarchyBuilder.class.php rename to HierarchyBuilder_body.php index c874696..0f65d33 100644 --- a/HierarchyBuilder.class.php +++ b/HierarchyBuilder_body.php @@ -44,33 +44,165 @@ const COLLAPSED = 'collapsed'; /** - * This function gives the section number for a target page within a - * specific hierarchy on a particular page. + * This parser function will give the section number of a page in a hierarchy. * - * Section numbers are not stored anywhere. The section number must be - * dynamically computed for each row whenever it is needed. As a result, we - * must retrieve the hierarchy that contains the page who's section number - * is being computed. + * The three required arguments are (in order): + * - Full page name + * - Full page name of the page containing the hierarchy + * - Property name of the property containing the hierarchy data * - * @param string $targetPageName: name of the target page for which you want - * the auto-number in the given hierarchyPage returned. - * @param string $hierarchyPageName: name of the page containing the hierarchy - * from which to retrieve numberings. - * @param string $hierarchyPropertyName: name of the property on the hierarchy - * page which contains the hierarchy data. (ex: Hierarchy Data). + * Example invokation: + * @code + * {{#hierarchySectionNumber:{{FULLPAGENAME}}|Table of Contents|Hierarchy Data}} + * @endcode * - * @return string: The section number for the target page or the empty string - * if the target page is not found within the hierarchy. + * @param $parser: Parser + * + * @return string: The section number of the specified page name within the + * specified hierarchy. */ - public static function getPageSectionNumber( - $targetPageName, - $hierarchyPageName, - $hierarchyPropertyName - ) { - $hierarchy = self::getPropertyFromPage( $hierarchyPageName, $hierarchyPropertyName ); - $pageSectionNumber = HierarchyBuilder::getSectionNumberFromHierarchy( $hierarchy, - $targetPageName ); - return $pageSectionNumber; + public static function hierarchySectionNumber( $parser ) { + $params = func_get_args(); + if ( count( $params ) != 4 ) { + $output = ""; + } else { + $pageName = $params[1]; + $hierarchyPageName = $params[2]; + $hierarchyPropertyName = $params[3]; + $output = HierarchyBuilder::getSectionNumberFromHierarchy( + $pageName, + $hierarchyPageName, + $hierarchyPropertyName + ); + } + return $parser->insertStripItem( $output, $parser->mStripState ); + } + + + + /** + * This parser function will return the immediate parent of a given page within + * a hierarchy on a page. + * + * 3 unnamed mandatory args: + * - pagename + * - hierarchy page + * - hierarchy property + * + * 5 named optional args: + * - sep = [, | ; | ... ] + * - template = any template taking a single param + * - introtemplate = any template with no params + * - outrotemplate = any tempalte with no params + * - link = [none] + * + * Example invokations: + * @code + * {{#hierarchyChildren:{{FULLPAGENAME}}|hierarchy page|hierarchy property}} + * {{#hierarchyChildren:{{FULLPAGENAME}}|hierarchy page|hierarchy property|sep=,}} + * {{#hierarchyChildren:{{FULLPAGENAME}}|hierarchy page|hierarchy property|sep=,|link=none}} + * {{#hierarchyChildren:{{FULLPAGENAME}}|hierarchy page|hierarchy property|sep=,|template=X|link=none}} + * {{#hierarchyChildren:{{FULLPAGENAME}}|hierarchy page|hierarchy property|sep=,|template=X|introtemplate=Y|outrotemplate=Z|link=none}} + * @endcode + * + * @param $parser: Parser + * + * @return string: The list of children of the specified page within the specified + * hierarchy. The list is returned as wikitext for formatted display according to + * the various separator, template, and link parameters. + */ + public static function hierarchyChildren( $parser ) { + $params = func_get_args(); + if ( count( $params ) < 4 ) { + $output = ''; + } else { + // mandatory arguments + $pageName = $params[1]; + $hierarchyPageName = $params[2]; + $hierarchyPropertyName = $params[3]; + + // look for all the optional args in any order. We really don't care if + // the right combination of optional parameters appears at this point. + // The logic for handling different parameter combinations will happen + // after pulling children when we attempt to return results. + $optionalParams = array_slice( $params, 4 ); + $optionalParams = self::parseParams( $optionalParams ); + // look for the template parameter + if ( isset( $optionalParams[HierarchyBuilder::TEMPLATE] ) ) { + $template = $optionalParams[HierarchyBuilder::TEMPLATE]; + } else { + $template = ''; + } + // look for the introtemplate parameter + if ( isset( $optionalParams[HierarchyBuilder::INTROTEMPLATE] ) ) { + $introTemplate = $optionalParams[HierarchyBuilder::INTROTEMPLATE]; + } else { + $introTemplate = ''; + } + // look for the outrotemplate parameter + if ( isset( $optionalParams[HierarchyBuilder::OUTROTEMPLATE] ) ) { + $outroTemplate = $optionalParams[HierarchyBuilder::OUTROTEMPLATE]; + } else { + $outroTemplate = ''; + } + // look for the link parameter + if ( isset( $optionalParams[HierarchyBuilder::LINK] ) ) { + $link = $optionalParams[HierarchyBuilder::LINK]; + } else { + $link = ''; + } + // look for the delimiter parameter + if ( isset( $optionalParams[HierarchyBuilder::SEPARATOR] ) ) { + $delimiter = $optionalParams[HierarchyBuilder::SEPARATOR]; + } else { + if ( $template != '' ) { + $delimiter = ''; + } else { + $delimiter = ','; + } + } + + // find the page children + $children = HierarchyBuilder::getPageChildren( $pageName, $hierarchyPageName, + $hierarchyPropertyName ); + + // format the output according to the optional params + $output = ''; + if ( count( $children ) > 0 ) { + if ( $template != '' ) { + $intro = $introTemplate != '' ? "{{{$introTemplate}}}\n" : ''; + $outro = $outroTemplate != '' ? "\n{{{$outroTemplate}}}" : ''; + $templateChildrenString = implode( + array_map( + function( $child ) use ( $template, $link ) { + if ( $link == 'none' ) { + return "{{" . $template . "|$child}}"; + } else { + return "{{" . $template . "|[[$child]]}}"; + } + } , + $children + ), + "$delimiter\n" + ); + $output = $intro . $templateChildrenString . $outro; + } else { + $childrenString = implode( + array_map( + function( $child ) use ( $link ) { + return $link == 'none' ? $child : "[[$child]]"; + } , + $children + ), + $delimiter + ); + + $output = $childrenString; + } + } + $output = $parser->recursiveTagParse( $output ); + } + return $parser->insertStripItem( $output, $parser->mStripState ); } /** @@ -102,7 +234,7 @@ * @return array: A list strings consisting of the hierarchical children of * the target page within the hierarchy. */ - public static function getPageChildren( $targetPageName, $hierarchyPageName, + private static function getPageChildren( $targetPageName, $hierarchyPageName, $hierarchyPropertyName ) { // handle the strange empty target case first @@ -189,6 +321,121 @@ return $children; } + /** + * This parser function will return a list of the immediate children of a given + * page within a hierarchy on a page. The list of chilren will be delimited by + * a specified character or the ',' character by default if no delimiter is given. + * + * 3 unnamed mandatory args: + * - pagename + * - hierarchy page + * - hierarchy property + * + * 1 named optional arg: + * - link = [none] + * + * Example invokations: + * @code + * {{#hierarchyParent:{{FULLPAGENAME}}|hierarchy page name|hierarchy property}} + * {{#hierarchyParent:{{FULLPAGENAME}}|hierarchy page name|hierarchy property|link=none}} + * @endcode + * + * @param $parser Parser + * + * @return string: The parent of the specified page within the specified hierarchy + * as wikitext for formatted display. + */ + public static function hierarchyParent( $parser ) { + $params = func_get_args(); + if ( count( $params ) < 4 ) { + $output = ""; + } else { + // mandatory args + $pageName = $params[1]; + $hierarchyPageName = $params[2]; + $hierarchyPropertyName = $params[3]; + // optional args (just link=none) + $optionalParams = array_slice( $params, 4 ); + $optionalParams = HierarchyBuilder::parseParams( $optionalParams ); + // look for the template parameter + if ( isset( $optionalParams[HierarchyBuilder::TEMPLATE] ) ) { + $template = $optionalParams[HierarchyBuilder::TEMPLATE]; + } else { + $template = ''; + } + // look for the introtemplate parameter + if ( isset( $optionalParams[HierarchyBuilder::INTROTEMPLATE] ) ) { + $introTemplate = $optionalParams[HierarchyBuilder::INTROTEMPLATE]; + } else { + $introTemplate = ''; + } + // look for the outrotemplate parameter + if ( isset( $optionalParams[HierarchyBuilder::OUTROTEMPLATE] ) ) { + $outroTemplate = $optionalParams[HierarchyBuilder::OUTROTEMPLATE]; + } else { + $outroTemplate = ''; + } + // look for the link parameter + if ( isset( $optionalParams[HierarchyBuilder::LINK] ) ) { + $link = $optionalParams[HierarchyBuilder::LINK]; + } else { + $link = ''; + } + // look for the delimiter parameter + if ( isset( $optionalParams[HierarchyBuilder::SEPARATOR] ) ) { + $delimiter = $optionalParams[HierarchyBuilder::SEPARATOR]; + } else { + if ( $template != '' ) { + $delimiter = ''; + } else { + $delimiter = ','; + } + } + + // find the parents + $parents = HierarchyBuilder::getPageParent( $pageName, $hierarchyPageName, + $hierarchyPropertyName ); + + // format the parents for return according to the optional arg + // this code is the same as below for children + $output = ''; + if ( count( $parents ) > 0 ) { + if ( $template != '' ) { + $intro = $introTemplate != '' ? "{{{$introTemplate}}}\n" : ''; + $outro = $outroTemplate != '' ? "\n{{{$outroTemplate}}}" : ''; + $templateParentString = implode( + array_map( + function( $parent ) use ( $template, $link ) { + if ( $link == 'none' ) { + return "{{" . $template . "|$parent}}"; + } else { + return "{{" . $template . "|[[$parent]]}}"; + } + } , + $parents + ), + "$delimiter\n" + ); + $output = $intro . $templateParentString . $outro; + } else { + $parentString = implode( + array_map( + function( $parent ) use ( $link ) { + return $link == 'none' ? $parent : "[[$parent]]"; + } , + $parents + ), + $delimiter + ); + + $output = $parentString; + } + } + $output = $parser->recursiveTagParse( $output ); + } + return $parser->insertStripItem( $output, $parser->mStripState ); + } + /** * Returns the hierarchical parent of a page within a hierarchy. * @@ -210,7 +457,7 @@ * the hierarchy. */ - public static function getPageParent( $targetPageName, $hierarchyPageName, + private static function getPageParent( $targetPageName, $hierarchyPageName, $hierarchyPropertyName ) { $hierarchy = self::getPropertyFromPage( $hierarchyPageName, $hierarchyPropertyName ); @@ -241,6 +488,141 @@ } /** + * This parser function will return only specific selected rows of a hierarchy + * in addition to any necessary contextual rows. + * + * The returned hierarchy is displayd similarly to the HierarchySelectFormInput, + * with each row preceeded by a checkbox. However, the checkboxes will be inactive. + * + * For a given set of selected rows, only those rows will be provided from the + * hierarchy in addition to the minimal necessary contextual rows needed to display + * the hierarchical relationships. For example, if a single selected row is given, + * but that row is a leaf node which is 5 levels deep within the hierarchy, then + * that row will be given along with each of its ancestors. This is conidered the + * "pruned" behavior. + * + * The "collapsed" behavior will not remove any branches of the hierarchy, even + * when those branches do not contain any of the specified selected rows. Instead, + * these unnecessary branches will be collapsed initially, allowing only the + * selected rows and their siblings to be shown. + * + * @param $parser: Parser + * @return I don't know yet. + * + * Example invokation: + * @code + * {{#hierarchySelected:<list of page names>|<hierarchy page name>|<hierarchy property>}} + * {{#hierarchySelected:<list of page names>|<hierarchy page name>|<hierarchy property>|pruned}} + * {{#hierarchySelected:<list of page names>|<hierarchy page name>|<hierarchy property>|collapsed}} + * @endcode + */ + public static function hierarchySelected( $parser ) { + $params = func_get_args(); + if ( count( $params ) < 4) { + $output = ''; + } else { + $selectedPages = $params[1]; + $hierarchyPageName = $params[2]; + $hierarchyPropertyName = $params[3]; + // if "pruned" is given, then set the displaymode to pruned. otherwise, "collapsed" + if ( isset( $params[4] ) && $params[4] == 'collapsed' ) { + $displayMode = 'collapsed'; + } else { + $displayMode = 'pruned'; + } + + $wikitextHierarchy = HierarchyBuilder::getPropertyFromPage( $hierarchyPageName, $hierarchyPropertyName ); + // this is where we ask HierarchyBuilder class to actually do the work for us. + $hierarchyTree = HierarchyTree::fromWikitext( $wikitextHierarchy ); + + $normalizedSelectedPages = + array_map( + function( $page ) { + $pagename = HierarchyBuilder::getPageNameFromHierarchyRow( $page ); + if ( $pagename == '' ) { + $pagename = trim( $page ); + } + return trim( $pagename ); + }, + explode( ',', $selectedPages ) + ); + + $mst = $hierarchyTree->getMST( $normalizedSelectedPages ); + + // output formatting + $flatNormalizedSelectedPages = + array_reduce( $normalizedSelectedPages, + function( $carry, $item ) { + if ( $carry == '' ) { + $carry = $item; + } else { + $carry .= ',' . $item; + } + return $carry; + } + ); + + $selected = htmlspecialchars( str_replace( " ", "%20", $flatNormalizedSelectedPages ) ); + + $output = ''; + if ( $displayMode == 'collapsed') { + $output = "<hierarchySelected collapsed selected=$selected>" . (string)$mst . '</hierarchySelected>'; + } else { + $output = "<hierarchySelected selected=$selected>" . (string)$mst . '</hierarchySelected>'; + } + + $output = $parser->recursiveTagParse( $output ); + + } + return $parser->insertStripItem( $output, $parser->mStripState ); + } + + /** + * This parser function displays a breadcrumb for a page within a hierarchy. + * + * The breadcrumb display consists of three pages: + * - previous page in the hierarchy + * - next page in the hierarchy + * - hierarchical parent page in the hierarchy. + * + * There are 3 required parameters for this parser function: + * - pagename - that page who's breadcrumb you want to display. + * - hierarchy page - the page that has the hierarchy on it. + * - hierarchy property - the property containing the hierarchy data on the + * hierarchy page. + * + * Example Usage: + * @code + * {{#hierarchyBreadcrumb:{{FULLPAGENAME}}|Table of Contents|Hierarchy Data}} + * @endcode + * + * @param $parser Parser + * + * @return string: Wikitext that displays the breadcrumb on the page. + */ + public static function hierarchyBreadcrumb( $parser ) { + $params = func_get_args(); + + + if ( count( $params ) < 4 ) { + $output = ""; + } else { + // $parser is always $params[0] + $currentPage = $params[1]; + $hierarchyPage = $params[2]; + $hierarchyProperty = $params[3]; + + $output = self::constructBreadcrumb( $currentPage, + $hierarchyPage, $hierarchyProperty ); + $output = $parser->recursiveTagParse( $output ); + } + + $parser->disableCache(); + return array( $parser->insertStripItem( $output, $parser->mStripState ), + 'noparse' => false ); + } + + /** * Compute and return the breadcrumb for a given page within a hierarchy. * * This function will compute and return the breadcrumb information for a @@ -261,7 +643,7 @@ * @return string: Formatted wikitext that will format and display the * breadcrumb information on the page. */ - public function hierarchyBreadcrumb( $currentPage, $hierarchyPage, + private static function constructBreadcrumb( $currentPage, $hierarchyPage, $hierarchyProperty ) { $hierarchy = self::getPropertyFromPage( $hierarchyPage, $hierarchyProperty ); @@ -292,11 +674,54 @@ // Note that if there is no hierarchical parent, then the parent will be empty. $parent = self::getParent( $hierarchyRows, $row, $i ); - return self::breadcrumb( $previous, $parent, $next ); + return self::formatBreadcrumb( $previous, $parent, $next ); } } return ''; + } + + /** + * Return the wikitext formatted breadcrumb using the given information. + * + * This function gives formatted wikitext for rendering a breadcrumb trail + * on a wikipage including the previous page, the parent page, and the next + * page within a hierarchy. + * + * @param string $previous: The name of the previous page in the hierarchy. + * @param string $parent: The name of the hierarchical parent page in the + * hierarchy. + * @param string $next: The name of the next page in the hierarchy. + * + * @return string: Formatted wikitext which renders breadcrumbs on a page. + */ + private function formatBreadcrumb( $previous, $parent, $next ) { + $breadcrumb = "{| width='100%'" . PHP_EOL; + if ( $previous != null ) { + if ( $previous == $parent ) { + $arrow = "↑"; + } else { + $arrow = "←"; + } + $breadcrumb .= "| width='33%' | " . $arrow . " [[" . $previous . "| " . + HierarchyBuilder::getPageDisplayName( $previous ) . "]]" . PHP_EOL; + } else { + $breadcrumb .= "| width='33%' | " . PHP_EOL; + } + if ( $parent != null && $parent != $previous ) { + $breadcrumb .= "| align='center' width='33%' | ↑ [[" . $parent . + "| " . HierarchyBuilder::getPageDisplayName( $parent ) . "]]" . PHP_EOL; + } else { + $breadcrumb .= "| width='33%' | " . PHP_EOL; + } + if ( $next != null ) { + $breadcrumb .= "| align='right' width='33%' | [[" . $next . "|" . + HierarchyBuilder::getPageDisplayName( $next ) . "]] →" . PHP_EOL; + } else { + $breadcrumb .= "| width='33%' | " . PHP_EOL; + } + $breadcrumb .= "|}" . PHP_EOL; + return $breadcrumb; } /** @@ -332,7 +757,7 @@ $parentRow = $hierarchyRows[$parentIdx]; $parentDepth = self::getDepthOfHierarchyRow( $parentRow ); - if ( $parentDepth == $currentDepth -1 ) { + if ( $parentDepth == $currentDepth - 1 ) { $parent = self::getPageNameFromHierarchyRow( $parentRow ); break; } @@ -369,7 +794,7 @@ * * @return number: The depth of $hierarchyRow. */ - public static function getDepthOfHierarchyRow( $hierarchyRow ) { + private static function getDepthOfHierarchyRow( $hierarchyRow ) { $numMatches = preg_match_all( self::DEPTHPATTERN, $hierarchyRow, $matches ); $depth = ( $numMatches > 0 ? strlen( $matches[1][0] ) : 0 ); return $depth; @@ -408,49 +833,6 @@ } /** - * Return the wikitext formatted breadcrumb using the given information. - * - * This function gives formatted wikitext for rendering a breadcrumb trail - * on a wikipage including the previous page, the parent page, and the next - * page within a hierarchy. - * - * @param string $previous: The name of the previous page in the hierarchy. - * @param string $parent: The name of the hierarchical parent page in the - * hierarchy. - * @param string $next: The name of the next page in the hierarchy. - * - * @return string: Formatted wikitext which renders breadcrumbs on a page. - */ - private function breadcrumb( $previous, $parent, $next ) { - $breadcrumb = "{| width='100%'" . PHP_EOL; - if ( $previous != null ) { - if ( $previous == $parent ) { - $arrow = "↑"; - } else { - $arrow = "←"; - } - $breadcrumb .= "| width='33%' | " . $arrow . " [[" . $previous . "| " . - HierarchyBuilder::getPageDisplayName( $previous ) . "]]" . PHP_EOL; - } else { - $breadcrumb .= "| width='33%' | " . PHP_EOL; - } - if ( $parent != null && $parent != $previous ) { - $breadcrumb .= "| align='center' width='33%' | ↑ [[" . $parent . - "| " . HierarchyBuilder::getPageDisplayName( $parent ) . "]]" . PHP_EOL; - } else { - $breadcrumb .= "| width='33%' | " . PHP_EOL; - } - if ( $next != null ) { - $breadcrumb .= "| align='right' width='33%' | [[" . $next . "|" . - HierarchyBuilder::getPageDisplayName( $next ) . "]] →" . PHP_EOL; - } else { - $breadcrumb .= "| width='33%' | " . PHP_EOL; - } - $breadcrumb .= "|}" . PHP_EOL; - return $breadcrumb; - } - - /** * Renders a wikitext formatted hierarchy on a page. * * This function implements the hierarchy tag extension for rendering a @@ -468,7 +850,7 @@ * * @return string: Html div that will contain the rendered hierarchy. */ - public function renderHierarchy( $input, $attributes, $parser, $frame ) { + public static function renderHierarchy( $input, $attributes, $parser, $frame ) { $hierarchyName = 'HierarchyDiv' . self::$m_hierarchy_num; self::$m_hierarchy_num++; @@ -518,17 +900,7 @@ $displayName = HierarchyBuilder::getPageDisplayName( $pageName ); - $titleiconArray = array(); - $pagetitleicons = ''; - if ( strlen( $titleiconproperty ) > 0 ) { - $pagetitleicons = HierarchyBuilder::getPageTitleIcons( $pageName, - $titleiconproperty ); - } - - $iconElement = ''; - if ( $pagetitleicons !== '' ) { - $iconElement = HierarchyBuilder::getIconHTML($pagetitleicons); - } + $iconElement = self::getPageTitleIconsHtml( $pageName, $titleiconproperty ); return $iconElement . Html::element( 'a', $pageLinkArray, $displayName ); } ); @@ -547,7 +919,12 @@ $script = Html::inlineScript( $script ); $wgOut->addScript( $script ); - return Html::element( 'div', array( 'id' => $hierarchyName ) ); + //return Html::element( 'div', array( 'id' => $hierarchyName ) ); + + $output = Html::element( 'div', array( 'id' => $hierarchyName ) ); + $parser->disableCache(); + return array( $parser->insertStripItem( $output, $parser->mStripState ), + 'noparse' => false ); } public function renderHierarchySelected( $input, $attributes, $parser, $frame ) { @@ -625,7 +1002,11 @@ $script = Html::inlineScript( $script ); $wgOut->addScript( $script ); - return Html::element( 'div', array( 'id' => $hierarchyName ) ); + $output = Html::element( 'div', array( 'id' => $hierarchyName ) ); + + $parser->disableCache(); + return array( $parser->insertStripItem( $output, $parser->mStripState ), + 'noparse' => false ); } /** @@ -714,8 +1095,7 @@ } catch (Exception $e) { wfLogWarning("[HierarchyBuilder.class.php][getPropertyFromPage] Something broke. Returning an empty string."); return ''; - } - + } } @@ -798,6 +1178,36 @@ } /** + * This function gives the section number for a target page within a + * specific hierarchy on a particular page. + * + * Section numbers are not stored anywhere. The section number must be + * dynamically computed for each row whenever it is needed. As a result, we + * must retrieve the hierarchy that contains the page who's section number + * is being computed. + * + * @param string $targetPageName: name of the target page for which you want + * the auto-number in the given hierarchyPage returned. + * @param string $hierarchyPageName: name of the page containing the hierarchy + * from which to retrieve numberings. + * @param string $hierarchyPropertyName: name of the property on the hierarchy + * page which contains the hierarchy data. (ex: Hierarchy Data). + * + * @return string: The section number for the target page or the empty string + * if the target page is not found within the hierarchy. + */ + /*public static function getPageSectionNumber( + $targetPageName, + $hierarchyPageName, + $hierarchyPropertyName + ) { + $hierarchy = self::getPropertyFromPage( $hierarchyPageName, $hierarchyPropertyName ); + $pageSectionNumber = HierarchyBuilder::getSectionNumberFromHierarchy( $hierarchy, + $targetPageName ); + return $pageSectionNumber; + }*/ + + /** * Returns the section number for a page within a wikitext formatted hierarchy. * * This function will search a hierarchy for a target page name and will @@ -813,9 +1223,14 @@ * * @return string: The section numer of the target page within the hierarchy. */ - public static function getSectionNumberFromHierarchy( $wikiTextHierarchy, $target ) { + private static function getSectionNumberFromHierarchy( + $targetPageName, + $hierarchyPageName, + $hierarchyPropertyName + ) { + $hierarchy = self::getPropertyFromPage( $hierarchyPageName, $hierarchyPropertyName ); $sectionNumber = self::getSectionNumberFromHierarchyHelper( - '[[hierarchy_root]]' . "\n" . $wikiTextHierarchy, '', '', $target ); + '[[hierarchy_root]]' . "\n" . $hierarchy, '', '', $targetPageName ); return $sectionNumber; } @@ -840,17 +1255,20 @@ * we cannot find the page within the hierarchy, then the empty string is * returned instead. */ - private static function getSectionNumberFromHierarchyHelper( $wikiTextHierarchy, $depth, - $sectionNumber, $target + private static function getSectionNumberFromHierarchyHelper( + $wikiTextHierarchy, + $depth, + $sectionNumber, + $target ) { - $rootAndChildren = HierarchyBuilder::splitHierarchy( $wikiTextHierarchy, $depth ); + $rootAndChildren = self::splitHierarchy( $wikiTextHierarchy, $depth ); // this is just the root row of this hierarchy (or subhierarchy) $root = $rootAndChildren[0]; // this is a list of direct children hierarchies of the root. It might // be an empty list though $children = array_slice( $rootAndChildren, 1 ); - $rootPageName = HierarchyBuilder::getPageNameFromHierarchyRow( $root, false ); + $rootPageName = self::getPageNameFromHierarchyRow( $root, false ); // if we are staring at the target then return the current section number for the target if ( $rootPageName == $target ) { @@ -903,6 +1321,104 @@ } /** + * This parser function will return the subhierarchy that is rooted at the specified + * node within a hierarchy. + * + * The three required arguments are (in order): + * - The root node of the subhierarchy within the overall hierarchy. If this + * argument is empty, then the entire hierarchy is returned. + * - Full page name of the page containing the hierarchy + * - Property name of the property containing the hierarchy data + * + * The optional argument is: + * - Format to specify if the results should be returned as a bulleted list as + * opposed to the default striped format. + * + * Example invokation: + * @code + * {{#hierarchySubtree:|Main Page|Hierarchy Data}} + * {{#hierarchySubtree:Hierarchy Builder|Main Page|Hierarchy Data}} + * {{#hierarchySubtree:Hierarchy Builder|Main Page|Hierarchy Data}} + * {{#hierarchySubtree:Hierarchy Builder|Main Page|Hierarchy Data|format=ul}} + * @endcode + * + * @param $parser: Parser + * + * @return string: The string containing the specified subhierarchy as though + * it were a standalone hierarchy. + */ + public static function hierarchySubtree( $parser ) { + $params = func_get_args(); + if ( count( $params ) < 4 ) { + $output = ''; + } else { + $rootNode = $params[1]; + $hierarchyPageName = $params[2]; + $hierarchyPropertyName = $params[3]; + + $optionalParams = array_slice( $params, 4 ); + $optionalParams = HierarchyBuilder::parseParams( $optionalParams ); + $format = ''; + if ( isset( $optionalParams[HierarchyBuilder::FORMAT] ) ) { + $format = $optionalParams[HierarchyBuilder::FORMAT]; + } + $titleiconproperty = ''; + if (isset( $optionalParams[HierarchyBuilder::TITLEICONPROPERTY] ) ) { + $titleiconproperty = $optionalParams[HierarchyBuilder::TITLEICONPROPERTY]; + } + $showroot = ''; + if ( isset( $optionalParams[HierarchyBuilder::SHOWROOT] ) ) { + $showroot = $optionalParams[HierarchyBuilder::SHOWROOT]; + } + if ( $rootNode == '' ) { + $showroot = 'showroot'; + } + $collapsed = ''; + if ( isset( $optionalParams[HierarchyBuilder::COLLAPSED] ) ) { + $collapsed = $optionalParams[HierarchyBuilder::COLLAPSED]; + } + + $output = HierarchyBuilder::getSubhierarchy( + $rootNode, + $hierarchyPageName, + $hierarchyPropertyName + ); + + // this is where we have to handle the default mode which is not showroot and not collapsed + if ( $showroot == '' ) { + // fix $output so only the children are given + $hierarchyrows = preg_split( '/\n/', $output ); + $root = $hierarchyrows[0]; + $children = array_slice( $hierarchyrows, 1 ); + + $depth = HierarchyBuilder::getDepthOfHierarchyRow( $root ); + $output = array_reduce( $children, + function( $carry, $item ) use ( $depth ) { + if ( $carry != '' ) { + $carry .= "\n" . substr( $item, strlen( $depth ) ); + } else { + $carry = substr( $item, strlen( $depth ) ); + } + return $carry; + } + ); + } + + // this is the default output display format + if ( $format != 'ul' ) { + if ( $titleiconproperty != '' ) { + $titleiconproperty = "titleiconproperty=\"$titleiconproperty\""; + } + $output = "<hierarchy $collapsed $titleiconproperty>$output</hierarchy>"; + } + // otherwise it's the bulleted format and we don't modify output. + + $output = $parser->recursiveTagParse( PHP_EOL . $output ); + } + return $parser->insertStripItem( $output, $parser->mStripState ); + } + + /** * This function returns the subhierarchy defined by its root node within * a specific hierarchy on a given page. * @@ -921,13 +1437,17 @@ * subhierarchy within the overall hierarchy who's root is $root. Otherwise, * if no such subhierarchy exists, the empty string is returned instead. */ - public static function getSubhierarchy( $root, $pagename, $propertyname ) { + private static function getSubhierarchy( $root, $pagename, $propertyname ) { $hierarchy = self::getPropertyFromPage( $pagename, $propertyname ); if ( $root == '' ) { return $hierarchy; } else { - return HierarchyBuilder::getSubhierarchyHelper( $root, "[[Hierarchy_Root]]\n" . $hierarchy, '' ); + return HierarchyBuilder::getSubhierarchyHelper( + $root, + "[[Hierarchy_Root]]\n" . $hierarchy, + '' + ); } } @@ -985,13 +1505,6 @@ return ''; } - public static function parseHierarchyToTree( $hierarchyPageName, $hierarchyPropertyName ) { - $hierarchy = HierarchyBuilder::getPropertyFromPage( $hierarchyPageName, $hierarchyPropertyName ); - $hierarchy = "[[Hierarchy_Root]]\n" . $hierarchy; - - HierarchyTree::parseHierarchyToTree( $hierarchy ); - } - /** * This function constructs the img html elements to display each of the * given titleicons. @@ -1000,7 +1513,7 @@ * * @return string: The html for rendering all of the titleicons. */ - public static function getIconHTML( $icons ) { + private static function getIconsHTML( $icons ) { $iconhtmls = array(); foreach ( $icons as $iconinfo ) { @@ -1084,4 +1597,57 @@ return $icons; } + + /** + * This function gives the titleicons in formatted displayable HTML for + * the specified page when using titleicons. + * + * @param string $page: Name of the page. + * @param string $titleIconProperty: Name of the property that stores + * titleicon urls for pages when titleicons are active. + * + * @return array: The pagename, titleiconname pairs for the specified page. + */ + public static function getPageTitleIconsHtml( $page, $titleIconProperty) { + $icons = self::getPageTitleIcons( $page, $titleIconProperty ); + return count($icons) > 0 ? self::getIconsHtml( $icons ) : ''; + } + + /** + * Helper function for parsing a list of named parser function parameters. + * + * @param array $params: A list of named parameters (e.g. "array('sep=|', 'link=none')) + * + * @return array: Associative array of named parameters. + */ + private static function parseParams( $params ) { + $paramsArray = array(); + foreach ( $params as $param ) { + $paramsArray += self::parseParam( $param ); + } + return $paramsArray; + } + + /** + * Helper function for parsing a single named parser function parameter. + * + * @param string $param: A single named parameter (e.g. 'link=none') + * + * @param array: A single element associative array containing the named parameter. + */ + private static function parseParam( $param ) { + $paramArray = array(); + $ret = preg_split( '/=/', $param, 2 ); + if ( count( $ret ) > 1 ) { + $paramArray[$ret[0]] = $ret[1]; + } else { + $paramArray[$ret[0]] = $ret[0]; + } + return $paramArray; + } + + public static function hbLog($className, $methodName, $message) { + wfErrorLog( "[".date("c")."]" . "[".$className."][".$methodName."] " . $message . "\n", '/home/kji/hierarchyBuilder.log' ); + } + } diff --git a/extension.json b/extension.json new file mode 100644 index 0000000..d3a92f0 --- /dev/null +++ b/extension.json @@ -0,0 +1,73 @@ +{ + "name": "HierarchyBuilder", + "version": "3.0.0", + "author": [ + "[https://www.mediawiki.org/wiki/User:Cindy.cicalese Cindy Cicalese]", + "[https://www.mediawiki.org/wiki/User:Kevin.ji Kevin Ji]" + ], + "url": "https://www.mediawiki.org/wiki/Extension:HierarchyBuilder", + "descriptionmsg": "hierarchybuilder-desc", + "type": "parserhook", + "MessagesDirs": { + "HierarchyBuilder": [ + "i18n" + ] + }, + "ExtensionMessagesFiles": { + "HierarchyBuilder": "HierarchyBuilder.i18n.php", + "HierarchyBuilderMagic": "HierarchyBuilder.i18n.magic.php" + }, + "AutoloadClasses": { + "HierarchyBuilder": "HierarchyBuilder_body.php", + "HierarchyBuilderHooks": "HierarchyBuilder.hooks.php", + "HierarchyFormInput": "/includes/HierarchyFormInput.php", + "HierarchySelectFormInput": "/includes/HierarchySelectFormInput.php", + "HierarchyTree": "/includes/HierarchyTree.php", + "TreeNode": "/includes/TreeNode.php" + }, + "callback": "HierarchyBuilderHooks::onRegistration", + "ResourceModules": { + "ext.HierarchyBuilder.jstree": { + "styles": "themes/apple/style.css", + "scripts": "/includes/jquery.jstree.js" + }, + "ext.HierarchyBuilder.render": { + "scripts": "/includes/renderHierarchy.js", + "styles": "/includes/renderHierarchy.css", + "dependencies": [ + "ext.HierarchyBuilder.jstree" + ] + }, + "ext.HierarchyBuilder.renderSelected": { + "scripts": "/includes/renderHierarchySelected.js", + "dependencies": [ + "ext.HierarchyBuilder.jstree" + ] + }, + "ext.HierarchyBuilder.edit": { + "scripts": "/includes/editHierarchy.js", + "styles": "/includes/editHierarchy.css", + "dependencies": [ + "ext.HierarchyBuilder.jstree", + "ext.semanticforms.main" + ] + }, + "ext.HierarchyBuilder.select": { + "scripts": "/includes/selectFromHierarchy.js", + "styles": "/includes/selectFromHierarchy.css", + "dependencies": [ + "ext.HierarchyBuilder.jstree", + "ext.semanticforms.main" + ] + } + }, + "ResourceFileModulePaths": { + "localBasePath": "", + "remoteExtPath": "HierarchyBuilder" + }, + "Hooks": { + "ParserFirstCallInit": [ + "HierarchyBuilderHooks::efHierarchyBuilderSetup" + ] + } +} diff --git a/HierarchyFormInput.php b/includes/HierarchyFormInput.php similarity index 88% rename from HierarchyFormInput.php rename to includes/HierarchyFormInput.php index cb780f6..5a005a0 100644 --- a/HierarchyFormInput.php +++ b/includes/HierarchyFormInput.php @@ -89,23 +89,25 @@ $unusedpagesmessage = wfMessage( 'hierarchybuilder-unusedpages' )->text(); $unusedpages = "<ul>" . - "<li class='hierarchy_root'><a>$unusedpagesmessage</a>" . - "<ul>"; - foreach ( $pages as $key => $value ) { - $name = $value; - $namehtml = "<a>$name<span style=display:none>$key</span></a>"; - - if ($titleiconProperty != '') { - $pagetitleicons = HierarchyBuilder::getPageTitleIcons( $key, $titleiconProperty ); - $pagetitleiconshtml = HierarchyBuilder::getIconHTML( $pagetitleicons ); - } else { - $pagetitleiconshtml = ''; - } + "<li class='hierarchy_root'><a>$unusedpagesmessage</a>"; + if ( count($pages) > 0 ) { + $unusedpages .= "<ul>"; + foreach ( $pages as $key => $value ) { + $name = $value; + $namehtml = "<a>$name<span style=display:none>$key</span></a>"; + + if ($titleiconProperty != '') { + $pagetitleiconshtml = HierarchyBuilder::getPageTitleIconsHtml( $key, $titleiconProperty ); + } else { + $pagetitleiconshtml = ''; + } - $pagerowhtml = "<li>" . $pagetitleiconshtml . $namehtml . "</li>"; - $unusedpages .= $pagerowhtml; + $pagerowhtml = "<li>" . $pagetitleiconshtml . $namehtml . "</li>"; + $unusedpages .= $pagerowhtml; + } + $unusedpages .= "</ul>"; } - $unusedpages .= "</ul></li></ul>"; + $unusedpages .= "</li></ul>"; $hierarchy = $this->wikitext2Html($this->mCurrentValue, $titleiconProperty); @@ -115,7 +117,6 @@ $jsattribs = array( 'divId' => $this->mDivId, 'hierarchy' => $hierarchy, - //'pages' => $pages, 'pages' => $unusedpages, 'isDisabled' => $this->mIsDisabled, 'hideinfo' => $hideinfoProperty, @@ -152,6 +153,7 @@ $nummatches = preg_match_all( $childdepthpattern, $subhierarchy, $matches ); $childrows = $nummatches > 0 ? $matches[0] : array(); $childsubhierarchies = array_slice( preg_split( $childdepthpattern, $subhierarchy ), 1 ); // chop off element 0 which is the root + $numchildren = count($childrows); //extract the root pagename $numMatches = preg_match_all( HierarchyBuilder::PAGENAMEPATTERN, $rootrow, $matches ); @@ -161,8 +163,7 @@ } else { if ($titleiconproperty != '') { - $roottitleicons = HierarchyBuilder::getPageTitleIcons( $rootpagename, $titleiconproperty ); - $roottitleiconshtml = HierarchyBuilder::getIconHTML( $roottitleicons ); + $roottitleiconshtml = HierarchyBuilder::getPageTitleIconsHtml( $rootpagename, $titleiconproperty ); } else { $roottitleiconshtml = ''; } @@ -173,9 +174,9 @@ $html = $depth == 0 ? "<li class='hierarchy_root'>" : '<li>'; $html .= $rootHtml; - if ( count($childrows) > 0 ) { + if ( $numchildren > 0 ) { $html .= '<ul>'; - for ( $i = 0; $i < count($childrows); $i++ ) { + for ( $i = 0; $i < $numchildren; $i++ ) { $childhierarchy = $childrows[$i] . "\n" . $childsubhierarchies[$i]; $html .= $this->wikitext2HtmlHelper($childhierarchy, $depth+1, $titleiconproperty); } diff --git a/HierarchySelectFormInput.php b/includes/HierarchySelectFormInput.php similarity index 100% rename from HierarchySelectFormInput.php rename to includes/HierarchySelectFormInput.php diff --git a/HierarchyTree.php b/includes/HierarchyTree.php similarity index 100% rename from HierarchyTree.php rename to includes/HierarchyTree.php diff --git a/TreeNode.php b/includes/TreeNode.php similarity index 100% rename from TreeNode.php rename to includes/TreeNode.php diff --git a/editHierarchy.css b/includes/editHierarchy.css similarity index 100% rename from editHierarchy.css rename to includes/editHierarchy.css diff --git a/editHierarchy.js b/includes/editHierarchy.js similarity index 97% rename from editHierarchy.js rename to includes/editHierarchy.js index 3f02c6b..34e148e 100644 --- a/editHierarchy.js +++ b/includes/editHierarchy.js @@ -81,6 +81,9 @@ var hierarchy = params.hierarchy; var unusedpages = params.unusedpages; + console.log("[editHierarchy.js][init] hierarchy = " + hierarchy); + console.log("[editHierarchy.js][init] pages = " + params.pages); + var jqDivId = params.divId; var hierarchyDivId = jqDivId + "_hierarchy"; var pageListDivId = jqDivId + "_pagelist"; @@ -88,7 +91,7 @@ var innerframeattr = "class='hierarchy_inner' width='50%;'"; var html = "<div class='hierarchy_outer' dir='ltr'>"; - if ( (params.hierarchy.length < 1) && (params.pages.length < 1) ) { + if ( ($(params.hierarchy).children().children("ul").length < 1) && ($(params.pages).children().children("ul").length < 1) ) { html += "<p>" + params.errormessage + "</p>"; } else { if (params.hideinfo == "false") { diff --git a/jquery.jstree.js b/includes/jquery.jstree.js similarity index 100% rename from jquery.jstree.js rename to includes/jquery.jstree.js diff --git a/renderHierarchy.css b/includes/renderHierarchy.css similarity index 100% rename from renderHierarchy.css rename to includes/renderHierarchy.css diff --git a/renderHierarchy.js b/includes/renderHierarchy.js similarity index 100% rename from renderHierarchy.js rename to includes/renderHierarchy.js diff --git a/renderHierarchySelected.js b/includes/renderHierarchySelected.js similarity index 100% rename from renderHierarchySelected.js rename to includes/renderHierarchySelected.js diff --git a/selectFromHierarchy.css b/includes/selectFromHierarchy.css similarity index 100% rename from selectFromHierarchy.css rename to includes/selectFromHierarchy.css diff --git a/selectFromHierarchy.js b/includes/selectFromHierarchy.js similarity index 100% rename from selectFromHierarchy.js rename to includes/selectFromHierarchy.js -- To view, visit https://gerrit.wikimedia.org/r/265261 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: I1d9e8727fd60ce481cca1dab4d0ed0b86dc7229f Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/HierarchyBuilder Gerrit-Branch: master Gerrit-Owner: Kji <k...@mitre.org> Gerrit-Reviewer: Kji <k...@mitre.org> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits