MediaWiki  1.23.12
Article.php
Go to the documentation of this file.
1 <?php
36 class Article implements Page {
45  protected $mContext;
46 
51  protected $mPage;
52 
57  public $mParserOptions;
58 
63  var $mContent; // !< #BC cruft
64 
70  var $mContentObject; // !<
71 
76  var $mContentLoaded = false; // !<
77 
83  var $mOldId; // !<
84 
89  var $mRedirectedFrom = null;
90 
95  var $mRedirectUrl = false; // !<
96 
101  var $mRevIdFetched = 0; // !<
102 
107  var $mRevision = null;
108 
113  var $mParserOutput;
114 
122  public function __construct( Title $title, $oldId = null ) {
123  $this->mOldId = $oldId;
124  $this->mPage = $this->newPage( $title );
125  }
126 
131  protected function newPage( Title $title ) {
132  return new WikiPage( $title );
133  }
134 
140  public static function newFromID( $id ) {
141  $t = Title::newFromID( $id );
142  # @todo FIXME: Doesn't inherit right
143  return $t == null ? null : new self( $t );
144  # return $t == null ? null : new static( $t ); // PHP 5.3
145  }
146 
154  public static function newFromTitle( $title, IContextSource $context ) {
155  if ( NS_MEDIA == $title->getNamespace() ) {
156  // FIXME: where should this go?
157  $title = Title::makeTitle( NS_FILE, $title->getDBkey() );
158  }
159 
160  $page = null;
161  wfRunHooks( 'ArticleFromTitle', array( &$title, &$page, $context ) );
162  if ( !$page ) {
163  switch ( $title->getNamespace() ) {
164  case NS_FILE:
165  $page = new ImagePage( $title );
166  break;
167  case NS_CATEGORY:
168  $page = new CategoryPage( $title );
169  break;
170  default:
171  $page = new Article( $title );
172  }
173  }
174  $page->setContext( $context );
175 
176  return $page;
177  }
178 
186  public static function newFromWikiPage( WikiPage $page, IContextSource $context ) {
187  $article = self::newFromTitle( $page->getTitle(), $context );
188  $article->mPage = $page; // override to keep process cached vars
189  return $article;
190  }
191 
197  public function setRedirectedFrom( Title $from ) {
198  $this->mRedirectedFrom = $from;
199  }
200 
206  public function getTitle() {
207  return $this->mPage->getTitle();
208  }
209 
216  public function getPage() {
217  return $this->mPage;
218  }
219 
223  public function clear() {
224  $this->mContentLoaded = false;
225 
226  $this->mRedirectedFrom = null; # Title object if set
227  $this->mRevIdFetched = 0;
228  $this->mRedirectUrl = false;
229 
230  $this->mPage->clear();
231  }
232 
245  public function getContent() {
246  ContentHandler::deprecated( __METHOD__, '1.21' );
247  $content = $this->getContentObject();
248  return ContentHandler::getContentText( $content );
249  }
250 
266  protected function getContentObject() {
267  wfProfileIn( __METHOD__ );
268 
269  if ( $this->mPage->getID() === 0 ) {
270  # If this is a MediaWiki:x message, then load the messages
271  # and return the message value for x.
272  if ( $this->getTitle()->getNamespace() == NS_MEDIAWIKI ) {
273  $text = $this->getTitle()->getDefaultMessageText();
274  if ( $text === false ) {
275  $text = '';
276  }
277 
278  $content = ContentHandler::makeContent( $text, $this->getTitle() );
279  } else {
280  $message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon';
281  $content = new MessageContent( $message, null, 'parsemag' );
282  }
283  } else {
285  $content = $this->mContentObject;
286  }
287 
288  wfProfileOut( __METHOD__ );
289  return $content;
290  }
291 
296  public function getOldID() {
297  if ( is_null( $this->mOldId ) ) {
298  $this->mOldId = $this->getOldIDFromRequest();
299  }
300 
301  return $this->mOldId;
302  }
303 
309  public function getOldIDFromRequest() {
310  $this->mRedirectUrl = false;
311 
312  $request = $this->getContext()->getRequest();
313  $oldid = $request->getIntOrNull( 'oldid' );
314 
315  if ( $oldid === null ) {
316  return 0;
317  }
318 
319  if ( $oldid !== 0 ) {
320  # Load the given revision and check whether the page is another one.
321  # In that case, update this instance to reflect the change.
322  if ( $oldid === $this->mPage->getLatest() ) {
323  $this->mRevision = $this->mPage->getRevision();
324  } else {
325  $this->mRevision = Revision::newFromId( $oldid );
326  if ( $this->mRevision !== null ) {
327  // Revision title doesn't match the page title given?
328  if ( $this->mPage->getID() != $this->mRevision->getPage() ) {
329  $function = array( get_class( $this->mPage ), 'newFromID' );
330  $this->mPage = call_user_func( $function, $this->mRevision->getPage() );
331  }
332  }
333  }
334  }
335 
336  if ( $request->getVal( 'direction' ) == 'next' ) {
337  $nextid = $this->getTitle()->getNextRevisionID( $oldid );
338  if ( $nextid ) {
339  $oldid = $nextid;
340  $this->mRevision = null;
341  } else {
342  $this->mRedirectUrl = $this->getTitle()->getFullURL( 'redirect=no' );
343  }
344  } elseif ( $request->getVal( 'direction' ) == 'prev' ) {
345  $previd = $this->getTitle()->getPreviousRevisionID( $oldid );
346  if ( $previd ) {
347  $oldid = $previd;
348  $this->mRevision = null;
349  }
350  }
351 
352  return $oldid;
353  }
354 
360  function loadContent() {
361  wfDeprecated( __METHOD__, '1.19' );
362  $this->fetchContent();
363  }
364 
379  function fetchContent() { #BC cruft!
380  ContentHandler::deprecated( __METHOD__, '1.21' );
381 
382  if ( $this->mContentLoaded && $this->mContent ) {
383  return $this->mContent;
384  }
385 
386  wfProfileIn( __METHOD__ );
387 
388  $content = $this->fetchContentObject();
389 
390  // @todo Get rid of mContent everywhere!
391  $this->mContent = ContentHandler::getContentText( $content );
392  ContentHandler::runLegacyHooks( 'ArticleAfterFetchContent', array( &$this, &$this->mContent ) );
393 
394  wfProfileOut( __METHOD__ );
395 
396  return $this->mContent;
397  }
398 
411  protected function fetchContentObject() {
412  if ( $this->mContentLoaded ) {
413  return $this->mContentObject;
414  }
415 
416  wfProfileIn( __METHOD__ );
417 
418  $this->mContentLoaded = true;
419  $this->mContent = null;
420 
421  $oldid = $this->getOldID();
422 
423  # Pre-fill content with error message so that if something
424  # fails we'll have something telling us what we intended.
425  //XXX: this isn't page content but a UI message. horrible.
426  $this->mContentObject = new MessageContent( 'missing-revision', array( $oldid ), array() );
427 
428  if ( $oldid ) {
429  # $this->mRevision might already be fetched by getOldIDFromRequest()
430  if ( !$this->mRevision ) {
431  $this->mRevision = Revision::newFromId( $oldid );
432  if ( !$this->mRevision ) {
433  wfDebug( __METHOD__ . " failed to retrieve specified revision, id $oldid\n" );
434  wfProfileOut( __METHOD__ );
435  return false;
436  }
437  }
438  } else {
439  if ( !$this->mPage->getLatest() ) {
440  wfDebug( __METHOD__ . " failed to find page data for title " .
441  $this->getTitle()->getPrefixedText() . "\n" );
442  wfProfileOut( __METHOD__ );
443  return false;
444  }
445 
446  $this->mRevision = $this->mPage->getRevision();
447 
448  if ( !$this->mRevision ) {
449  wfDebug( __METHOD__ . " failed to retrieve current page, rev_id " .
450  $this->mPage->getLatest() . "\n" );
451  wfProfileOut( __METHOD__ );
452  return false;
453  }
454  }
455 
456  // @todo FIXME: Horrible, horrible! This content-loading interface just plain sucks.
457  // We should instead work with the Revision object when we need it...
458  // Loads if user is allowed
459  $this->mContentObject = $this->mRevision->getContent(
461  $this->getContext()->getUser()
462  );
463  $this->mRevIdFetched = $this->mRevision->getId();
464 
465  wfRunHooks( 'ArticleAfterFetchContentObject', array( &$this, &$this->mContentObject ) );
466 
467  wfProfileOut( __METHOD__ );
468 
469  return $this->mContentObject;
470  }
471 
477  public function isCurrent() {
478  # If no oldid, this is the current version.
479  if ( $this->getOldID() == 0 ) {
480  return true;
481  }
482 
483  return $this->mPage->exists() && $this->mRevision && $this->mRevision->isCurrent();
484  }
485 
493  public function getRevisionFetched() {
494  $this->fetchContentObject();
495 
496  return $this->mRevision;
497  }
498 
504  public function getRevIdFetched() {
505  if ( $this->mRevIdFetched ) {
506  return $this->mRevIdFetched;
507  } else {
508  return $this->mPage->getLatest();
509  }
510  }
511 
516  public function view() {
517  global $wgUseFileCache, $wgUseETag, $wgDebugToolbar;
518 
519  wfProfileIn( __METHOD__ );
520 
521  # Get variables from query string
522  # As side effect this will load the revision and update the title
523  # in a revision ID is passed in the request, so this should remain
524  # the first call of this method even if $oldid is used way below.
525  $oldid = $this->getOldID();
526 
527  $user = $this->getContext()->getUser();
528  # Another whitelist check in case getOldID() is altering the title
529  $permErrors = $this->getTitle()->getUserPermissionsErrors( 'read', $user );
530  if ( count( $permErrors ) ) {
531  wfDebug( __METHOD__ . ": denied on secondary read check\n" );
532  wfProfileOut( __METHOD__ );
533  throw new PermissionsError( 'read', $permErrors );
534  }
535 
536  $outputPage = $this->getContext()->getOutput();
537  # getOldID() may as well want us to redirect somewhere else
538  if ( $this->mRedirectUrl ) {
539  $outputPage->redirect( $this->mRedirectUrl );
540  wfDebug( __METHOD__ . ": redirecting due to oldid\n" );
541  wfProfileOut( __METHOD__ );
542 
543  return;
544  }
545 
546  # If we got diff in the query, we want to see a diff page instead of the article.
547  if ( $this->getContext()->getRequest()->getCheck( 'diff' ) ) {
548  wfDebug( __METHOD__ . ": showing diff page\n" );
549  $this->showDiffPage();
550  wfProfileOut( __METHOD__ );
551 
552  return;
553  }
554 
555  # Set page title (may be overridden by DISPLAYTITLE)
556  $outputPage->setPageTitle( $this->getTitle()->getPrefixedText() );
557 
558  $outputPage->setArticleFlag( true );
559  # Allow frames by default
560  $outputPage->allowClickjacking();
561 
562  $parserCache = ParserCache::singleton();
563 
564  $parserOptions = $this->getParserOptions();
565  # Render printable version, use printable version cache
566  if ( $outputPage->isPrintable() ) {
567  $parserOptions->setIsPrintable( true );
568  $parserOptions->setEditSection( false );
569  } elseif ( !$this->isCurrent() || !$this->getTitle()->quickUserCan( 'edit', $user ) ) {
570  $parserOptions->setEditSection( false );
571  }
572 
573  # Try client and file cache
574  if ( !$wgDebugToolbar && $oldid === 0 && $this->mPage->checkTouched() ) {
575  if ( $wgUseETag ) {
576  $outputPage->setETag( $parserCache->getETag( $this, $parserOptions ) );
577  }
578 
579  # Is it client cached?
580  if ( $outputPage->checkLastModified( $this->mPage->getTouched() ) ) {
581  wfDebug( __METHOD__ . ": done 304\n" );
582  wfProfileOut( __METHOD__ );
583 
584  return;
585  # Try file cache
586  } elseif ( $wgUseFileCache && $this->tryFileCache() ) {
587  wfDebug( __METHOD__ . ": done file cache\n" );
588  # tell wgOut that output is taken care of
589  $outputPage->disable();
590  $this->mPage->doViewUpdates( $user, $oldid );
591  wfProfileOut( __METHOD__ );
592 
593  return;
594  }
595  }
596 
597  # Should the parser cache be used?
598  $useParserCache = $this->mPage->isParserCacheUsed( $parserOptions, $oldid );
599  wfDebug( 'Article::view using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" );
600  if ( $user->getStubThreshold() ) {
601  wfIncrStats( 'pcache_miss_stub' );
602  }
603 
604  $this->showRedirectedFromHeader();
605  $this->showNamespaceHeader();
606 
607  # Iterate through the possible ways of constructing the output text.
608  # Keep going until $outputDone is set, or we run out of things to do.
609  $pass = 0;
610  $outputDone = false;
611  $this->mParserOutput = false;
612 
613  while ( !$outputDone && ++$pass ) {
614  switch ( $pass ) {
615  case 1:
616  wfRunHooks( 'ArticleViewHeader', array( &$this, &$outputDone, &$useParserCache ) );
617  break;
618  case 2:
619  # Early abort if the page doesn't exist
620  if ( !$this->mPage->exists() ) {
621  wfDebug( __METHOD__ . ": showing missing article\n" );
622  $this->showMissingArticle();
623  $this->mPage->doViewUpdates( $user );
624  wfProfileOut( __METHOD__ );
625  return;
626  }
627 
628  # Try the parser cache
629  if ( $useParserCache ) {
630  $this->mParserOutput = $parserCache->get( $this, $parserOptions );
631 
632  if ( $this->mParserOutput !== false ) {
633  if ( $oldid ) {
634  wfDebug( __METHOD__ . ": showing parser cache contents for current rev permalink\n" );
635  $this->setOldSubtitle( $oldid );
636  } else {
637  wfDebug( __METHOD__ . ": showing parser cache contents\n" );
638  }
639  $outputPage->addParserOutput( $this->mParserOutput );
640  # Ensure that UI elements requiring revision ID have
641  # the correct version information.
642  $outputPage->setRevisionId( $this->mPage->getLatest() );
643  # Preload timestamp to avoid a DB hit
644  $cachedTimestamp = $this->mParserOutput->getTimestamp();
645  if ( $cachedTimestamp !== null ) {
646  $outputPage->setRevisionTimestamp( $cachedTimestamp );
647  $this->mPage->setTimestamp( $cachedTimestamp );
648  }
649  $outputDone = true;
650  }
651  }
652  break;
653  case 3:
654  # This will set $this->mRevision if needed
655  $this->fetchContentObject();
656 
657  # Are we looking at an old revision
658  if ( $oldid && $this->mRevision ) {
659  $this->setOldSubtitle( $oldid );
660 
661  if ( !$this->showDeletedRevisionHeader() ) {
662  wfDebug( __METHOD__ . ": cannot view deleted revision\n" );
663  wfProfileOut( __METHOD__ );
664  return;
665  }
666  }
667 
668  # Ensure that UI elements requiring revision ID have
669  # the correct version information.
670  $outputPage->setRevisionId( $this->getRevIdFetched() );
671  # Preload timestamp to avoid a DB hit
672  $outputPage->setRevisionTimestamp( $this->getTimestamp() );
673 
674  # Pages containing custom CSS or JavaScript get special treatment
675  if ( $this->getTitle()->isCssOrJsPage() || $this->getTitle()->isCssJsSubpage() ) {
676  wfDebug( __METHOD__ . ": showing CSS/JS source\n" );
677  $this->showCssOrJsPage();
678  $outputDone = true;
679  } elseif ( !wfRunHooks( 'ArticleContentViewCustom',
680  array( $this->fetchContentObject(), $this->getTitle(), $outputPage ) ) ) {
681 
682  # Allow extensions do their own custom view for certain pages
683  $outputDone = true;
684  } elseif ( !ContentHandler::runLegacyHooks( 'ArticleViewCustom',
685  array( $this->fetchContentObject(), $this->getTitle(), $outputPage ) ) ) {
686 
687  # Allow extensions do their own custom view for certain pages
688  $outputDone = true;
689  } else {
690  $content = $this->getContentObject();
691  $rt = $content ? $content->getRedirectChain() : null;
692  if ( $rt ) {
693  wfDebug( __METHOD__ . ": showing redirect=no page\n" );
694  # Viewing a redirect page (e.g. with parameter redirect=no)
695  $outputPage->addHTML( $this->viewRedirect( $rt ) );
696  # Parse just to get categories, displaytitle, etc.
697  $this->mParserOutput = $content->getParserOutput( $this->getTitle(), $oldid, $parserOptions, false );
698  $outputPage->addParserOutputNoText( $this->mParserOutput );
699  $outputDone = true;
700  }
701  }
702  break;
703  case 4:
704  # Run the parse, protected by a pool counter
705  wfDebug( __METHOD__ . ": doing uncached parse\n" );
706 
707  $poolArticleView = new PoolWorkArticleView( $this->getPage(), $parserOptions,
708  $this->getRevIdFetched(), $useParserCache, $this->getContentObject() );
709 
710  if ( !$poolArticleView->execute() ) {
711  $error = $poolArticleView->getError();
712  if ( $error ) {
713  $outputPage->clearHTML(); // for release() errors
714  $outputPage->enableClientCache( false );
715  $outputPage->setRobotPolicy( 'noindex,nofollow' );
716 
717  $errortext = $error->getWikiText( false, 'view-pool-error' );
718  $outputPage->addWikiText( '<div class="errorbox">' . $errortext . '</div>' );
719  }
720  # Connection or timeout error
721  wfProfileOut( __METHOD__ );
722  return;
723  }
724 
725  $this->mParserOutput = $poolArticleView->getParserOutput();
726  $outputPage->addParserOutput( $this->mParserOutput );
727 
728  # Don't cache a dirty ParserOutput object
729  if ( $poolArticleView->getIsDirty() ) {
730  $outputPage->setSquidMaxage( 0 );
731  $outputPage->addHTML( "<!-- parser cache is expired, " .
732  "sending anyway due to pool overload-->\n" );
733  }
734 
735  $outputDone = true;
736  break;
737  # Should be unreachable, but just in case...
738  default:
739  break 2;
740  }
741  }
742 
743  # Get the ParserOutput actually *displayed* here.
744  # Note that $this->mParserOutput is the *current* version output.
745  $pOutput = ( $outputDone instanceof ParserOutput )
746  ? $outputDone // object fetched by hook
747  : $this->mParserOutput;
748 
749  # Adjust title for main page & pages with displaytitle
750  if ( $pOutput ) {
751  $this->adjustDisplayTitle( $pOutput );
752  }
753 
754  # For the main page, overwrite the <title> element with the con-
755  # tents of 'pagetitle-view-mainpage' instead of the default (if
756  # that's not empty).
757  # This message always exists because it is in the i18n files
758  if ( $this->getTitle()->isMainPage() ) {
759  $msg = wfMessage( 'pagetitle-view-mainpage' )->inContentLanguage();
760  if ( !$msg->isDisabled() ) {
761  $outputPage->setHTMLTitle( $msg->title( $this->getTitle() )->text() );
762  }
763  }
764 
765  # Check for any __NOINDEX__ tags on the page using $pOutput
766  $policy = $this->getRobotPolicy( 'view', $pOutput );
767  $outputPage->setIndexPolicy( $policy['index'] );
768  $outputPage->setFollowPolicy( $policy['follow'] );
769 
770  $this->showViewFooter();
771  $this->mPage->doViewUpdates( $user, $oldid );
772 
773  $outputPage->addModules( 'mediawiki.action.view.postEdit' );
774 
775  wfProfileOut( __METHOD__ );
776  }
777 
782  public function adjustDisplayTitle( ParserOutput $pOutput ) {
783  # Adjust the title if it was set by displaytitle, -{T|}- or language conversion
784  $titleText = $pOutput->getTitleText();
785  if ( strval( $titleText ) !== '' ) {
786  $this->getContext()->getOutput()->setPageTitle( $titleText );
787  }
788  }
789 
796  public function showDiffPage() {
797  $request = $this->getContext()->getRequest();
798  $user = $this->getContext()->getUser();
799  $diff = $request->getVal( 'diff' );
800  $rcid = $request->getVal( 'rcid' );
801  $diffOnly = $request->getBool( 'diffonly', $user->getOption( 'diffonly' ) );
802  $purge = $request->getVal( 'action' ) == 'purge';
803  $unhide = $request->getInt( 'unhide' ) == 1;
804  $oldid = $this->getOldID();
805 
806  $rev = $this->getRevisionFetched();
807 
808  if ( !$rev ) {
809  $this->getContext()->getOutput()->setPageTitle( wfMessage( 'errorpagetitle' ) );
810  $this->getContext()->getOutput()->addWikiMsg( 'difference-missing-revision', $oldid, 1 );
811  return;
812  }
813 
814  $contentHandler = $rev->getContentHandler();
815  $de = $contentHandler->createDifferenceEngine(
816  $this->getContext(),
817  $oldid,
818  $diff,
819  $rcid,
820  $purge,
821  $unhide
822  );
823 
824  // DifferenceEngine directly fetched the revision:
825  $this->mRevIdFetched = $de->mNewid;
826  $de->showDiffPage( $diffOnly );
827 
828  // Run view updates for the newer revision being diffed (and shown
829  // below the diff if not $diffOnly).
830  list( $old, $new ) = $de->mapDiffPrevNext( $oldid, $diff );
831  // New can be false, convert it to 0 - this conveniently means the latest revision
832  $this->mPage->doViewUpdates( $user, (int)$new );
833  }
834 
845  protected function showCssOrJsPage( $showCacheHint = true ) {
846  $outputPage = $this->getContext()->getOutput();
847 
848  if ( $showCacheHint ) {
849  $dir = $this->getContext()->getLanguage()->getDir();
850  $lang = $this->getContext()->getLanguage()->getCode();
851 
852  $outputPage->wrapWikiMsg(
853  "<div id='mw-clearyourcache' lang='$lang' dir='$dir' class='mw-content-$dir'>\n$1\n</div>",
854  'clearyourcache'
855  );
856  }
857 
858  $this->fetchContentObject();
859 
860  if ( $this->mContentObject ) {
861  // Give hooks a chance to customise the output
863  'ShowRawCssJs',
864  array( $this->mContentObject, $this->getTitle(), $outputPage ) )
865  ) {
866  $po = $this->mContentObject->getParserOutput( $this->getTitle() );
867  $outputPage->addHTML( $po->getText() );
868  }
869  }
870  }
871 
879  public function getRobotPolicy( $action, $pOutput = null ) {
880  global $wgArticleRobotPolicies, $wgNamespaceRobotPolicies, $wgDefaultRobotPolicy;
881 
882  $ns = $this->getTitle()->getNamespace();
883 
884  # Don't index user and user talk pages for blocked users (bug 11443)
885  if ( ( $ns == NS_USER || $ns == NS_USER_TALK ) && !$this->getTitle()->isSubpage() ) {
886  $specificTarget = null;
887  $vagueTarget = null;
888  $titleText = $this->getTitle()->getText();
889  if ( IP::isValid( $titleText ) ) {
890  $vagueTarget = $titleText;
891  } else {
892  $specificTarget = $titleText;
893  }
894  if ( Block::newFromTarget( $specificTarget, $vagueTarget ) instanceof Block ) {
895  return array(
896  'index' => 'noindex',
897  'follow' => 'nofollow'
898  );
899  }
900  }
901 
902  if ( $this->mPage->getID() === 0 || $this->getOldID() ) {
903  # Non-articles (special pages etc), and old revisions
904  return array(
905  'index' => 'noindex',
906  'follow' => 'nofollow'
907  );
908  } elseif ( $this->getContext()->getOutput()->isPrintable() ) {
909  # Discourage indexing of printable versions, but encourage following
910  return array(
911  'index' => 'noindex',
912  'follow' => 'follow'
913  );
914  } elseif ( $this->getContext()->getRequest()->getInt( 'curid' ) ) {
915  # For ?curid=x urls, disallow indexing
916  return array(
917  'index' => 'noindex',
918  'follow' => 'follow'
919  );
920  }
921 
922  # Otherwise, construct the policy based on the various config variables.
923  $policy = self::formatRobotPolicy( $wgDefaultRobotPolicy );
924 
925  if ( isset( $wgNamespaceRobotPolicies[$ns] ) ) {
926  # Honour customised robot policies for this namespace
927  $policy = array_merge(
928  $policy,
929  self::formatRobotPolicy( $wgNamespaceRobotPolicies[$ns] )
930  );
931  }
932  if ( $this->getTitle()->canUseNoindex() && is_object( $pOutput ) && $pOutput->getIndexPolicy() ) {
933  # __INDEX__ and __NOINDEX__ magic words, if allowed. Incorporates
934  # a final sanity check that we have really got the parser output.
935  $policy = array_merge(
936  $policy,
937  array( 'index' => $pOutput->getIndexPolicy() )
938  );
939  }
940 
941  if ( isset( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] ) ) {
942  # (bug 14900) site config can override user-defined __INDEX__ or __NOINDEX__
943  $policy = array_merge(
944  $policy,
945  self::formatRobotPolicy( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] )
946  );
947  }
948 
949  return $policy;
950  }
951 
959  public static function formatRobotPolicy( $policy ) {
960  if ( is_array( $policy ) ) {
961  return $policy;
962  } elseif ( !$policy ) {
963  return array();
964  }
965 
966  $policy = explode( ',', $policy );
967  $policy = array_map( 'trim', $policy );
968 
969  $arr = array();
970  foreach ( $policy as $var ) {
971  if ( in_array( $var, array( 'index', 'noindex' ) ) ) {
972  $arr['index'] = $var;
973  } elseif ( in_array( $var, array( 'follow', 'nofollow' ) ) ) {
974  $arr['follow'] = $var;
975  }
976  }
977 
978  return $arr;
979  }
980 
988  public function showRedirectedFromHeader() {
989  global $wgRedirectSources;
990  $outputPage = $this->getContext()->getOutput();
991 
992  $rdfrom = $this->getContext()->getRequest()->getVal( 'rdfrom' );
993 
994  if ( isset( $this->mRedirectedFrom ) ) {
995  // This is an internally redirected page view.
996  // We'll need a backlink to the source page for navigation.
997  if ( wfRunHooks( 'ArticleViewRedirect', array( &$this ) ) ) {
998  $redir = Linker::linkKnown(
999  $this->mRedirectedFrom,
1000  null,
1001  array(),
1002  array( 'redirect' => 'no' )
1003  );
1004 
1005  $outputPage->addSubtitle( wfMessage( 'redirectedfrom' )->rawParams( $redir ) );
1006 
1007  // Set the fragment if one was specified in the redirect
1008  if ( $this->getTitle()->hasFragment() ) {
1009  $outputPage->addJsConfigVars( 'wgRedirectToFragment', $this->getTitle()->getFragmentForURL() );
1010  $outputPage->addModules( 'mediawiki.action.view.redirectToFragment' );
1011  }
1012 
1013  // Add a <link rel="canonical"> tag
1014  $outputPage->setCanonicalUrl( $this->getTitle()->getLocalURL() );
1015 
1016  // Tell the output object that the user arrived at this article through a redirect
1017  $outputPage->setRedirectedFrom( $this->mRedirectedFrom );
1018 
1019  return true;
1020  }
1021  } elseif ( $rdfrom ) {
1022  // This is an externally redirected view, from some other wiki.
1023  // If it was reported from a trusted site, supply a backlink.
1024  if ( $wgRedirectSources && preg_match( $wgRedirectSources, $rdfrom ) ) {
1025  $redir = Linker::makeExternalLink( $rdfrom, $rdfrom );
1026  $outputPage->addSubtitle( wfMessage( 'redirectedfrom' )->rawParams( $redir ) );
1028  return true;
1029  }
1030  }
1031 
1032  return false;
1033  }
1034 
1039  public function showNamespaceHeader() {
1040  if ( $this->getTitle()->isTalkPage() ) {
1041  if ( !wfMessage( 'talkpageheader' )->isDisabled() ) {
1042  $this->getContext()->getOutput()->wrapWikiMsg(
1043  "<div class=\"mw-talkpageheader\">\n$1\n</div>",
1044  array( 'talkpageheader' )
1045  );
1046  }
1047  }
1048  }
1049 
1053  public function showViewFooter() {
1054  # check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page
1055  if ( $this->getTitle()->getNamespace() == NS_USER_TALK
1056  && IP::isValid( $this->getTitle()->getText() )
1057  ) {
1058  $this->getContext()->getOutput()->addWikiMsg( 'anontalkpagetext' );
1059  }
1060 
1061  // Show a footer allowing the user to patrol the shown revision or page if possible
1062  $patrolFooterShown = $this->showPatrolFooter();
1063 
1064  wfRunHooks( 'ArticleViewFooter', array( $this, $patrolFooterShown ) );
1065  }
1066 
1076  public function showPatrolFooter() {
1077  global $wgUseNPPatrol, $wgUseRCPatrol, $wgEnableAPI, $wgEnableWriteAPI;
1078 
1079  $outputPage = $this->getContext()->getOutput();
1080  $user = $this->getContext()->getUser();
1081  $cache = wfGetMainCache();
1082  $rc = false;
1083 
1084  if ( !$this->getTitle()->quickUserCan( 'patrol', $user )
1085  || !( $wgUseRCPatrol || $wgUseNPPatrol )
1086  ) {
1087  // Patrolling is disabled or the user isn't allowed to
1088  return false;
1089  }
1090 
1091  wfProfileIn( __METHOD__ );
1092 
1093  // New page patrol: Get the timestamp of the oldest revison which
1094  // the revision table holds for the given page. Then we look
1095  // whether it's within the RC lifespan and if it is, we try
1096  // to get the recentchanges row belonging to that entry
1097  // (with rc_new = 1).
1098 
1099  // Check for cached results
1100  if ( $cache->get( wfMemcKey( 'NotPatrollablePage', $this->getTitle()->getArticleID() ) ) ) {
1101  wfProfileOut( __METHOD__ );
1102  return false;
1103  }
1104 
1105  if ( $this->mRevision
1106  && !RecentChange::isInRCLifespan( $this->mRevision->getTimestamp(), 21600 )
1107  ) {
1108  // The current revision is already older than what could be in the RC table
1109  // 6h tolerance because the RC might not be cleaned out regularly
1110  wfProfileOut( __METHOD__ );
1111  return false;
1112  }
1113 
1114  $dbr = wfGetDB( DB_SLAVE );
1115  $oldestRevisionTimestamp = $dbr->selectField(
1116  'revision',
1117  'MIN( rev_timestamp )',
1118  array( 'rev_page' => $this->getTitle()->getArticleID() ),
1119  __METHOD__
1120  );
1121 
1122  if ( $oldestRevisionTimestamp
1123  && RecentChange::isInRCLifespan( $oldestRevisionTimestamp, 21600 )
1124  ) {
1125  // 6h tolerance because the RC might not be cleaned out regularly
1127  array(
1128  'rc_new' => 1,
1129  'rc_timestamp' => $oldestRevisionTimestamp,
1130  'rc_namespace' => $this->getTitle()->getNamespace(),
1131  'rc_cur_id' => $this->getTitle()->getArticleID(),
1132  'rc_patrolled' => 0
1133  ),
1134  __METHOD__,
1135  array( 'USE INDEX' => 'new_name_timestamp' )
1136  );
1137  }
1138 
1139  if ( !$rc ) {
1140  // No RC entry around
1141 
1142  // Cache the information we gathered above in case we can't patrol
1143  // Don't cache in case we can patrol as this could change
1144  $cache->set( wfMemcKey( 'NotPatrollablePage', $this->getTitle()->getArticleID() ), '1' );
1145 
1146  wfProfileOut( __METHOD__ );
1147  return false;
1148  }
1149 
1150  if ( $rc->getPerformer()->getName() == $user->getName() ) {
1151  // Don't show a patrol link for own creations. If the user could
1152  // patrol them, they already would be patrolled
1153  wfProfileOut( __METHOD__ );
1154  return false;
1155  }
1156 
1157  $rcid = $rc->getAttribute( 'rc_id' );
1158 
1159  $token = $user->getEditToken( $rcid );
1160 
1161  $outputPage->preventClickjacking();
1162  if ( $wgEnableAPI && $wgEnableWriteAPI && $user->isAllowed( 'writeapi' ) ) {
1163  $outputPage->addModules( 'mediawiki.page.patrol.ajax' );
1164  }
1165 
1167  $this->getTitle(),
1168  wfMessage( 'markaspatrolledtext' )->escaped(),
1169  array(),
1170  array(
1171  'action' => 'markpatrolled',
1172  'rcid' => $rcid,
1173  'token' => $token,
1174  )
1175  );
1176 
1177  $outputPage->addHTML(
1178  "<div class='patrollink'>" .
1179  wfMessage( 'markaspatrolledlink' )->rawParams( $link )->escaped() .
1180  '</div>'
1181  );
1182 
1183  wfProfileOut( __METHOD__ );
1184  return true;
1185  }
1186 
1191  public function showMissingArticle() {
1192  global $wgSend404Code;
1193  $outputPage = $this->getContext()->getOutput();
1194  // Whether the page is a root user page of an existing user (but not a subpage)
1195  $validUserPage = false;
1196 
1197  # Show info in user (talk) namespace. Does the user exist? Is he blocked?
1198  if ( $this->getTitle()->getNamespace() == NS_USER
1199  || $this->getTitle()->getNamespace() == NS_USER_TALK
1200  ) {
1201  $parts = explode( '/', $this->getTitle()->getText() );
1202  $rootPart = $parts[0];
1203  $user = User::newFromName( $rootPart, false /* allow IP users*/ );
1204  $ip = User::isIP( $rootPart );
1205 
1206  if ( !( $user && $user->isLoggedIn() ) && !$ip ) { # User does not exist
1207  $outputPage->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n\$1\n</div>",
1208  array( 'userpage-userdoesnotexist-view', wfEscapeWikiText( $rootPart ) ) );
1209  } elseif ( $user->isBlocked() ) { # Show log extract if the user is currently blocked
1211  $outputPage,
1212  'block',
1213  $user->getUserPage(),
1214  '',
1215  array(
1216  'lim' => 1,
1217  'showIfEmpty' => false,
1218  'msgKey' => array(
1219  'blocked-notice-logextract',
1220  $user->getName() # Support GENDER in notice
1221  )
1222  )
1223  );
1224  $validUserPage = !$this->getTitle()->isSubpage();
1225  } else {
1226  $validUserPage = !$this->getTitle()->isSubpage();
1227  }
1228  }
1229 
1230  wfRunHooks( 'ShowMissingArticle', array( $this ) );
1231 
1232  // Give extensions a chance to hide their (unrelated) log entries
1233  $logTypes = array( 'delete', 'move' );
1234  $conds = array( "log_action != 'revision'" );
1235  wfRunHooks( 'Article::MissingArticleConditions', array( &$conds, $logTypes ) );
1236 
1237  # Show delete and move logs
1238  LogEventsList::showLogExtract( $outputPage, $logTypes, $this->getTitle(), '',
1239  array( 'lim' => 10,
1240  'conds' => $conds,
1241  'showIfEmpty' => false,
1242  'msgKey' => array( 'moveddeleted-notice' ) )
1243  );
1244 
1245  if ( !$this->mPage->hasViewableContent() && $wgSend404Code && !$validUserPage ) {
1246  // If there's no backing content, send a 404 Not Found
1247  // for better machine handling of broken links.
1248  $this->getContext()->getRequest()->response()->header( "HTTP/1.1 404 Not Found" );
1249  }
1250 
1251  if ( $validUserPage ) {
1252  // Also apply the robot policy for nonexisting user pages (as those aren't served as 404)
1253  $policy = $this->getRobotPolicy( 'view' );
1254  $outputPage->setIndexPolicy( $policy['index'] );
1255  $outputPage->setFollowPolicy( $policy['follow'] );
1256  }
1257 
1258  $hookResult = wfRunHooks( 'BeforeDisplayNoArticleText', array( $this ) );
1259 
1260  if ( ! $hookResult ) {
1261  return;
1262  }
1263 
1264  # Show error message
1265  $oldid = $this->getOldID();
1266  if ( $oldid ) {
1267  $text = wfMessage( 'missing-revision', $oldid )->plain();
1268  } elseif ( $this->getTitle()->getNamespace() === NS_MEDIAWIKI ) {
1269  // Use the default message text
1270  $text = $this->getTitle()->getDefaultMessageText();
1271  } elseif ( $this->getTitle()->quickUserCan( 'create', $this->getContext()->getUser() )
1272  && $this->getTitle()->quickUserCan( 'edit', $this->getContext()->getUser() )
1273  ) {
1274  $message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon';
1275  $text = wfMessage( $message )->plain();
1276  } else {
1277  $text = wfMessage( 'noarticletext-nopermission' )->plain();
1278  }
1279  $text = "<div class='noarticletext'>\n$text\n</div>";
1280 
1281  $outputPage->addWikiText( $text );
1282  }
1283 
1290  public function showDeletedRevisionHeader() {
1291  if ( !$this->mRevision->isDeleted( Revision::DELETED_TEXT ) ) {
1292  // Not deleted
1293  return true;
1294  }
1295 
1296  $outputPage = $this->getContext()->getOutput();
1297  $user = $this->getContext()->getUser();
1298  // If the user is not allowed to see it...
1299  if ( !$this->mRevision->userCan( Revision::DELETED_TEXT, $user ) ) {
1300  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1301  'rev-deleted-text-permission' );
1302 
1303  return false;
1304  // If the user needs to confirm that they want to see it...
1305  } elseif ( $this->getContext()->getRequest()->getInt( 'unhide' ) != 1 ) {
1306  # Give explanation and add a link to view the revision...
1307  $oldid = intval( $this->getOldID() );
1308  $link = $this->getTitle()->getFullURL( "oldid={$oldid}&unhide=1" );
1309  $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
1310  'rev-suppressed-text-unhide' : 'rev-deleted-text-unhide';
1311  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1312  array( $msg, $link ) );
1313 
1314  return false;
1315  // We are allowed to see...
1316  } else {
1317  $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
1318  'rev-suppressed-text-view' : 'rev-deleted-text-view';
1319  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n", $msg );
1320 
1321  return true;
1322  }
1323  }
1324 
1333  public function setOldSubtitle( $oldid = 0 ) {
1334  if ( !wfRunHooks( 'DisplayOldSubtitle', array( &$this, &$oldid ) ) ) {
1335  return;
1336  }
1337 
1338  $unhide = $this->getContext()->getRequest()->getInt( 'unhide' ) == 1;
1339 
1340  # Cascade unhide param in links for easy deletion browsing
1341  $extraParams = array();
1342  if ( $unhide ) {
1343  $extraParams['unhide'] = 1;
1344  }
1345 
1346  if ( $this->mRevision && $this->mRevision->getId() === $oldid ) {
1347  $revision = $this->mRevision;
1348  } else {
1349  $revision = Revision::newFromId( $oldid );
1350  }
1351 
1352  $timestamp = $revision->getTimestamp();
1353 
1354  $current = ( $oldid == $this->mPage->getLatest() );
1355  $language = $this->getContext()->getLanguage();
1356  $user = $this->getContext()->getUser();
1357 
1358  $td = $language->userTimeAndDate( $timestamp, $user );
1359  $tddate = $language->userDate( $timestamp, $user );
1360  $tdtime = $language->userTime( $timestamp, $user );
1361 
1362  # Show user links if allowed to see them. If hidden, then show them only if requested...
1363  $userlinks = Linker::revUserTools( $revision, !$unhide );
1364 
1365  $infomsg = $current && !wfMessage( 'revision-info-current' )->isDisabled()
1366  ? 'revision-info-current'
1367  : 'revision-info';
1368 
1369  $outputPage = $this->getContext()->getOutput();
1370  $outputPage->addSubtitle( "<div id=\"mw-{$infomsg}\">" . wfMessage( $infomsg,
1371  $td )->rawParams( $userlinks )->params( $revision->getID(), $tddate,
1372  $tdtime, $revision->getUser() )->parse() . "</div>" );
1373 
1374  $lnk = $current
1375  ? wfMessage( 'currentrevisionlink' )->escaped()
1377  $this->getTitle(),
1378  wfMessage( 'currentrevisionlink' )->escaped(),
1379  array(),
1380  $extraParams
1381  );
1382  $curdiff = $current
1383  ? wfMessage( 'diff' )->escaped()
1385  $this->getTitle(),
1386  wfMessage( 'diff' )->escaped(),
1387  array(),
1388  array(
1389  'diff' => 'cur',
1390  'oldid' => $oldid
1391  ) + $extraParams
1392  );
1393  $prev = $this->getTitle()->getPreviousRevisionID( $oldid );
1394  $prevlink = $prev
1396  $this->getTitle(),
1397  wfMessage( 'previousrevision' )->escaped(),
1398  array(),
1399  array(
1400  'direction' => 'prev',
1401  'oldid' => $oldid
1402  ) + $extraParams
1403  )
1404  : wfMessage( 'previousrevision' )->escaped();
1405  $prevdiff = $prev
1407  $this->getTitle(),
1408  wfMessage( 'diff' )->escaped(),
1409  array(),
1410  array(
1411  'diff' => 'prev',
1412  'oldid' => $oldid
1413  ) + $extraParams
1414  )
1415  : wfMessage( 'diff' )->escaped();
1416  $nextlink = $current
1417  ? wfMessage( 'nextrevision' )->escaped()
1419  $this->getTitle(),
1420  wfMessage( 'nextrevision' )->escaped(),
1421  array(),
1422  array(
1423  'direction' => 'next',
1424  'oldid' => $oldid
1425  ) + $extraParams
1426  );
1427  $nextdiff = $current
1428  ? wfMessage( 'diff' )->escaped()
1430  $this->getTitle(),
1431  wfMessage( 'diff' )->escaped(),
1432  array(),
1433  array(
1434  'diff' => 'next',
1435  'oldid' => $oldid
1436  ) + $extraParams
1437  );
1438 
1439  $cdel = Linker::getRevDeleteLink( $user, $revision, $this->getTitle() );
1440  if ( $cdel !== '' ) {
1441  $cdel .= ' ';
1442  }
1443 
1444  $outputPage->addSubtitle( "<div id=\"mw-revision-nav\">" . $cdel .
1445  wfMessage( 'revision-nav' )->rawParams(
1446  $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff
1447  )->escaped() . "</div>" );
1448  }
1449 
1458  public function viewRedirect( $target, $appendSubtitle = true, $forceKnown = false ) {
1459  global $wgStylePath;
1460 
1461  if ( !is_array( $target ) ) {
1462  $target = array( $target );
1463  }
1464 
1465  $lang = $this->getTitle()->getPageLanguage();
1466  $imageDir = $lang->getDir();
1467 
1468  if ( $appendSubtitle ) {
1469  $out = $this->getContext()->getOutput();
1470  $out->addSubtitle( wfMessage( 'redirectpagesub' )->parse() );
1471  }
1472 
1473  // the loop prepends the arrow image before the link, so the first case needs to be outside
1474 
1476  $title = array_shift( $target );
1477 
1478  if ( $forceKnown ) {
1479  $link = Linker::linkKnown( $title, htmlspecialchars( $title->getFullText() ) );
1480  } else {
1481  $link = Linker::link( $title, htmlspecialchars( $title->getFullText() ) );
1482  }
1483 
1484  $nextRedirect = $wgStylePath . '/common/images/nextredirect' . $imageDir . '.png';
1485  $alt = $lang->isRTL() ? '←' : '→';
1486 
1487  // Automatically append redirect=no to each link, since most of them are
1488  // redirect pages themselves.
1490  foreach ( $target as $rt ) {
1491  $link .= Html::element( 'img', array( 'src' => $nextRedirect, 'alt' => $alt ) );
1492  if ( $forceKnown ) {
1494  $rt,
1495  htmlspecialchars( $rt->getFullText(),
1496  array(),
1497  array( 'redirect' => 'no' )
1498  )
1499  );
1500  } else {
1501  $link .= Linker::link(
1502  $rt,
1503  htmlspecialchars( $rt->getFullText() ),
1504  array(),
1505  array( 'redirect' => 'no' )
1506  );
1507  }
1508  }
1509 
1510  $imageUrl = $wgStylePath . '/common/images/redirect' . $imageDir . '.png';
1511  return '<div class="redirectMsg">' .
1512  Html::element( 'img', array( 'src' => $imageUrl, 'alt' => '#REDIRECT' ) ) .
1513  '<span class="redirectText">' . $link . '</span></div>';
1514  }
1515 
1519  public function render() {
1520  $this->getContext()->getOutput()->setArticleBodyOnly( true );
1521  $this->getContext()->getOutput()->enableSectionEditLinks( false );
1522  $this->view();
1523  }
1528  public function protect() {
1529  $form = new ProtectionForm( $this );
1530  $form->execute();
1531  }
1532 
1536  public function unprotect() {
1537  $this->protect();
1538  }
1539 
1543  public function delete() {
1544  # This code desperately needs to be totally rewritten
1545 
1546  $title = $this->getTitle();
1547  $user = $this->getContext()->getUser();
1548 
1549  # Check permissions
1550  $permission_errors = $title->getUserPermissionsErrors( 'delete', $user );
1551  if ( count( $permission_errors ) ) {
1552  throw new PermissionsError( 'delete', $permission_errors );
1553  }
1554 
1555  # Read-only check...
1556  if ( wfReadOnly() ) {
1557  throw new ReadOnlyError;
1558  }
1559 
1560  # Better double-check that it hasn't been deleted yet!
1561  $this->mPage->loadPageData( 'fromdbmaster' );
1562  if ( !$this->mPage->exists() ) {
1563  $deleteLogPage = new LogPage( 'delete' );
1564  $outputPage = $this->getContext()->getOutput();
1565  $outputPage->setPageTitle( wfMessage( 'cannotdelete-title', $title->getPrefixedText() ) );
1566  $outputPage->wrapWikiMsg( "<div class=\"error mw-error-cannotdelete\">\n$1\n</div>",
1567  array( 'cannotdelete', wfEscapeWikiText( $title->getPrefixedText() ) )
1568  );
1569  $outputPage->addHTML(
1570  Xml::element( 'h2', null, $deleteLogPage->getName()->text() )
1571  );
1573  $outputPage,
1574  'delete',
1575  $title
1576  );
1577 
1578  return;
1579  }
1580 
1581  $request = $this->getContext()->getRequest();
1582  $deleteReasonList = $request->getText( 'wpDeleteReasonList', 'other' );
1583  $deleteReason = $request->getText( 'wpReason' );
1584 
1585  if ( $deleteReasonList == 'other' ) {
1586  $reason = $deleteReason;
1587  } elseif ( $deleteReason != '' ) {
1588  // Entry from drop down menu + additional comment
1589  $colonseparator = wfMessage( 'colon-separator' )->inContentLanguage()->text();
1590  $reason = $deleteReasonList . $colonseparator . $deleteReason;
1591  } else {
1592  $reason = $deleteReasonList;
1593  }
1594 
1595  if ( $request->wasPosted() && $user->matchEditToken( $request->getVal( 'wpEditToken' ),
1596  array( 'delete', $this->getTitle()->getPrefixedText() ) )
1597  ) {
1598  # Flag to hide all contents of the archived revisions
1599  $suppress = $request->getVal( 'wpSuppress' ) && $user->isAllowed( 'suppressrevision' );
1600 
1601  $this->doDelete( $reason, $suppress );
1602 
1603  WatchAction::doWatchOrUnwatch( $request->getCheck( 'wpWatch' ), $title, $user );
1604 
1605  return;
1606  }
1607 
1608  // Generate deletion reason
1609  $hasHistory = false;
1610  if ( !$reason ) {
1611  try {
1612  $reason = $this->generateReason( $hasHistory );
1613  } catch ( MWException $e ) {
1614  # if a page is horribly broken, we still want to be able to
1615  # delete it. So be lenient about errors here.
1616  wfDebug( "Error while building auto delete summary: $e" );
1617  $reason = '';
1618  }
1619  }
1620 
1621  // If the page has a history, insert a warning
1622  if ( $hasHistory ) {
1623  $revisions = $this->mTitle->estimateRevisionCount();
1624  // @todo FIXME: i18n issue/patchwork message
1625  $this->getContext()->getOutput()->addHTML( '<strong class="mw-delete-warning-revisions">' .
1626  wfMessage( 'historywarning' )->numParams( $revisions )->parse() .
1627  wfMessage( 'word-separator' )->plain() . Linker::linkKnown( $title,
1628  wfMessage( 'history' )->escaped(),
1629  array( 'rel' => 'archives' ),
1630  array( 'action' => 'history' ) ) .
1631  '</strong>'
1632  );
1633 
1634  if ( $this->mTitle->isBigDeletion() ) {
1635  global $wgDeleteRevisionsLimit;
1636  $this->getContext()->getOutput()->wrapWikiMsg( "<div class='error'>\n$1\n</div>\n",
1637  array(
1638  'delete-warning-toobig',
1639  $this->getContext()->getLanguage()->formatNum( $wgDeleteRevisionsLimit )
1640  )
1641  );
1642  }
1643  }
1644 
1645  $this->confirmDelete( $reason );
1646  }
1647 
1653  public function confirmDelete( $reason ) {
1654  wfDebug( "Article::confirmDelete\n" );
1655 
1656  $outputPage = $this->getContext()->getOutput();
1657  $outputPage->setPageTitle( wfMessage( 'delete-confirm', $this->getTitle()->getPrefixedText() ) );
1658  $outputPage->addBacklinkSubtitle( $this->getTitle() );
1659  $outputPage->setRobotPolicy( 'noindex,nofollow' );
1660  $backlinkCache = $this->getTitle()->getBacklinkCache();
1661  if ( $backlinkCache->hasLinks( 'pagelinks' ) || $backlinkCache->hasLinks( 'templatelinks' ) ) {
1662  $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
1663  'deleting-backlinks-warning' );
1664  }
1665  $outputPage->addWikiMsg( 'confirmdeletetext' );
1666 
1667  wfRunHooks( 'ArticleConfirmDelete', array( $this, $outputPage, &$reason ) );
1668 
1669  $user = $this->getContext()->getUser();
1670 
1671  if ( $user->isAllowed( 'suppressrevision' ) ) {
1672  $suppress = "<tr id=\"wpDeleteSuppressRow\">
1673  <td></td>
1674  <td class='mw-input'><strong>" .
1675  Xml::checkLabel( wfMessage( 'revdelete-suppress' )->text(),
1676  'wpSuppress', 'wpSuppress', false, array( 'tabindex' => '4' ) ) .
1677  "</strong></td>
1678  </tr>";
1679  } else {
1680  $suppress = '';
1681  }
1682  $checkWatch = $user->getBoolOption( 'watchdeletion' ) || $user->isWatched( $this->getTitle() );
1683 
1684  $form = Xml::openElement( 'form', array( 'method' => 'post',
1685  'action' => $this->getTitle()->getLocalURL( 'action=delete' ), 'id' => 'deleteconfirm' ) ) .
1686  Xml::openElement( 'fieldset', array( 'id' => 'mw-delete-table' ) ) .
1687  Xml::tags( 'legend', null, wfMessage( 'delete-legend' )->escaped() ) .
1688  Xml::openElement( 'table', array( 'id' => 'mw-deleteconfirm-table' ) ) .
1689  "<tr id=\"wpDeleteReasonListRow\">
1690  <td class='mw-label'>" .
1691  Xml::label( wfMessage( 'deletecomment' )->text(), 'wpDeleteReasonList' ) .
1692  "</td>
1693  <td class='mw-input'>" .
1695  'wpDeleteReasonList',
1696  wfMessage( 'deletereason-dropdown' )->inContentLanguage()->text(),
1697  wfMessage( 'deletereasonotherlist' )->inContentLanguage()->text(),
1698  '',
1699  'wpReasonDropDown',
1700  1
1701  ) .
1702  "</td>
1703  </tr>
1704  <tr id=\"wpDeleteReasonRow\">
1705  <td class='mw-label'>" .
1706  Xml::label( wfMessage( 'deleteotherreason' )->text(), 'wpReason' ) .
1707  "</td>
1708  <td class='mw-input'>" .
1709  Html::input( 'wpReason', $reason, 'text', array(
1710  'size' => '60',
1711  'maxlength' => '255',
1712  'tabindex' => '2',
1713  'id' => 'wpReason',
1714  'autofocus'
1715  ) ) .
1716  "</td>
1717  </tr>";
1718 
1719  # Disallow watching if user is not logged in
1720  if ( $user->isLoggedIn() ) {
1721  $form .= "
1722  <tr>
1723  <td></td>
1724  <td class='mw-input'>" .
1725  Xml::checkLabel( wfMessage( 'watchthis' )->text(),
1726  'wpWatch', 'wpWatch', $checkWatch, array( 'tabindex' => '3' ) ) .
1727  "</td>
1728  </tr>";
1729  }
1730 
1731  $form .= "
1732  $suppress
1733  <tr>
1734  <td></td>
1735  <td class='mw-submit'>" .
1736  Xml::submitButton( wfMessage( 'deletepage' )->text(),
1737  array( 'name' => 'wpConfirmB', 'id' => 'wpConfirmB', 'tabindex' => '5' ) ) .
1738  "</td>
1739  </tr>" .
1740  Xml::closeElement( 'table' ) .
1741  Xml::closeElement( 'fieldset' ) .
1742  Html::hidden(
1743  'wpEditToken',
1744  $user->getEditToken( array( 'delete', $this->getTitle()->getPrefixedText() ) )
1745  ) .
1746  Xml::closeElement( 'form' );
1747 
1748  if ( $user->isAllowed( 'editinterface' ) ) {
1749  $title = Title::makeTitle( NS_MEDIAWIKI, 'Deletereason-dropdown' );
1750  $link = Linker::link(
1751  $title,
1752  wfMessage( 'delete-edit-reasonlist' )->escaped(),
1753  array(),
1754  array( 'action' => 'edit' )
1755  );
1756  $form .= '<p class="mw-delete-editreasons">' . $link . '</p>';
1757  }
1758 
1759  $outputPage->addHTML( $form );
1760 
1761  $deleteLogPage = new LogPage( 'delete' );
1762  $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
1763  LogEventsList::showLogExtract( $outputPage, 'delete',
1764  $this->getTitle()
1765  );
1766  }
1767 
1773  public function doDelete( $reason, $suppress = false ) {
1774  $error = '';
1775  $outputPage = $this->getContext()->getOutput();
1776  $status = $this->mPage->doDeleteArticleReal( $reason, $suppress, 0, true, $error );
1777 
1778  if ( $status->isGood() ) {
1779  $deleted = $this->getTitle()->getPrefixedText();
1780 
1781  $outputPage->setPageTitle( wfMessage( 'actioncomplete' ) );
1782  $outputPage->setRobotPolicy( 'noindex,nofollow' );
1783 
1784  $loglink = '[[Special:Log/delete|' . wfMessage( 'deletionlog' )->text() . ']]';
1785 
1786  $outputPage->addWikiMsg( 'deletedtext', wfEscapeWikiText( $deleted ), $loglink );
1787  $outputPage->returnToMain( false );
1788  } else {
1789  $outputPage->setPageTitle(
1790  wfMessage( 'cannotdelete-title',
1791  $this->getTitle()->getPrefixedText() )
1792  );
1793 
1794  if ( $error == '' ) {
1795  $outputPage->addWikiText(
1796  "<div class=\"error mw-error-cannotdelete\">\n" . $status->getWikiText() . "\n</div>"
1797  );
1798  $deleteLogPage = new LogPage( 'delete' );
1799  $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
1800 
1802  $outputPage,
1803  'delete',
1804  $this->getTitle()
1805  );
1806  } else {
1807  $outputPage->addHTML( $error );
1808  }
1809  }
1810  }
1811 
1812  /* Caching functions */
1813 
1821  protected function tryFileCache() {
1822  static $called = false;
1823 
1824  if ( $called ) {
1825  wfDebug( "Article::tryFileCache(): called twice!?\n" );
1826  return false;
1827  }
1828 
1829  $called = true;
1830  if ( $this->isFileCacheable() ) {
1831  $cache = HTMLFileCache::newFromTitle( $this->getTitle(), 'view' );
1832  if ( $cache->isCacheGood( $this->mPage->getTouched() ) ) {
1833  wfDebug( "Article::tryFileCache(): about to load file\n" );
1834  $cache->loadFromFileCache( $this->getContext() );
1835  return true;
1836  } else {
1837  wfDebug( "Article::tryFileCache(): starting buffer\n" );
1838  ob_start( array( &$cache, 'saveToFileCache' ) );
1839  }
1840  } else {
1841  wfDebug( "Article::tryFileCache(): not cacheable\n" );
1842  }
1843 
1844  return false;
1845  }
1846 
1851  public function isFileCacheable() {
1852  $cacheable = false;
1853 
1854  if ( HTMLFileCache::useFileCache( $this->getContext() ) ) {
1855  $cacheable = $this->mPage->getID()
1856  && !$this->mRedirectedFrom && !$this->getTitle()->isRedirect();
1857  // Extension may have reason to disable file caching on some pages.
1858  if ( $cacheable ) {
1859  $cacheable = wfRunHooks( 'IsFileCacheable', array( &$this ) );
1860  }
1861  }
1862 
1863  return $cacheable;
1864  }
1865 
1879  public function getParserOutput( $oldid = null, User $user = null ) {
1880  //XXX: bypasses mParserOptions and thus setParserOptions()
1881 
1882  if ( $user === null ) {
1883  $parserOptions = $this->getParserOptions();
1884  } else {
1885  $parserOptions = $this->mPage->makeParserOptions( $user );
1886  }
1887 
1888  return $this->mPage->getParserOutput( $parserOptions, $oldid );
1889  }
1890 
1897  public function setParserOptions( ParserOptions $options ) {
1898  if ( $this->mParserOptions ) {
1899  throw new MWException( "can't change parser options after they have already been set" );
1900  }
1901 
1902  // clone, so if $options is modified later, it doesn't confuse the parser cache.
1903  $this->mParserOptions = clone $options;
1904  }
1905 
1910  public function getParserOptions() {
1911  if ( !$this->mParserOptions ) {
1912  $this->mParserOptions = $this->mPage->makeParserOptions( $this->getContext() );
1913  }
1914  // Clone to allow modifications of the return value without affecting cache
1915  return clone $this->mParserOptions;
1916  }
1917 
1924  public function setContext( $context ) {
1925  $this->mContext = $context;
1926  }
1927 
1934  public function getContext() {
1935  if ( $this->mContext instanceof IContextSource ) {
1937  } else {
1938  wfDebug( __METHOD__ . " called and \$mContext is null. " .
1939  "Return RequestContext::getMain(); for sanity\n" );
1940  return RequestContext::getMain();
1941  }
1942  }
1943 
1948  public function info() {
1949  wfDeprecated( __METHOD__, '1.19' );
1950  Action::factory( 'info', $this )->show();
1951  }
1952 
1958  public function purge() {
1959  return Action::factory( 'purge', $this )->show();
1960  }
1961 
1966  public function revert() {
1967  wfDeprecated( __METHOD__, '1.19' );
1968  Action::factory( 'revert', $this )->show();
1969  }
1970 
1975  public function rollback() {
1976  wfDeprecated( __METHOD__, '1.19' );
1977  Action::factory( 'rollback', $this )->show();
1978  }
1979 
1986  public function __get( $fname ) {
1987  if ( property_exists( $this->mPage, $fname ) ) {
1988  #wfWarn( "Access to raw $fname field " . __CLASS__ );
1989  return $this->mPage->$fname;
1990  }
1991  trigger_error( 'Inaccessible property via __get(): ' . $fname, E_USER_NOTICE );
1992  }
1993 
2001  public function __set( $fname, $fvalue ) {
2002  if ( property_exists( $this->mPage, $fname ) ) {
2003  #wfWarn( "Access to raw $fname field of " . __CLASS__ );
2004  $this->mPage->$fname = $fvalue;
2005  // Note: extensions may want to toss on new fields
2006  } elseif ( !in_array( $fname, array( 'mContext', 'mPage' ) ) ) {
2007  $this->mPage->$fname = $fvalue;
2008  } else {
2009  trigger_error( 'Inaccessible property via __set(): ' . $fname, E_USER_NOTICE );
2010  }
2011  }
2012 
2021  public function __call( $fname, $args ) {
2022  if ( is_callable( array( $this->mPage, $fname ) ) ) {
2023  #wfWarn( "Call to " . __CLASS__ . "::$fname; please use WikiPage instead" );
2024  return call_user_func_array( array( $this->mPage, $fname ), $args );
2025  }
2026  trigger_error( 'Inaccessible function via __call(): ' . $fname, E_USER_ERROR );
2027  }
2028 
2029  // ****** B/C functions to work-around PHP silliness with __call and references ****** //
2030 
2039  public function doUpdateRestrictions( array $limit, array $expiry, &$cascade,
2040  $reason, User $user
2041  ) {
2042  return $this->mPage->doUpdateRestrictions( $limit, $expiry, $cascade, $reason, $user );
2043  }
2044 
2052  public function updateRestrictions( $limit = array(), $reason = '',
2053  &$cascade = 0, $expiry = array()
2054  ) {
2055  return $this->mPage->doUpdateRestrictions(
2056  $limit,
2057  $expiry,
2058  $cascade,
2059  $reason,
2060  $this->getContext()->getUser()
2061  );
2062  }
2063 
2072  public function doDeleteArticle( $reason, $suppress = false, $id = 0,
2073  $commit = true, &$error = ''
2074  ) {
2075  return $this->mPage->doDeleteArticle( $reason, $suppress, $id, $commit, $error );
2076  }
2077 
2087  public function doRollback( $fromP, $summary, $token, $bot, &$resultDetails, User $user = null ) {
2088  $user = is_null( $user ) ? $this->getContext()->getUser() : $user;
2089  return $this->mPage->doRollback( $fromP, $summary, $token, $bot, $resultDetails, $user );
2090  }
2091 
2100  public function commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser = null ) {
2101  $guser = is_null( $guser ) ? $this->getContext()->getUser() : $guser;
2102  return $this->mPage->commitRollback( $fromP, $summary, $bot, $resultDetails, $guser );
2103  }
2104 
2109  public function generateReason( &$hasHistory ) {
2110  $title = $this->mPage->getTitle();
2111  $handler = ContentHandler::getForTitle( $title );
2112  return $handler->getAutoDeleteReason( $title, $hasHistory );
2113  }
2114 
2115  // ****** B/C functions for static methods ( __callStatic is PHP>=5.3 ) ****** //
2116 
2120  public static function selectFields() {
2121  return WikiPage::selectFields();
2122  }
2123 
2127  public static function onArticleCreate( $title ) {
2129  }
2130 
2134  public static function onArticleDelete( $title ) {
2136  }
2137 
2141  public static function onArticleEdit( $title ) {
2143  }
2144 
2152  public static function getAutosummary( $oldtext, $newtext, $flags ) {
2153  return WikiPage::getAutosummary( $oldtext, $newtext, $flags );
2154  }
2155  // ******
2156 }
ReadOnlyError
Show an error when the wiki is locked/read-only and the user tries to do something that requires writ...
Definition: ReadOnlyError.php:28
Article\__call
__call( $fname, $args)
Use PHP's magic __call handler to transform instance calls to WikiPage functions for backwards compat...
Definition: Article.php:2009
Xml\checkLabel
static checkLabel( $label, $name, $id, $checked=false, $attribs=array())
Convenience function to build an HTML checkbox with a label.
Definition: Xml.php:433
Article\showMissingArticle
showMissingArticle()
Show the error text for a missing article.
Definition: Article.php:1179
ContentHandler\deprecated
static deprecated( $func, $version, $component=false)
Logs a deprecation warning, visible if $wgDevelopmentWarnings, but only if self::$enableDeprecationWa...
Definition: ContentHandler.php:1030
ParserOptions
Set options of the Parser.
Definition: ParserOptions.php:31
Title\makeTitle
static & makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:398
Page
Abstract class for type hinting (accepts WikiPage, Article, ImagePage, CategoryPage)
Definition: WikiPage.php:26
Revision\DELETED_RESTRICTED
const DELETED_RESTRICTED
Definition: Revision.php:68
WikiPage\onArticleCreate
static onArticleCreate( $title)
The onArticle*() functions are supposed to be a kind of hooks which should be called whenever any of ...
Definition: WikiPage.php:3066
Article\showCssOrJsPage
showCssOrJsPage( $showCacheHint=true)
Show a page view for a page formatted as CSS or JavaScript.
Definition: Article.php:833
ParserOutput
Definition: ParserOutput.php:24
Article\formatRobotPolicy
static formatRobotPolicy( $policy)
Converts a String robot policy into an associative array, to allow merging of several policies using ...
Definition: Article.php:947
php
skin txt MediaWiki includes four core it has been set as the default in MediaWiki since the replacing Monobook it had been been the default skin since before being replaced by Vector largely rewritten in while keeping its appearance Several legacy skins were removed in the as the burden of supporting them became too heavy to bear Those in etc for skin dependent CSS etc for skin dependent JavaScript These can also be customised on a per user by etc This feature has led to a wide variety of user styles becoming that gallery is a good place to ending in php
Definition: skin.txt:62
Article\view
view()
This is the default action of the index.php entry point: just view the page of the given title.
Definition: Article.php:504
Revision\newFromId
static newFromId( $id, $flags=0)
Load a page revision from a given revision ID number.
Definition: Revision.php:88
Article\purge
purge()
Handle action=purge.
Definition: Article.php:1946
Xml\tags
static tags( $element, $attribs=null, $contents)
Same as Xml::element(), but does not escape contents.
Definition: Xml.php:131
Article\onArticleCreate
static onArticleCreate( $title)
Definition: Article.php:2115
Article\getOldIDFromRequest
getOldIDFromRequest()
Sets $this->mRedirectUrl to a correct URL if the query parameters are incorrect.
Definition: Article.php:297
Article\showPatrolFooter
showPatrolFooter()
If patrol is possible, output a patrol UI box.
Definition: Article.php:1064
is
We use the convention $dbr for read and $dbw for write to help you keep track of whether the database object is a the world will explode Or to be a subsequent write query which succeeded on the master may fail when replicated to the slave due to a unique key collision Replication on the slave will stop and it may take hours to repair the database and get it back online Setting read_only in my cnf on the slave will avoid this but given the dire we prefer to have as many checks as possible We provide a but the wrapper functions like please read the documentation for except in special pages derived from QueryPage It s a common pitfall for new developers to submit code containing SQL queries which examine huge numbers of rows Remember that COUNT * is(N), counting rows in atable is like counting beans in a bucket.------------------------------------------------------------------------ Replication------------------------------------------------------------------------The largest installation of MediaWiki, Wikimedia, uses a large set ofslave MySQL servers replicating writes made to a master MySQL server. Itis important to understand the issues associated with this setup if youwant to write code destined for Wikipedia.It 's often the case that the best algorithm to use for a given taskdepends on whether or not replication is in use. Due to our unabashedWikipedia-centrism, we often just use the replication-friendly version, but if you like, you can use wfGetLB() ->getServerCount() > 1 tocheck to see if replication is in use.===Lag===Lag primarily occurs when large write queries are sent to the master.Writes on the master are executed in parallel, but they are executed inserial when they are replicated to the slaves. The master writes thequery to the binlog when the transaction is committed. The slaves pollthe binlog and start executing the query as soon as it appears. They canservice reads while they are performing a write query, but will not readanything more from the binlog and thus will perform no more writes. Thismeans that if the write query runs for a long time, the slaves will lagbehind the master for the time it takes for the write query to complete.Lag can be exacerbated by high read load. MediaWiki 's load balancer willstop sending reads to a slave when it is lagged by more than 30 seconds.If the load ratios are set incorrectly, or if there is too much loadgenerally, this may lead to a slave permanently hovering around 30seconds lag.If all slaves are lagged by more than 30 seconds, MediaWiki will stopwriting to the database. All edits and other write operations will berefused, with an error returned to the user. This gives the slaves achance to catch up. Before we had this mechanism, the slaves wouldregularly lag by several minutes, making review of recent editsdifficult.In addition to this, MediaWiki attempts to ensure that the user seesevents occurring on the wiki in chronological order. A few seconds of lagcan be tolerated, as long as the user sees a consistent picture fromsubsequent requests. This is done by saving the master binlog positionin the session, and then at the start of each request, waiting for theslave to catch up to that position before doing any reads from it. Ifthis wait times out, reads are allowed anyway, but the request isconsidered to be in "lagged slave mode". Lagged slave mode can bechecked by calling wfGetLB() ->getLaggedSlaveMode(). The onlypractical consequence at present is a warning displayed in the pagefooter.===Lag avoidance===To avoid excessive lag, queries which write large numbers of rows shouldbe split up, generally to write one row at a time. Multi-row INSERT ...SELECT queries are the worst offenders should be avoided altogether.Instead do the select first and then the insert.===Working with lag===Despite our best efforts, it 's not practical to guarantee a low-lagenvironment. Lag will usually be less than one second, but mayoccasionally be up to 30 seconds. For scalability, it 's very importantto keep load on the master low, so simply sending all your queries tothe master is not the answer. So when you have a genuine need forup-to-date data, the following approach is advised:1) Do a quick query to the master for a sequence number or timestamp 2) Run the full query on the slave and check if it matches the data you gotfrom the master 3) If it doesn 't, run the full query on the masterTo avoid swamping the master every time the slaves lag, use of thisapproach should be kept to a minimum. In most cases you should just readfrom the slave and let the user deal with the delay.------------------------------------------------------------------------ Lock contention------------------------------------------------------------------------Due to the high write rate on Wikipedia(and some other wikis), MediaWiki developers need to be very careful to structure their writesto avoid long-lasting locks. By default, MediaWiki opens a transactionat the first query, and commits it before the output is sent. Locks willbe held from the time when the query is done until the commit. So youcan reduce lock time by doing as much processing as possible before youdo your write queries.Often this approach is not good enough, and it becomes necessary toenclose small groups of queries in their own transaction. Use thefollowing syntax:$dbw=wfGetDB(DB_MASTER
Article\tryFileCache
tryFileCache()
checkLastModified returns true if it has taken care of all output to the client that is necessary for...
Definition: Article.php:1809
wfGetDB
& wfGetDB( $db, $groups=array(), $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:3706
text
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add text
Definition: design.txt:12
$timestamp
if( $limit) $timestamp
Definition: importImages.php:104
$form
usually copyright or history_copyright This message must be in HTML not wikitext $subpages will be ignored and the rest of subPageSubtitle() will run. 'SkinTemplateBuildNavUrlsNav_urlsAfterPermalink' whether MediaWiki currently thinks this is a CSS JS page Hooks may change this value to override the return value of Title::isCssOrJsPage(). 'TitleIsAlwaysKnown' whether MediaWiki currently thinks this page is known isMovable() always returns false. $title whether MediaWiki currently thinks this page is movable Hooks may change this value to override the return value of Title::isMovable(). 'TitleIsWikitextPage' whether MediaWiki currently thinks this is a wikitext page Hooks may change this value to override the return value of Title::isWikitextPage() 'TitleMove' use UploadVerification and UploadVerifyFile instead $form
Definition: hooks.txt:2578
WikiPage\getAutosummary
static getAutosummary( $oldtext, $newtext, $flags)
Return an applicable autosummary if one exists for the given edit.
Definition: WikiPage.php:3213
Article\$mParserOutput
ParserOutput $mParserOutput
ParserOutput object $mParserOutput.
Definition: Article.php:101
$pass
$pass
Definition: UtfNormalGenerate.php:131
wfProfileIn
wfProfileIn( $functionname)
Begin profiling of a function.
Definition: Profiler.php:33
Article\getAutosummary
static getAutosummary( $oldtext, $newtext, $flags)
Definition: Article.php:2140
CategoryPage
Special handling for category description pages, showing pages, subcategories and file that belong to...
Definition: CategoryPage.php:28
WikiPage\onArticleEdit
static onArticleEdit( $title)
Purge caches on page update etc.
Definition: WikiPage.php:3134
$from
$from
Definition: importImages.php:90
WikiPage
Class representing a MediaWiki article and history.
Definition: WikiPage.php:37
RecentChange\newFromConds
static newFromConds( $conds, $fname=__METHOD__, $options=array())
Find the first recent change matching some specific conditions.
Definition: RecentChange.php:136
$fname
if(!defined( 'MEDIAWIKI')) $fname
This file is not a valid entry point, perform no further processing unless MEDIAWIKI is defined.
Definition: Setup.php:35
Article\getRobotPolicy
getRobotPolicy( $action, $pOutput=null)
Get the robot policy to be used for the current view.
Definition: Article.php:867
PoolWorkArticleView
Definition: WikiPage.php:3476
NS_FILE
const NS_FILE
Definition: Defines.php:85
$limit
if( $sleep) $limit
Definition: importImages.php:99
Article\updateRestrictions
updateRestrictions( $limit=array(), $reason='', &$cascade=0, $expiry=array())
Definition: Article.php:2040
Block\newFromTarget
static newFromTarget( $specificTarget, $vagueTarget=null, $fromMaster=false)
Given a target and the target's type, get an existing Block object if possible.
Definition: Block.php:970
wfReadOnly
wfReadOnly()
Check whether the wiki is in read-only mode.
Definition: GlobalFunctions.php:1360
User\newFromName
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:389
Article\showDiffPage
showDiffPage()
Show a diff page according to current request variables.
Definition: Article.php:784
ContentHandler\getForTitle
static getForTitle(Title $title)
Returns the appropriate ContentHandler singleton for the given title.
Definition: ContentHandler.php:259
Html\hidden
static hidden( $name, $value, $attribs=array())
Convenience function to produce an input element with type=hidden.
Definition: Html.php:665
ImagePage
Class for viewing MediaWiki file description pages.
Definition: ImagePage.php:28
PermissionsError
Show an error when a user tries to do something they do not have the necessary permissions for.
Definition: PermissionsError.php:28
Article\confirmDelete
confirmDelete( $reason)
Output deletion confirmation dialog.
Definition: Article.php:1641
Article\$mContext
IContextSource $mContext
The context this Article is executed in $mContext.
Definition: Article.php:44
$flags
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition: hooks.txt:2118
$link
set to $title object and return false for a match for latest after cache objects are set use the ContentHandler facility to handle CSS and JavaScript for highlighting & $link
Definition: hooks.txt:2154
Article\adjustDisplayTitle
adjustDisplayTitle(ParserOutput $pOutput)
Adjust title for pages with displaytitle, -{T|}- or language conversion.
Definition: Article.php:770
Article\showNamespaceHeader
showNamespaceHeader()
Show a header specific to the namespace currently being viewed, like [[MediaWiki:Talkpagetext]].
Definition: Article.php:1027
Xml\openElement
static openElement( $element, $attribs=null)
This opens an XML element.
Definition: Xml.php:109
Article\$mContentObject
Content $mContentObject
Content of the revision we are working on.
Definition: Article.php:65
ProtectionForm
Handles the page protection UI and backend.
Definition: ProtectionForm.php:29
Linker\linkKnown
static linkKnown( $target, $html=null, $customAttribs=array(), $query=array(), $options=array( 'known', 'noclasses'))
Identical to link(), except $options defaults to 'known'.
Definition: Linker.php:264
Linker\makeExternalLink
static makeExternalLink( $url, $text, $escape=true, $linktype='', $attribs=array(), $title=null)
Make an external link.
Definition: Linker.php:1034
Article\info
info()
Info about this page.
Definition: Article.php:1936
ContentHandler\runLegacyHooks
static runLegacyHooks( $event, $args=array(), $warn=null)
Call a legacy hook that uses text instead of Content objects.
Definition: ContentHandler.php:1053
Article\isFileCacheable
isFileCacheable()
Check if the page can be cached.
Definition: Article.php:1839
$dbr
$dbr
Definition: testCompression.php:48
Linker\link
static link( $target, $html=null, $customAttribs=array(), $query=array(), $options=array())
This function returns an HTML link to the given target.
Definition: Linker.php:192
Revision\FOR_THIS_USER
const FOR_THIS_USER
Definition: Revision.php:73
Revision
Definition: Revision.php:26
Article\unprotect
unprotect()
action=unprotect handler (alias)
Definition: Article.php:1524
Article\fetchContentObject
fetchContentObject()
Get text content object Does NOT follow redirects.
Definition: Article.php:399
Article\fetchContent
fetchContent()
Get text of an article from database Does NOT follow redirects.
Definition: Article.php:367
Article\clear
clear()
Clear the object.
Definition: Article.php:211
Article\newFromWikiPage
static newFromWikiPage(WikiPage $page, IContextSource $context)
Create an Article object of the appropriate class for the given page.
Definition: Article.php:174
wfGetMainCache
wfGetMainCache()
Get the main cache object.
Definition: GlobalFunctions.php:4014
Article\onArticleDelete
static onArticleDelete( $title)
Definition: Article.php:2122
MWException
MediaWiki exception.
Definition: MWException.php:26
wfMemcKey
wfMemcKey()
Get a cache key.
Definition: GlobalFunctions.php:3627
$out
$out
Definition: UtfNormalGenerate.php:167
Article\getTitle
getTitle()
Get the title object of the article.
Definition: Article.php:194
Article\revert
revert()
Handle action=revert.
Definition: Article.php:1954
Article\render
render()
Handle action=render.
Definition: Article.php:1507
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
Definition: GlobalFunctions.php:1174
Html\element
static element( $element, $attribs=array(), $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:148
wfIncrStats
wfIncrStats( $key, $count=1)
Increment a statistics counter.
Definition: GlobalFunctions.php:1351
Article\setRedirectedFrom
setRedirectedFrom(Title $from)
Tell the page view functions that this view was redirected from another page on the wiki.
Definition: Article.php:185
HTMLFileCache\useFileCache
static useFileCache(IContextSource $context)
Check if pages can be cached for this request/user.
Definition: HTMLFileCache.php:90
WikiPage\selectFields
static selectFields()
Return the list of revision fields that should be selected to create a new page.
Definition: WikiPage.php:271
Linker\revUserTools
static revUserTools( $rev, $isPublic=false)
Generate a user tool link cluster if the current user is allowed to view it.
Definition: Linker.php:1219
Article\__construct
__construct(Title $title, $oldId=null)
Constructor and clear the article.
Definition: Article.php:110
Article\showDeletedRevisionHeader
showDeletedRevisionHeader()
If the revision requested for view is deleted, check permissions.
Definition: Article.php:1278
WikiPage\onArticleDelete
static onArticleDelete( $title)
Clears caches when article is deleted.
Definition: WikiPage.php:3087
Article\doRollback
doRollback( $fromP, $summary, $token, $bot, &$resultDetails, User $user=null)
Definition: Article.php:2075
WikiPage\getTitle
getTitle()
Get the title object of the article.
Definition: WikiPage.php:221
wfProfileOut
wfProfileOut( $functionname='missing')
Stop profiling of a function.
Definition: Profiler.php:46
LogPage
Class to simplify the use of log pages.
Definition: LogPage.php:32
wfMessage
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return an< a > element with HTML attributes $attribs and contents $html will be returned If you return $ret will be returned and may include noclasses after processing after in associative array form externallinks including delete and has completed for all link tables default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock() - offset Set to overwrite offset parameter in $wgRequest set to '' to unset offset - wrap String Wrap the message in html(usually something like "&lt
Article\setOldSubtitle
setOldSubtitle( $oldid=0)
Generate the navigation links when browsing through an article revisions It shows the information as:...
Definition: Article.php:1321
Xml\element
static element( $element, $attribs=null, $contents='', $allowShortTag=true)
Format an XML element with given attributes and, optionally, text content.
Definition: Xml.php:39
Article\getPage
getPage()
Get the WikiPage object of this instance.
Definition: Article.php:204
Article\getContext
getContext()
Gets the context this Article is executed in.
Definition: Article.php:1922
wfRunHooks
wfRunHooks( $event, array $args=array(), $deprecatedVersion=null)
Call hook functions defined in $wgHooks.
Definition: GlobalFunctions.php:4058
User\isIP
static isIP( $name)
Does the string match an anonymous IPv4 address?
Definition: User.php:555
WatchAction\doWatchOrUnwatch
static doWatchOrUnwatch( $watch, Title $title, User $user)
Watch or unwatch a page.
Definition: WatchAction.php:106
array
the array() calling protocol came about after MediaWiki 1.4rc1.
List of Api Query prop modules.
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
Article\newFromID
static newFromID( $id)
Constructor from a page id.
Definition: Article.php:128
Article\newPage
newPage(Title $title)
Definition: Article.php:119
NS_CATEGORY
const NS_CATEGORY
Definition: Defines.php:93
Article\$mRevIdFetched
int $mRevIdFetched
Revision ID of revision we are working on $mRevIdFetched.
Definition: Article.php:91
Html\input
static input( $name, $value='', $type='text', $attribs=array())
Convenience function to produce an "<input>" element.
Definition: Html.php:648
Article\isCurrent
isCurrent()
Returns true if the currently-referenced revision is the current edit to this page (and it exists).
Definition: Article.php:465
list
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition: deferred.txt:11
ContentHandler\makeContent
static makeContent( $text, Title $title=null, $modelId=null, $format=null)
Convenience function for creating a Content object from a given textual representation.
Definition: ContentHandler.php:144
Article\$mContentLoaded
bool $mContentLoaded
Is the content ($mContent) already loaded? $mContentLoaded.
Definition: Article.php:70
Article\setParserOptions
setParserOptions(ParserOptions $options)
Override the ParserOptions used to render the primary article wikitext.
Definition: Article.php:1885
Article\$mRedirectedFrom
Title $mRedirectedFrom
Title from which we were redirected here $mRedirectedFrom.
Definition: Article.php:81
$options
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Definition: hooks.txt:1530
Article\loadContent
loadContent()
Load the revision (including text) into this object.
Definition: Article.php:348
Article\doDelete
doDelete( $reason, $suppress=false)
Perform a deletion and output success or failure messages.
Definition: Article.php:1761
ParserOutput\getTitleText
getTitleText()
Definition: ParserOutput.php:124
wfDebug
wfDebug( $text, $dest='all')
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:980
$title
presenting them properly to the user as errors is done by the caller $title
Definition: hooks.txt:1324
NS_USER_TALK
const NS_USER_TALK
Definition: Defines.php:82
Article\getContent
getContent()
Note that getContent/loadContent do not follow redirects anymore.
Definition: Article.php:233
user
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such and we might be restricted by PHP settings such as safe mode or open_basedir We cannot assume that the software even has read access anywhere useful Many shared hosts run all users web applications under the same user
Definition: distributors.txt:9
ParserCache\singleton
static singleton()
Get an instance of this object.
Definition: ParserCache.php:35
Article\$mOldId
int null $mOldId
The oldid of the article that is to be shown, 0 for the current revision $mOldId.
Definition: Article.php:76
NS_MEDIA
const NS_MEDIA
Definition: Defines.php:67
Linker\getRevDeleteLink
static getRevDeleteLink(User $user, Revision $rev, Title $title)
Get a revision-deletion link, or disabled link, or nothing, depending on user permissions & the setti...
Definition: Linker.php:2155
Article\onArticleEdit
static onArticleEdit( $title)
Definition: Article.php:2129
Article\protect
protect()
action=protect handler
Definition: Article.php:1516
HTMLFileCache\newFromTitle
static newFromTitle( $title, $action)
Construct an ObjectFileCache from a Title and an action.
Definition: HTMLFileCache.php:39
wfEscapeWikiText
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
Definition: GlobalFunctions.php:2124
Article\commitRollback
commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser=null)
Definition: Article.php:2088
Article\rollback
rollback()
Handle action=rollback.
Definition: Article.php:1963
Article\getParserOutput
getParserOutput( $oldid=null, User $user=null)
#-
Definition: Article.php:1867
Article\$mParserOptions
ParserOptions $mParserOptions
ParserOptions object for $wgUser articles $mParserOptions.
Definition: Article.php:54
Article\getContentObject
getContentObject()
Returns a Content object representing the pages effective display content, not necessarily the revisi...
Definition: Article.php:254
RequestContext\getMain
static getMain()
Static methods.
Definition: RequestContext.php:420
IP\isValid
static isValid( $ip)
Validate an IP address.
Definition: IP.php:108
$user
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a account $user
Definition: hooks.txt:237
Article\$mRedirectUrl
string false $mRedirectUrl
URL to redirect to or false if none $mRedirectUrl.
Definition: Article.php:86
IContextSource
Interface for objects which can provide a context on request.
Definition: IContextSource.php:29
Article\__get
__get( $fname)
Use PHP's magic __get handler to handle accessing of raw WikiPage fields for backwards compatibility.
Definition: Article.php:1974
$summary
$summary
Definition: importImages.php:120
Content
Base interface for content objects.
Definition: Content.php:34
$rev
presenting them properly to the user as errors is done by the caller return true use this to change the list i e etc $rev
Definition: hooks.txt:1337
$args
if( $line===false) $args
Definition: cdb.php:62
Article\doDeleteArticle
doDeleteArticle( $reason, $suppress=false, $id=0, $commit=true, &$error='')
Definition: Article.php:2060
DB_SLAVE
const DB_SLAVE
Definition: Defines.php:55
Title
Represents a title within MediaWiki.
Definition: Title.php:35
Article\selectFields
static selectFields()
Definition: Article.php:2108
ContentHandler\getContentText
static getContentText(Content $content=null)
Convenience function for getting flat text from a Content object.
Definition: ContentHandler.php:94
Xml\closeElement
static closeElement( $element)
Shortcut to close an XML element.
Definition: Xml.php:118
$cache
$cache
Definition: mcc.php:32
Xml\listDropDown
static listDropDown( $name='', $list='', $other='', $selected='', $class='', $tabindex=null)
Build a drop-down box from a textual list.
Definition: Xml.php:497
$dir
if(count( $args)==0) $dir
Definition: importImages.php:49
Article\getRevIdFetched
getRevIdFetched()
Use this to fetch the rev ID used on page views.
Definition: Article.php:492
MessageContent
Wrapper allowing us to handle a system message as a Content object.
Definition: MessageContent.php:36
in
Prior to maintenance scripts were a hodgepodge of code that had no cohesion or formal method of action Beginning in
Definition: maintenance.txt:1
Article\showRedirectedFromHeader
showRedirectedFromHeader()
If this request is a redirect view, send "redirected from" subtitle to the output.
Definition: Article.php:976
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
Block
Definition: Block.php:22
RecentChange\isInRCLifespan
static isInRCLifespan( $timestamp, $tolerance=0)
Check whether the given timestamp is new enough to have a RC row with a given tolerance as the recent...
Definition: RecentChange.php:877
NS_USER
const NS_USER
Definition: Defines.php:81
Article\doUpdateRestrictions
doUpdateRestrictions(array $limit, array $expiry, &$cascade, $reason, User $user)
Definition: Article.php:2027
Xml\submitButton
static submitButton( $value, $attribs=array())
Convenience function to build an HTML submit button.
Definition: Xml.php:463
Article\$mRevision
Revision $mRevision
Revision we are working on $mRevision.
Definition: Article.php:96
Article\getParserOptions
getParserOptions()
Get parser options suitable for rendering the primary article wikitext.
Definition: Article.php:1898
NS_MEDIAWIKI
const NS_MEDIAWIKI
Definition: Defines.php:87
$t
$t
Definition: testCompression.php:65
Article
Class for viewing MediaWiki article and history.
Definition: Article.php:36
$error
usually copyright or history_copyright This message must be in HTML not wikitext $subpages will be ignored and the rest of subPageSubtitle() will run. 'SkinTemplateBuildNavUrlsNav_urlsAfterPermalink' whether MediaWiki currently thinks this is a CSS JS page Hooks may change this value to override the return value of Title::isCssOrJsPage(). 'TitleIsAlwaysKnown' whether MediaWiki currently thinks this page is known isMovable() always returns false. $title whether MediaWiki currently thinks this page is movable Hooks may change this value to override the return value of Title::isMovable(). 'TitleIsWikitextPage' whether MediaWiki currently thinks this is a wikitext page Hooks may change this value to override the return value of Title::isWikitextPage() 'TitleMove' use UploadVerification and UploadVerifyFile instead where the first element is the message key and the remaining elements are used as parameters to the message based on mime etc Preferred in most cases over UploadVerification object with all info about the upload string as detected by MediaWiki Handlers will typically only apply for specific mime types object & $error
Definition: hooks.txt:2578
Xml\label
static label( $label, $id, $attribs=array())
Convenience function to build an HTML form label.
Definition: Xml.php:374
Article\getOldID
getOldID()
Definition: Article.php:284
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:59
Article\setContext
setContext( $context)
Sets the context this Article is executed in.
Definition: Article.php:1912
Title\newFromID
static newFromID( $id, $flags=0)
Create a new Title from an article ID.
Definition: Title.php:297
$article
Using a hook running we can avoid having all this option specific stuff in our mainline code Using the function array $article
Definition: hooks.txt:78
Article\getRevisionFetched
getRevisionFetched()
Get the fetched Revision object depending on request parameters or null on failure.
Definition: Article.php:481
Article\viewRedirect
viewRedirect( $target, $appendSubtitle=true, $forceKnown=false)
View redirect.
Definition: Article.php:1446
Revision\DELETED_TEXT
const DELETED_TEXT
Definition: Revision.php:65
Action\factory
static factory( $action, Page $page, IContextSource $context=null)
Get an appropriate Action subclass for the given action.
Definition: Action.php:88
Article\$mContent
string $mContent
Text of the revision we are working on $mContent.
Definition: Article.php:59
Article\__set
__set( $fname, $fvalue)
Use PHP's magic __set handler to handle setting of raw WikiPage fields for backwards compatibility.
Definition: Article.php:1989
ParserOutput\getIndexPolicy
getIndexPolicy()
Definition: ParserOutput.php:143
Article\showViewFooter
showViewFooter()
Show the footer section of an ordinary page view.
Definition: Article.php:1041
Article\$mPage
WikiPage $mPage
The WikiPage object of this instance $mPage.
Definition: Article.php:49
$e
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException' returning false will NOT prevent logging $e
Definition: hooks.txt:1632
LogEventsList\showLogExtract
static showLogExtract(&$out, $types=array(), $page='', $user='', $param=array())
Show log extract.
Definition: LogEventsList.php:507
Article\newFromTitle
static newFromTitle( $title, IContextSource $context)
Create an Article object of the appropriate class for the given page.
Definition: Article.php:142
Article\generateReason
generateReason(&$hasHistory)
Definition: Article.php:2097