Jack Phoenix has uploaded a new change for review. https://gerrit.wikimedia.org/r/108045
Change subject: New class for parsing MediaWiki:Sidebar-like messages into modern, nested navigation menus. ...................................................................... New class for parsing MediaWiki:Sidebar-like messages into modern, nested navigation menus. This allows to reduce the amount of code duplication needed by custom skins, as well as ease the porting of WordPress themes into MediaWiki (many WordPress themes feature a nested navigation menu). Currently the Nimbus and Monaco skins implement a similar feature, just without this class. Based on NavigationService by various Wikia developers. See https://www.mediawiki.org/wiki/Manual:NestedMenuParser for some more documentation and an example on how to use this class with a custom skin. Change-Id: Ib8dc2787f70dc80be6ba9d3c962edc1463247263 --- M RELEASE-NOTES-1.23 M includes/AutoLoader.php A includes/NestedMenuParser.php 3 files changed, 200 insertions(+), 0 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core refs/changes/45/108045/1 diff --git a/RELEASE-NOTES-1.23 b/RELEASE-NOTES-1.23 index ca8c91b..49d85cf 100644 --- a/RELEASE-NOTES-1.23 +++ b/RELEASE-NOTES-1.23 @@ -75,6 +75,10 @@ * WikitextContent will now render redirects with the expected "redirect" header, rather than as an ordered list. Code calling Article::viewRedirect can probably be changed to no longer special-case redirects. +* Added the NestedMenuParser class to allow skins to turn interface messages + formatted similarily to MediaWiki:Sidebar into nested navigation menus. + This allows third-party skins to create navigation menus similar to those + found on various custom WordPress themes. === Bug fixes in 1.23 === * (bug 41759) The "updated since last visit" markers (on history pages, recent diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index bab00f9..79c2ea0 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -153,6 +153,7 @@ 'MWHttpRequest' => 'includes/HttpFunctions.php', 'MWInit' => 'includes/Init.php', 'MWNamespace' => 'includes/Namespace.php', + 'NestedMenuParser' => 'includes/NestedMenuParser.php', 'OutputPage' => 'includes/OutputPage.php', 'Page' => 'includes/WikiPage.php', 'PageQueryPage' => 'includes/PageQueryPage.php', diff --git a/includes/NestedMenuParser.php b/includes/NestedMenuParser.php new file mode 100644 index 0000000..1a0c705 --- /dev/null +++ b/includes/NestedMenuParser.php @@ -0,0 +1,195 @@ +<?php +/** + * A more advanced parser for parsing messages like MediaWiki:Sidebar into + * pretty, modern, nested navigation menus. + * + * This has been forked from Oasis' NavigationService. + * The class name was changed, "magic word" handling was removed from + * parseMessage() and some (related) unused functions were also removed. + * + * @file + * @since 1.23 + * @author Inez KorczyĆski + * @see https://github.com/Wikia/app/blob/release-136.020/includes/wikia/services/NavigationService.class.php + * @see https://github.com/Wikia/app/commit/5b132a9dfff4f87c544791295749e44e4b724b92 + * @see Skin::buildSidebar(), Skin::addToSidebar(), Skin::addToSidebarPlain() + */ +class NestedMenuParser { + + /** + * Is the message we're supposed to parse in the wiki's content language + * (true) or not? + * @var bool $forContent + */ + private $forContent = false; + + /** + * Internal version number used to create the memcached keys in parseMessage() + */ + const version = '0.01'; + + /** + * Parses a system message by exploding along newlines. + * + * @param string $messageName Name of the MediaWiki message to parse + * @param array $maxChildrenAtLevel + * @param int $duration Cache duration for memcached calls + * @param bool $forContent Is the message we're supposed to parse in the + * wiki's content language (true) or not? + * @return Array + */ + public function parseMessage( $messageName, $maxChildrenAtLevel = array(), $duration, $forContent = false ) { + wfProfileIn( __METHOD__ ); + + global $wgLang, $wgContLang, $wgMemc; + + $this->forContent = $forContent; + + $useCache = $wgLang->getCode() == $wgContLang->getCode(); + + if ( $useCache || $this->forContent ) { + $cacheKey = wfMemcKey( $messageName, self::version ); + $nodes = $wgMemc->get( $cacheKey ); + } + + if ( empty( $nodes ) ) { + if ( $this->forContent ) { + $lines = explode( "\n", wfMessage( $messageName )->inContentLanguage()->text() ); + } else { + $lines = explode( "\n", wfMessage( $messageName )->text() ); + } + $nodes = $this->parseLines( $lines, $maxChildrenAtLevel ); + + if ( $useCache || $this->forContent ) { + $wgMemc->set( $cacheKey, $nodes, $duration ); + } + } + + wfProfileOut( __METHOD__ ); + return $nodes; + } + + /** + * Function used by parseMessage() above. + * + * @param $lines String: newline-separated lines from the supplied MW: msg + * @param $maxChildrenAtLevel Array: + * @return Array + */ + private function parseLines( $lines, $maxChildrenAtLevel = array() ) { + wfProfileIn( __METHOD__ ); + + $nodes = array(); + + if ( is_array( $lines ) && count( $lines ) > 0 ) { + $lastDepth = 0; + $i = 0; + $lastSkip = null; + + foreach ( $lines as $line ) { + // we are interested only in lines that are not empty and start with asterisk + if ( trim( $line ) != '' && $line{0} == '*' ) { + $depth = strrpos( $line, '*' ) + 1; + + if ( $lastSkip !== null && $depth >= $lastSkip ) { + continue; + } else { + $lastSkip = null; + } + + if ( $depth == $lastDepth + 1 ) { + $parentIndex = $i; + } elseif ( $depth == $lastDepth ) { + $parentIndex = $nodes[$i]['parentIndex']; + } else { + for ( $x = $i; $x >= 0; $x-- ) { + if ( $x == 0 ) { + $parentIndex = 0; + break; + } + if ( $nodes[$x]['depth'] <= $depth - 1 ) { + $parentIndex = $x; + break; + } + } + } + + if ( isset( $maxChildrenAtLevel[$depth - 1] ) ) { + if ( isset( $nodes[$parentIndex]['children'] ) ) { + if ( count( $nodes[$parentIndex]['children'] ) >= $maxChildrenAtLevel[$depth - 1] ) { + $lastSkip = $depth; + continue; + } + } + } + + $node = $this->parseOneLine( $line ); + $node['parentIndex'] = $parentIndex; + $node['depth'] = $depth; + + $nodes[$node['parentIndex']]['children'][] = $i + 1; + $nodes[$i + 1] = $node; + $lastDepth = $node['depth']; + $i++; + } + } + } + + wfProfileOut( __METHOD__ ); + return $nodes; + } + + /** + * @param string $line Line to parse + * @return Array containing original, text and href keys (original + */ + private function parseOneLine( $line ) { + wfProfileIn( __METHOD__ ); + + // trim spaces and asterisks from line and then split it to maximum two chunks + $lineArr = explode( '|', trim( $line, '* ' ), 2 ); + + // trim [ and ] from line to have just http://en.wikipedia.org instead of [http://en.wikipedia.org] for external links + $lineArr[0] = trim( $lineArr[0], '[]' ); + + if ( count( $lineArr ) == 2 && $lineArr[1] != '' ) { + $link = trim( wfMessage( $lineArr[0] )->inContentLanguage()->text() ); + $desc = trim( $lineArr[1] ); + } else { + $link = $desc = trim( $lineArr[0] ); + } + + $text = $this->forContent ? wfMessage( $desc )->inContentLanguage() : wfMessage( $desc ); + if ( $text->isDisabled() ) { + $text = $desc; + } + + if ( wfMessage( $lineArr[0] )->isDisabled() ) { + $link = $lineArr[0]; + } + + if ( preg_match( '/^(?:' . wfUrlProtocols() . ')/', $link ) ) { + $href = $link; + } else { + if ( empty( $link ) ) { + $href = '#'; + } elseif ( $link{0} == '#' ) { + $href = '#'; + } else { + $title = Title::newFromText( $link ); + if ( is_object( $title ) ) { + $href = $title->fixSpecialName()->getLocalURL(); + } else { + $href = '#'; + } + } + } + + wfProfileOut( __METHOD__ ); + return array( + 'original' => $lineArr[0], + 'text' => $text, + 'href' => $href + ); + } +} \ No newline at end of file -- To view, visit https://gerrit.wikimedia.org/r/108045 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Ib8dc2787f70dc80be6ba9d3c962edc1463247263 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/core Gerrit-Branch: master Gerrit-Owner: Jack Phoenix <j...@countervandalism.net> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits