MediaWiki  REL1_20
InfoAction.php
Go to the documentation of this file.
00001 <?php
00025 class InfoAction extends FormlessAction {
00031         public function getName() {
00032                 return 'info';
00033         }
00034 
00040         public function requiresUnblock() {
00041                 return false;
00042         }
00043 
00049         public function requiresWrite() {
00050                 return false;
00051         }
00052 
00058         public function onView() {
00059                 $content = '';
00060 
00061                 // Validate revision
00062                 $oldid = $this->page->getOldID();
00063                 if ( $oldid ) {
00064                         $revision = $this->page->getRevisionFetched();
00065 
00066                         // Revision is missing
00067                         if ( $revision === null ) {
00068                                 return $this->msg( 'missing-revision', $oldid )->parse();
00069                         }
00070 
00071                         // Revision is not current
00072                         if ( !$revision->isCurrent() ) {
00073                                 return $this->msg( 'pageinfo-not-current' )->plain();
00074                         }
00075                 }
00076 
00077                 // Page header
00078                 if ( !$this->msg( 'pageinfo-header' )->isDisabled() ) {
00079                         $content .= $this->msg( 'pageinfo-header' )->parse();
00080                 }
00081 
00082                 // Hide "This page is a member of # hidden categories" explanation
00083                 $content .= Html::element( 'style', array(),
00084                         '.mw-hiddenCategoriesExplanation { display: none; }' );
00085 
00086                 // Hide "Templates used on this page" explanation
00087                 $content .= Html::element( 'style', array(),
00088                         '.mw-templatesUsedExplanation { display: none; }' );
00089 
00090                 // Get page information
00091                 $pageInfo = $this->pageInfo();
00092 
00093                 // Allow extensions to add additional information
00094                 wfRunHooks( 'InfoAction', array( $this->getContext(), &$pageInfo ) );
00095 
00096                 // Render page information
00097                 foreach ( $pageInfo as $header => $infoTable ) {
00098                         $content .= $this->makeHeader( $this->msg( "pageinfo-${header}" )->escaped() );
00099                         $table = '';
00100                         foreach ( $infoTable as $infoRow ) {
00101                                 $name = ( $infoRow[0] instanceof Message ) ? $infoRow[0]->escaped() : $infoRow[0];
00102                                 $value = ( $infoRow[1] instanceof Message ) ? $infoRow[1]->escaped() : $infoRow[1];
00103                                 $table = $this->addRow( $table, $name, $value );
00104                         }
00105                         $content = $this->addTable( $content, $table );
00106                 }
00107 
00108                 // Page footer
00109                 if ( !$this->msg( 'pageinfo-footer' )->isDisabled() ) {
00110                         $content .= $this->msg( 'pageinfo-footer' )->parse();
00111                 }
00112 
00113                 // Page credits
00114                 /*if ( $this->page->exists() ) {
00115                         $content .= Html::rawElement( 'div', array( 'id' => 'mw-credits' ), $this->getContributors() );
00116                 }*/
00117 
00118                 return $content;
00119         }
00120 
00127         protected function makeHeader( $header ) {
00128                 global $wgParser;
00129                 $spanAttribs = array( 'class' => 'mw-headline', 'id' => $wgParser->guessSectionNameFromWikiText( $header ) );
00130                 return Html::rawElement( 'h2', array(), Html::element( 'span', $spanAttribs, $header ) );
00131         }
00132 
00141         protected function addRow( $table, $name, $value ) {
00142                 return $table . Html::rawElement( 'tr', array(),
00143                         Html::rawElement( 'td', array( 'style' => 'vertical-align: top;' ), $name ) .
00144                         Html::rawElement( 'td', array(), $value )
00145                 );
00146         }
00147 
00155         protected function addTable( $content, $table ) {
00156                 return $content . Html::rawElement( 'table', array( 'class' => 'wikitable mw-page-info' ),
00157                         $table );
00158         }
00159 
00165         protected function pageInfo() {
00166                 global $wgContLang, $wgRCMaxAge;
00167 
00168                 $user = $this->getUser();
00169                 $lang = $this->getLanguage();
00170                 $title = $this->getTitle();
00171                 $id = $title->getArticleID();
00172 
00173                 // Get page information that would be too "expensive" to retrieve by normal means
00174                 $pageCounts = self::pageCounts( $title, $user );
00175 
00176                 // Get page properties
00177                 $dbr = wfGetDB( DB_SLAVE );
00178                 $result = $dbr->select(
00179                         'page_props',
00180                         array( 'pp_propname', 'pp_value' ),
00181                         array( 'pp_page' => $id ),
00182                         __METHOD__
00183                 );
00184 
00185                 $pageProperties = array();
00186                 foreach ( $result as $row ) {
00187                         $pageProperties[$row->pp_propname] = $row->pp_value;
00188                 }
00189 
00190                 // Basic information
00191                 $pageInfo = array();
00192                 $pageInfo['header-basic'] = array();
00193 
00194                 // Display title
00195                 $displayTitle = $title->getPrefixedText();
00196                 if ( !empty( $pageProperties['displaytitle'] ) ) {
00197                         $displayTitle = $pageProperties['displaytitle'];
00198                 }
00199 
00200                 $pageInfo['header-basic'][] = array(
00201                         $this->msg( 'pageinfo-display-title' ), $displayTitle
00202                 );
00203 
00204                 // Default sort key
00205                 $sortKey = $title->getCategorySortKey();
00206                 if ( !empty( $pageProperties['defaultsort'] ) ) {
00207                         $sortKey = $pageProperties['defaultsort'];
00208                 }
00209 
00210                 $pageInfo['header-basic'][] = array( $this->msg( 'pageinfo-default-sort' ), $sortKey );
00211 
00212                 // Page length (in bytes)
00213                 $pageInfo['header-basic'][] = array(
00214                         $this->msg( 'pageinfo-length' ), $lang->formatNum( $title->getLength() )
00215                 );
00216 
00217                 // Page ID (number not localised, as it's a database ID)
00218                 $pageInfo['header-basic'][] = array( $this->msg( 'pageinfo-article-id' ), $id );
00219 
00220                 // Search engine status
00221                 $pOutput = new ParserOutput();
00222                 if ( isset( $pageProperties['noindex'] ) ) {
00223                         $pOutput->setIndexPolicy( 'noindex' );
00224                 }
00225 
00226                 // Use robot policy logic
00227                 $policy = $this->page->getRobotPolicy( 'view', $pOutput );
00228                 $pageInfo['header-basic'][] = array(
00229                         $this->msg( 'pageinfo-robot-policy' ), $this->msg( "pageinfo-robot-${policy['index']}" )
00230                 );
00231 
00232                 if ( isset( $pageCounts['views'] ) ) {
00233                         // Number of views
00234                         $pageInfo['header-basic'][] = array(
00235                                 $this->msg( 'pageinfo-views' ), $lang->formatNum( $pageCounts['views'] )
00236                         );
00237                 }
00238 
00239                 if ( isset( $pageCounts['watchers'] ) ) {
00240                         // Number of page watchers
00241                         $pageInfo['header-basic'][] = array(
00242                                 $this->msg( 'pageinfo-watchers' ), $lang->formatNum( $pageCounts['watchers'] )
00243                         );
00244                 }
00245 
00246                 // Redirects to this page
00247                 $whatLinksHere = SpecialPage::getTitleFor( 'Whatlinkshere', $title->getPrefixedText() );
00248                 $pageInfo['header-basic'][] = array(
00249                         Linker::link(
00250                                 $whatLinksHere,
00251                                 $this->msg( 'pageinfo-redirects-name' )->escaped(),
00252                                 array(),
00253                                 array( 'hidelinks' => 1, 'hidetrans' => 1 )
00254                         ),
00255                         $this->msg( 'pageinfo-redirects-value' )
00256                                 ->numParams( count( $title->getRedirectsHere() ) )
00257                 );
00258 
00259                 // Subpages of this page, if subpages are enabled for the current NS
00260                 if ( MWNamespace::hasSubpages( $title->getNamespace() ) ) {
00261                         $prefixIndex = SpecialPage::getTitleFor( 'Prefixindex', $title->getPrefixedText() . '/' );
00262                         $pageInfo['header-basic'][] = array(
00263                                 Linker::link( $prefixIndex, $this->msg( 'pageinfo-subpages-name' )->escaped() ),
00264                                 $this->msg( 'pageinfo-subpages-value' )
00265                                         ->numParams(
00266                                                 $pageCounts['subpages']['total'],
00267                                                 $pageCounts['subpages']['redirects'],
00268                                                 $pageCounts['subpages']['nonredirects'] )
00269                         );
00270                 }
00271 
00272                 // Page protection
00273                 $pageInfo['header-restrictions'] = array();
00274 
00275                 // Page protection
00276                 foreach ( $title->getRestrictionTypes() as $restrictionType ) {
00277                         $protectionLevel = implode( ', ', $title->getRestrictions( $restrictionType ) );
00278 
00279                         if ( $protectionLevel == '' ) {
00280                                 // Allow all users
00281                                 $message = $this->msg( 'protect-default' )->escaped();
00282                         } else {
00283                                 // Administrators only
00284                                 $message = $this->msg( "protect-level-$protectionLevel" );
00285                                 if ( $message->isDisabled() ) {
00286                                         // Require "$1" permission
00287                                         $message = $this->msg( "protect-fallback", $protectionLevel )->parse();
00288                                 } else {
00289                                         $message = $message->escaped();
00290                                 }
00291                         }
00292 
00293                         $pageInfo['header-restrictions'][] = array(
00294                                 $this->msg( "restriction-$restrictionType" ), $message
00295                         );
00296                 }
00297 
00298                 if ( !$this->page->exists() ) {
00299                         return $pageInfo;
00300                 }
00301 
00302                 // Edit history
00303                 $pageInfo['header-edits'] = array();
00304 
00305                 $firstRev = $this->page->getOldestRevision();
00306 
00307                 // Page creator
00308                 $pageInfo['header-edits'][] = array(
00309                         $this->msg( 'pageinfo-firstuser' ),
00310                         Linker::revUserTools( $firstRev )
00311                 );
00312 
00313                 // Date of page creation
00314                 $pageInfo['header-edits'][] = array(
00315                         $this->msg( 'pageinfo-firsttime' ),
00316                         Linker::linkKnown(
00317                                 $title,
00318                                 $lang->userTimeAndDate( $firstRev->getTimestamp(), $user ),
00319                                 array(),
00320                                 array( 'oldid' => $firstRev->getId() )
00321                         )
00322                 );
00323 
00324                 // Latest editor
00325                 $pageInfo['header-edits'][] = array(
00326                         $this->msg( 'pageinfo-lastuser' ),
00327                         Linker::revUserTools( $this->page->getRevision() )
00328                 );
00329 
00330                 // Date of latest edit
00331                 $pageInfo['header-edits'][] = array(
00332                         $this->msg( 'pageinfo-lasttime' ),
00333                         Linker::linkKnown(
00334                                 $title,
00335                                 $lang->userTimeAndDate( $this->page->getTimestamp(), $user ),
00336                                 array(),
00337                                 array( 'oldid' => $this->page->getLatest() )
00338                         )
00339                 );
00340 
00341                 // Total number of edits
00342                 $pageInfo['header-edits'][] = array(
00343                         $this->msg( 'pageinfo-edits' ), $lang->formatNum( $pageCounts['edits'] )
00344                 );
00345 
00346                 // Total number of distinct authors
00347                 $pageInfo['header-edits'][] = array(
00348                         $this->msg( 'pageinfo-authors' ), $lang->formatNum( $pageCounts['authors'] )
00349                 );
00350 
00351                 // Recent number of edits (within past 30 days)
00352                 $pageInfo['header-edits'][] = array(
00353                         $this->msg( 'pageinfo-recent-edits', $lang->formatDuration( $wgRCMaxAge ) ),
00354                         $lang->formatNum( $pageCounts['recent_edits'] )
00355                 );
00356 
00357                 // Recent number of distinct authors
00358                 $pageInfo['header-edits'][] = array(
00359                         $this->msg( 'pageinfo-recent-authors' ), $lang->formatNum( $pageCounts['recent_authors'] )
00360                 );
00361 
00362                 // Array of MagicWord objects
00363                 $magicWords = MagicWord::getDoubleUnderscoreArray();
00364 
00365                 // Array of magic word IDs
00366                 $wordIDs = $magicWords->names;
00367 
00368                 // Array of IDs => localized magic words
00369                 $localizedWords = $wgContLang->getMagicWords();
00370 
00371                 $listItems = array();
00372                 foreach ( $pageProperties as $property => $value ) {
00373                         if ( in_array( $property, $wordIDs ) ) {
00374                                 $listItems[] = Html::element( 'li', array(), $localizedWords[$property][1] );
00375                         }
00376                 }
00377 
00378                 $localizedList = Html::rawElement( 'ul', array(), implode( '', $listItems ) );
00379                 $hiddenCategories = $this->page->getHiddenCategories();
00380                 $transcludedTemplates = $title->getTemplateLinksFrom();
00381 
00382                 if ( count( $listItems ) > 0
00383                         || count( $hiddenCategories ) > 0
00384                         || count( $transcludedTemplates ) > 0 ) {
00385                         // Page properties
00386                         $pageInfo['header-properties'] = array();
00387 
00388                         // Magic words
00389                         if ( count( $listItems ) > 0 ) {
00390                                 $pageInfo['header-properties'][] = array(
00391                                         $this->msg( 'pageinfo-magic-words' )->numParams( count( $listItems ) ),
00392                                         $localizedList
00393                                 );
00394                         }
00395 
00396                         // Hidden categories
00397                         if ( count( $hiddenCategories ) > 0 ) {
00398                                 $pageInfo['header-properties'][] = array(
00399                                         $this->msg( 'pageinfo-hidden-categories' )
00400                                                 ->numParams( count( $hiddenCategories ) ),
00401                                         Linker::formatHiddenCategories( $hiddenCategories )
00402                                 );
00403                         }
00404 
00405                         // Transcluded templates
00406                         if ( count( $transcludedTemplates ) > 0 ) {
00407                                 $pageInfo['header-properties'][] = array(
00408                                         $this->msg( 'pageinfo-templates' )
00409                                                 ->numParams( count( $transcludedTemplates ) ),
00410                                         Linker::formatTemplates( $transcludedTemplates )
00411                                 );
00412                         }
00413                 }
00414 
00415                 return $pageInfo;
00416         }
00417 
00425         protected static function pageCounts( $title, $user ) {
00426                 global $wgRCMaxAge, $wgDisableCounters;
00427 
00428                 wfProfileIn( __METHOD__ );
00429                 $id = $title->getArticleID();
00430 
00431                 $dbr = wfGetDB( DB_SLAVE );
00432                 $result = array();
00433 
00434                 if ( !$wgDisableCounters ) {
00435                         // Number of views
00436                         $views = (int) $dbr->selectField(
00437                                 'page',
00438                                 'page_counter',
00439                                 array( 'page_id' => $id ),
00440                                 __METHOD__
00441                         );
00442                         $result['views'] = $views;
00443                 }
00444 
00445                 if ( $user->isAllowed( 'unwatchedpages' ) ) {
00446                         // Number of page watchers
00447                         $watchers = (int) $dbr->selectField(
00448                                 'watchlist',
00449                                 'COUNT(*)',
00450                                 array(
00451                                         'wl_namespace' => $title->getNamespace(),
00452                                         'wl_title'     => $title->getDBkey(),
00453                                 ),
00454                                 __METHOD__
00455                         );
00456                         $result['watchers'] = $watchers;
00457                 }
00458 
00459                 // Total number of edits
00460                 $edits = (int) $dbr->selectField(
00461                         'revision',
00462                         'COUNT(rev_page)',
00463                         array( 'rev_page' => $id ),
00464                         __METHOD__
00465                 );
00466                 $result['edits'] = $edits;
00467 
00468                 // Total number of distinct authors
00469                 $authors = (int) $dbr->selectField(
00470                         'revision',
00471                         'COUNT(DISTINCT rev_user_text)',
00472                         array( 'rev_page' => $id ),
00473                         __METHOD__
00474                 );
00475                 $result['authors'] = $authors;
00476 
00477                 // "Recent" threshold defined by $wgRCMaxAge
00478                 $threshold = $dbr->timestamp( time() - $wgRCMaxAge );
00479 
00480                 // Recent number of edits
00481                 $edits = (int) $dbr->selectField(
00482                         'revision',
00483                         'COUNT(rev_page)',
00484                         array(
00485                                 'rev_page' => $id ,
00486                                 "rev_timestamp >= $threshold"
00487                         ),
00488                         __METHOD__
00489                 );
00490                 $result['recent_edits'] = $edits;
00491 
00492                 // Recent number of distinct authors
00493                 $authors = (int) $dbr->selectField(
00494                         'revision',
00495                         'COUNT(DISTINCT rev_user_text)',
00496                         array(
00497                                 'rev_page' => $id,
00498                                 "rev_timestamp >= $threshold"
00499                         ),
00500                         __METHOD__
00501                 );
00502                 $result['recent_authors'] = $authors;
00503 
00504                 // Subpages (if enabled)
00505                 if ( MWNamespace::hasSubpages( $title->getNamespace() ) ) {
00506                         $conds = array( 'page_namespace' => $title->getNamespace() );
00507                         $conds[] = 'page_title ' . $dbr->buildLike( $title->getDBkey() . '/', $dbr->anyString() );
00508 
00509                         // Subpages of this page (redirects)
00510                         $conds['page_is_redirect'] = 1;
00511                         $result['subpages']['redirects'] = (int) $dbr->selectField(
00512                                 'page',
00513                                 'COUNT(page_id)',
00514                                 $conds,
00515                                 __METHOD__ );
00516 
00517                         // Subpages of this page (non-redirects)
00518                         $conds['page_is_redirect'] = 0;
00519                         $result['subpages']['nonredirects'] = (int) $dbr->selectField(
00520                                 'page',
00521                                 'COUNT(page_id)',
00522                                 $conds,
00523                                 __METHOD__
00524                         );
00525 
00526                         // Subpages of this page (total)
00527                         $result['subpages']['total'] = $result['subpages']['redirects']
00528                                 + $result['subpages']['nonredirects'];
00529                 }
00530 
00531                 wfProfileOut( __METHOD__ );
00532                 return $result;
00533         }
00534 
00540         protected function getPageTitle() {
00541                 return $this->msg( 'pageinfo-title', $this->getTitle()->getPrefixedText() )->text();
00542         }
00543 
00548         protected function getContributors() {
00549                 global $wgHiddenPrefs;
00550 
00551                 $contributors = $this->page->getContributors();
00552                 $real_names = array();
00553                 $user_names = array();
00554                 $anon_ips = array();
00555 
00556                 # Sift for real versus user names
00557                 foreach ( $contributors as $user ) {
00558                         $page = $user->isAnon()
00559                                 ? SpecialPage::getTitleFor( 'Contributions', $user->getName() )
00560                                 : $user->getUserPage();
00561 
00562                         if ( $user->getID() == 0 ) {
00563                                 $anon_ips[] = Linker::link( $page, htmlspecialchars( $user->getName() ) );
00564                         } elseif ( !in_array( 'realname', $wgHiddenPrefs ) && $user->getRealName() ) {
00565                                 $real_names[] = Linker::link( $page, htmlspecialchars( $user->getRealName() ) );
00566                         } else {
00567                                 $user_names[] = Linker::link( $page, htmlspecialchars( $user->getName() ) );
00568                         }
00569                 }
00570 
00571                 $lang = $this->getLanguage();
00572 
00573                 $real = $lang->listToText( $real_names );
00574 
00575                 # "ThisSite user(s) A, B and C"
00576                 if ( count( $user_names ) ) {
00577                         $user = $this->msg( 'siteusers' )->rawParams( $lang->listToText( $user_names ) )->params(
00578                                 count( $user_names ) )->escaped();
00579                 } else {
00580                         $user = false;
00581                 }
00582 
00583                 if ( count( $anon_ips ) ) {
00584                         $anon = $this->msg( 'anonusers' )->rawParams( $lang->listToText( $anon_ips ) )->params(
00585                                 count( $anon_ips ) )->escaped();
00586                 } else {
00587                         $anon = false;
00588                 }
00589 
00590                 # This is the big list, all mooshed together. We sift for blank strings
00591                 $fulllist = array();
00592                 foreach ( array( $real, $user, $anon ) as $s ) {
00593                         if ( $s !== '' ) {
00594                                 array_push( $fulllist, $s );
00595                         }
00596                 }
00597 
00598                 $count = count( $fulllist );
00599                 # "Based on work by ..."
00600                 return $count
00601                         ? $this->msg( 'othercontribs' )->rawParams(
00602                                 $lang->listToText( $fulllist ) )->params( $count )->escaped()
00603                         : '';
00604         }
00605 
00611         protected function getDescription() {
00612                 return '';
00613         }
00614 }