r70966 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r70965‎ | r70966 | r70967 >
Date:15:19, 12 August 2010
Author:peter17
Status:resolved (Comments)
Tags:
Comment:
Add Special:GlobalTemplateUsage to show the usage of any page on distant wikis; copied and adapted from Extension:GlobalUsage which should be built-in
Modified paths:
  • /branches/iwtransclusion/phase3/includes/AutoLoader.php (modified) (history)
  • /branches/iwtransclusion/phase3/includes/SpecialPage.php (modified) (history)
  • /branches/iwtransclusion/phase3/includes/specials/SpecialGlobalTemplateUsage.php (added) (history)
  • /branches/iwtransclusion/phase3/languages/messages/MessagesEn.php (modified) (history)

Diff [purge]

Index: branches/iwtransclusion/phase3/includes/SpecialPage.php
@@ -173,6 +173,7 @@
174174 # Page tools
175175 'ComparePages' => 'SpecialComparePages',
176176 'Export' => 'SpecialExport',
 177+ 'GlobalTemplateUsage' => 'SpecialGlobalTemplateUsage',
177178 'Import' => 'SpecialImport',
178179 'Undelete' => 'UndeleteForm',
179180 'Whatlinkshere' => 'SpecialWhatlinkshere',
Index: branches/iwtransclusion/phase3/includes/specials/SpecialGlobalTemplateUsage.php
@@ -0,0 +1,442 @@
 2+<?php
 3+/**
 4+ * This file has been copied from Extension:GlobalUsage and adapted
 5+ * to show the usage of a template instead of a file.
 6+ * Special page to show global template usage. Also contains hook functions for
 7+ * showing usage on an template page.
 8+ */
 9+
 10+class SpecialGlobalTemplateUsage extends SpecialPage {
 11+ public function __construct() {
 12+ parent::__construct( 'GlobalTemplateUsage', 'globaltemplateusage' );
 13+ }
 14+
 15+ /**
 16+ * Entry point
 17+ */
 18+ public function execute( $par ) {
 19+ global $wgOut, $wgRequest;
 20+
 21+ $target = $par ? $par : $wgRequest->getVal( 'target' );
 22+ $this->target = Title::newFromText( $target );
 23+
 24+ $this->setHeaders();
 25+
 26+ $this->showForm();
 27+
 28+ if ( is_null( $this->target ) )
 29+ {
 30+ $wgOut->setPageTitle( wfMsg( 'globaltemplateusage' ) );
 31+ return;
 32+ }
 33+
 34+ $wgOut->setPageTitle( wfMsg( 'globaltemplateusage-for', $this->target->getPrefixedText() ) );
 35+
 36+ $this->showResult();
 37+ }
 38+
 39+ /**
 40+ * Shows the search form
 41+ */
 42+ private function showForm() {
 43+ global $wgScript, $wgOut, $wgRequest;
 44+
 45+ /* Build form */
 46+ $html = Xml::openElement( 'form', array( 'action' => $wgScript ) ) . "\n";
 47+ // Name of SpecialPage
 48+ $html .= Xml::hidden( 'title', $this->getTitle( )->getPrefixedText( ) ) . "\n";
 49+ // Limit
 50+ $html .= Xml::hidden( 'limit', $wgRequest->getInt( 'limit', 50 ) );
 51+ // Input box with target prefilled if available
 52+ $formContent = "\t" . Xml::input( 'target', 40, is_null( $this->target ) ? ''
 53+ : $this->target->getPrefixedText( ) )
 54+ // Submit button
 55+ . "\n\t" . Xml::element( 'input', array(
 56+ 'type' => 'submit',
 57+ 'value' => wfMsg( 'globaltemplateusage-ok' )
 58+ ) );
 59+
 60+ // Wrap the entire form in a nice fieldset
 61+ $html .= Xml::fieldSet( wfMsg( 'globaltemplateusage-text' ), $formContent ) . "\n</form>";
 62+
 63+ $wgOut->addHtml( $html );
 64+ }
 65+
 66+ /**
 67+ * Creates as queryer and executes it based on $wgRequest
 68+ */
 69+ private function showResult() {
 70+ global $wgRequest;
 71+
 72+ $query = new GlobalTemplateUsageQuery( $this->target );
 73+
 74+ // Extract params from $wgRequest
 75+ if ( $wgRequest->getText( 'from' ) ) {
 76+ $query->setOffset( $wgRequest->getText( 'from' ) );
 77+ } elseif ( $wgRequest->getText( 'to' ) ) {
 78+ $query->setOffset( $wgRequest->getText( 'to' ), true );
 79+ }
 80+ $query->setLimit( $wgRequest->getInt( 'limit', 50 ) );
 81+
 82+ // Perform query
 83+ $query->execute();
 84+
 85+ // Show result
 86+ global $wgOut;
 87+
 88+ // Don't show form element if there is no data
 89+ if ( $query->count() == 0 ) {
 90+ $wgOut->addWikiMsg( 'globaltemplateusage-no-results', $this->target->getPrefixedText( ) );
 91+ return;
 92+ }
 93+
 94+ $offset = $query->getOffsetString( );
 95+ $navbar = $this->getNavBar( $query );
 96+ $targetName = $this->target->getPrefixedText( );
 97+
 98+ // Top navbar
 99+ $wgOut->addHtml( $navbar );
 100+
 101+ $wgOut->addHtml( '<div id="mw-globaltemplateusage-result">' );
 102+ foreach ( $query->getSingleTemplateResult() as $wiki => $result ) {
 103+ $wgOut->addHtml(
 104+ '<h2>' . wfMsgExt(
 105+ 'globaltemplateusage-on-wiki', 'parseinline',
 106+ $targetName, WikiMap::getWikiName( $wiki ) )
 107+ . "</h2><ul>\n" );
 108+ foreach ( $result as $item ) {
 109+ $wgOut->addHtml( "\t<li>" . self::formatItem( $item ) . "</li>\n" );
 110+ }
 111+ $wgOut->addHtml( "</ul>\n" );
 112+ }
 113+ $wgOut->addHtml( '</div>' );
 114+
 115+ // Bottom navbar
 116+ $wgOut->addHtml( $navbar );
 117+ }
 118+
 119+ /**
 120+ * Helper to format a specific item
 121+ */
 122+ public static function formatItem( $item ) {
 123+ if ( !$item['namespace'] ) {
 124+ $page = $item['title'];
 125+ } else {
 126+ $page = "{$item['namespace']}:{$item['title']}";
 127+ }
 128+
 129+ $link = WikiMap::makeForeignLink( $item['wiki'], $page,
 130+ str_replace( '_', ' ', $page ) );
 131+ // Return only the title if no link can be constructed
 132+ return $link === false ? $page : $link;
 133+ }
 134+
 135+ /**
 136+ * Helper function to create the navbar, stolen from wfViewPrevNext
 137+ *
 138+ * @param $query GlobalTemplateUsageQuery An executed GlobalTemplateUsageQuery object
 139+ * @return string Navbar HTML
 140+ */
 141+ protected function getNavBar( $query ) {
 142+ global $wgLang, $wgUser;
 143+
 144+ $skin = $wgUser->getSkin();
 145+
 146+ $target = $this->target->getPrefixedText();
 147+ $limit = $query->getLimit();
 148+ $fmtLimit = $wgLang->formatNum( $limit );
 149+
 150+ # Find out which strings are for the prev and which for the next links
 151+ $offset = $query->getOffsetString();
 152+ $continue = $query->getContinueString();
 153+ if ( $query->isReversed() ) {
 154+ $from = $offset;
 155+ $to = $continue;
 156+ } else {
 157+ $from = $continue;
 158+ $to = $offset;
 159+ }
 160+
 161+ # Get prev/next link display text
 162+ $prev = wfMsgExt( 'prevn', array( 'parsemag', 'escape' ), $fmtLimit );
 163+ $next = wfMsgExt( 'nextn', array( 'parsemag', 'escape' ), $fmtLimit );
 164+ # Get prev/next link title text
 165+ $pTitle = wfMsgExt( 'prevn-title', array( 'parsemag', 'escape' ), $fmtLimit );
 166+ $nTitle = wfMsgExt( 'nextn-title', array( 'parsemag', 'escape' ), $fmtLimit );
 167+
 168+ # Fetch the title object
 169+ $title = $this->getTitle();
 170+
 171+ # Make 'previous' link
 172+ if ( $to ) {
 173+ $attr = array( 'title' => $pTitle, 'class' => 'mw-prevlink' );
 174+ $q = array( 'limit' => $limit, 'to' => $to, 'target' => $target );
 175+ $plink = $skin->link( $title, $prev, $attr, $q );
 176+ } else {
 177+ $plink = $prev;
 178+ }
 179+
 180+ # Make 'next' link
 181+ if ( $from ) {
 182+ $attr = array( 'title' => $nTitle, 'class' => 'mw-nextlink' );
 183+ $q = array( 'limit' => $limit, 'from' => $from, 'target' => $target );
 184+ $nlink = $skin->link( $title, $next, $attr, $q );
 185+ } else {
 186+ $nlink = $next;
 187+ }
 188+
 189+ # Make links to set number of items per page
 190+ $numLinks = array();
 191+ foreach ( array( 20, 50, 100, 250, 500 ) as $num ) {
 192+ $fmtLimit = $wgLang->formatNum( $num );
 193+
 194+ $q = array( 'offset' => $offset, 'limit' => $num, 'target' => $target );
 195+ $lTitle = wfMsgExt( 'shown-title', array( 'parsemag', 'escape' ), $num );
 196+ $attr = array( 'title' => $lTitle, 'class' => 'mw-numlink' );
 197+
 198+ $numLinks[] = $skin->link( $title, $fmtLimit, $attr, $q );
 199+ }
 200+ $nums = $wgLang->pipeList( $numLinks );
 201+
 202+ return wfMsgHtml( 'viewprevnext', $plink, $nlink, $nums );
 203+ }
 204+}
 205+
 206+/**
 207+ * This class has been copied from Extension:GlobalUsage / GlobalUsageQuery.php
 208+ * Extension:GlobalUsage should be built-in and the GlobalUsageQuery adapted
 209+ * to be able to fetch the global usage of templates as well as files.
 210+ */
 211+class GlobalTemplateUsageQuery {
 212+ private $limit = 50;
 213+ private $offset;
 214+ private $hasMore = false;
 215+ private $result;
 216+ private $continue;
 217+ private $reversed = false;
 218+ private $target = null;
 219+
 220+ /**
 221+ * @param $target mixed Title or db key, or array of db keys of target(s)
 222+ */
 223+ public function __construct( $target ) {
 224+ global $wgGlobalDatabase;
 225+ $this->db = wfGetDB( DB_SLAVE, array(), $wgGlobalDatabase );
 226+ $this->target = $target;
 227+ $this->offset = array();
 228+ }
 229+
 230+ /**
 231+ * Set the offset parameter
 232+ *
 233+ * @param $offset string offset
 234+ * @param $reversed bool True if this is the upper offset
 235+ */
 236+ public function setOffset( $offset, $reversed = null ) {
 237+ if ( !is_null( $reversed ) ) {
 238+ $this->reversed = $reversed;
 239+ }
 240+
 241+ if ( !is_array( $offset ) ) {
 242+ $offset = explode( '|', $offset );
 243+ }
 244+
 245+ if ( count( $offset ) == 3 ) {
 246+ $this->offset = $offset;
 247+ return true;
 248+ } else {
 249+ return false;
 250+ }
 251+ }
 252+ /**
 253+ * Return the offset set by the user
 254+ *
 255+ * @return array offset
 256+ */
 257+ public function getOffsetString() {
 258+ return implode( '|', $this->offset );
 259+ }
 260+
 261+ /**
 262+ * Is the result reversed
 263+ *
 264+ * @return bool
 265+ */
 266+ public function isReversed() {
 267+ return $this->reversed;
 268+ }
 269+
 270+ /**
 271+ * Returns the string used for continuation
 272+ *
 273+ * @return string
 274+ *
 275+ */
 276+ public function getContinueString() {
 277+ if ( $this->hasMore() )
 278+ return "{$this->lastRow->gtl_to_title}|{$this->lastRow->gtl_from_wiki}|{$this->lastRow->gtl_from_page}";
 279+ else
 280+ return '';
 281+ }
 282+
 283+ /**
 284+ * Set the maximum amount of items to return. Capped at 500.
 285+ *
 286+ * @param $limit int The limit
 287+ */
 288+ public function setLimit( $limit ) {
 289+ $this->limit = min( $limit, 500 );
 290+ }
 291+
 292+ /**
 293+ * Returns the user set limit
 294+ */
 295+ public function getLimit() {
 296+ return $this->limit;
 297+ }
 298+
 299+ /**
 300+ * Executes the query
 301+ */
 302+ public function execute() {
 303+ global $wgLocalInterwiki;
 304+
 305+ /* Construct a where clause */
 306+ // Add target template(s)
 307+ $where = array( 'gtl_to_prefix' => $wgLocalInterwiki,
 308+ 'gtl_to_namespace' => $this->target->getNamespace( ),
 309+ 'gtl_to_title' => $this->target->getDBkey( )
 310+ );
 311+
 312+ // Set the continuation condition
 313+ $order = 'ASC';
 314+ if ( $this->offset ) {
 315+ $qTo = $this->db->addQuotes( $this->offset[0] );
 316+ $qWiki = $this->db->addQuotes( $this->offset[1] );
 317+ $qPage = intval( $this->offset[2] );
 318+
 319+ // Check which limit we got in order to determine which way to traverse rows
 320+ if ( $this->reversed ) {
 321+ // Reversed traversal; do not include offset row
 322+ $op1 = '<';
 323+ $op2 = '<';
 324+ $order = 'DESC';
 325+ } else {
 326+ // Normal traversal; include offset row
 327+ $op1 = '>';
 328+ $op2 = '>=';
 329+ $order = 'ASC';
 330+ }
 331+
 332+ $where[] = "(gtl_to_title $op1 $qTo) OR " .
 333+ "(gtl_to_title = $qTo AND gtl_from_wiki $op1 $qWiki) OR " .
 334+ "(gtl_to_title = $qTo AND gtl_from_wiki = $qWiki AND gtl_from_page $op2 $qPage)";
 335+ }
 336+
 337+ /* Perform select (Duh.) */
 338+ $res = $this->db->select( 'globaltemplatelinks',
 339+ array(
 340+ 'gtl_to_title',
 341+ 'gtl_from_wiki',
 342+ 'gtl_from_page',
 343+ 'gtl_from_namespace',
 344+ 'gtl_from_title'
 345+ ),
 346+ $where,
 347+ __METHOD__,
 348+ array(
 349+ 'ORDER BY' => "gtl_to_title $order, gtl_from_wiki $order, gtl_from_page $order",
 350+ // Select an extra row to check whether we have more rows available
 351+ 'LIMIT' => $this->limit + 1,
 352+ )
 353+ );
 354+
 355+ /* Process result */
 356+ // Always return the result in the same order; regardless whether reversed was specified
 357+ // reversed is really only used to determine from which direction the offset is
 358+ $rows = array();
 359+ foreach ( $res as $row ) {
 360+ $rows[] = $row;
 361+ }
 362+ if ( $this->reversed ) {
 363+ $rows = array_reverse( $rows );
 364+ }
 365+
 366+ // Build the result array
 367+ $count = 0;
 368+ $this->hasMore = false;
 369+ $this->result = array();
 370+ foreach ( $rows as $row ) {
 371+ $count++;
 372+ if ( $count > $this->limit ) {
 373+ // We've reached the extra row that indicates that there are more rows
 374+ $this->hasMore = true;
 375+ $this->lastRow = $row;
 376+ break;
 377+ }
 378+
 379+ if ( !isset( $this->result[$row->gtl_to_title] ) ) {
 380+ $this->result[$row->gtl_to_title] = array();
 381+ }
 382+ if ( !isset( $this->result[$row->gtl_to_title][$row->gtl_from_wiki] ) ) {
 383+ $this->result[$row->gtl_to_title][$row->gtl_from_wiki] = array();
 384+ }
 385+
 386+ $this->result[$row->gtl_to_title][$row->gtl_from_wiki][] = array(
 387+ 'template' => $row->gtl_to_title,
 388+ 'id' => $row->gtl_from_page,
 389+ 'namespace' => $row->gtl_from_namespace,
 390+ 'title' => $row->gtl_from_title,
 391+ 'wiki' => $row->gtl_from_wiki,
 392+ );
 393+ }
 394+ }
 395+ /**
 396+ * Returns the result set. The result is a 4 dimensional array
 397+ * (file, wiki, page), whose items are arrays with keys:
 398+ * - template: File name
 399+ * - id: Page id
 400+ * - namespace: Page namespace text
 401+ * - title: Unprefixed page title
 402+ * - wiki: Wiki id
 403+ *
 404+ * @return array Result set
 405+ */
 406+ public function getResult() {
 407+ return $this->result;
 408+ }
 409+
 410+ /**
 411+ * Returns a 3 dimensional array with the result of the first file. Useful
 412+ * if only one template was queried.
 413+ *
 414+ * For further information see documentation of getResult()
 415+ *
 416+ * @return array Result set
 417+ */
 418+ public function getSingleTemplateResult() {
 419+ if ( $this->result ) {
 420+ return current( $this->result );
 421+ } else {
 422+ return array();
 423+ }
 424+ }
 425+
 426+ /**
 427+ * Returns whether there are more results
 428+ *
 429+ * @return bool
 430+ */
 431+ public function hasMore() {
 432+ return $this->hasMore;
 433+ }
 434+
 435+ /**
 436+ * Returns the result length
 437+ *
 438+ * @return int
 439+ */
 440+ public function count() {
 441+ return count( $this->result );
 442+ }
 443+}
Property changes on: branches/iwtransclusion/phase3/includes/specials/SpecialGlobalTemplateUsage.php
___________________________________________________________________
Added: svn:eol-style
1444 + native
Index: branches/iwtransclusion/phase3/includes/AutoLoader.php
@@ -591,6 +591,7 @@
592592 'SpecialComparePages' => 'includes/specials/SpecialComparePages.php',
593593 'SpecialExport' => 'includes/specials/SpecialExport.php',
594594 'SpecialFilepath' => 'includes/specials/SpecialFilepath.php',
 595+ 'SpecialGlobalTemplateUsage' => 'includes/specials/SpecialGlobalTemplateUsage.php',
595596 'SpecialImport' => 'includes/specials/SpecialImport.php',
596597 'SpecialListGroupRights' => 'includes/specials/SpecialListgrouprights.php',
597598 'SpecialLockdb' => 'includes/specials/SpecialLockdb.php',
Index: branches/iwtransclusion/phase3/languages/messages/MessagesEn.php
@@ -4315,6 +4315,18 @@
43164316 'compare-rev2' => 'Revision 2',
43174317 'compare-submit' => 'Compare',
43184318
 4319+# Special:GlobalTemplateUsage
 4320+'globaltemplateusage' => 'Global template usage',
 4321+'globaltemplateusage-for' => 'Global template usage for "$1"',
 4322+'globaltemplateusage-desc' => '[[Special:GlobalTemplateUsage|Special page]] to view global template usage',
 4323+'globaltemplateusage-ok' => 'Search',
 4324+'globaltemplateusage-text' => 'Search global template usage',
 4325+'globaltemplateusage-no-results' => '[[$1]] is not used on other wikis.',
 4326+'globaltemplateusage-on-wiki' => 'Usage on $2',
 4327+'globaltemplateusage-of-file' => 'The following other wikis use this template:',
 4328+'globaltemplateusage-more' => 'View [[{{#Special:GlobalUsage}}/$1|more global usage]] of this template.',
 4329+'globaltemplateusage-filterlocal' => 'Do not show local usage',
 4330+
43194331 # Database error messages
43204332 'dberr-header' => 'This wiki has a problem',
43214333 'dberr-problems' => 'Sorry!

Follow-up revisions

RevisionCommit summaryAuthorDate
r87111Merge r70966, r71049reedy00:54, 29 April 2011
r92996Merge r87111, which is a merge of r70966, r71049...reedy18:31, 24 July 2011
r96596Per r70966: rm unused msgdemon19:00, 8 September 2011

Comments

#Comment by Peter17 (talk | contribs)   15:23, 12 August 2010

Special:GlobalTemplateUsage works the same as Special:GlobalUsage from Extension:GlobalUsage but deals with templates instead of images. I copied 2 classes from that extension and kept approx. 90% of the code as it was. I really think that Extension:GlobalUsage should be built-in. If so, a lot of code can be factorized in some GlobalUsage classes.

#Comment by Platonides (talk | contribs)   15:50, 12 August 2010

Try to create such GlobalUsage class. Then GlobalUsage can be adapted to use it when this is merged.

#Comment by Peter17 (talk | contribs)   13:31, 13 August 2010

I'd like to, but I am afraid I will create conflicts between MediaWiki's code and GlobalUsage's... I think this should be done while building GlobalUsage in... I would like to do that, but not before the end of my GSoC...

#Comment by Bryan (talk | contribs)   21:10, 17 August 2010

I think this revolves about two issues:

  1. No proper multi-column querier in core (or at least where I could find it)
  2. Generalizing globalusage to be useful to globaltemplatelinks, globalimagelinks, globalpagelinks, globalwhateverlinks.

The first thing should definitely in core, for the second I'm not sure it belongs in core; perhaps a general GlobalUsage extension which covers GlobalImageUsage, GlobalTemplateUsage, etc.

#Comment by Platonides (talk | contribs)   21:12, 20 August 2010

A shared extension dependence is not bad, but since the iwtransclusion branch is intended to be merged in core, that Globalusage would need to be in core, too.

#Comment by Catrope (talk | contribs)   18:32, 12 August 2010
+		parent::__construct( 'GlobalTemplateUsage', 'globaltemplateusage' );

This restricts usage of this special page to users with the globaltemplateusage right, which is probably not what you want. Suggest removing this param.

#Comment by Peter17 (talk | contribs)   22:22, 12 August 2010

Weird. Sure, I'll remove it. Thanks!

#Comment by Nikerabbit (talk | contribs)   08:05, 4 September 2011
+'globaltemplateusage-for'  => 'Global template usage for "$1"',

Wouldn't it make sense to link the template to the template page?

#Comment by Nikerabbit (talk | contribs)   08:07, 4 September 2011

Where is -desc message used?

#Comment by Peter17 (talk | contribs)   23:02, 6 September 2011

I received an email asking me to fix this. However, when I download the current (latest) version of the iwtransclusion branch, I can't find the 'globaltemplateusage-*' lines in languages/messages/MessagesEn.php

It seems they were removed by this diff: r1=86864&r2=86865 http://svn.wikimedia.org/viewvc/mediawiki/branches/iwtransclusion/phase3/languages/messages/MessagesEn.php?&pathrev=86865&r1=86864&r2=86865

#Comment by Peter17 (talk | contribs)   23:04, 6 September 2011

I meant this diff: http://goo.gl/Z3w4s

#Comment by 😂 (talk | contribs)   19:01, 8 September 2011

All of this was merged to trunk, and the globaltemplateusage- messages do exist in MessagesEn (around line 4604). I removed the -desc message per the above comment in r96596, changing back to resolved.

Status & tagging log