summaryrefslogtreecommitdiff
path: root/includes/skins
diff options
context:
space:
mode:
Diffstat (limited to 'includes/skins')
-rw-r--r--includes/skins/BaseTemplate.php644
-rw-r--r--includes/skins/MediaWikiI18N.php52
-rw-r--r--includes/skins/QuickTemplate.php194
-rw-r--r--includes/skins/Skin.php176
-rw-r--r--includes/skins/SkinApi.php71
-rw-r--r--includes/skins/SkinApiTemplate.php63
-rw-r--r--includes/skins/SkinFactory.php110
-rw-r--r--includes/skins/SkinFallbackTemplate.php29
-rw-r--r--includes/skins/SkinTemplate.php908
9 files changed, 1186 insertions, 1061 deletions
diff --git a/includes/skins/BaseTemplate.php b/includes/skins/BaseTemplate.php
new file mode 100644
index 00000000..25df0f93
--- /dev/null
+++ b/includes/skins/BaseTemplate.php
@@ -0,0 +1,644 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * New base template for a skin's template extended from QuickTemplate
+ * this class features helper methods that provide common ways of interacting
+ * with the data stored in the QuickTemplate
+ */
+abstract class BaseTemplate extends QuickTemplate {
+
+ /**
+ * Get a Message object with its context set
+ *
+ * @param string $name Message name
+ * @return Message
+ */
+ public function getMsg( $name ) {
+ return $this->getSkin()->msg( $name );
+ }
+
+ function msg( $str ) {
+ echo $this->getMsg( $str )->escaped();
+ }
+
+ function msgHtml( $str ) {
+ echo $this->getMsg( $str )->text();
+ }
+
+ function msgWiki( $str ) {
+ echo $this->getMsg( $str )->parseAsBlock();
+ }
+
+ /**
+ * Create an array of common toolbox items from the data in the quicktemplate
+ * stored by SkinTemplate.
+ * The resulting array is built according to a format intended to be passed
+ * through makeListItem to generate the html.
+ * @return array
+ */
+ function getToolbox() {
+
+ $toolbox = array();
+ if ( isset( $this->data['nav_urls']['whatlinkshere'] )
+ && $this->data['nav_urls']['whatlinkshere']
+ ) {
+ $toolbox['whatlinkshere'] = $this->data['nav_urls']['whatlinkshere'];
+ $toolbox['whatlinkshere']['id'] = 't-whatlinkshere';
+ }
+ if ( isset( $this->data['nav_urls']['recentchangeslinked'] )
+ && $this->data['nav_urls']['recentchangeslinked']
+ ) {
+ $toolbox['recentchangeslinked'] = $this->data['nav_urls']['recentchangeslinked'];
+ $toolbox['recentchangeslinked']['msg'] = 'recentchangeslinked-toolbox';
+ $toolbox['recentchangeslinked']['id'] = 't-recentchangeslinked';
+ }
+ if ( isset( $this->data['feeds'] ) && $this->data['feeds'] ) {
+ $toolbox['feeds']['id'] = 'feedlinks';
+ $toolbox['feeds']['links'] = array();
+ foreach ( $this->data['feeds'] as $key => $feed ) {
+ $toolbox['feeds']['links'][$key] = $feed;
+ $toolbox['feeds']['links'][$key]['id'] = "feed-$key";
+ $toolbox['feeds']['links'][$key]['rel'] = 'alternate';
+ $toolbox['feeds']['links'][$key]['type'] = "application/{$key}+xml";
+ $toolbox['feeds']['links'][$key]['class'] = 'feedlink';
+ }
+ }
+ foreach ( array( 'contributions', 'log', 'blockip', 'emailuser',
+ 'userrights', 'upload', 'specialpages' ) as $special
+ ) {
+ if ( isset( $this->data['nav_urls'][$special] ) && $this->data['nav_urls'][$special] ) {
+ $toolbox[$special] = $this->data['nav_urls'][$special];
+ $toolbox[$special]['id'] = "t-$special";
+ }
+ }
+ if ( isset( $this->data['nav_urls']['print'] ) && $this->data['nav_urls']['print'] ) {
+ $toolbox['print'] = $this->data['nav_urls']['print'];
+ $toolbox['print']['id'] = 't-print';
+ $toolbox['print']['rel'] = 'alternate';
+ $toolbox['print']['msg'] = 'printableversion';
+ }
+ if ( isset( $this->data['nav_urls']['permalink'] ) && $this->data['nav_urls']['permalink'] ) {
+ $toolbox['permalink'] = $this->data['nav_urls']['permalink'];
+ if ( $toolbox['permalink']['href'] === '' ) {
+ unset( $toolbox['permalink']['href'] );
+ $toolbox['ispermalink']['tooltiponly'] = true;
+ $toolbox['ispermalink']['id'] = 't-ispermalink';
+ $toolbox['ispermalink']['msg'] = 'permalink';
+ } else {
+ $toolbox['permalink']['id'] = 't-permalink';
+ }
+ }
+ if ( isset( $this->data['nav_urls']['info'] ) && $this->data['nav_urls']['info'] ) {
+ $toolbox['info'] = $this->data['nav_urls']['info'];
+ $toolbox['info']['id'] = 't-info';
+ }
+
+ Hooks::run( 'BaseTemplateToolbox', array( &$this, &$toolbox ) );
+ return $toolbox;
+ }
+
+ /**
+ * Create an array of personal tools items from the data in the quicktemplate
+ * stored by SkinTemplate.
+ * The resulting array is built according to a format intended to be passed
+ * through makeListItem to generate the html.
+ * This is in reality the same list as already stored in personal_urls
+ * however it is reformatted so that you can just pass the individual items
+ * to makeListItem instead of hardcoding the element creation boilerplate.
+ * @return array
+ */
+ function getPersonalTools() {
+ $personal_tools = array();
+ foreach ( $this->get( 'personal_urls' ) as $key => $plink ) {
+ # The class on a personal_urls item is meant to go on the <a> instead
+ # of the <li> so we have to use a single item "links" array instead
+ # of using most of the personal_url's keys directly.
+ $ptool = array(
+ 'links' => array(
+ array( 'single-id' => "pt-$key" ),
+ ),
+ 'id' => "pt-$key",
+ );
+ if ( isset( $plink['active'] ) ) {
+ $ptool['active'] = $plink['active'];
+ }
+ foreach ( array( 'href', 'class', 'text', 'dir' ) as $k ) {
+ if ( isset( $plink[$k] ) ) {
+ $ptool['links'][0][$k] = $plink[$k];
+ }
+ }
+ $personal_tools[$key] = $ptool;
+ }
+ return $personal_tools;
+ }
+
+ function getSidebar( $options = array() ) {
+ // Force the rendering of the following portals
+ $sidebar = $this->data['sidebar'];
+ if ( !isset( $sidebar['SEARCH'] ) ) {
+ $sidebar['SEARCH'] = true;
+ }
+ if ( !isset( $sidebar['TOOLBOX'] ) ) {
+ $sidebar['TOOLBOX'] = true;
+ }
+ if ( !isset( $sidebar['LANGUAGES'] ) ) {
+ $sidebar['LANGUAGES'] = true;
+ }
+
+ if ( !isset( $options['search'] ) || $options['search'] !== true ) {
+ unset( $sidebar['SEARCH'] );
+ }
+ if ( isset( $options['toolbox'] ) && $options['toolbox'] === false ) {
+ unset( $sidebar['TOOLBOX'] );
+ }
+ if ( isset( $options['languages'] ) && $options['languages'] === false ) {
+ unset( $sidebar['LANGUAGES'] );
+ }
+
+ $boxes = array();
+ foreach ( $sidebar as $boxName => $content ) {
+ if ( $content === false ) {
+ continue;
+ }
+ switch ( $boxName ) {
+ case 'SEARCH':
+ // Search is a special case, skins should custom implement this
+ $boxes[$boxName] = array(
+ 'id' => 'p-search',
+ 'header' => $this->getMsg( 'search' )->text(),
+ 'generated' => false,
+ 'content' => true,
+ );
+ break;
+ case 'TOOLBOX':
+ $msgObj = $this->getMsg( 'toolbox' );
+ $boxes[$boxName] = array(
+ 'id' => 'p-tb',
+ 'header' => $msgObj->exists() ? $msgObj->text() : 'toolbox',
+ 'generated' => false,
+ 'content' => $this->getToolbox(),
+ );
+ break;
+ case 'LANGUAGES':
+ if ( $this->data['language_urls'] ) {
+ $msgObj = $this->getMsg( 'otherlanguages' );
+ $boxes[$boxName] = array(
+ 'id' => 'p-lang',
+ 'header' => $msgObj->exists() ? $msgObj->text() : 'otherlanguages',
+ 'generated' => false,
+ 'content' => $this->data['language_urls'],
+ );
+ }
+ break;
+ default:
+ $msgObj = $this->getMsg( $boxName );
+ $boxes[$boxName] = array(
+ 'id' => "p-$boxName",
+ 'header' => $msgObj->exists() ? $msgObj->text() : $boxName,
+ 'generated' => true,
+ 'content' => $content,
+ );
+ break;
+ }
+ }
+
+ // HACK: Compatibility with extensions still using SkinTemplateToolboxEnd
+ $hookContents = null;
+ if ( isset( $boxes['TOOLBOX'] ) ) {
+ ob_start();
+ // We pass an extra 'true' at the end so extensions using BaseTemplateToolbox
+ // can abort and avoid outputting double toolbox links
+ Hooks::run( 'SkinTemplateToolboxEnd', array( &$this, true ) );
+ $hookContents = ob_get_contents();
+ ob_end_clean();
+ if ( !trim( $hookContents ) ) {
+ $hookContents = null;
+ }
+ }
+ // END hack
+
+ if ( isset( $options['htmlOnly'] ) && $options['htmlOnly'] === true ) {
+ foreach ( $boxes as $boxName => $box ) {
+ if ( is_array( $box['content'] ) ) {
+ $content = '<ul>';
+ foreach ( $box['content'] as $key => $val ) {
+ $content .= "\n " . $this->makeListItem( $key, $val );
+ }
+ // HACK, shove the toolbox end onto the toolbox if we're rendering itself
+ if ( $hookContents ) {
+ $content .= "\n $hookContents";
+ }
+ // END hack
+ $content .= "\n</ul>\n";
+ $boxes[$boxName]['content'] = $content;
+ }
+ }
+ } else {
+ if ( $hookContents ) {
+ $boxes['TOOLBOXEND'] = array(
+ 'id' => 'p-toolboxend',
+ 'header' => $boxes['TOOLBOX']['header'],
+ 'generated' => false,
+ 'content' => "<ul>{$hookContents}</ul>",
+ );
+ // HACK: Make sure that TOOLBOXEND is sorted next to TOOLBOX
+ $boxes2 = array();
+ foreach ( $boxes as $key => $box ) {
+ if ( $key === 'TOOLBOXEND' ) {
+ continue;
+ }
+ $boxes2[$key] = $box;
+ if ( $key === 'TOOLBOX' ) {
+ $boxes2['TOOLBOXEND'] = $boxes['TOOLBOXEND'];
+ }
+ }
+ $boxes = $boxes2;
+ // END hack
+ }
+ }
+
+ return $boxes;
+ }
+
+ /**
+ * @param string $name
+ */
+ protected function renderAfterPortlet( $name ) {
+ $content = '';
+ Hooks::run( 'BaseTemplateAfterPortlet', array( $this, $name, &$content ) );
+
+ if ( $content !== '' ) {
+ echo "<div class='after-portlet after-portlet-$name'>$content</div>";
+ }
+
+ }
+
+ /**
+ * Makes a link, usually used by makeListItem to generate a link for an item
+ * in a list used in navigation lists, portlets, portals, sidebars, etc...
+ *
+ * @param string $key Usually a key from the list you are generating this
+ * link from.
+ * @param array $item Contains some of a specific set of keys.
+ *
+ * The text of the link will be generated either from the contents of the
+ * "text" key in the $item array, if a "msg" key is present a message by
+ * that name will be used, and if neither of those are set the $key will be
+ * used as a message name.
+ *
+ * If a "href" key is not present makeLink will just output htmlescaped text.
+ * The "href", "id", "class", "rel", and "type" keys are used as attributes
+ * for the link if present.
+ *
+ * If an "id" or "single-id" (if you don't want the actual id to be output
+ * on the link) is present it will be used to generate a tooltip and
+ * accesskey for the link.
+ *
+ * The keys "context" and "primary" are ignored; these keys are used
+ * internally by skins and are not supposed to be included in the HTML
+ * output.
+ *
+ * If you don't want an accesskey, set $item['tooltiponly'] = true;
+ *
+ * @param array $options Can be used to affect the output of a link.
+ * Possible options are:
+ * - 'text-wrapper' key to specify a list of elements to wrap the text of
+ * a link in. This should be an array of arrays containing a 'tag' and
+ * optionally an 'attributes' key. If you only have one element you don't
+ * need to wrap it in another array. eg: To use <a><span>...</span></a>
+ * in all links use array( 'text-wrapper' => array( 'tag' => 'span' ) )
+ * for your options.
+ * - 'link-class' key can be used to specify additional classes to apply
+ * to all links.
+ * - 'link-fallback' can be used to specify a tag to use instead of "<a>"
+ * if there is no link. eg: If you specify 'link-fallback' => 'span' than
+ * any non-link will output a "<span>" instead of just text.
+ *
+ * @return string
+ */
+ function makeLink( $key, $item, $options = array() ) {
+ if ( isset( $item['text'] ) ) {
+ $text = $item['text'];
+ } else {
+ $text = $this->translator->translate( isset( $item['msg'] ) ? $item['msg'] : $key );
+ }
+
+ $html = htmlspecialchars( $text );
+
+ if ( isset( $options['text-wrapper'] ) ) {
+ $wrapper = $options['text-wrapper'];
+ if ( isset( $wrapper['tag'] ) ) {
+ $wrapper = array( $wrapper );
+ }
+ while ( count( $wrapper ) > 0 ) {
+ $element = array_pop( $wrapper );
+ $html = Html::rawElement( $element['tag'], isset( $element['attributes'] )
+ ? $element['attributes']
+ : null, $html );
+ }
+ }
+
+ if ( isset( $item['href'] ) || isset( $options['link-fallback'] ) ) {
+ $attrs = $item;
+ foreach ( array( 'single-id', 'text', 'msg', 'tooltiponly', 'context', 'primary' ) as $k ) {
+ unset( $attrs[$k] );
+ }
+
+ if ( isset( $item['id'] ) && !isset( $item['single-id'] ) ) {
+ $item['single-id'] = $item['id'];
+ }
+ if ( isset( $item['single-id'] ) ) {
+ if ( isset( $item['tooltiponly'] ) && $item['tooltiponly'] ) {
+ $title = Linker::titleAttrib( $item['single-id'] );
+ if ( $title !== false ) {
+ $attrs['title'] = $title;
+ }
+ } else {
+ $tip = Linker::tooltipAndAccesskeyAttribs( $item['single-id'] );
+ if ( isset( $tip['title'] ) && $tip['title'] !== false ) {
+ $attrs['title'] = $tip['title'];
+ }
+ if ( isset( $tip['accesskey'] ) && $tip['accesskey'] !== false ) {
+ $attrs['accesskey'] = $tip['accesskey'];
+ }
+ }
+ }
+ if ( isset( $options['link-class'] ) ) {
+ if ( isset( $attrs['class'] ) ) {
+ $attrs['class'] .= " {$options['link-class']}";
+ } else {
+ $attrs['class'] = $options['link-class'];
+ }
+ }
+ $html = Html::rawElement( isset( $attrs['href'] )
+ ? 'a'
+ : $options['link-fallback'], $attrs, $html );
+ }
+
+ return $html;
+ }
+
+ /**
+ * Generates a list item for a navigation, portlet, portal, sidebar... list
+ *
+ * @param string $key Usually a key from the list you are generating this link from.
+ * @param array $item Array of list item data containing some of a specific set of keys.
+ * The "id", "class" and "itemtitle" keys will be used as attributes for the list item,
+ * if "active" contains a value of true a "active" class will also be appended to class.
+ *
+ * @param array $options
+ *
+ * If you want something other than a "<li>" you can pass a tag name such as
+ * "tag" => "span" in the $options array to change the tag used.
+ * link/content data for the list item may come in one of two forms
+ * A "links" key may be used, in which case it should contain an array with
+ * a list of links to include inside the list item, see makeLink for the
+ * format of individual links array items.
+ *
+ * Otherwise the relevant keys from the list item $item array will be passed
+ * to makeLink instead. Note however that "id" and "class" are used by the
+ * list item directly so they will not be passed to makeLink
+ * (however the link will still support a tooltip and accesskey from it)
+ * If you need an id or class on a single link you should include a "links"
+ * array with just one link item inside of it. If you want to add a title
+ * to the list item itself, you can set "itemtitle" to the value.
+ * $options is also passed on to makeLink calls
+ *
+ * @return string
+ */
+ function makeListItem( $key, $item, $options = array() ) {
+ if ( isset( $item['links'] ) ) {
+ $links = array();
+ foreach ( $item['links'] as $linkKey => $link ) {
+ $links[] = $this->makeLink( $linkKey, $link, $options );
+ }
+ $html = implode( ' ', $links );
+ } else {
+ $link = $item;
+ // These keys are used by makeListItem and shouldn't be passed on to the link
+ foreach ( array( 'id', 'class', 'active', 'tag', 'itemtitle' ) as $k ) {
+ unset( $link[$k] );
+ }
+ if ( isset( $item['id'] ) && !isset( $item['single-id'] ) ) {
+ // The id goes on the <li> not on the <a> for single links
+ // but makeSidebarLink still needs to know what id to use when
+ // generating tooltips and accesskeys.
+ $link['single-id'] = $item['id'];
+ }
+ $html = $this->makeLink( $key, $link, $options );
+ }
+
+ $attrs = array();
+ foreach ( array( 'id', 'class' ) as $attr ) {
+ if ( isset( $item[$attr] ) ) {
+ $attrs[$attr] = $item[$attr];
+ }
+ }
+ if ( isset( $item['active'] ) && $item['active'] ) {
+ if ( !isset( $attrs['class'] ) ) {
+ $attrs['class'] = '';
+ }
+ $attrs['class'] .= ' active';
+ $attrs['class'] = trim( $attrs['class'] );
+ }
+ if ( isset( $item['itemtitle'] ) ) {
+ $attrs['title'] = $item['itemtitle'];
+ }
+ return Html::rawElement( isset( $options['tag'] ) ? $options['tag'] : 'li', $attrs, $html );
+ }
+
+ function makeSearchInput( $attrs = array() ) {
+ $realAttrs = array(
+ 'type' => 'search',
+ 'name' => 'search',
+ 'placeholder' => wfMessage( 'searchsuggest-search' )->text(),
+ 'value' => $this->get( 'search', '' ),
+ );
+ $realAttrs = array_merge( $realAttrs, Linker::tooltipAndAccesskeyAttribs( 'search' ), $attrs );
+ return Html::element( 'input', $realAttrs );
+ }
+
+ function makeSearchButton( $mode, $attrs = array() ) {
+ switch ( $mode ) {
+ case 'go':
+ case 'fulltext':
+ $realAttrs = array(
+ 'type' => 'submit',
+ 'name' => $mode,
+ 'value' => $this->translator->translate(
+ $mode == 'go' ? 'searcharticle' : 'searchbutton' ),
+ );
+ $realAttrs = array_merge(
+ $realAttrs,
+ Linker::tooltipAndAccesskeyAttribs( "search-$mode" ),
+ $attrs
+ );
+ return Html::element( 'input', $realAttrs );
+ case 'image':
+ $buttonAttrs = array(
+ 'type' => 'submit',
+ 'name' => 'button',
+ );
+ $buttonAttrs = array_merge(
+ $buttonAttrs,
+ Linker::tooltipAndAccesskeyAttribs( 'search-fulltext' ),
+ $attrs
+ );
+ unset( $buttonAttrs['src'] );
+ unset( $buttonAttrs['alt'] );
+ unset( $buttonAttrs['width'] );
+ unset( $buttonAttrs['height'] );
+ $imgAttrs = array(
+ 'src' => $attrs['src'],
+ 'alt' => isset( $attrs['alt'] )
+ ? $attrs['alt']
+ : $this->translator->translate( 'searchbutton' ),
+ 'width' => isset( $attrs['width'] ) ? $attrs['width'] : null,
+ 'height' => isset( $attrs['height'] ) ? $attrs['height'] : null,
+ );
+ return Html::rawElement( 'button', $buttonAttrs, Html::element( 'img', $imgAttrs ) );
+ default:
+ throw new MWException( 'Unknown mode passed to BaseTemplate::makeSearchButton' );
+ }
+ }
+
+ /**
+ * Returns an array of footerlinks trimmed down to only those footer links that
+ * are valid.
+ * If you pass "flat" as an option then the returned array will be a flat array
+ * of footer icons instead of a key/value array of footerlinks arrays broken
+ * up into categories.
+ * @param string $option
+ * @return array|mixed
+ */
+ function getFooterLinks( $option = null ) {
+ $footerlinks = $this->get( 'footerlinks' );
+
+ // Reduce footer links down to only those which are being used
+ $validFooterLinks = array();
+ foreach ( $footerlinks as $category => $links ) {
+ $validFooterLinks[$category] = array();
+ foreach ( $links as $link ) {
+ if ( isset( $this->data[$link] ) && $this->data[$link] ) {
+ $validFooterLinks[$category][] = $link;
+ }
+ }
+ if ( count( $validFooterLinks[$category] ) <= 0 ) {
+ unset( $validFooterLinks[$category] );
+ }
+ }
+
+ if ( $option == 'flat' ) {
+ // fold footerlinks into a single array using a bit of trickery
+ $validFooterLinks = call_user_func_array(
+ 'array_merge',
+ array_values( $validFooterLinks )
+ );
+ }
+
+ return $validFooterLinks;
+ }
+
+ /**
+ * Returns an array of footer icons filtered down by options relevant to how
+ * the skin wishes to display them.
+ * If you pass "icononly" as the option all footer icons which do not have an
+ * image icon set will be filtered out.
+ * If you pass "nocopyright" then MediaWiki's copyright icon will not be included
+ * in the list of footer icons. This is mostly useful for skins which only
+ * display the text from footericons instead of the images and don't want a
+ * duplicate copyright statement because footerlinks already rendered one.
+ * @param string $option
+ * @return string
+ */
+ function getFooterIcons( $option = null ) {
+ // Generate additional footer icons
+ $footericons = $this->get( 'footericons' );
+
+ if ( $option == 'icononly' ) {
+ // Unset any icons which don't have an image
+ foreach ( $footericons as &$footerIconsBlock ) {
+ foreach ( $footerIconsBlock as $footerIconKey => $footerIcon ) {
+ if ( !is_string( $footerIcon ) && !isset( $footerIcon['src'] ) ) {
+ unset( $footerIconsBlock[$footerIconKey] );
+ }
+ }
+ }
+ // Redo removal of any empty blocks
+ foreach ( $footericons as $footerIconsKey => &$footerIconsBlock ) {
+ if ( count( $footerIconsBlock ) <= 0 ) {
+ unset( $footericons[$footerIconsKey] );
+ }
+ }
+ } elseif ( $option == 'nocopyright' ) {
+ unset( $footericons['copyright']['copyright'] );
+ if ( count( $footericons['copyright'] ) <= 0 ) {
+ unset( $footericons['copyright'] );
+ }
+ }
+
+ return $footericons;
+ }
+
+ /**
+ * Get the suggested HTML for page status indicators: icons (or short text snippets) usually
+ * displayed in the top-right corner of the page, outside of the main content.
+ *
+ * Your skin may implement this differently, for example by handling some indicator names
+ * specially with a different UI. However, it is recommended to use a `<div class="mw-indicator"
+ * id="mw-indicator-<id>" />` as a wrapper element for each indicator, for better compatibility
+ * with extensions and user scripts.
+ *
+ * The raw data is available in `$this->data['indicators']` as an associative array (keys:
+ * identifiers, values: contents) internally ordered by keys.
+ *
+ * @return string HTML
+ * @since 1.25
+ */
+ public function getIndicators() {
+ $out = "<div class=\"mw-indicators\">\n";
+ foreach ( $this->data['indicators'] as $id => $content ) {
+ $out .= Html::rawElement(
+ 'div',
+ array(
+ 'id' => Sanitizer::escapeId( "mw-indicator-$id" ),
+ 'class' => 'mw-indicator',
+ ),
+ $content
+ ) . "\n";
+ }
+ $out .= "</div>\n";
+ return $out;
+ }
+
+ /**
+ * Output the basic end-page trail including bottomscripts, reporttime, and
+ * debug stuff. This should be called right before outputting the closing
+ * body and html tags.
+ */
+ function printTrail() {
+?>
+<?php echo MWDebug::getDebugHTML( $this->getSkin()->getContext() ); ?>
+<?php $this->html( 'bottomscripts' ); /* JS call to runBodyOnloadHook */ ?>
+<?php $this->html( 'reporttime' ) ?>
+<?php
+ }
+}
diff --git a/includes/skins/MediaWikiI18N.php b/includes/skins/MediaWikiI18N.php
new file mode 100644
index 00000000..6e48d04a
--- /dev/null
+++ b/includes/skins/MediaWikiI18N.php
@@ -0,0 +1,52 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * Wrapper object for MediaWiki's localization functions,
+ * to be passed to the template engine.
+ *
+ * @private
+ * @ingroup Skins
+ */
+class MediaWikiI18N {
+ private $context = array();
+
+ function set( $varName, $value ) {
+ $this->context[$varName] = $value;
+ }
+
+ function translate( $value ) {
+
+ // Hack for i18n:attributes in PHPTAL 1.0.0 dev version as of 2004-10-23
+ $value = preg_replace( '/^string:/', '', $value );
+
+ $value = wfMessage( $value )->text();
+ // interpolate variables
+ $m = array();
+ while ( preg_match( '/\$([0-9]*?)/sm', $value, $m ) ) {
+ list( $src, $var ) = $m;
+ wfSuppressWarnings();
+ $varValue = $this->context[$var];
+ wfRestoreWarnings();
+ $value = str_replace( $src, $varValue, $value );
+ }
+ return $value;
+ }
+}
diff --git a/includes/skins/QuickTemplate.php b/includes/skins/QuickTemplate.php
new file mode 100644
index 00000000..905e537e
--- /dev/null
+++ b/includes/skins/QuickTemplate.php
@@ -0,0 +1,194 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * Generic wrapper for template functions, with interface
+ * compatible with what we use of PHPTAL 0.7.
+ * @ingroup Skins
+ */
+abstract class QuickTemplate {
+
+ /** @var Config $config */
+ protected $config;
+
+ /**
+ * @param Config $config
+ */
+ function __construct( Config $config = null ) {
+ $this->data = array();
+ $this->translator = new MediaWikiI18N();
+ if ( $config === null ) {
+ wfDebug( __METHOD__ . ' was called with no Config instance passed to it' );
+ $config = ConfigFactory::getDefaultInstance()->makeConfig( 'main' );
+ }
+ $this->config = $config;
+ }
+
+ /**
+ * Sets the value $value to $name
+ * @param string $name
+ * @param mixed $value
+ */
+ public function set( $name, $value ) {
+ $this->data[$name] = $value;
+ }
+
+ /**
+ * extends the value of data with name $name with the value $value
+ * @since 1.25
+ * @param string $name
+ * @param mixed $value
+ */
+ public function extend( $name, $value ) {
+ if ( $this->haveData( $name ) ) {
+ $this->data[$name] = $this->data[$name] . $value;
+ } else {
+ $this->data[$name] = $value;
+ }
+ }
+
+ /**
+ * Gets the template data requested
+ * @since 1.22
+ * @param string $name Key for the data
+ * @param mixed $default Optional default (or null)
+ * @return mixed The value of the data requested or the deafult
+ */
+ public function get( $name, $default = null ) {
+ if ( isset( $this->data[$name] ) ) {
+ return $this->data[$name];
+ } else {
+ return $default;
+ }
+ }
+
+ /**
+ * @param string $name
+ * @param mixed $value
+ */
+ public function setRef( $name, &$value ) {
+ $this->data[$name] =& $value;
+ }
+
+ /**
+ * @param MediaWikiI18N $t
+ */
+ public function setTranslator( &$t ) {
+ $this->translator = &$t;
+ }
+
+ /**
+ * Main function, used by classes that subclass QuickTemplate
+ * to show the actual HTML output
+ */
+ abstract public function execute();
+
+ /**
+ * @private
+ * @param string $str
+ * @return string
+ */
+ function text( $str ) {
+ echo htmlspecialchars( $this->data[$str] );
+ }
+
+ /**
+ * @private
+ * @param string $str
+ * @return string
+ */
+ function html( $str ) {
+ echo $this->data[$str];
+ }
+
+ /**
+ * @private
+ * @param string $str
+ * @return string
+ */
+ function msg( $str ) {
+ echo htmlspecialchars( $this->translator->translate( $str ) );
+ }
+
+ /**
+ * @private
+ * @param string $str
+ * @return string
+ */
+ function msgHtml( $str ) {
+ echo $this->translator->translate( $str );
+ }
+
+ /**
+ * An ugly, ugly hack.
+ * @private
+ * @param string $str
+ * @return string
+ */
+ function msgWiki( $str ) {
+ global $wgOut;
+
+ $text = $this->translator->translate( $str );
+ echo $wgOut->parse( $text );
+ }
+
+ /**
+ * @private
+ * @param string $str
+ * @return bool
+ */
+ function haveData( $str ) {
+ return isset( $this->data[$str] );
+ }
+
+ /**
+ * @private
+ *
+ * @param string $str
+ * @return bool
+ */
+ function haveMsg( $str ) {
+ $msg = $this->translator->translate( $str );
+ return ( $msg != '-' ) && ( $msg != '' ); # ????
+ }
+
+ /**
+ * Get the Skin object related to this object
+ *
+ * @return Skin
+ */
+ public function getSkin() {
+ return $this->data['skin'];
+ }
+
+ /**
+ * Fetch the output of a QuickTemplate and return it
+ *
+ * @since 1.23
+ * @return string
+ */
+ public function getHTML() {
+ ob_start();
+ $this->execute();
+ $html = ob_get_contents();
+ ob_end_clean();
+ return $html;
+ }
+}
diff --git a/includes/skins/Skin.php b/includes/skins/Skin.php
index 2f6a7101..ac7a85ba 100644
--- a/includes/skins/Skin.php
+++ b/includes/skins/Skin.php
@@ -168,11 +168,9 @@ abstract class Skin extends ContextSource {
* @param OutputPage $out
*/
function initPage( OutputPage $out ) {
- wfProfileIn( __METHOD__ );
$this->preloadExistence();
- wfProfileOut( __METHOD__ );
}
/**
@@ -239,25 +237,33 @@ abstract class Skin extends ContextSource {
* Preload the existence of three commonly-requested pages in a single query
*/
function preloadExistence() {
+ $titles = array();
+
$user = $this->getUser();
+ $title = $this->getRelevantTitle();
// User/talk link
- $titles = array( $user->getUserPage(), $user->getTalkPage() );
+ if ( $user->isLoggedIn() || $this->showIPinHeader() ) {
+ $titles[] = $user->getUserPage();
+ $titles[] = $user->getTalkPage();
+ }
// Other tab link
- if ( $this->getTitle()->isSpecialPage() ) {
+ if ( $title->isSpecialPage() ) {
// nothing
- } elseif ( $this->getTitle()->isTalkPage() ) {
- $titles[] = $this->getTitle()->getSubjectPage();
+ } elseif ( $title->isTalkPage() ) {
+ $titles[] = $title->getSubjectPage();
} else {
- $titles[] = $this->getTitle()->getTalkPage();
+ $titles[] = $title->getTalkPage();
}
- wfRunHooks( 'SkinPreloadExistence', array( &$titles, $this ) );
+ Hooks::run( 'SkinPreloadExistence', array( &$titles, $this ) );
- $lb = new LinkBatch( $titles );
- $lb->setCaller( __METHOD__ );
- $lb->execute();
+ if ( count( $titles ) ) {
+ $lb = new LinkBatch( $titles );
+ $lb->setCaller( __METHOD__ );
+ $lb->execute();
+ }
}
/**
@@ -333,8 +339,13 @@ abstract class Skin extends ContextSource {
$this->mRelevantUser = User::newFromName( $rootUser, false );
} else {
$user = User::newFromName( $rootUser, false );
- if ( $user && $user->isLoggedIn() ) {
- $this->mRelevantUser = $user;
+
+ if ( $user ) {
+ $user->load( User::READ_NORMAL );
+
+ if ( $user->isLoggedIn() ) {
+ $this->mRelevantUser = $user;
+ }
}
}
return $this->mRelevantUser;
@@ -474,9 +485,10 @@ abstract class Skin extends ContextSource {
$msg = $this->msg( 'pagecategories' )->numParams( count( $allCats['normal'] ) )->escaped();
$linkPage = wfMessage( 'pagecategorieslink' )->inContentLanguage()->text();
+ $title = Title::newFromText( $linkPage );
+ $link = $title ? Linker::link( $title, $msg ) : $msg;
$s .= '<div id="mw-normal-catlinks" class="mw-normal-catlinks">' .
- Linker::link( Title::newFromText( $linkPage ), $msg )
- . $colon . '<ul>' . $t . '</ul>' . '</div>';
+ $link . $colon . '<ul>' . $t . '</ul>' . '</div>';
}
# Hidden categories
@@ -579,7 +591,7 @@ abstract class Skin extends ContextSource {
protected function afterContentHook() {
$data = '';
- if ( wfRunHooks( 'SkinAfterContent', array( &$data, $this ) ) ) {
+ if ( Hooks::run( 'SkinAfterContent', array( &$data, $this ) ) ) {
// adding just some spaces shouldn't toggle the output
// of the whole <div/>, so we use trim() here
if ( trim( $data ) != '' ) {
@@ -616,7 +628,7 @@ abstract class Skin extends ContextSource {
// OutputPage::getBottomScripts() which takes a Skin param. This should be cleaned
// up at some point
$bottomScriptText = $this->getOutput()->getBottomScripts();
- wfRunHooks( 'SkinAfterBottomScripts', array( $this, &$bottomScriptText ) );
+ Hooks::run( 'SkinAfterBottomScripts', array( $this, &$bottomScriptText ) );
return $bottomScriptText;
}
@@ -637,22 +649,23 @@ abstract class Skin extends ContextSource {
$url = htmlspecialchars( wfExpandIRI( $this->getTitle()->getCanonicalURL() ) );
}
- return $this->msg( 'retrievedfrom', '<a dir="ltr" href="' . $url
- . '">' . $url . '</a>' )->text();
+ return $this->msg( 'retrievedfrom' )
+ ->rawParams( '<a dir="ltr" href="' . $url. '">' . $url . '</a>' )
+ ->parse();
}
/**
- * @return string
+ * @return string HTML
*/
function getUndeleteLink() {
$action = $this->getRequest()->getVal( 'action', 'view' );
- if ( $this->getUser()->isAllowed( 'deletedhistory' ) &&
- ( $this->getTitle()->getArticleID() == 0 || $action == 'history' ) ) {
+ if ( $this->getTitle()->userCan( 'deletedhistory', $this->getUser() ) &&
+ ( !$this->getTitle()->exists() || $action == 'history' ) ) {
$n = $this->getTitle()->isDeleted();
if ( $n ) {
- if ( $this->getUser()->isAllowed( 'undelete' ) ) {
+ if ( $this->getTitle()->quickUserCan( 'undelete', $this->getUser() ) ) {
$msg = 'thisisdeleted';
} else {
$msg = 'viewdeleted';
@@ -662,7 +675,7 @@ abstract class Skin extends ContextSource {
Linker::linkKnown(
SpecialPage::getTitleFor( 'Undelete', $this->getTitle()->getPrefixedDBkey() ),
$this->msg( 'restorelink' )->numParams( $n )->escaped() )
- )->text();
+ )->escaped();
}
}
@@ -676,7 +689,7 @@ abstract class Skin extends ContextSource {
$out = $this->getOutput();
$subpages = '';
- if ( !wfRunHooks( 'SkinSubPageSubtitle', array( &$subpages, $this, $out ) ) ) {
+ if ( !Hooks::run( 'SkinSubPageSubtitle', array( &$subpages, $this, $out ) ) ) {
return $subpages;
}
@@ -785,7 +798,7 @@ abstract class Skin extends ContextSource {
// @todo Remove deprecated $forContent param from hook handlers and then remove here.
$forContent = true;
- wfRunHooks(
+ Hooks::run(
'SkinCopyrightFooter',
array( $this->getTitle(), $type, &$msg, &$link, &$forContent )
);
@@ -829,10 +842,19 @@ abstract class Skin extends ContextSource {
function getPoweredBy() {
global $wgResourceBasePath;
- $url = htmlspecialchars( "$wgResourceBasePath/resources/assets/poweredby_mediawiki_88x31.png" );
- $text = '<a href="//www.mediawiki.org/"><img src="' . $url
- . '" height="31" width="88" alt="Powered by MediaWiki" /></a>';
- wfRunHooks( 'SkinGetPoweredBy', array( &$text, $this ) );
+ $url1 = htmlspecialchars(
+ "$wgResourceBasePath/resources/assets/poweredby_mediawiki_88x31.png"
+ );
+ $url1_5 = htmlspecialchars(
+ "$wgResourceBasePath/resources/assets/poweredby_mediawiki_132x47.png"
+ );
+ $url2 = htmlspecialchars(
+ "$wgResourceBasePath/resources/assets/poweredby_mediawiki_176x62.png"
+ );
+ $text = '<a href="//www.mediawiki.org/"><img src="' . $url1
+ . '" srcset="' . $url1_5 . ' 1.5x, ' . $url2 . ' 2x" '
+ . 'height="31" width="88" alt="Powered by MediaWiki" /></a>';
+ Hooks::run( 'SkinGetPoweredBy', array( &$text, $this ) );
return $text;
}
@@ -852,13 +874,13 @@ abstract class Skin extends ContextSource {
if ( $timestamp ) {
$d = $this->getLanguage()->userDate( $timestamp, $this->getUser() );
$t = $this->getLanguage()->userTime( $timestamp, $this->getUser() );
- $s = ' ' . $this->msg( 'lastmodifiedat', $d, $t )->text();
+ $s = ' ' . $this->msg( 'lastmodifiedat', $d, $t )->parse();
} else {
$s = '';
}
if ( wfGetLB()->getLaggedSlaveMode() ) {
- $s .= ' <strong>' . $this->msg( 'laggedslavemode' )->text() . '</strong>';
+ $s .= ' <strong>' . $this->msg( 'laggedslavemode' )->parse() . '</strong>';
}
return $s;
@@ -942,6 +964,10 @@ abstract class Skin extends ContextSource {
// but we make the link target be the one site-wide page.
$title = Title::newFromText( $this->msg( $page )->inContentLanguage()->text() );
+ if ( !$title ) {
+ return '';
+ }
+
return Linker::linkKnown(
$title,
$this->msg( $desc )->escaped()
@@ -1031,6 +1057,7 @@ abstract class Skin extends ContextSource {
*
* @param string $name The name or path of a skin resource file
* @return string The fully resolved style path url including styleversion
+ * @throws MWException
*/
function getSkinStylePath( $name ) {
global $wgStylePath, $wgStyleVersion;
@@ -1150,7 +1177,7 @@ abstract class Skin extends ContextSource {
return array(
'href' => $title->getLocalURL( $urlaction ),
- 'exists' => $title->getArticleID() != 0,
+ 'exists' => $title->isKnown(),
);
}
@@ -1208,16 +1235,14 @@ abstract class Skin extends ContextSource {
*/
function buildSidebar() {
global $wgMemc, $wgEnableSidebarCache, $wgSidebarCacheExpiry;
- wfProfileIn( __METHOD__ );
$key = wfMemcKey( 'sidebar', $this->getLanguage()->getCode() );
if ( $wgEnableSidebarCache ) {
$cachedsidebar = $wgMemc->get( $key );
if ( $cachedsidebar ) {
- wfRunHooks( 'SidebarBeforeOutput', array( $this, &$cachedsidebar ) );
+ Hooks::run( 'SidebarBeforeOutput', array( $this, &$cachedsidebar ) );
- wfProfileOut( __METHOD__ );
return $cachedsidebar;
}
}
@@ -1225,14 +1250,13 @@ abstract class Skin extends ContextSource {
$bar = array();
$this->addToSidebar( $bar, 'sidebar' );
- wfRunHooks( 'SkinBuildSidebar', array( $this, &$bar ) );
+ Hooks::run( 'SkinBuildSidebar', array( $this, &$bar ) );
if ( $wgEnableSidebarCache ) {
$wgMemc->set( $key, $bar, $wgSidebarCacheExpiry );
}
- wfRunHooks( 'SidebarBeforeOutput', array( $this, &$bar ) );
+ Hooks::run( 'SidebarBeforeOutput', array( $this, &$bar ) );
- wfProfileOut( __METHOD__ );
return $bar;
}
@@ -1370,7 +1394,7 @@ abstract class Skin extends ContextSource {
$out = $this->getOutput();
// Allow extensions to disable or modify the new messages alert
- if ( !wfRunHooks( 'GetNewMessagesAlert', array( &$newMessagesAlert, $newtalks, $user, $out ) ) ) {
+ if ( !Hooks::run( 'GetNewMessagesAlert', array( &$newMessagesAlert, $newtalks, $user, $out ) ) ) {
return '';
}
if ( $newMessagesAlert ) {
@@ -1453,13 +1477,12 @@ abstract class Skin extends ContextSource {
* Get a cached notice
*
* @param string $name Message name, or 'default' for $wgSiteNotice
- * @return string HTML fragment
+ * @return string|bool HTML fragment, or false to indicate that the caller
+ * should fall back to the next notice in its sequence
*/
private function getCachedNotice( $name ) {
global $wgRenderHashAppend, $parserMemc, $wgContLang;
- wfProfileIn( __METHOD__ );
-
$needParse = false;
if ( $name === 'default' ) {
@@ -1467,13 +1490,13 @@ abstract class Skin extends ContextSource {
global $wgSiteNotice;
$notice = $wgSiteNotice;
if ( empty( $notice ) ) {
- wfProfileOut( __METHOD__ );
return false;
}
} else {
$msg = $this->msg( $name )->inContentLanguage();
- if ( $msg->isDisabled() ) {
- wfProfileOut( __METHOD__ );
+ if ( $msg->isBlank() ) {
+ return '';
+ } elseif ( $msg->isDisabled() ) {
return false;
}
$notice = $msg->plain();
@@ -1500,7 +1523,6 @@ abstract class Skin extends ContextSource {
$notice = Html::rawElement( 'div', array( 'id' => 'localNotice',
'lang' => $wgContLang->getHtmlCode(), 'dir' => $wgContLang->getDir() ), $notice );
- wfProfileOut( __METHOD__ );
return $notice;
}
@@ -1510,7 +1532,6 @@ abstract class Skin extends ContextSource {
* @return string HTML fragment
*/
function getNamespaceNotice() {
- wfProfileIn( __METHOD__ );
$key = 'namespacenotice-' . $this->getTitle()->getNsText();
$namespaceNotice = $this->getCachedNotice( $key );
@@ -1520,7 +1541,6 @@ abstract class Skin extends ContextSource {
$namespaceNotice = '';
}
- wfProfileOut( __METHOD__ );
return $namespaceNotice;
}
@@ -1530,27 +1550,25 @@ abstract class Skin extends ContextSource {
* @return string HTML fragment
*/
function getSiteNotice() {
- wfProfileIn( __METHOD__ );
$siteNotice = '';
- if ( wfRunHooks( 'SiteNoticeBefore', array( &$siteNotice, $this ) ) ) {
+ if ( Hooks::run( 'SiteNoticeBefore', array( &$siteNotice, $this ) ) ) {
if ( is_object( $this->getUser() ) && $this->getUser()->isLoggedIn() ) {
$siteNotice = $this->getCachedNotice( 'sitenotice' );
} else {
$anonNotice = $this->getCachedNotice( 'anonnotice' );
- if ( !$anonNotice ) {
+ if ( $anonNotice === false ) {
$siteNotice = $this->getCachedNotice( 'sitenotice' );
} else {
$siteNotice = $anonNotice;
}
}
- if ( !$siteNotice ) {
+ if ( $siteNotice === false ) {
$siteNotice = $this->getCachedNotice( 'default' );
}
}
- wfRunHooks( 'SiteNoticeAfter', array( &$siteNotice, $this ) );
- wfProfileOut( __METHOD__ );
+ Hooks::run( 'SiteNoticeAfter', array( &$siteNotice, $this ) );
return $siteNotice;
}
@@ -1580,20 +1598,46 @@ abstract class Skin extends ContextSource {
$attribs['title'] = wfMessage( 'editsectionhint' )->rawParams( $tooltip )
->inLanguage( $lang )->text();
}
- $link = Linker::link( $nt, wfMessage( 'editsection' )->inLanguage( $lang )->text(),
- $attribs,
- array( 'action' => 'edit', 'section' => $section ),
- array( 'noclasses', 'known' )
+
+ $links = array(
+ 'editsection' => array(
+ 'text' => wfMessage( 'editsection' )->inLanguage( $lang )->escaped(),
+ 'targetTitle' => $nt,
+ 'attribs' => $attribs,
+ 'query' => array( 'action' => 'edit', 'section' => $section ),
+ 'options' => array( 'noclasses', 'known' )
+ )
);
- # Add the brackets and the span and run the hook.
- $result = '<span class="mw-editsection">'
- . '<span class="mw-editsection-bracket">[</span>'
- . $link
- . '<span class="mw-editsection-bracket">]</span>'
- . '</span>';
+ Hooks::run( 'SkinEditSectionLinks', array( $this, $nt, $section, $tooltip, &$links, $lang ) );
+
+ $result = '<span class="mw-editsection"><span class="mw-editsection-bracket">[</span>';
- wfRunHooks( 'DoEditSectionLink', array( $this, $nt, $section, $tooltip, &$result, $lang ) );
+ $linksHtml = array();
+ foreach ( $links as $k => $linkDetails ) {
+ $linksHtml[] = Linker::link(
+ $linkDetails['targetTitle'],
+ $linkDetails['text'],
+ $linkDetails['attribs'],
+ $linkDetails['query'],
+ $linkDetails['options']
+ );
+ }
+
+ $result .= implode(
+ '<span class="mw-editsection-divider">'
+ . wfMessage( 'pipe-separator' )->inLanguage( $lang )->text()
+ . '</span>',
+ $linksHtml
+ );
+
+ $result .= '<span class="mw-editsection-bracket">]</span></span>';
+ // Deprecated, use SkinEditSectionLinks hook instead
+ Hooks::run(
+ 'DoEditSectionLink',
+ array( $this, $nt, $section, $tooltip, &$result, $lang ),
+ '1.25'
+ );
return $result;
}
diff --git a/includes/skins/SkinApi.php b/includes/skins/SkinApi.php
new file mode 100644
index 00000000..2fef2209
--- /dev/null
+++ b/includes/skins/SkinApi.php
@@ -0,0 +1,71 @@
+<?php
+/**
+ * Extremely basic "skin" for API output, which needs to output a page without
+ * the usual skin elements but still using CSS, JS, and such via OutputPage and
+ * ResourceLoader.
+ *
+ * Created on Sep 08, 2014
+ *
+ * Copyright © 2014 Brad Jorsch <bjorsch@wikimedia.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * SkinTemplate class for API output
+ * @since 1.25
+ */
+class SkinApi extends SkinTemplate {
+ public $skinname = 'apioutput';
+ public $template = 'SkinApiTemplate';
+
+ public function setupSkinUserCss( OutputPage $out ) {
+ parent::setupSkinUserCss( $out );
+ $out->addModuleStyles( 'mediawiki.skinning.interface' );
+ }
+
+ // Skip work and hooks for stuff we don't use
+
+ function buildSidebar() {
+ return array();
+ }
+
+ function getNewtalks() {
+ return '';
+ }
+
+ function getSiteNotice() {
+ return '';
+ }
+
+ public function getLanguages() {
+ return array();
+ }
+
+ protected function buildPersonalUrls() {
+ return array();
+ }
+
+ protected function buildContentNavigationUrls() {
+ return array();
+ }
+
+ protected function buildNavUrls() {
+ return array();
+ }
+}
diff --git a/includes/skins/SkinApiTemplate.php b/includes/skins/SkinApiTemplate.php
new file mode 100644
index 00000000..97b70382
--- /dev/null
+++ b/includes/skins/SkinApiTemplate.php
@@ -0,0 +1,63 @@
+<?php
+/**
+ * Extremely basic "skin" for API output, which needs to output a page without
+ * the usual skin elements but still using CSS, JS, and such via OutputPage and
+ * ResourceLoader.
+ *
+ * Created on Sep 08, 2014
+ *
+ * Copyright © 2014 Brad Jorsch <bjorsch@wikimedia.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * BaseTemplate class for the 'apioutput' skin
+ * @since 1.25
+ */
+class SkinApiTemplate extends BaseTemplate {
+
+ public function execute() {
+ $this->html( 'headelement' ) ?>
+
+ <div class="mw-body" role="main">
+ <h1 class="firstHeading"><?php $this->html( 'title' ) ?></h1>
+ <div class="mw-body-content">
+ <?php $this->html( 'bodytext' ) ?>
+ </div>
+ </div>
+
+ <?php $this->printTrail() ?>
+ </body></html>
+<?php
+ }
+
+ // Skip work and hooks for stuff we don't use
+
+ function getToolbox() {
+ return array();
+ }
+
+ function getPersonalTools() {
+ return array();
+ }
+
+ function getSidebar( $options = array() ) {
+ return array();
+ }
+}
diff --git a/includes/skins/SkinFactory.php b/includes/skins/SkinFactory.php
index fb408577..ffbe6293 100644
--- a/includes/skins/SkinFactory.php
+++ b/includes/skins/SkinFactory.php
@@ -40,13 +40,6 @@ class SkinFactory {
* @var array
*/
private $displayNames = array();
- /**
- * Map of name => class name without "Skin" prefix, for legacy skins using the autodiscovery
- * mechanism
- *
- * @var array
- */
- private $legacySkins = array();
/**
* @var SkinFactory
@@ -83,109 +76,13 @@ class SkinFactory {
}
/**
- * @return array
- */
- private function getLegacySkinNames() {
- static $skinsInitialised = false;
-
- if ( !$skinsInitialised || !count( $this->legacySkins ) ) {
- # Get a list of available skins
- # Build using the regular expression '^(.*).php$'
- # Array keys are all lower case, array value keep the case used by filename
- #
- wfProfileIn( __METHOD__ . '-init' );
-
- global $wgStyleDirectory;
-
- $skinDir = dir( $wgStyleDirectory );
-
- if ( $skinDir !== false && $skinDir !== null ) {
- # while code from www.php.net
- while ( false !== ( $file = $skinDir->read() ) ) {
- // Skip non-PHP files, hidden files, and '.dep' includes
- $matches = array();
-
- if ( preg_match( '/^([^.]*)\.php$/', $file, $matches ) ) {
- $aSkin = $matches[1];
-
- // Explicitly disallow loading core skins via the autodiscovery mechanism.
- //
- // They should be loaded already (in a non-autodicovery way), but old files might still
- // exist on the server because our MW version upgrade process is widely documented as
- // requiring just copying over all files, without removing old ones.
- //
- // This is one of the reasons we should have never used autodiscovery in the first
- // place. This hack can be safely removed when autodiscovery is gone.
- if ( in_array( $aSkin, array( 'CologneBlue', 'Modern', 'MonoBook', 'Vector' ) ) ) {
- wfLogWarning(
- "An old copy of the $aSkin skin was found in your skins/ directory. " .
- "You should remove it to avoid problems in the future." .
- "See https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery for details."
- );
- continue;
- }
-
- wfLogWarning(
- "A skin using autodiscovery mechanism, $aSkin, was found in your skins/ directory. " .
- "The mechanism will be removed in MediaWiki 1.25 and the skin will no longer be recognized. " .
- "See https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery for information how to fix this."
- );
- $this->legacySkins[strtolower( $aSkin )] = $aSkin;
- }
- }
- $skinDir->close();
- }
- $skinsInitialised = true;
- wfProfileOut( __METHOD__ . '-init' );
- }
- return $this->legacySkins;
-
- }
-
- /**
* Returns an associative array of:
* skin name => human readable name
*
* @return array
*/
public function getSkinNames() {
- return array_merge(
- $this->getLegacySkinNames(),
- $this->displayNames
- );
- }
-
- /**
- * Get a legacy skin which uses the autodiscovery mechanism.
- *
- * @param string $name
- * @return Skin|bool False if the skin couldn't be constructed
- */
- private function getLegacySkin( $name ) {
- $skinNames = $this->getLegacySkinNames();
- if ( !isset( $skinNames[$name] ) ) {
- return false;
- }
- $skinName = $skinNames[$name];
- $className = "Skin{$skinName}";
-
- # Grab the skin class and initialise it.
- if ( !class_exists( $className ) ) {
- global $wgStyleDirectory;
- require_once "{$wgStyleDirectory}/{$skinName}.php";
-
- # Check if we got it
- if ( !class_exists( $className ) ) {
- # DO NOT die if the class isn't found. This breaks maintenance
- # scripts and can cause a user account to be unrecoverable
- # except by SQL manipulation if a previously valid skin name
- # is no longer valid.
- return false;
- }
- }
- $skin = new $className( $name );
- return $skin;
-
+ return $this->displayNames;
}
/**
@@ -197,11 +94,6 @@ class SkinFactory {
*/
public function makeSkin( $name ) {
if ( !isset( $this->factoryFunctions[$name] ) ) {
- // Check the legacy autodiscovery method of skin loading
- $legacy = $this->getLegacySkin( $name );
- if ( $legacy ) {
- return $legacy;
- }
throw new SkinException( "No registered builder available for $name." );
}
$skin = call_user_func( $this->factoryFunctions[$name], $name );
diff --git a/includes/skins/SkinFallbackTemplate.php b/includes/skins/SkinFallbackTemplate.php
index 603ee5c5..1c5f3a6f 100644
--- a/includes/skins/SkinFallbackTemplate.php
+++ b/includes/skins/SkinFallbackTemplate.php
@@ -17,16 +17,18 @@ class SkinFallbackTemplate extends BaseTemplate {
* @return array
*/
private function findInstalledSkins() {
- $styleDirectory = $this->config->get( 'StyleDirectory' ); // @todo we should inject this directly?
+ $styleDirectory = $this->config->get( 'StyleDirectory' );
// Get all subdirectories which might contains skins
$possibleSkins = scandir( $styleDirectory );
$possibleSkins = array_filter( $possibleSkins, function ( $maybeDir ) use ( $styleDirectory ) {
return $maybeDir !== '.' && $maybeDir !== '..' && is_dir( "$styleDirectory/$maybeDir" );
} );
- // Only keep the ones that contain a .php file with the same name inside
+ // Filter out skins that aren't installed
$possibleSkins = array_filter( $possibleSkins, function ( $skinDir ) use ( $styleDirectory ) {
- return is_file( "$styleDirectory/$skinDir/$skinDir.php" );
+ return
+ is_file( "$styleDirectory/$skinDir/skin.json" )
+ || is_file( "$styleDirectory/$skinDir/$skinDir.php" );
} );
return $possibleSkins;
@@ -56,7 +58,7 @@ class SkinFallbackTemplate extends BaseTemplate {
} else {
$skinsInstalledText[] = $this->getMsg( 'default-skin-not-found-row-disabled' )
->params( $normalizedKey, $skin )->plain();
- $skinsInstalledSnippet[] = "require_once \"\$IP/skins/$skin/$skin.php\";";
+ $skinsInstalledSnippet[] = $this->getSnippetForSkin( $skin );
}
}
@@ -73,6 +75,21 @@ class SkinFallbackTemplate extends BaseTemplate {
}
/**
+ * Get the appropriate LocalSettings.php snippet to enable the given skin
+ *
+ * @param string $skin
+ * @return string
+ */
+ private function getSnippetForSkin( $skin ) {
+ global $IP;
+ if ( file_exists( "$IP/skins/$skin/skin.json" ) ) {
+ return "wfLoadSkin( '$skin' );";
+ } else {
+ return "require_once \"\$IP/skins/$skin/$skin.php\";";
+ }
+ }
+
+ /**
* Outputs the entire contents of the page. No navigation (other than search box), just the big
* warning message and page content.
*/
@@ -91,9 +108,7 @@ class SkinFallbackTemplate extends BaseTemplate {
</form>
<div class="mw-body" role="main">
- <h1 class="firstHeading">
- <span dir="auto"><?php $this->html( 'title' ) ?></span>
- </h1>
+ <h1 class="firstHeading"><?php $this->html( 'title' ) ?></h1>
<div class="mw-body-content">
<?php $this->html( 'bodytext' ) ?>
diff --git a/includes/skins/SkinTemplate.php b/includes/skins/SkinTemplate.php
index b66862b8..61aad921 100644
--- a/includes/skins/SkinTemplate.php
+++ b/includes/skins/SkinTemplate.php
@@ -1,7 +1,5 @@
<?php
/**
- * Base class for template-based skins.
- *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@@ -21,41 +19,8 @@
*/
/**
- * Wrapper object for MediaWiki's localization functions,
- * to be passed to the template engine.
+ * Base class for template-based skins.
*
- * @private
- * @ingroup Skins
- */
-class MediaWikiI18N {
- private $context = array();
-
- function set( $varName, $value ) {
- $this->context[$varName] = $value;
- }
-
- function translate( $value ) {
- wfProfileIn( __METHOD__ );
-
- // Hack for i18n:attributes in PHPTAL 1.0.0 dev version as of 2004-10-23
- $value = preg_replace( '/^string:/', '', $value );
-
- $value = wfMessage( $value )->text();
- // interpolate variables
- $m = array();
- while ( preg_match( '/\$([0-9]*?)/sm', $value, $m ) ) {
- list( $src, $var ) = $m;
- wfSuppressWarnings();
- $varValue = $this->context[$var];
- wfRestoreWarnings();
- $value = str_replace( $src, $varValue, $value );
- }
- wfProfileOut( __METHOD__ );
- return $value;
- }
-}
-
-/**
* Template-filler skin base class
* Formerly generic PHPTal (http://phptal.sourceforge.net/) skin
* Based on Brion's smarty skin
@@ -89,7 +54,8 @@ class SkinTemplate extends Skin {
$out->addModuleStyles( array(
'mediawiki.legacy.shared',
'mediawiki.legacy.commonPrint',
- 'mediawiki.ui.button'
+ 'mediawiki.ui.button',
+ 'mediawiki.sectionAnchor'
) );
}
@@ -198,7 +164,7 @@ class SkinTemplate extends Skin {
'lang' => $ilInterwikiCodeBCP47,
'hreflang' => $ilInterwikiCodeBCP47,
);
- wfRunHooks(
+ Hooks::run(
'SkinTemplateGetLanguageLink',
array( &$languageLink, $languageLinkTitle, $this->getTitle(), $this->getOutput() )
);
@@ -210,17 +176,13 @@ class SkinTemplate extends Skin {
}
protected function setupTemplateForOutput() {
- wfProfileIn( __METHOD__ );
$request = $this->getRequest();
$user = $this->getUser();
$title = $this->getTitle();
- wfProfileIn( __METHOD__ . '-init' );
$tpl = $this->setupTemplate( $this->template, 'skins' );
- wfProfileOut( __METHOD__ . '-init' );
- wfProfileIn( __METHOD__ . '-stuff' );
$this->thispage = $title->getPrefixedDBkey();
$this->titletxt = $title->getPrefixedText();
$this->userpage = $user->getUserPage()->getPrefixedText();
@@ -243,10 +205,6 @@ class SkinTemplate extends Skin {
$this->userpageUrlDetails = self::makeKnownUrlDetails( $this->userpage );
}
- wfProfileOut( __METHOD__ . '-stuff' );
-
- wfProfileOut( __METHOD__ );
-
return $tpl;
}
@@ -256,26 +214,22 @@ class SkinTemplate extends Skin {
* @param OutputPage $out
*/
function outputPage( OutputPage $out = null ) {
- wfProfileIn( __METHOD__ );
Profiler::instance()->setTemplated( true );
$oldContext = null;
if ( $out !== null ) {
- // @todo Add wfDeprecated in 1.20
+ // Deprecated since 1.20, note added in 1.25
+ wfDeprecated( __METHOD__, '1.25' );
$oldContext = $this->getContext();
$this->setContext( $out->getContext() );
}
$out = $this->getOutput();
- wfProfileIn( __METHOD__ . '-init' );
$this->initPage( $out );
- wfProfileOut( __METHOD__ . '-init' );
$tpl = $this->prepareQuickTemplate( $out );
// execute template
- wfProfileIn( __METHOD__ . '-execute' );
$res = $tpl->execute();
- wfProfileOut( __METHOD__ . '-execute' );
// result may be an error
$this->printOrError( $res );
@@ -284,7 +238,6 @@ class SkinTemplate extends Skin {
$this->setContext( $oldContext );
}
- wfProfileOut( __METHOD__ );
}
/**
@@ -295,18 +248,15 @@ class SkinTemplate extends Skin {
*/
protected function prepareQuickTemplate() {
global $wgContLang, $wgScript, $wgStylePath, $wgMimeType, $wgJsMimeType,
- $wgDisableCounters, $wgSitename, $wgLogo, $wgMaxCredits,
- $wgShowCreditsIfMax, $wgPageShowWatchingUsers, $wgArticlePath,
+ $wgSitename, $wgLogo, $wgMaxCredits,
+ $wgShowCreditsIfMax, $wgArticlePath,
$wgScriptPath, $wgServer;
- wfProfileIn( __METHOD__ );
-
$title = $this->getTitle();
$request = $this->getRequest();
$out = $this->getOutput();
$tpl = $this->setupTemplateForOutput();
- wfProfileIn( __METHOD__ . '-stuff2' );
$tpl->set( 'title', $out->getPageTitle() );
$tpl->set( 'pagetitle', $out->getHTMLTitle() );
$tpl->set( 'displaytitle', $out->mPageLinkTitle );
@@ -401,39 +351,17 @@ class SkinTemplate extends Skin {
$tpl->set( 'userlangattributes', $attrs );
}
- wfProfileOut( __METHOD__ . '-stuff2' );
-
- wfProfileIn( __METHOD__ . '-stuff3' );
$tpl->set( 'newtalk', $this->getNewtalks() );
$tpl->set( 'logo', $this->logoText() );
$tpl->set( 'copyright', false );
+ // No longer used
$tpl->set( 'viewcount', false );
$tpl->set( 'lastmod', false );
$tpl->set( 'credits', false );
$tpl->set( 'numberofwatchingusers', false );
if ( $out->isArticle() && $title->exists() ) {
if ( $this->isRevisionCurrent() ) {
- if ( !$wgDisableCounters ) {
- $viewcount = $this->getWikiPage()->getCount();
- if ( $viewcount ) {
- $tpl->set( 'viewcount', $this->msg( 'viewcount' )->numParams( $viewcount )->parse() );
- }
- }
-
- if ( $wgPageShowWatchingUsers ) {
- $dbr = wfGetDB( DB_SLAVE );
- $num = $dbr->selectField( 'watchlist', 'COUNT(*)',
- array( 'wl_title' => $title->getDBkey(), 'wl_namespace' => $title->getNamespace() ),
- __METHOD__
- );
- if ( $num > 0 ) {
- $tpl->set( 'numberofwatchingusers',
- $this->msg( 'number_of_watching_users_pageview' )->numParams( $num )->parse()
- );
- }
- }
-
if ( $wgMaxCredits != 0 ) {
$tpl->set( 'credits', Action::factory( 'credits', $this->getWikiPage(),
$this->getContext() )->getCredits( $wgMaxCredits, $wgShowCreditsIfMax ) );
@@ -443,9 +371,7 @@ class SkinTemplate extends Skin {
}
$tpl->set( 'copyright', $this->getCopyright() );
}
- wfProfileOut( __METHOD__ . '-stuff3' );
- wfProfileIn( __METHOD__ . '-stuff4' );
$tpl->set( 'copyrightico', $this->getCopyrightIcon() );
$tpl->set( 'poweredbyico', $this->getPoweredBy() );
$tpl->set( 'disclaimer', $this->disclaimerLink() );
@@ -455,7 +381,6 @@ class SkinTemplate extends Skin {
$tpl->set( 'footerlinks', array(
'info' => array(
'lastmod',
- 'viewcount',
'numberofwatchingusers',
'credits',
'copyright',
@@ -486,6 +411,8 @@ class SkinTemplate extends Skin {
}
}
+ $tpl->set( 'indicators', $out->getIndicators() );
+
$tpl->set( 'sitenotice', $this->getSiteNotice() );
$tpl->set( 'bottomscripts', $this->bottomScripts() );
$tpl->set( 'printfooter', $this->printSource() );
@@ -515,9 +442,7 @@ class SkinTemplate extends Skin {
} else {
$tpl->set( 'language_urls', false );
}
- wfProfileOut( __METHOD__ . '-stuff4' );
- wfProfileIn( __METHOD__ . '-stuff5' );
# Personal toolbar
$tpl->set( 'personal_urls', $this->buildPersonalUrls() );
$content_navigation = $this->buildContentNavigationUrls();
@@ -536,11 +461,11 @@ class SkinTemplate extends Skin {
$tpl->set( 'reporttime', wfReportTime() );
// original version by hansm
- if ( !wfRunHooks( 'SkinTemplateOutputPageBeforeExec', array( &$this, &$tpl ) ) ) {
+ if ( !Hooks::run( 'SkinTemplateOutputPageBeforeExec', array( &$this, &$tpl ) ) ) {
wfDebug( __METHOD__ . ": Hook SkinTemplateOutputPageBeforeExec broke outputPage execution!\n" );
}
- // Set the bodytext to another key so that skins can just output it on it's own
+ // Set the bodytext to another key so that skins can just output it on its own
// and output printfooter and debughtml separately
$tpl->set( 'bodycontent', $tpl->data['bodytext'] );
@@ -557,9 +482,7 @@ class SkinTemplate extends Skin {
// allow extensions adding stuff after the page content.
// See Skin::afterContentHook() for further documentation.
$tpl->set( 'dataAfterContent', $this->afterContentHook() );
- wfProfileOut( __METHOD__ . '-stuff5' );
- wfProfileOut( __METHOD__ );
return $tpl;
}
@@ -623,7 +546,6 @@ class SkinTemplate extends Skin {
$title = $this->getTitle();
$request = $this->getRequest();
$pageurl = $title->getLocalURL();
- wfProfileIn( __METHOD__ );
/* set up the default links for the personal toolbar */
$personal_urls = array();
@@ -755,8 +677,7 @@ class SkinTemplate extends Skin {
$personal_urls['login'] = $login_url;
}
- wfRunHooks( 'PersonalUrls', array( &$personal_urls, &$title, $this ) );
- wfProfileOut( __METHOD__ );
+ Hooks::run( 'PersonalUrls', array( &$personal_urls, &$title, $this ) );
return $personal_urls;
}
@@ -796,12 +717,12 @@ class SkinTemplate extends Skin {
$text = $msg->text();
} else {
global $wgContLang;
- $text = $wgContLang->getFormattedNsText(
+ $text = $wgContLang->getConverter()->convertNamespace(
MWNamespace::getSubject( $title->getNamespace() ) );
}
$result = array();
- if ( !wfRunHooks( 'SkinTemplateTabAction', array( &$this,
+ if ( !Hooks::run( 'SkinTemplateTabAction', array( &$this,
$title, $message, $selected, $checkEdit,
&$classes, &$query, &$text, &$result ) ) ) {
return $result;
@@ -823,17 +744,20 @@ class SkinTemplate extends Skin {
self::checkTitle( $title, $name );
return array(
'href' => $title->getLocalURL( $urlaction ),
- 'exists' => $title->getArticleID() != 0,
+ 'exists' => $title->isKnown(),
);
}
+ /**
+ * @todo is this even used?
+ */
function makeArticleUrlDetails( $name, $urlaction = '' ) {
$title = Title::newFromText( $name );
$title = $title->getSubjectPage();
self::checkTitle( $title, $name );
return array(
'href' => $title->getLocalURL( $urlaction ),
- 'exists' => $title->getArticleID() != 0,
+ 'exists' => $title->exists(),
);
}
@@ -874,8 +798,6 @@ class SkinTemplate extends Skin {
protected function buildContentNavigationUrls() {
global $wgDisableLangConversion;
- wfProfileIn( __METHOD__ );
-
// Display tabs for the relevant title rather than always the title itself
$title = $this->getRelevantTitle();
$onPage = $title->equals( $this->getTitle() );
@@ -897,7 +819,7 @@ class SkinTemplate extends Skin {
$userCanRead = $title->quickUserCan( 'read', $user );
$preventActiveTabs = false;
- wfRunHooks( 'SkinTemplatePreventOtherActiveTabs', array( &$this, &$preventActiveTabs ) );
+ Hooks::run( 'SkinTemplatePreventOtherActiveTabs', array( &$this, &$preventActiveTabs ) );
// Checks if page is some kind of content
if ( $title->canExist() ) {
@@ -961,8 +883,6 @@ class SkinTemplate extends Skin {
);
}
- wfProfileIn( __METHOD__ . '-edit' );
-
// Checks if user can edit the current page if it exists or create it otherwise
if ( $title->quickUserCan( 'edit', $user )
&& ( $title->exists() || $title->quickUserCan( 'create', $user ) )
@@ -1019,9 +939,7 @@ class SkinTemplate extends Skin {
'primary' => true, // don't collapse this in vector
);
}
- wfProfileOut( __METHOD__ . '-edit' );
- wfProfileIn( __METHOD__ . '-live' );
// Checks if the page exists
if ( $title->exists() ) {
// Adds history view link
@@ -1030,7 +948,6 @@ class SkinTemplate extends Skin {
'text' => wfMessageFallback( "$skname-view-history", 'history_short' )
->setContext( $this->getContext() )->text(),
'href' => $title->getLocalURL( 'action=history' ),
- 'rel' => 'archives',
);
if ( $title->quickUserCan( 'delete', $user ) ) {
@@ -1056,7 +973,7 @@ class SkinTemplate extends Skin {
if ( $user->isAllowed( 'deletedhistory' ) ) {
$n = $title->isDeleted();
if ( $n ) {
- $undelTitle = SpecialPage::getTitleFor( 'Undelete' );
+ $undelTitle = SpecialPage::getTitleFor( 'Undelete', $title->getPrefixedDBkey() );
// If the user can't undelete but can view deleted
// history show them a "View .. deleted" tab instead.
$msgKey = $user->isAllowed( 'undelete' ) ? 'undelete' : 'viewdeleted';
@@ -1064,7 +981,7 @@ class SkinTemplate extends Skin {
'class' => $this->getTitle()->isSpecial( 'Undelete' ) ? 'selected' : false,
'text' => wfMessageFallback( "$skname-action-$msgKey", "{$msgKey}_short" )
->setContext( $this->getContext() )->numParams( $n )->text(),
- 'href' => $undelTitle->getLocalURL( array( 'target' => $title->getPrefixedDBkey() ) )
+ 'href' => $undelTitle->getLocalURL()
);
}
}
@@ -1082,8 +999,6 @@ class SkinTemplate extends Skin {
);
}
- wfProfileOut( __METHOD__ . '-live' );
-
// Checks if the user is logged in
if ( $this->loggedin && $user->isAllowedAll( 'viewmywatchlist', 'editmywatchlist' ) ) {
/**
@@ -1106,7 +1021,7 @@ class SkinTemplate extends Skin {
}
}
- wfRunHooks( 'SkinTemplateNavigation', array( &$this, &$content_navigation ) );
+ Hooks::run( 'SkinTemplateNavigation', array( &$this, &$content_navigation ) );
if ( $userCanRead && !$wgDisableLangConversion ) {
$pageLang = $title->getPageLanguage();
@@ -1148,12 +1063,12 @@ class SkinTemplate extends Skin {
'context' => 'subject'
);
- wfRunHooks( 'SkinTemplateNavigation::SpecialPage',
+ Hooks::run( 'SkinTemplateNavigation::SpecialPage',
array( &$this, &$content_navigation ) );
}
// Equiv to SkinTemplateContentActions
- wfRunHooks( 'SkinTemplateNavigation::Universal', array( &$this, &$content_navigation ) );
+ Hooks::run( 'SkinTemplateNavigation::Universal', array( &$this, &$content_navigation ) );
// Setup xml ids and tooltip info
foreach ( $content_navigation as $section => &$links ) {
@@ -1190,8 +1105,6 @@ class SkinTemplate extends Skin {
}
}
- wfProfileOut( __METHOD__ );
-
return $content_navigation;
}
@@ -1202,8 +1115,6 @@ class SkinTemplate extends Skin {
*/
private function buildContentActionUrls( $content_navigation ) {
- wfProfileIn( __METHOD__ );
-
// content_actions has been replaced with content_navigation for backwards
// compatibility and also for skins that just want simple tabs content_actions
// is now built by flattening the content_navigation arrays into one
@@ -1235,8 +1146,6 @@ class SkinTemplate extends Skin {
}
}
- wfProfileOut( __METHOD__ );
-
return $content_actions;
}
@@ -1247,8 +1156,6 @@ class SkinTemplate extends Skin {
protected function buildNavUrls() {
global $wgUploadNavigationUrl;
- wfProfileIn( __METHOD__ );
-
$out = $this->getOutput();
$request = $this->getRequest();
@@ -1295,7 +1202,7 @@ class SkinTemplate extends Skin {
}
// Use the copy of revision ID in case this undocumented, shady hook tries to mess with internals
- wfRunHooks( 'SkinTemplateBuildNavUrlsNav_urlsAfterPermalink',
+ Hooks::run( 'SkinTemplateBuildNavUrlsNav_urlsAfterPermalink',
array( &$this, &$nav_urls, &$revid, &$revid ) );
}
@@ -1309,7 +1216,7 @@ class SkinTemplate extends Skin {
'href' => $this->getTitle()->getLocalURL( "action=info" )
);
- if ( $this->getTitle()->getArticleID() ) {
+ if ( $this->getTitle()->exists() ) {
$nav_urls['recentchangeslinked'] = array(
'href' => SpecialPage::getTitleFor( 'Recentchangeslinked', $this->thispage )->getLocalURL()
);
@@ -1353,7 +1260,6 @@ class SkinTemplate extends Skin {
}
}
- wfProfileOut( __METHOD__ );
return $nav_urls;
}
@@ -1365,759 +1271,3 @@ class SkinTemplate extends Skin {
return $this->getTitle()->getNamespaceKey();
}
}
-
-/**
- * Generic wrapper for template functions, with interface
- * compatible with what we use of PHPTAL 0.7.
- * @ingroup Skins
- */
-abstract class QuickTemplate {
-
- /** @var Config $config */
- protected $config;
-
- /**
- * @param Config $config
- */
- function __construct( Config $config = null ) {
- $this->data = array();
- $this->translator = new MediaWikiI18N();
- if ( $config === null ) {
- wfDebug( __METHOD__ . ' was called with no Config instance passed to it' );
- $config = ConfigFactory::getDefaultInstance()->makeConfig( 'main' );
- }
- $this->config = $config;
- }
-
- /**
- * Sets the value $value to $name
- * @param string $name
- * @param mixed $value
- */
- public function set( $name, $value ) {
- $this->data[$name] = $value;
- }
-
- /**
- * Gets the template data requested
- * @since 1.22
- * @param string $name Key for the data
- * @param mixed $default Optional default (or null)
- * @return mixed The value of the data requested or the deafult
- */
- public function get( $name, $default = null ) {
- if ( isset( $this->data[$name] ) ) {
- return $this->data[$name];
- } else {
- return $default;
- }
- }
-
- /**
- * @param string $name
- * @param mixed $value
- */
- public function setRef( $name, &$value ) {
- $this->data[$name] =& $value;
- }
-
- /**
- * @param MediaWikiI18N $t
- */
- public function setTranslator( &$t ) {
- $this->translator = &$t;
- }
-
- /**
- * Main function, used by classes that subclass QuickTemplate
- * to show the actual HTML output
- */
- abstract public function execute();
-
- /**
- * @private
- * @param string $str
- * @return string
- */
- function text( $str ) {
- echo htmlspecialchars( $this->data[$str] );
- }
-
- /**
- * @private
- * @param string $str
- * @return string
- */
- function html( $str ) {
- echo $this->data[$str];
- }
-
- /**
- * @private
- * @param string $str
- * @return string
- */
- function msg( $str ) {
- echo htmlspecialchars( $this->translator->translate( $str ) );
- }
-
- /**
- * @private
- * @param string $str
- * @return string
- */
- function msgHtml( $str ) {
- echo $this->translator->translate( $str );
- }
-
- /**
- * An ugly, ugly hack.
- * @private
- * @param string $str
- * @return string
- */
- function msgWiki( $str ) {
- global $wgOut;
-
- $text = $this->translator->translate( $str );
- echo $wgOut->parse( $text );
- }
-
- /**
- * @private
- * @param string $str
- * @return bool
- */
- function haveData( $str ) {
- return isset( $this->data[$str] );
- }
-
- /**
- * @private
- *
- * @param string $str
- * @return bool
- */
- function haveMsg( $str ) {
- $msg = $this->translator->translate( $str );
- return ( $msg != '-' ) && ( $msg != '' ); # ????
- }
-
- /**
- * Get the Skin object related to this object
- *
- * @return Skin
- */
- public function getSkin() {
- return $this->data['skin'];
- }
-
- /**
- * Fetch the output of a QuickTemplate and return it
- *
- * @since 1.23
- * @return string
- */
- public function getHTML() {
- ob_start();
- $this->execute();
- $html = ob_get_contents();
- ob_end_clean();
- return $html;
- }
-}
-
-/**
- * New base template for a skin's template extended from QuickTemplate
- * this class features helper methods that provide common ways of interacting
- * with the data stored in the QuickTemplate
- */
-abstract class BaseTemplate extends QuickTemplate {
-
- /**
- * Get a Message object with its context set
- *
- * @param string $name Message name
- * @return Message
- */
- public function getMsg( $name ) {
- return $this->getSkin()->msg( $name );
- }
-
- function msg( $str ) {
- echo $this->getMsg( $str )->escaped();
- }
-
- function msgHtml( $str ) {
- echo $this->getMsg( $str )->text();
- }
-
- function msgWiki( $str ) {
- echo $this->getMsg( $str )->parseAsBlock();
- }
-
- /**
- * Create an array of common toolbox items from the data in the quicktemplate
- * stored by SkinTemplate.
- * The resulting array is built according to a format intended to be passed
- * through makeListItem to generate the html.
- * @return array
- */
- function getToolbox() {
- wfProfileIn( __METHOD__ );
-
- $toolbox = array();
- if ( isset( $this->data['nav_urls']['whatlinkshere'] )
- && $this->data['nav_urls']['whatlinkshere']
- ) {
- $toolbox['whatlinkshere'] = $this->data['nav_urls']['whatlinkshere'];
- $toolbox['whatlinkshere']['id'] = 't-whatlinkshere';
- }
- if ( isset( $this->data['nav_urls']['recentchangeslinked'] )
- && $this->data['nav_urls']['recentchangeslinked']
- ) {
- $toolbox['recentchangeslinked'] = $this->data['nav_urls']['recentchangeslinked'];
- $toolbox['recentchangeslinked']['msg'] = 'recentchangeslinked-toolbox';
- $toolbox['recentchangeslinked']['id'] = 't-recentchangeslinked';
- }
- if ( isset( $this->data['feeds'] ) && $this->data['feeds'] ) {
- $toolbox['feeds']['id'] = 'feedlinks';
- $toolbox['feeds']['links'] = array();
- foreach ( $this->data['feeds'] as $key => $feed ) {
- $toolbox['feeds']['links'][$key] = $feed;
- $toolbox['feeds']['links'][$key]['id'] = "feed-$key";
- $toolbox['feeds']['links'][$key]['rel'] = 'alternate';
- $toolbox['feeds']['links'][$key]['type'] = "application/{$key}+xml";
- $toolbox['feeds']['links'][$key]['class'] = 'feedlink';
- }
- }
- foreach ( array( 'contributions', 'log', 'blockip', 'emailuser',
- 'userrights', 'upload', 'specialpages' ) as $special
- ) {
- if ( isset( $this->data['nav_urls'][$special] ) && $this->data['nav_urls'][$special] ) {
- $toolbox[$special] = $this->data['nav_urls'][$special];
- $toolbox[$special]['id'] = "t-$special";
- }
- }
- if ( isset( $this->data['nav_urls']['print'] ) && $this->data['nav_urls']['print'] ) {
- $toolbox['print'] = $this->data['nav_urls']['print'];
- $toolbox['print']['id'] = 't-print';
- $toolbox['print']['rel'] = 'alternate';
- $toolbox['print']['msg'] = 'printableversion';
- }
- if ( isset( $this->data['nav_urls']['permalink'] ) && $this->data['nav_urls']['permalink'] ) {
- $toolbox['permalink'] = $this->data['nav_urls']['permalink'];
- if ( $toolbox['permalink']['href'] === '' ) {
- unset( $toolbox['permalink']['href'] );
- $toolbox['ispermalink']['tooltiponly'] = true;
- $toolbox['ispermalink']['id'] = 't-ispermalink';
- $toolbox['ispermalink']['msg'] = 'permalink';
- } else {
- $toolbox['permalink']['id'] = 't-permalink';
- }
- }
- if ( isset( $this->data['nav_urls']['info'] ) && $this->data['nav_urls']['info'] ) {
- $toolbox['info'] = $this->data['nav_urls']['info'];
- $toolbox['info']['id'] = 't-info';
- }
-
- wfRunHooks( 'BaseTemplateToolbox', array( &$this, &$toolbox ) );
- wfProfileOut( __METHOD__ );
- return $toolbox;
- }
-
- /**
- * Create an array of personal tools items from the data in the quicktemplate
- * stored by SkinTemplate.
- * The resulting array is built according to a format intended to be passed
- * through makeListItem to generate the html.
- * This is in reality the same list as already stored in personal_urls
- * however it is reformatted so that you can just pass the individual items
- * to makeListItem instead of hardcoding the element creation boilerplate.
- * @return array
- */
- function getPersonalTools() {
- $personal_tools = array();
- foreach ( $this->get( 'personal_urls' ) as $key => $plink ) {
- # The class on a personal_urls item is meant to go on the <a> instead
- # of the <li> so we have to use a single item "links" array instead
- # of using most of the personal_url's keys directly.
- $ptool = array(
- 'links' => array(
- array( 'single-id' => "pt-$key" ),
- ),
- 'id' => "pt-$key",
- );
- if ( isset( $plink['active'] ) ) {
- $ptool['active'] = $plink['active'];
- }
- foreach ( array( 'href', 'class', 'text', 'dir' ) as $k ) {
- if ( isset( $plink[$k] ) ) {
- $ptool['links'][0][$k] = $plink[$k];
- }
- }
- $personal_tools[$key] = $ptool;
- }
- return $personal_tools;
- }
-
- function getSidebar( $options = array() ) {
- // Force the rendering of the following portals
- $sidebar = $this->data['sidebar'];
- if ( !isset( $sidebar['SEARCH'] ) ) {
- $sidebar['SEARCH'] = true;
- }
- if ( !isset( $sidebar['TOOLBOX'] ) ) {
- $sidebar['TOOLBOX'] = true;
- }
- if ( !isset( $sidebar['LANGUAGES'] ) ) {
- $sidebar['LANGUAGES'] = true;
- }
-
- if ( !isset( $options['search'] ) || $options['search'] !== true ) {
- unset( $sidebar['SEARCH'] );
- }
- if ( isset( $options['toolbox'] ) && $options['toolbox'] === false ) {
- unset( $sidebar['TOOLBOX'] );
- }
- if ( isset( $options['languages'] ) && $options['languages'] === false ) {
- unset( $sidebar['LANGUAGES'] );
- }
-
- $boxes = array();
- foreach ( $sidebar as $boxName => $content ) {
- if ( $content === false ) {
- continue;
- }
- switch ( $boxName ) {
- case 'SEARCH':
- // Search is a special case, skins should custom implement this
- $boxes[$boxName] = array(
- 'id' => 'p-search',
- 'header' => $this->getMsg( 'search' )->text(),
- 'generated' => false,
- 'content' => true,
- );
- break;
- case 'TOOLBOX':
- $msgObj = $this->getMsg( 'toolbox' );
- $boxes[$boxName] = array(
- 'id' => 'p-tb',
- 'header' => $msgObj->exists() ? $msgObj->text() : 'toolbox',
- 'generated' => false,
- 'content' => $this->getToolbox(),
- );
- break;
- case 'LANGUAGES':
- if ( $this->data['language_urls'] ) {
- $msgObj = $this->getMsg( 'otherlanguages' );
- $boxes[$boxName] = array(
- 'id' => 'p-lang',
- 'header' => $msgObj->exists() ? $msgObj->text() : 'otherlanguages',
- 'generated' => false,
- 'content' => $this->data['language_urls'],
- );
- }
- break;
- default:
- $msgObj = $this->getMsg( $boxName );
- $boxes[$boxName] = array(
- 'id' => "p-$boxName",
- 'header' => $msgObj->exists() ? $msgObj->text() : $boxName,
- 'generated' => true,
- 'content' => $content,
- );
- break;
- }
- }
-
- // HACK: Compatibility with extensions still using SkinTemplateToolboxEnd
- $hookContents = null;
- if ( isset( $boxes['TOOLBOX'] ) ) {
- ob_start();
- // We pass an extra 'true' at the end so extensions using BaseTemplateToolbox
- // can abort and avoid outputting double toolbox links
- wfRunHooks( 'SkinTemplateToolboxEnd', array( &$this, true ) );
- $hookContents = ob_get_contents();
- ob_end_clean();
- if ( !trim( $hookContents ) ) {
- $hookContents = null;
- }
- }
- // END hack
-
- if ( isset( $options['htmlOnly'] ) && $options['htmlOnly'] === true ) {
- foreach ( $boxes as $boxName => $box ) {
- if ( is_array( $box['content'] ) ) {
- $content = '<ul>';
- foreach ( $box['content'] as $key => $val ) {
- $content .= "\n " . $this->makeListItem( $key, $val );
- }
- // HACK, shove the toolbox end onto the toolbox if we're rendering itself
- if ( $hookContents ) {
- $content .= "\n $hookContents";
- }
- // END hack
- $content .= "\n</ul>\n";
- $boxes[$boxName]['content'] = $content;
- }
- }
- } else {
- if ( $hookContents ) {
- $boxes['TOOLBOXEND'] = array(
- 'id' => 'p-toolboxend',
- 'header' => $boxes['TOOLBOX']['header'],
- 'generated' => false,
- 'content' => "<ul>{$hookContents}</ul>",
- );
- // HACK: Make sure that TOOLBOXEND is sorted next to TOOLBOX
- $boxes2 = array();
- foreach ( $boxes as $key => $box ) {
- if ( $key === 'TOOLBOXEND' ) {
- continue;
- }
- $boxes2[$key] = $box;
- if ( $key === 'TOOLBOX' ) {
- $boxes2['TOOLBOXEND'] = $boxes['TOOLBOXEND'];
- }
- }
- $boxes = $boxes2;
- // END hack
- }
- }
-
- return $boxes;
- }
-
- /**
- * @param string $name
- */
- protected function renderAfterPortlet( $name ) {
- $content = '';
- wfRunHooks( 'BaseTemplateAfterPortlet', array( $this, $name, &$content ) );
-
- if ( $content !== '' ) {
- echo "<div class='after-portlet after-portlet-$name'>$content</div>";
- }
-
- }
-
- /**
- * Makes a link, usually used by makeListItem to generate a link for an item
- * in a list used in navigation lists, portlets, portals, sidebars, etc...
- *
- * @param string $key Usually a key from the list you are generating this
- * link from.
- * @param array $item Contains some of a specific set of keys.
- *
- * The text of the link will be generated either from the contents of the
- * "text" key in the $item array, if a "msg" key is present a message by
- * that name will be used, and if neither of those are set the $key will be
- * used as a message name.
- *
- * If a "href" key is not present makeLink will just output htmlescaped text.
- * The "href", "id", "class", "rel", and "type" keys are used as attributes
- * for the link if present.
- *
- * If an "id" or "single-id" (if you don't want the actual id to be output
- * on the link) is present it will be used to generate a tooltip and
- * accesskey for the link.
- *
- * The keys "context" and "primary" are ignored; these keys are used
- * internally by skins and are not supposed to be included in the HTML
- * output.
- *
- * If you don't want an accesskey, set $item['tooltiponly'] = true;
- *
- * @param array $options Can be used to affect the output of a link.
- * Possible options are:
- * - 'text-wrapper' key to specify a list of elements to wrap the text of
- * a link in. This should be an array of arrays containing a 'tag' and
- * optionally an 'attributes' key. If you only have one element you don't
- * need to wrap it in another array. eg: To use <a><span>...</span></a>
- * in all links use array( 'text-wrapper' => array( 'tag' => 'span' ) )
- * for your options.
- * - 'link-class' key can be used to specify additional classes to apply
- * to all links.
- * - 'link-fallback' can be used to specify a tag to use instead of "<a>"
- * if there is no link. eg: If you specify 'link-fallback' => 'span' than
- * any non-link will output a "<span>" instead of just text.
- *
- * @return string
- */
- function makeLink( $key, $item, $options = array() ) {
- if ( isset( $item['text'] ) ) {
- $text = $item['text'];
- } else {
- $text = $this->translator->translate( isset( $item['msg'] ) ? $item['msg'] : $key );
- }
-
- $html = htmlspecialchars( $text );
-
- if ( isset( $options['text-wrapper'] ) ) {
- $wrapper = $options['text-wrapper'];
- if ( isset( $wrapper['tag'] ) ) {
- $wrapper = array( $wrapper );
- }
- while ( count( $wrapper ) > 0 ) {
- $element = array_pop( $wrapper );
- $html = Html::rawElement( $element['tag'], isset( $element['attributes'] )
- ? $element['attributes']
- : null, $html );
- }
- }
-
- if ( isset( $item['href'] ) || isset( $options['link-fallback'] ) ) {
- $attrs = $item;
- foreach ( array( 'single-id', 'text', 'msg', 'tooltiponly', 'context', 'primary' ) as $k ) {
- unset( $attrs[$k] );
- }
-
- if ( isset( $item['id'] ) && !isset( $item['single-id'] ) ) {
- $item['single-id'] = $item['id'];
- }
- if ( isset( $item['single-id'] ) ) {
- if ( isset( $item['tooltiponly'] ) && $item['tooltiponly'] ) {
- $title = Linker::titleAttrib( $item['single-id'] );
- if ( $title !== false ) {
- $attrs['title'] = $title;
- }
- } else {
- $tip = Linker::tooltipAndAccesskeyAttribs( $item['single-id'] );
- if ( isset( $tip['title'] ) && $tip['title'] !== false ) {
- $attrs['title'] = $tip['title'];
- }
- if ( isset( $tip['accesskey'] ) && $tip['accesskey'] !== false ) {
- $attrs['accesskey'] = $tip['accesskey'];
- }
- }
- }
- if ( isset( $options['link-class'] ) ) {
- if ( isset( $attrs['class'] ) ) {
- $attrs['class'] .= " {$options['link-class']}";
- } else {
- $attrs['class'] = $options['link-class'];
- }
- }
- $html = Html::rawElement( isset( $attrs['href'] )
- ? 'a'
- : $options['link-fallback'], $attrs, $html );
- }
-
- return $html;
- }
-
- /**
- * Generates a list item for a navigation, portlet, portal, sidebar... list
- *
- * @param string $key Usually a key from the list you are generating this link from.
- * @param array $item Array of list item data containing some of a specific set of keys.
- * The "id", "class" and "itemtitle" keys will be used as attributes for the list item,
- * if "active" contains a value of true a "active" class will also be appended to class.
- *
- * @param array $options
- *
- * If you want something other than a "<li>" you can pass a tag name such as
- * "tag" => "span" in the $options array to change the tag used.
- * link/content data for the list item may come in one of two forms
- * A "links" key may be used, in which case it should contain an array with
- * a list of links to include inside the list item, see makeLink for the
- * format of individual links array items.
- *
- * Otherwise the relevant keys from the list item $item array will be passed
- * to makeLink instead. Note however that "id" and "class" are used by the
- * list item directly so they will not be passed to makeLink
- * (however the link will still support a tooltip and accesskey from it)
- * If you need an id or class on a single link you should include a "links"
- * array with just one link item inside of it. If you want to add a title
- * to the list item itself, you can set "itemtitle" to the value.
- * $options is also passed on to makeLink calls
- *
- * @return string
- */
- function makeListItem( $key, $item, $options = array() ) {
- if ( isset( $item['links'] ) ) {
- $links = array();
- foreach ( $item['links'] as $linkKey => $link ) {
- $links[] = $this->makeLink( $linkKey, $link, $options );
- }
- $html = implode( ' ', $links );
- } else {
- $link = $item;
- // These keys are used by makeListItem and shouldn't be passed on to the link
- foreach ( array( 'id', 'class', 'active', 'tag', 'itemtitle' ) as $k ) {
- unset( $link[$k] );
- }
- if ( isset( $item['id'] ) && !isset( $item['single-id'] ) ) {
- // The id goes on the <li> not on the <a> for single links
- // but makeSidebarLink still needs to know what id to use when
- // generating tooltips and accesskeys.
- $link['single-id'] = $item['id'];
- }
- $html = $this->makeLink( $key, $link, $options );
- }
-
- $attrs = array();
- foreach ( array( 'id', 'class' ) as $attr ) {
- if ( isset( $item[$attr] ) ) {
- $attrs[$attr] = $item[$attr];
- }
- }
- if ( isset( $item['active'] ) && $item['active'] ) {
- if ( !isset( $attrs['class'] ) ) {
- $attrs['class'] = '';
- }
- $attrs['class'] .= ' active';
- $attrs['class'] = trim( $attrs['class'] );
- }
- if ( isset( $item['itemtitle'] ) ) {
- $attrs['title'] = $item['itemtitle'];
- }
- return Html::rawElement( isset( $options['tag'] ) ? $options['tag'] : 'li', $attrs, $html );
- }
-
- function makeSearchInput( $attrs = array() ) {
- $realAttrs = array(
- 'type' => 'search',
- 'name' => 'search',
- 'placeholder' => wfMessage( 'searchsuggest-search' )->text(),
- 'value' => $this->get( 'search', '' ),
- );
- $realAttrs = array_merge( $realAttrs, Linker::tooltipAndAccesskeyAttribs( 'search' ), $attrs );
- return Html::element( 'input', $realAttrs );
- }
-
- function makeSearchButton( $mode, $attrs = array() ) {
- switch ( $mode ) {
- case 'go':
- case 'fulltext':
- $realAttrs = array(
- 'type' => 'submit',
- 'name' => $mode,
- 'value' => $this->translator->translate(
- $mode == 'go' ? 'searcharticle' : 'searchbutton' ),
- );
- $realAttrs = array_merge(
- $realAttrs,
- Linker::tooltipAndAccesskeyAttribs( "search-$mode" ),
- $attrs
- );
- return Html::element( 'input', $realAttrs );
- case 'image':
- $buttonAttrs = array(
- 'type' => 'submit',
- 'name' => 'button',
- );
- $buttonAttrs = array_merge(
- $buttonAttrs,
- Linker::tooltipAndAccesskeyAttribs( 'search-fulltext' ),
- $attrs
- );
- unset( $buttonAttrs['src'] );
- unset( $buttonAttrs['alt'] );
- unset( $buttonAttrs['width'] );
- unset( $buttonAttrs['height'] );
- $imgAttrs = array(
- 'src' => $attrs['src'],
- 'alt' => isset( $attrs['alt'] )
- ? $attrs['alt']
- : $this->translator->translate( 'searchbutton' ),
- 'width' => isset( $attrs['width'] ) ? $attrs['width'] : null,
- 'height' => isset( $attrs['height'] ) ? $attrs['height'] : null,
- );
- return Html::rawElement( 'button', $buttonAttrs, Html::element( 'img', $imgAttrs ) );
- default:
- throw new MWException( 'Unknown mode passed to BaseTemplate::makeSearchButton' );
- }
- }
-
- /**
- * Returns an array of footerlinks trimmed down to only those footer links that
- * are valid.
- * If you pass "flat" as an option then the returned array will be a flat array
- * of footer icons instead of a key/value array of footerlinks arrays broken
- * up into categories.
- * @param string $option
- * @return array|mixed
- */
- function getFooterLinks( $option = null ) {
- $footerlinks = $this->get( 'footerlinks' );
-
- // Reduce footer links down to only those which are being used
- $validFooterLinks = array();
- foreach ( $footerlinks as $category => $links ) {
- $validFooterLinks[$category] = array();
- foreach ( $links as $link ) {
- if ( isset( $this->data[$link] ) && $this->data[$link] ) {
- $validFooterLinks[$category][] = $link;
- }
- }
- if ( count( $validFooterLinks[$category] ) <= 0 ) {
- unset( $validFooterLinks[$category] );
- }
- }
-
- if ( $option == 'flat' ) {
- // fold footerlinks into a single array using a bit of trickery
- $validFooterLinks = call_user_func_array(
- 'array_merge',
- array_values( $validFooterLinks )
- );
- }
-
- return $validFooterLinks;
- }
-
- /**
- * Returns an array of footer icons filtered down by options relevant to how
- * the skin wishes to display them.
- * If you pass "icononly" as the option all footer icons which do not have an
- * image icon set will be filtered out.
- * If you pass "nocopyright" then MediaWiki's copyright icon will not be included
- * in the list of footer icons. This is mostly useful for skins which only
- * display the text from footericons instead of the images and don't want a
- * duplicate copyright statement because footerlinks already rendered one.
- * @param string $option
- * @return string
- */
- function getFooterIcons( $option = null ) {
- // Generate additional footer icons
- $footericons = $this->get( 'footericons' );
-
- if ( $option == 'icononly' ) {
- // Unset any icons which don't have an image
- foreach ( $footericons as &$footerIconsBlock ) {
- foreach ( $footerIconsBlock as $footerIconKey => $footerIcon ) {
- if ( !is_string( $footerIcon ) && !isset( $footerIcon['src'] ) ) {
- unset( $footerIconsBlock[$footerIconKey] );
- }
- }
- }
- // Redo removal of any empty blocks
- foreach ( $footericons as $footerIconsKey => &$footerIconsBlock ) {
- if ( count( $footerIconsBlock ) <= 0 ) {
- unset( $footericons[$footerIconsKey] );
- }
- }
- } elseif ( $option == 'nocopyright' ) {
- unset( $footericons['copyright']['copyright'] );
- if ( count( $footericons['copyright'] ) <= 0 ) {
- unset( $footericons['copyright'] );
- }
- }
-
- return $footericons;
- }
-
- /**
- * Output the basic end-page trail including bottomscripts, reporttime, and
- * debug stuff. This should be called right before outputting the closing
- * body and html tags.
- */
- function printTrail() { ?>
-<?php echo MWDebug::getDebugHTML( $this->getSkin()->getContext() ); ?>
-<?php $this->html( 'bottomscripts' ); /* JS call to runBodyOnloadHook */ ?>
-<?php $this->html( 'reporttime' ) ?>
-<?php
- }
-}