MediaWiki  1.23.6
WikiPage.php
Go to the documentation of this file.
1 <?php
26 interface Page {
27 }
28 
37 class WikiPage implements Page, IDBAccessObject {
38  // Constants for $mDataLoadedFrom and related
39 
43  public $mTitle = null;
44 
48  public $mDataLoaded = false; // !< Boolean
49  public $mIsRedirect = false; // !< Boolean
50  public $mLatest = false; // !< Integer (false means "not loaded")
54  public $mPreparedEdit = false;
55 
59  protected $mId = null;
60 
65 
69  protected $mRedirectTarget = null;
70 
74  protected $mLastRevision = null;
75 
79  protected $mTimestamp = '';
80 
84  protected $mTouched = '19700101000000';
85 
89  protected $mLinksUpdated = '19700101000000';
90 
94  protected $mCounter = null;
95 
100  public function __construct( Title $title ) {
101  $this->mTitle = $title;
102  }
103 
112  public static function factory( Title $title ) {
113  $ns = $title->getNamespace();
114 
115  if ( $ns == NS_MEDIA ) {
116  throw new MWException( "NS_MEDIA is a virtual namespace; use NS_FILE." );
117  } elseif ( $ns < 0 ) {
118  throw new MWException( "Invalid or virtual namespace $ns given." );
119  }
120 
121  switch ( $ns ) {
122  case NS_FILE:
123  $page = new WikiFilePage( $title );
124  break;
125  case NS_CATEGORY:
126  $page = new WikiCategoryPage( $title );
127  break;
128  default:
129  $page = new WikiPage( $title );
130  }
131 
132  return $page;
133  }
134 
145  public static function newFromID( $id, $from = 'fromdb' ) {
146  // page id's are never 0 or negative, see bug 61166
147  if ( $id < 1 ) {
148  return null;
149  }
150 
152  $db = wfGetDB( $from === self::READ_LATEST ? DB_MASTER : DB_SLAVE );
153  $row = $db->selectRow( 'page', self::selectFields(), array( 'page_id' => $id ), __METHOD__ );
154  if ( !$row ) {
155  return null;
156  }
157  return self::newFromRow( $row, $from );
158  }
159 
172  public static function newFromRow( $row, $from = 'fromdb' ) {
173  $page = self::factory( Title::newFromRow( $row ) );
174  $page->loadFromRow( $row, $from );
175  return $page;
176  }
177 
184  private static function convertSelectType( $type ) {
185  switch ( $type ) {
186  case 'fromdb':
187  return self::READ_NORMAL;
188  case 'fromdbmaster':
189  return self::READ_LATEST;
190  case 'forupdate':
191  return self::READ_LOCKING;
192  default:
193  // It may already be an integer or whatever else
194  return $type;
195  }
196  }
197 
208  public function getActionOverrides() {
209  $content_handler = $this->getContentHandler();
210  return $content_handler->getActionOverrides();
211  }
212 
222  public function getContentHandler() {
224  }
225 
230  public function getTitle() {
231  return $this->mTitle;
232  }
233 
238  public function clear() {
239  $this->mDataLoaded = false;
240  $this->mDataLoadedFrom = self::READ_NONE;
241 
242  $this->clearCacheFields();
243  }
244 
249  protected function clearCacheFields() {
250  $this->mId = null;
251  $this->mCounter = null;
252  $this->mRedirectTarget = null; // Title object if set
253  $this->mLastRevision = null; // Latest revision
254  $this->mTouched = '19700101000000';
255  $this->mLinksUpdated = '19700101000000';
256  $this->mTimestamp = '';
257  $this->mIsRedirect = false;
258  $this->mLatest = false;
259  // Bug 57026: do not clear mPreparedEdit since prepareTextForEdit() already checks
260  // the requested rev ID and content against the cached one for equality. For most
261  // content types, the output should not change during the lifetime of this cache.
262  // Clearing it can cause extra parses on edit for no reason.
263  }
264 
270  public function clearPreparedEdit() {
271  $this->mPreparedEdit = false;
272  }
273 
280  public static function selectFields() {
281  global $wgContentHandlerUseDB;
282 
283  $fields = array(
284  'page_id',
285  'page_namespace',
286  'page_title',
287  'page_restrictions',
288  'page_counter',
289  'page_is_redirect',
290  'page_is_new',
291  'page_random',
292  'page_touched',
293  'page_links_updated',
294  'page_latest',
295  'page_len',
296  );
297 
298  if ( $wgContentHandlerUseDB ) {
299  $fields[] = 'page_content_model';
300  }
301 
302  return $fields;
303  }
304 
312  protected function pageData( $dbr, $conditions, $options = array() ) {
313  $fields = self::selectFields();
314 
315  wfRunHooks( 'ArticlePageDataBefore', array( &$this, &$fields ) );
316 
317  $row = $dbr->selectRow( 'page', $fields, $conditions, __METHOD__, $options );
318 
319  wfRunHooks( 'ArticlePageDataAfter', array( &$this, &$row ) );
320 
321  return $row;
322  }
323 
333  public function pageDataFromTitle( $dbr, $title, $options = array() ) {
334  return $this->pageData( $dbr, array(
335  'page_namespace' => $title->getNamespace(),
336  'page_title' => $title->getDBkey() ), $options );
337  }
338 
347  public function pageDataFromId( $dbr, $id, $options = array() ) {
348  return $this->pageData( $dbr, array( 'page_id' => $id ), $options );
349  }
350 
363  public function loadPageData( $from = 'fromdb' ) {
365  if ( is_int( $from ) && $from <= $this->mDataLoadedFrom ) {
366  // We already have the data from the correct location, no need to load it twice.
367  return;
368  }
369 
370  if ( $from === self::READ_LOCKING ) {
371  $data = $this->pageDataFromTitle( wfGetDB( DB_MASTER ), $this->mTitle, array( 'FOR UPDATE' ) );
372  } elseif ( $from === self::READ_LATEST ) {
373  $data = $this->pageDataFromTitle( wfGetDB( DB_MASTER ), $this->mTitle );
374  } elseif ( $from === self::READ_NORMAL ) {
375  $data = $this->pageDataFromTitle( wfGetDB( DB_SLAVE ), $this->mTitle );
376  // Use a "last rev inserted" timestamp key to diminish the issue of slave lag.
377  // Note that DB also stores the master position in the session and checks it.
378  $touched = $this->getCachedLastEditTime();
379  if ( $touched ) { // key set
380  if ( !$data || $touched > wfTimestamp( TS_MW, $data->page_touched ) ) {
382  $data = $this->pageDataFromTitle( wfGetDB( DB_MASTER ), $this->mTitle );
383  }
384  }
385  } else {
386  // No idea from where the caller got this data, assume slave database.
387  $data = $from;
389  }
390 
391  $this->loadFromRow( $data, $from );
392  }
393 
406  public function loadFromRow( $data, $from ) {
407  $lc = LinkCache::singleton();
408  $lc->clearLink( $this->mTitle );
409 
410  if ( $data ) {
411  $lc->addGoodLinkObjFromRow( $this->mTitle, $data );
412 
413  $this->mTitle->loadFromRow( $data );
414 
415  // Old-fashioned restrictions
416  $this->mTitle->loadRestrictions( $data->page_restrictions );
417 
418  $this->mId = intval( $data->page_id );
419  $this->mCounter = intval( $data->page_counter );
420  $this->mTouched = wfTimestamp( TS_MW, $data->page_touched );
421  $this->mLinksUpdated = wfTimestampOrNull( TS_MW, $data->page_links_updated );
422  $this->mIsRedirect = intval( $data->page_is_redirect );
423  $this->mLatest = intval( $data->page_latest );
424  // Bug 37225: $latest may no longer match the cached latest Revision object.
425  // Double-check the ID of any cached latest Revision object for consistency.
426  if ( $this->mLastRevision && $this->mLastRevision->getId() != $this->mLatest ) {
427  $this->mLastRevision = null;
428  $this->mTimestamp = '';
429  }
430  } else {
431  $lc->addBadLinkObj( $this->mTitle );
432 
433  $this->mTitle->loadFromRow( false );
434 
435  $this->clearCacheFields();
436 
437  $this->mId = 0;
438  }
439 
440  $this->mDataLoaded = true;
441  $this->mDataLoadedFrom = self::convertSelectType( $from );
442  }
443 
447  public function getId() {
448  if ( !$this->mDataLoaded ) {
449  $this->loadPageData();
450  }
451  return $this->mId;
452  }
453 
457  public function exists() {
458  if ( !$this->mDataLoaded ) {
459  $this->loadPageData();
460  }
461  return $this->mId > 0;
462  }
463 
472  public function hasViewableContent() {
473  return $this->exists() || $this->mTitle->isAlwaysKnown();
474  }
475 
479  public function getCount() {
480  if ( !$this->mDataLoaded ) {
481  $this->loadPageData();
482  }
483 
484  return $this->mCounter;
485  }
486 
492  public function isRedirect() {
493  $content = $this->getContent();
494  if ( !$content ) {
495  return false;
496  }
497 
498  return $content->isRedirect();
499  }
500 
511  public function getContentModel() {
512  if ( $this->exists() ) {
513  // look at the revision's actual content model
514  $rev = $this->getRevision();
515 
516  if ( $rev !== null ) {
517  return $rev->getContentModel();
518  } else {
519  $title = $this->mTitle->getPrefixedDBkey();
520  wfWarn( "Page $title exists but has no (visible) revisions!" );
521  }
522  }
523 
524  // use the default model for this page
525  return $this->mTitle->getContentModel();
526  }
527 
532  public function checkTouched() {
533  if ( !$this->mDataLoaded ) {
534  $this->loadPageData();
535  }
536  return !$this->mIsRedirect;
537  }
538 
543  public function getTouched() {
544  if ( !$this->mDataLoaded ) {
545  $this->loadPageData();
546  }
547  return $this->mTouched;
548  }
549 
554  public function getLinksTimestamp() {
555  if ( !$this->mDataLoaded ) {
556  $this->loadPageData();
557  }
558  return $this->mLinksUpdated;
559  }
560 
565  public function getLatest() {
566  if ( !$this->mDataLoaded ) {
567  $this->loadPageData();
568  }
569  return (int)$this->mLatest;
570  }
571 
576  public function getOldestRevision() {
577  wfProfileIn( __METHOD__ );
578 
579  // Try using the slave database first, then try the master
580  $continue = 2;
581  $db = wfGetDB( DB_SLAVE );
582  $revSelectFields = Revision::selectFields();
583 
584  $row = null;
585  while ( $continue ) {
586  $row = $db->selectRow(
587  array( 'page', 'revision' ),
588  $revSelectFields,
589  array(
590  'page_namespace' => $this->mTitle->getNamespace(),
591  'page_title' => $this->mTitle->getDBkey(),
592  'rev_page = page_id'
593  ),
594  __METHOD__,
595  array(
596  'ORDER BY' => 'rev_timestamp ASC'
597  )
598  );
599 
600  if ( $row ) {
601  $continue = 0;
602  } else {
603  $db = wfGetDB( DB_MASTER );
604  $continue--;
605  }
606  }
607 
608  wfProfileOut( __METHOD__ );
609  return $row ? Revision::newFromRow( $row ) : null;
610  }
611 
616  protected function loadLastEdit() {
617  if ( $this->mLastRevision !== null ) {
618  return; // already loaded
619  }
620 
621  $latest = $this->getLatest();
622  if ( !$latest ) {
623  return; // page doesn't exist or is missing page_latest info
624  }
625 
626  // Bug 37225: if session S1 loads the page row FOR UPDATE, the result always includes the
627  // latest changes committed. This is true even within REPEATABLE-READ transactions, where
628  // S1 normally only sees changes committed before the first S1 SELECT. Thus we need S1 to
629  // also gets the revision row FOR UPDATE; otherwise, it may not find it since a page row
630  // UPDATE and revision row INSERT by S2 may have happened after the first S1 SELECT.
631  // http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html#isolevel_repeatable-read.
632  $flags = ( $this->mDataLoadedFrom == self::READ_LOCKING ) ? Revision::READ_LOCKING : 0;
633  $revision = Revision::newFromPageId( $this->getId(), $latest, $flags );
634  if ( $revision ) { // sanity
635  $this->setLastEdit( $revision );
636  }
637  }
638 
642  protected function setLastEdit( Revision $revision ) {
643  $this->mLastRevision = $revision;
644  $this->mTimestamp = $revision->getTimestamp();
645  }
646 
651  public function getRevision() {
652  $this->loadLastEdit();
653  if ( $this->mLastRevision ) {
654  return $this->mLastRevision;
655  }
656  return null;
657  }
658 
672  public function getContent( $audience = Revision::FOR_PUBLIC, User $user = null ) {
673  $this->loadLastEdit();
674  if ( $this->mLastRevision ) {
675  return $this->mLastRevision->getContent( $audience, $user );
676  }
677  return null;
678  }
679 
692  public function getText( $audience = Revision::FOR_PUBLIC, User $user = null ) { // @todo deprecated, replace usage!
693  ContentHandler::deprecated( __METHOD__, '1.21' );
694 
695  $this->loadLastEdit();
696  if ( $this->mLastRevision ) {
697  return $this->mLastRevision->getText( $audience, $user );
698  }
699  return false;
700  }
701 
708  public function getRawText() {
709  ContentHandler::deprecated( __METHOD__, '1.21' );
710 
711  return $this->getText( Revision::RAW );
712  }
713 
717  public function getTimestamp() {
718  // Check if the field has been filled by WikiPage::setTimestamp()
719  if ( !$this->mTimestamp ) {
720  $this->loadLastEdit();
721  }
722 
723  return wfTimestamp( TS_MW, $this->mTimestamp );
724  }
725 
731  public function setTimestamp( $ts ) {
732  $this->mTimestamp = wfTimestamp( TS_MW, $ts );
733  }
734 
744  public function getUser( $audience = Revision::FOR_PUBLIC, User $user = null ) {
745  $this->loadLastEdit();
746  if ( $this->mLastRevision ) {
747  return $this->mLastRevision->getUser( $audience, $user );
748  } else {
749  return -1;
750  }
751  }
752 
763  public function getCreator( $audience = Revision::FOR_PUBLIC, User $user = null ) {
764  $revision = $this->getOldestRevision();
765  if ( $revision ) {
766  $userName = $revision->getUserText( $audience, $user );
767  return User::newFromName( $userName, false );
768  } else {
769  return null;
770  }
771  }
772 
782  public function getUserText( $audience = Revision::FOR_PUBLIC, User $user = null ) {
783  $this->loadLastEdit();
784  if ( $this->mLastRevision ) {
785  return $this->mLastRevision->getUserText( $audience, $user );
786  } else {
787  return '';
788  }
789  }
790 
800  public function getComment( $audience = Revision::FOR_PUBLIC, User $user = null ) {
801  $this->loadLastEdit();
802  if ( $this->mLastRevision ) {
803  return $this->mLastRevision->getComment( $audience, $user );
804  } else {
805  return '';
806  }
807  }
808 
814  public function getMinorEdit() {
815  $this->loadLastEdit();
816  if ( $this->mLastRevision ) {
817  return $this->mLastRevision->isMinor();
818  } else {
819  return false;
820  }
821  }
822 
828  protected function getCachedLastEditTime() {
829  global $wgMemc;
830  $key = wfMemcKey( 'page-lastedit', md5( $this->mTitle->getPrefixedDBkey() ) );
831  return $wgMemc->get( $key );
832  }
833 
840  public function setCachedLastEditTime( $timestamp ) {
841  global $wgMemc;
842  $key = wfMemcKey( 'page-lastedit', md5( $this->mTitle->getPrefixedDBkey() ) );
843  $wgMemc->set( $key, wfTimestamp( TS_MW, $timestamp ), 60 * 15 );
844  }
845 
854  public function isCountable( $editInfo = false ) {
855  global $wgArticleCountMethod;
856 
857  if ( !$this->mTitle->isContentPage() ) {
858  return false;
859  }
860 
861  if ( $editInfo ) {
862  $content = $editInfo->pstContent;
863  } else {
864  $content = $this->getContent();
865  }
866 
867  if ( !$content || $content->isRedirect() ) {
868  return false;
869  }
870 
871  $hasLinks = null;
872 
873  if ( $wgArticleCountMethod === 'link' ) {
874  // nasty special case to avoid re-parsing to detect links
875 
876  if ( $editInfo ) {
877  // ParserOutput::getLinks() is a 2D array of page links, so
878  // to be really correct we would need to recurse in the array
879  // but the main array should only have items in it if there are
880  // links.
881  $hasLinks = (bool)count( $editInfo->output->getLinks() );
882  } else {
883  $hasLinks = (bool)wfGetDB( DB_SLAVE )->selectField( 'pagelinks', 1,
884  array( 'pl_from' => $this->getId() ), __METHOD__ );
885  }
886  }
887 
888  return $content->isCountable( $hasLinks );
889  }
890 
898  public function getRedirectTarget() {
899  if ( !$this->mTitle->isRedirect() ) {
900  return null;
901  }
902 
903  if ( $this->mRedirectTarget !== null ) {
904  return $this->mRedirectTarget;
905  }
906 
907  // Query the redirect table
908  $dbr = wfGetDB( DB_SLAVE );
909  $row = $dbr->selectRow( 'redirect',
910  array( 'rd_namespace', 'rd_title', 'rd_fragment', 'rd_interwiki' ),
911  array( 'rd_from' => $this->getId() ),
912  __METHOD__
913  );
914 
915  // rd_fragment and rd_interwiki were added later, populate them if empty
916  if ( $row && !is_null( $row->rd_fragment ) && !is_null( $row->rd_interwiki ) ) {
917  $this->mRedirectTarget = Title::makeTitle(
918  $row->rd_namespace, $row->rd_title,
919  $row->rd_fragment, $row->rd_interwiki );
920  return $this->mRedirectTarget;
921  }
922 
923  // This page doesn't have an entry in the redirect table
924  $this->mRedirectTarget = $this->insertRedirect();
926  }
927 
934  public function insertRedirect() {
935  // recurse through to only get the final target
936  $content = $this->getContent();
937  $retval = $content ? $content->getUltimateRedirectTarget() : null;
938  if ( !$retval ) {
939  return null;
940  }
942  return $retval;
943  }
944 
950  public function insertRedirectEntry( $rt ) {
951  $dbw = wfGetDB( DB_MASTER );
952  $dbw->replace( 'redirect', array( 'rd_from' ),
953  array(
954  'rd_from' => $this->getId(),
955  'rd_namespace' => $rt->getNamespace(),
956  'rd_title' => $rt->getDBkey(),
957  'rd_fragment' => $rt->getFragment(),
958  'rd_interwiki' => $rt->getInterwiki(),
959  ),
960  __METHOD__
961  );
962  }
963 
969  public function followRedirect() {
970  return $this->getRedirectURL( $this->getRedirectTarget() );
971  }
972 
980  public function getRedirectURL( $rt ) {
981  if ( !$rt ) {
982  return false;
983  }
984 
985  if ( $rt->isExternal() ) {
986  if ( $rt->isLocal() ) {
987  // Offsite wikis need an HTTP redirect.
988  //
989  // This can be hard to reverse and may produce loops,
990  // so they may be disabled in the site configuration.
991  $source = $this->mTitle->getFullURL( 'redirect=no' );
992  return $rt->getFullURL( array( 'rdfrom' => $source ) );
993  } else {
994  // External pages pages without "local" bit set are not valid
995  // redirect targets
996  return false;
997  }
998  }
999 
1000  if ( $rt->isSpecialPage() ) {
1001  // Gotta handle redirects to special pages differently:
1002  // Fill the HTTP response "Location" header and ignore
1003  // the rest of the page we're on.
1004  //
1005  // Some pages are not valid targets
1006  if ( $rt->isValidRedirectTarget() ) {
1007  return $rt->getFullURL();
1008  } else {
1009  return false;
1010  }
1011  }
1013  return $rt;
1014  }
1015 
1021  public function getContributors() {
1022  // @todo FIXME: This is expensive; cache this info somewhere.
1023 
1024  $dbr = wfGetDB( DB_SLAVE );
1025 
1026  if ( $dbr->implicitGroupby() ) {
1027  $realNameField = 'user_real_name';
1028  } else {
1029  $realNameField = 'MIN(user_real_name) AS user_real_name';
1030  }
1031 
1032  $tables = array( 'revision', 'user' );
1033 
1034  $fields = array(
1035  'user_id' => 'rev_user',
1036  'user_name' => 'rev_user_text',
1037  $realNameField,
1038  'timestamp' => 'MAX(rev_timestamp)',
1039  );
1040 
1041  $conds = array( 'rev_page' => $this->getId() );
1042 
1043  // The user who made the top revision gets credited as "this page was last edited by
1044  // John, based on contributions by Tom, Dick and Harry", so don't include them twice.
1045  $user = $this->getUser();
1046  if ( $user ) {
1047  $conds[] = "rev_user != $user";
1048  } else {
1049  $conds[] = "rev_user_text != {$dbr->addQuotes( $this->getUserText() )}";
1050  }
1051 
1052  $conds[] = "{$dbr->bitAnd( 'rev_deleted', Revision::DELETED_USER )} = 0"; // username hidden?
1053 
1054  $jconds = array(
1055  'user' => array( 'LEFT JOIN', 'rev_user = user_id' ),
1056  );
1057 
1058  $options = array(
1059  'GROUP BY' => array( 'rev_user', 'rev_user_text' ),
1060  'ORDER BY' => 'timestamp DESC',
1061  );
1062 
1063  $res = $dbr->select( $tables, $fields, $conds, __METHOD__, $options, $jconds );
1064  return new UserArrayFromResult( $res );
1065  }
1066 
1073  public function getLastNAuthors( $num, $revLatest = 0 ) {
1074  wfProfileIn( __METHOD__ );
1075  // First try the slave
1076  // If that doesn't have the latest revision, try the master
1077  $continue = 2;
1078  $db = wfGetDB( DB_SLAVE );
1079 
1080  do {
1081  $res = $db->select( array( 'page', 'revision' ),
1082  array( 'rev_id', 'rev_user_text' ),
1083  array(
1084  'page_namespace' => $this->mTitle->getNamespace(),
1085  'page_title' => $this->mTitle->getDBkey(),
1086  'rev_page = page_id'
1087  ), __METHOD__,
1088  array(
1089  'ORDER BY' => 'rev_timestamp DESC',
1090  'LIMIT' => $num
1091  )
1092  );
1093 
1094  if ( !$res ) {
1095  wfProfileOut( __METHOD__ );
1096  return array();
1097  }
1098 
1099  $row = $db->fetchObject( $res );
1100 
1101  if ( $continue == 2 && $revLatest && $row->rev_id != $revLatest ) {
1102  $db = wfGetDB( DB_MASTER );
1103  $continue--;
1104  } else {
1105  $continue = 0;
1106  }
1107  } while ( $continue );
1108 
1109  $authors = array( $row->rev_user_text );
1110 
1111  foreach ( $res as $row ) {
1112  $authors[] = $row->rev_user_text;
1113  }
1114 
1115  wfProfileOut( __METHOD__ );
1116  return $authors;
1117  }
1118 
1126  public function isParserCacheUsed( ParserOptions $parserOptions, $oldid ) {
1127  global $wgEnableParserCache;
1128 
1129  return $wgEnableParserCache
1130  && $parserOptions->getStubThreshold() == 0
1131  && $this->exists()
1132  && ( $oldid === null || $oldid === 0 || $oldid === $this->getLatest() )
1133  && $this->getContentHandler()->isParserCacheSupported();
1134  }
1135 
1147  public function getParserOutput( ParserOptions $parserOptions, $oldid = null ) {
1148  wfProfileIn( __METHOD__ );
1149 
1150  $useParserCache = $this->isParserCacheUsed( $parserOptions, $oldid );
1151  wfDebug( __METHOD__ . ': using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" );
1152  if ( $parserOptions->getStubThreshold() ) {
1153  wfIncrStats( 'pcache_miss_stub' );
1154  }
1155 
1156  if ( $useParserCache ) {
1157  $parserOutput = ParserCache::singleton()->get( $this, $parserOptions );
1158  if ( $parserOutput !== false ) {
1159  wfProfileOut( __METHOD__ );
1160  return $parserOutput;
1161  }
1162  }
1163 
1164  if ( $oldid === null || $oldid === 0 ) {
1165  $oldid = $this->getLatest();
1166  }
1167 
1168  $pool = new PoolWorkArticleView( $this, $parserOptions, $oldid, $useParserCache );
1169  $pool->execute();
1170 
1171  wfProfileOut( __METHOD__ );
1173  return $pool->getParserOutput();
1174  }
1175 
1181  public function doViewUpdates( User $user, $oldid = 0 ) {
1182  global $wgDisableCounters;
1183  if ( wfReadOnly() ) {
1184  return;
1185  }
1186 
1187  // Don't update page view counters on views from bot users (bug 14044)
1188  if ( !$wgDisableCounters && !$user->isAllowed( 'bot' ) && $this->exists() ) {
1190  DeferredUpdates::addUpdate( new SiteStatsUpdate( 1, 0, 0 ) );
1191  }
1193  // Update newtalk / watchlist notification status
1194  $user->clearNotification( $this->mTitle, $oldid );
1195  }
1196 
1201  public function doPurge() {
1202  global $wgUseSquid;
1203 
1204  if ( !wfRunHooks( 'ArticlePurge', array( &$this ) ) ) {
1205  return false;
1206  }
1207 
1208  // Invalidate the cache
1209  $this->mTitle->invalidateCache();
1210 
1211  if ( $wgUseSquid ) {
1212  // Commit the transaction before the purge is sent
1213  $dbw = wfGetDB( DB_MASTER );
1214  $dbw->commit( __METHOD__ );
1215 
1216  // Send purge
1217  $update = SquidUpdate::newSimplePurge( $this->mTitle );
1218  $update->doUpdate();
1219  }
1220 
1221  if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
1222  // @todo move this logic to MessageCache
1223 
1224  if ( $this->exists() ) {
1225  // NOTE: use transclusion text for messages.
1226  // This is consistent with MessageCache::getMsgFromNamespace()
1227 
1228  $content = $this->getContent();
1229  $text = $content === null ? null : $content->getWikitextForTransclusion();
1230 
1231  if ( $text === null ) {
1232  $text = false;
1233  }
1234  } else {
1235  $text = false;
1236  }
1237 
1238  MessageCache::singleton()->replace( $this->mTitle->getDBkey(), $text );
1239  }
1240  return true;
1241  }
1242 
1253  public function insertOn( $dbw ) {
1254  wfProfileIn( __METHOD__ );
1255 
1256  $page_id = $dbw->nextSequenceValue( 'page_page_id_seq' );
1257  $dbw->insert( 'page', array(
1258  'page_id' => $page_id,
1259  'page_namespace' => $this->mTitle->getNamespace(),
1260  'page_title' => $this->mTitle->getDBkey(),
1261  'page_counter' => 0,
1262  'page_restrictions' => '',
1263  'page_is_redirect' => 0, // Will set this shortly...
1264  'page_is_new' => 1,
1265  'page_random' => wfRandom(),
1266  'page_touched' => $dbw->timestamp(),
1267  'page_latest' => 0, // Fill this in shortly...
1268  'page_len' => 0, // Fill this in shortly...
1269  ), __METHOD__, 'IGNORE' );
1270 
1271  $affected = $dbw->affectedRows();
1272 
1273  if ( $affected ) {
1274  $newid = $dbw->insertId();
1275  $this->mId = $newid;
1276  $this->mTitle->resetArticleID( $newid );
1277  }
1278  wfProfileOut( __METHOD__ );
1279 
1280  return $affected ? $newid : false;
1281  }
1282 
1298  public function updateRevisionOn( $dbw, $revision, $lastRevision = null, $lastRevIsRedirect = null ) {
1299  global $wgContentHandlerUseDB;
1300 
1301  wfProfileIn( __METHOD__ );
1302 
1303  $content = $revision->getContent();
1304  $len = $content ? $content->getSize() : 0;
1305  $rt = $content ? $content->getUltimateRedirectTarget() : null;
1306 
1307  $conditions = array( 'page_id' => $this->getId() );
1308 
1309  if ( !is_null( $lastRevision ) ) {
1310  // An extra check against threads stepping on each other
1311  $conditions['page_latest'] = $lastRevision;
1312  }
1313 
1314  $now = wfTimestampNow();
1315  $row = array( /* SET */
1316  'page_latest' => $revision->getId(),
1317  'page_touched' => $dbw->timestamp( $now ),
1318  'page_is_new' => ( $lastRevision === 0 ) ? 1 : 0,
1319  'page_is_redirect' => $rt !== null ? 1 : 0,
1320  'page_len' => $len,
1321  );
1322 
1323  if ( $wgContentHandlerUseDB ) {
1324  $row['page_content_model'] = $revision->getContentModel();
1325  }
1326 
1327  $dbw->update( 'page',
1328  $row,
1329  $conditions,
1330  __METHOD__ );
1331 
1332  $result = $dbw->affectedRows() > 0;
1333  if ( $result ) {
1334  $this->updateRedirectOn( $dbw, $rt, $lastRevIsRedirect );
1335  $this->setLastEdit( $revision );
1336  $this->setCachedLastEditTime( $now );
1337  $this->mLatest = $revision->getId();
1338  $this->mIsRedirect = (bool)$rt;
1339  // Update the LinkCache.
1340  LinkCache::singleton()->addGoodLinkObj( $this->getId(), $this->mTitle, $len, $this->mIsRedirect,
1341  $this->mLatest, $revision->getContentModel() );
1342  }
1343 
1344  wfProfileOut( __METHOD__ );
1345  return $result;
1346  }
1347 
1359  public function updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect = null ) {
1360  // Always update redirects (target link might have changed)
1361  // Update/Insert if we don't know if the last revision was a redirect or not
1362  // Delete if changing from redirect to non-redirect
1363  $isRedirect = !is_null( $redirectTitle );
1364 
1365  if ( !$isRedirect && $lastRevIsRedirect === false ) {
1366  return true;
1367  }
1368 
1369  wfProfileIn( __METHOD__ );
1370  if ( $isRedirect ) {
1371  $this->insertRedirectEntry( $redirectTitle );
1372  } else {
1373  // This is not a redirect, remove row from redirect table
1374  $where = array( 'rd_from' => $this->getId() );
1375  $dbw->delete( 'redirect', $where, __METHOD__ );
1376  }
1377 
1378  if ( $this->getTitle()->getNamespace() == NS_FILE ) {
1379  RepoGroup::singleton()->getLocalRepo()->invalidateImageRedirect( $this->getTitle() );
1380  }
1381  wfProfileOut( __METHOD__ );
1382 
1383  return ( $dbw->affectedRows() != 0 );
1384  }
1394  public function updateIfNewerOn( $dbw, $revision ) {
1395  wfProfileIn( __METHOD__ );
1396 
1397  $row = $dbw->selectRow(
1398  array( 'revision', 'page' ),
1399  array( 'rev_id', 'rev_timestamp', 'page_is_redirect' ),
1400  array(
1401  'page_id' => $this->getId(),
1402  'page_latest=rev_id' ),
1403  __METHOD__ );
1404 
1405  if ( $row ) {
1406  if ( wfTimestamp( TS_MW, $row->rev_timestamp ) >= $revision->getTimestamp() ) {
1407  wfProfileOut( __METHOD__ );
1408  return false;
1409  }
1410  $prev = $row->rev_id;
1411  $lastRevIsRedirect = (bool)$row->page_is_redirect;
1412  } else {
1413  // No or missing previous revision; mark the page as new
1414  $prev = 0;
1415  $lastRevIsRedirect = null;
1416  }
1417 
1418  $ret = $this->updateRevisionOn( $dbw, $revision, $prev, $lastRevIsRedirect );
1419 
1420  wfProfileOut( __METHOD__ );
1421  return $ret;
1422  }
1423 
1434  public function getUndoContent( Revision $undo, Revision $undoafter = null ) {
1435  $handler = $undo->getContentHandler();
1436  return $handler->getUndoContent( $this->getRevision(), $undo, $undoafter );
1437  }
1438 
1448  public function getUndoText( Revision $undo, Revision $undoafter = null ) {
1449  ContentHandler::deprecated( __METHOD__, '1.21' );
1450 
1451  $this->loadLastEdit();
1452 
1453  if ( $this->mLastRevision ) {
1454  if ( is_null( $undoafter ) ) {
1455  $undoafter = $undo->getPrevious();
1456  }
1457 
1458  $handler = $this->getContentHandler();
1459  $undone = $handler->getUndoContent( $this->mLastRevision, $undo, $undoafter );
1460 
1461  if ( !$undone ) {
1462  return false;
1463  } else {
1464  return ContentHandler::getContentText( $undone );
1465  }
1466  }
1467 
1468  return false;
1469  }
1470 
1482  public function replaceSection( $section, $text, $sectionTitle = '', $edittime = null ) {
1483  ContentHandler::deprecated( __METHOD__, '1.21' );
1484 
1485  if ( strval( $section ) == '' ) { //NOTE: keep condition in sync with condition in replaceSectionContent!
1486  // Whole-page edit; let the whole text through
1487  return $text;
1488  }
1489 
1490  if ( !$this->supportsSections() ) {
1491  throw new MWException( "sections not supported for content model " .
1492  $this->getContentHandler()->getModelID() );
1493  }
1494 
1495  // could even make section title, but that's not required.
1497 
1498  $newContent = $this->replaceSectionContent( $section, $sectionContent, $sectionTitle,
1499  $edittime );
1500 
1501  return ContentHandler::getContentText( $newContent );
1502  }
1512  public function supportsSections() {
1513  return $this->getContentHandler()->supportsSections();
1514  }
1515 
1527  public function replaceSectionContent( $section, Content $sectionContent, $sectionTitle = '',
1528  $edittime = null ) {
1529  wfProfileIn( __METHOD__ );
1530 
1531  if ( strval( $section ) == '' ) {
1532  // Whole-page edit; let the whole text through
1533  $newContent = $sectionContent;
1534  } else {
1535  if ( !$this->supportsSections() ) {
1536  wfProfileOut( __METHOD__ );
1537  throw new MWException( "sections not supported for content model " .
1538  $this->getContentHandler()->getModelID() );
1539  }
1540 
1541  // Bug 30711: always use current version when adding a new section
1542  if ( is_null( $edittime ) || $section == 'new' ) {
1543  $oldContent = $this->getContent();
1544  } else {
1545  $dbw = wfGetDB( DB_MASTER );
1546  $rev = Revision::loadFromTimestamp( $dbw, $this->mTitle, $edittime );
1547 
1548  if ( !$rev ) {
1549  wfDebug( "WikiPage::replaceSection asked for bogus section (page: " .
1550  $this->getId() . "; section: $section; edittime: $edittime)\n" );
1551  wfProfileOut( __METHOD__ );
1552  return null;
1553  }
1554 
1555  $oldContent = $rev->getContent();
1556  }
1557 
1558  if ( ! $oldContent ) {
1559  wfDebug( __METHOD__ . ": no page text\n" );
1560  wfProfileOut( __METHOD__ );
1561  return null;
1562  }
1563 
1564  // FIXME: $oldContent might be null?
1565  $newContent = $oldContent->replaceSection( $section, $sectionContent, $sectionTitle );
1566  }
1567 
1568  wfProfileOut( __METHOD__ );
1569  return $newContent;
1570  }
1571 
1577  public function checkFlags( $flags ) {
1578  if ( !( $flags & EDIT_NEW ) && !( $flags & EDIT_UPDATE ) ) {
1579  if ( $this->exists() ) {
1580  $flags |= EDIT_UPDATE;
1581  } else {
1582  $flags |= EDIT_NEW;
1583  }
1584  }
1585 
1586  return $flags;
1587  }
1588 
1638  public function doEdit( $text, $summary, $flags = 0, $baseRevId = false, $user = null ) {
1639  ContentHandler::deprecated( __METHOD__, '1.21' );
1640 
1641  $content = ContentHandler::makeContent( $text, $this->getTitle() );
1642 
1643  return $this->doEditContent( $content, $summary, $flags, $baseRevId, $user );
1644  }
1645 
1694  public function doEditContent( Content $content, $summary, $flags = 0, $baseRevId = false,
1695  User $user = null, $serialisation_format = null
1696  ) {
1697  global $wgUser, $wgUseAutomaticEditSummaries, $wgUseRCPatrol, $wgUseNPPatrol;
1698 
1699  // Low-level sanity check
1700  if ( $this->mTitle->getText() === '' ) {
1701  throw new MWException( 'Something is trying to edit an article with an empty title' );
1702  }
1703 
1704  wfProfileIn( __METHOD__ );
1705 
1706  if ( !$content->getContentHandler()->canBeUsedOn( $this->getTitle() ) ) {
1707  wfProfileOut( __METHOD__ );
1708  return Status::newFatal( 'content-not-allowed-here',
1710  $this->getTitle()->getPrefixedText() );
1711  }
1712 
1713  $user = is_null( $user ) ? $wgUser : $user;
1714  $status = Status::newGood( array() );
1715 
1716  // Load the data from the master database if needed.
1717  // The caller may already loaded it from the master or even loaded it using
1718  // SELECT FOR UPDATE, so do not override that using clear().
1719  $this->loadPageData( 'fromdbmaster' );
1720 
1721  $flags = $this->checkFlags( $flags );
1722 
1723  // handle hook
1724  $hook_args = array( &$this, &$user, &$content, &$summary,
1725  $flags & EDIT_MINOR, null, null, &$flags, &$status );
1726 
1727  if ( !wfRunHooks( 'PageContentSave', $hook_args )
1728  || !ContentHandler::runLegacyHooks( 'ArticleSave', $hook_args ) ) {
1729 
1730  wfDebug( __METHOD__ . ": ArticleSave or ArticleSaveContent hook aborted save!\n" );
1731 
1732  if ( $status->isOK() ) {
1733  $status->fatal( 'edit-hook-aborted' );
1734  }
1735 
1736  wfProfileOut( __METHOD__ );
1737  return $status;
1738  }
1739 
1740  // Silently ignore EDIT_MINOR if not allowed
1741  $isminor = ( $flags & EDIT_MINOR ) && $user->isAllowed( 'minoredit' );
1742  $bot = $flags & EDIT_FORCE_BOT;
1743 
1744  $old_content = $this->getContent( Revision::RAW ); // current revision's content
1745 
1746  $oldsize = $old_content ? $old_content->getSize() : 0;
1747  $oldid = $this->getLatest();
1748  $oldIsRedirect = $this->isRedirect();
1749  $oldcountable = $this->isCountable();
1750 
1751  $handler = $content->getContentHandler();
1752 
1753  // Provide autosummaries if one is not provided and autosummaries are enabled.
1754  if ( $wgUseAutomaticEditSummaries && $flags & EDIT_AUTOSUMMARY && $summary == '' ) {
1755  if ( !$old_content ) {
1756  $old_content = null;
1757  }
1758  $summary = $handler->getAutosummary( $old_content, $content, $flags );
1759  }
1760 
1761  $editInfo = $this->prepareContentForEdit( $content, null, $user, $serialisation_format );
1762  $serialized = $editInfo->pst;
1763 
1767  $content = $editInfo->pstContent;
1768  $newsize = $content->getSize();
1769 
1770  $dbw = wfGetDB( DB_MASTER );
1771  $now = wfTimestampNow();
1772  $this->mTimestamp = $now;
1773 
1774  if ( $flags & EDIT_UPDATE ) {
1775  // Update article, but only if changed.
1776  $status->value['new'] = false;
1777 
1778  if ( !$oldid ) {
1779  // Article gone missing
1780  wfDebug( __METHOD__ . ": EDIT_UPDATE specified but article doesn't exist\n" );
1781  $status->fatal( 'edit-gone-missing' );
1782 
1783  wfProfileOut( __METHOD__ );
1784  return $status;
1785  } elseif ( !$old_content ) {
1786  // Sanity check for bug 37225
1787  wfProfileOut( __METHOD__ );
1788  throw new MWException( "Could not find text for current revision {$oldid}." );
1789  }
1790 
1791  $revision = new Revision( array(
1792  'page' => $this->getId(),
1793  'title' => $this->getTitle(), // for determining the default content model
1794  'comment' => $summary,
1795  'minor_edit' => $isminor,
1796  'text' => $serialized,
1797  'len' => $newsize,
1798  'parent_id' => $oldid,
1799  'user' => $user->getId(),
1800  'user_text' => $user->getName(),
1801  'timestamp' => $now,
1802  'content_model' => $content->getModel(),
1803  'content_format' => $serialisation_format,
1804  ) ); // XXX: pass content object?!
1805 
1806  $changed = !$content->equals( $old_content );
1807 
1808  if ( $changed ) {
1809  if ( !$content->isValid() ) {
1810  wfProfileOut( __METHOD__ );
1811  throw new MWException( "New content failed validity check!" );
1812  }
1813 
1814  $dbw->begin( __METHOD__ );
1815  try {
1816 
1817  $prepStatus = $content->prepareSave( $this, $flags, $baseRevId, $user );
1818  $status->merge( $prepStatus );
1819 
1820  if ( !$status->isOK() ) {
1821  $dbw->rollback( __METHOD__ );
1822 
1823  wfProfileOut( __METHOD__ );
1824  return $status;
1825  }
1826  $revisionId = $revision->insertOn( $dbw );
1827 
1828  // Update page
1829  //
1830  // Note that we use $this->mLatest instead of fetching a value from the master DB
1831  // during the course of this function. This makes sure that EditPage can detect
1832  // edit conflicts reliably, either by $ok here, or by $article->getTimestamp()
1833  // before this function is called. A previous function used a separate query, this
1834  // creates a window where concurrent edits can cause an ignored edit conflict.
1835  $ok = $this->updateRevisionOn( $dbw, $revision, $oldid, $oldIsRedirect );
1836 
1837  if ( !$ok ) {
1838  // Belated edit conflict! Run away!!
1839  $status->fatal( 'edit-conflict' );
1840 
1841  $dbw->rollback( __METHOD__ );
1842 
1843  wfProfileOut( __METHOD__ );
1844  return $status;
1845  }
1846 
1847  wfRunHooks( 'NewRevisionFromEditComplete', array( $this, $revision, $baseRevId, $user ) );
1848  // Update recentchanges
1849  if ( !( $flags & EDIT_SUPPRESS_RC ) ) {
1850  // Mark as patrolled if the user can do so
1851  $patrolled = $wgUseRCPatrol && !count(
1852  $this->mTitle->getUserPermissionsErrors( 'autopatrol', $user ) );
1853  // Add RC row to the DB
1854  $rc = RecentChange::notifyEdit( $now, $this->mTitle, $isminor, $user, $summary,
1855  $oldid, $this->getTimestamp(), $bot, '', $oldsize, $newsize,
1856  $revisionId, $patrolled
1857  );
1858 
1859  // Log auto-patrolled edits
1860  if ( $patrolled ) {
1861  PatrolLog::record( $rc, true, $user );
1862  }
1863  }
1864  $user->incEditCount();
1865  } catch ( MWException $e ) {
1866  $dbw->rollback( __METHOD__ );
1867  // Question: Would it perhaps be better if this method turned all
1868  // exceptions into $status's?
1869  throw $e;
1870  }
1871  $dbw->commit( __METHOD__ );
1872  } else {
1873  // Bug 32948: revision ID must be set to page {{REVISIONID}} and
1874  // related variables correctly
1875  $revision->setId( $this->getLatest() );
1876  }
1877 
1878  // Update links tables, site stats, etc.
1879  $this->doEditUpdates(
1880  $revision,
1881  $user,
1882  array(
1883  'changed' => $changed,
1884  'oldcountable' => $oldcountable
1885  )
1886  );
1887 
1888  if ( !$changed ) {
1889  $status->warning( 'edit-no-change' );
1890  $revision = null;
1891  // Update page_touched, this is usually implicit in the page update
1892  // Other cache updates are done in onArticleEdit()
1893  $this->mTitle->invalidateCache();
1894  }
1895  } else {
1896  // Create new article
1897  $status->value['new'] = true;
1898 
1899  $dbw->begin( __METHOD__ );
1900  try {
1901 
1902  $prepStatus = $content->prepareSave( $this, $flags, $baseRevId, $user );
1903  $status->merge( $prepStatus );
1904 
1905  if ( !$status->isOK() ) {
1906  $dbw->rollback( __METHOD__ );
1907 
1908  wfProfileOut( __METHOD__ );
1909  return $status;
1910  }
1911 
1912  $status->merge( $prepStatus );
1913 
1914  // Add the page record; stake our claim on this title!
1915  // This will return false if the article already exists
1916  $newid = $this->insertOn( $dbw );
1917 
1918  if ( $newid === false ) {
1919  $dbw->rollback( __METHOD__ );
1920  $status->fatal( 'edit-already-exists' );
1921 
1922  wfProfileOut( __METHOD__ );
1923  return $status;
1924  }
1925 
1926  // Save the revision text...
1927  $revision = new Revision( array(
1928  'page' => $newid,
1929  'title' => $this->getTitle(), // for determining the default content model
1930  'comment' => $summary,
1931  'minor_edit' => $isminor,
1932  'text' => $serialized,
1933  'len' => $newsize,
1934  'user' => $user->getId(),
1935  'user_text' => $user->getName(),
1936  'timestamp' => $now,
1937  'content_model' => $content->getModel(),
1938  'content_format' => $serialisation_format,
1939  ) );
1940  $revisionId = $revision->insertOn( $dbw );
1941 
1942  // Bug 37225: use accessor to get the text as Revision may trim it
1943  $content = $revision->getContent(); // sanity; get normalized version
1944 
1945  if ( $content ) {
1946  $newsize = $content->getSize();
1947  }
1948 
1949  // Update the page record with revision data
1950  $this->updateRevisionOn( $dbw, $revision, 0 );
1951 
1952  wfRunHooks( 'NewRevisionFromEditComplete', array( $this, $revision, false, $user ) );
1953 
1954  // Update recentchanges
1955  if ( !( $flags & EDIT_SUPPRESS_RC ) ) {
1956  // Mark as patrolled if the user can do so
1957  $patrolled = ( $wgUseRCPatrol || $wgUseNPPatrol ) && !count(
1958  $this->mTitle->getUserPermissionsErrors( 'autopatrol', $user ) );
1959  // Add RC row to the DB
1960  $rc = RecentChange::notifyNew( $now, $this->mTitle, $isminor, $user, $summary, $bot,
1961  '', $newsize, $revisionId, $patrolled );
1962 
1963  // Log auto-patrolled edits
1964  if ( $patrolled ) {
1965  PatrolLog::record( $rc, true, $user );
1966  }
1967  }
1968  $user->incEditCount();
1969 
1970  } catch ( MWException $e ) {
1971  $dbw->rollback( __METHOD__ );
1972  throw $e;
1973  }
1974  $dbw->commit( __METHOD__ );
1975 
1976  // Update links, etc.
1977  $this->doEditUpdates( $revision, $user, array( 'created' => true ) );
1978 
1979  $hook_args = array( &$this, &$user, $content, $summary,
1980  $flags & EDIT_MINOR, null, null, &$flags, $revision );
1981 
1982  ContentHandler::runLegacyHooks( 'ArticleInsertComplete', $hook_args );
1983  wfRunHooks( 'PageContentInsertComplete', $hook_args );
1984  }
1985 
1986  // Do updates right now unless deferral was requested
1987  if ( !( $flags & EDIT_DEFER_UPDATES ) ) {
1989  }
1990 
1991  // Return the new revision (or null) to the caller
1992  $status->value['revision'] = $revision;
1993 
1994  $hook_args = array( &$this, &$user, $content, $summary,
1995  $flags & EDIT_MINOR, null, null, &$flags, $revision, &$status, $baseRevId );
1996 
1997  ContentHandler::runLegacyHooks( 'ArticleSaveComplete', $hook_args );
1998  wfRunHooks( 'PageContentSaveComplete', $hook_args );
1999 
2000  // Promote user to any groups they meet the criteria for
2001  $user->addAutopromoteOnceGroups( 'onEdit' );
2002 
2003  wfProfileOut( __METHOD__ );
2004  return $status;
2005  }
2006 
2021  public function makeParserOptions( $context ) {
2022  $options = $this->getContentHandler()->makeParserOptions( $context );
2023 
2024  if ( $this->getTitle()->isConversionTable() ) {
2025  // @todo ConversionTable should become a separate content model, so we don't need special cases like this one.
2026  $options->disableContentConversion();
2027  }
2028 
2029  return $options;
2030  }
2031 
2038  public function prepareTextForEdit( $text, $revid = null, User $user = null ) {
2039  ContentHandler::deprecated( __METHOD__, '1.21' );
2040  $content = ContentHandler::makeContent( $text, $this->getTitle() );
2041  return $this->prepareContentForEdit( $content, $revid, $user );
2042  }
2043 
2057  public function prepareContentForEdit( Content $content, $revid = null, User $user = null,
2058  $serialization_format = null
2059  ) {
2061  $user = is_null( $user ) ? $wgUser : $user;
2062  //XXX: check $user->getId() here???
2063 
2064  // Use a sane default for $serialization_format, see bug 57026
2065  if ( $serialization_format === null ) {
2066  $serialization_format = $content->getContentHandler()->getDefaultFormat();
2067  }
2068 
2069  if ( $this->mPreparedEdit
2070  && $this->mPreparedEdit->newContent
2071  && $this->mPreparedEdit->newContent->equals( $content )
2072  && $this->mPreparedEdit->revid == $revid
2073  && $this->mPreparedEdit->format == $serialization_format
2074  // XXX: also check $user here?
2075  ) {
2076  // Already prepared
2077  return $this->mPreparedEdit;
2078  }
2079 
2081  wfRunHooks( 'ArticlePrepareTextForEdit', array( $this, $popts ) );
2082 
2083  $edit = (object)array();
2084  $edit->revid = $revid;
2085  $edit->timestamp = wfTimestampNow();
2086 
2087  $edit->pstContent = $content ? $content->preSaveTransform( $this->mTitle, $user, $popts ) : null;
2088 
2089  $edit->format = $serialization_format;
2090  $edit->popts = $this->makeParserOptions( 'canonical' );
2091  $edit->output = $edit->pstContent ? $edit->pstContent->getParserOutput( $this->mTitle, $revid, $edit->popts ) : null;
2092 
2093  $edit->newContent = $content;
2094  $edit->oldContent = $this->getContent( Revision::RAW );
2095 
2096  // NOTE: B/C for hooks! don't use these fields!
2097  $edit->newText = $edit->newContent ? ContentHandler::getContentText( $edit->newContent ) : '';
2098  $edit->oldText = $edit->oldContent ? ContentHandler::getContentText( $edit->oldContent ) : '';
2099  $edit->pst = $edit->pstContent ? $edit->pstContent->serialize( $serialization_format ) : '';
2100 
2101  $this->mPreparedEdit = $edit;
2102  return $edit;
2103  }
2104 
2121  public function doEditUpdates( Revision $revision, User $user, array $options = array() ) {
2122  global $wgEnableParserCache;
2123 
2124  wfProfileIn( __METHOD__ );
2125 
2126  $options += array( 'changed' => true, 'created' => false, 'oldcountable' => null );
2127  $content = $revision->getContent();
2128 
2129  // Parse the text
2130  // Be careful not to do pre-save transform twice: $text is usually
2131  // already pre-save transformed once.
2132  if ( !$this->mPreparedEdit || $this->mPreparedEdit->output->getFlag( 'vary-revision' ) ) {
2133  wfDebug( __METHOD__ . ": No prepared edit or vary-revision is set...\n" );
2134  $editInfo = $this->prepareContentForEdit( $content, $revision->getId(), $user );
2135  } else {
2136  wfDebug( __METHOD__ . ": No vary-revision, using prepared edit...\n" );
2137  $editInfo = $this->mPreparedEdit;
2138  }
2139 
2140  // Save it to the parser cache
2141  if ( $wgEnableParserCache ) {
2142  $parserCache = ParserCache::singleton();
2143  $parserCache->save(
2144  $editInfo->output, $this, $editInfo->popts, $editInfo->timestamp, $editInfo->revid
2145  );
2146  }
2147 
2148  // Update the links tables and other secondary data
2149  if ( $content ) {
2150  $recursive = $options['changed']; // bug 50785
2151  $updates = $content->getSecondaryDataUpdates(
2152  $this->getTitle(), null, $recursive, $editInfo->output );
2153  DataUpdate::runUpdates( $updates );
2154  }
2155 
2156  wfRunHooks( 'ArticleEditUpdates', array( &$this, &$editInfo, $options['changed'] ) );
2157 
2158  if ( wfRunHooks( 'ArticleEditUpdatesDeleteFromRecentchanges', array( &$this ) ) ) {
2159  if ( 0 == mt_rand( 0, 99 ) ) {
2160  // Flush old entries from the `recentchanges` table; we do this on
2161  // random requests so as to avoid an increase in writes for no good reason
2163  }
2164  }
2165 
2166  if ( !$this->exists() ) {
2167  wfProfileOut( __METHOD__ );
2168  return;
2169  }
2170 
2171  $id = $this->getId();
2172  $title = $this->mTitle->getPrefixedDBkey();
2173  $shortTitle = $this->mTitle->getDBkey();
2174 
2175  if ( !$options['changed'] ) {
2176  $good = 0;
2177  $total = 0;
2178  } elseif ( $options['created'] ) {
2179  $good = (int)$this->isCountable( $editInfo );
2180  $total = 1;
2181  } elseif ( $options['oldcountable'] !== null ) {
2182  $good = (int)$this->isCountable( $editInfo ) - (int)$options['oldcountable'];
2183  $total = 0;
2184  } else {
2185  $good = 0;
2186  $total = 0;
2187  }
2188 
2189  DeferredUpdates::addUpdate( new SiteStatsUpdate( 0, 1, $good, $total ) );
2190  DeferredUpdates::addUpdate( new SearchUpdate( $id, $title, $content ) );
2191 
2192  // If this is another user's talk page, update newtalk.
2193  // Don't do this if $options['changed'] = false (null-edits) nor if
2194  // it's a minor edit and the user doesn't want notifications for those.
2195  if ( $options['changed']
2196  && $this->mTitle->getNamespace() == NS_USER_TALK
2197  && $shortTitle != $user->getTitleKey()
2198  && !( $revision->isMinor() && $user->isAllowed( 'nominornewtalk' ) )
2199  ) {
2200  $recipient = User::newFromName( $shortTitle, false );
2201  if ( !$recipient ) {
2202  wfDebug( __METHOD__ . ": invalid username\n" );
2203  } else {
2204  // Allow extensions to prevent user notification when a new message is added to their talk page
2205  if ( wfRunHooks( 'ArticleEditUpdateNewTalk', array( &$this, $recipient ) ) ) {
2206  if ( User::isIP( $shortTitle ) ) {
2207  // An anonymous user
2208  $recipient->setNewtalk( true, $revision );
2209  } elseif ( $recipient->isLoggedIn() ) {
2210  $recipient->setNewtalk( true, $revision );
2211  } else {
2212  wfDebug( __METHOD__ . ": don't need to notify a nonexistent user\n" );
2213  }
2214  }
2215  }
2216  }
2217 
2218  if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
2219  // XXX: could skip pseudo-messages like js/css here, based on content model.
2220  $msgtext = $content ? $content->getWikitextForTransclusion() : null;
2221  if ( $msgtext === false || $msgtext === null ) {
2222  $msgtext = '';
2223  }
2224 
2225  MessageCache::singleton()->replace( $shortTitle, $msgtext );
2226  }
2227 
2228  if ( $options['created'] ) {
2229  self::onArticleCreate( $this->mTitle );
2230  } else {
2231  self::onArticleEdit( $this->mTitle );
2232  }
2233 
2234  wfProfileOut( __METHOD__ );
2235  }
2236 
2249  public function doQuickEdit( $text, User $user, $comment = '', $minor = 0 ) {
2250  ContentHandler::deprecated( __METHOD__, "1.21" );
2251 
2252  $content = ContentHandler::makeContent( $text, $this->getTitle() );
2253  $this->doQuickEditContent( $content, $user, $comment, $minor );
2254  }
2255 
2267  public function doQuickEditContent( Content $content, User $user, $comment = '', $minor = false,
2268  $serialisation_format = null
2269  ) {
2270  wfProfileIn( __METHOD__ );
2271 
2272  $serialized = $content->serialize( $serialisation_format );
2273 
2274  $dbw = wfGetDB( DB_MASTER );
2275  $revision = new Revision( array(
2276  'title' => $this->getTitle(), // for determining the default content model
2277  'page' => $this->getId(),
2278  'text' => $serialized,
2279  'length' => $content->getSize(),
2280  'comment' => $comment,
2281  'minor_edit' => $minor ? 1 : 0,
2282  ) ); // XXX: set the content object?
2283  $revision->insertOn( $dbw );
2284  $this->updateRevisionOn( $dbw, $revision );
2285 
2286  wfRunHooks( 'NewRevisionFromEditComplete', array( $this, $revision, false, $user ) );
2287 
2288  wfProfileOut( __METHOD__ );
2289  }
2290 
2302  public function doUpdateRestrictions( array $limit, array $expiry, &$cascade, $reason, User $user ) {
2303  global $wgCascadingRestrictionLevels, $wgContLang;
2304 
2305  if ( wfReadOnly() ) {
2306  return Status::newFatal( 'readonlytext', wfReadOnlyReason() );
2307  }
2308 
2309  $this->loadPageData( 'fromdbmaster' );
2310  $restrictionTypes = $this->mTitle->getRestrictionTypes();
2311  $id = $this->getId();
2312 
2313  if ( !$cascade ) {
2314  $cascade = false;
2315  }
2316 
2317  // Take this opportunity to purge out expired restrictions
2319 
2320  // @todo FIXME: Same limitations as described in ProtectionForm.php (line 37);
2321  // we expect a single selection, but the schema allows otherwise.
2322  $isProtected = false;
2323  $protect = false;
2324  $changed = false;
2325 
2326  $dbw = wfGetDB( DB_MASTER );
2327 
2328  foreach ( $restrictionTypes as $action ) {
2329  if ( !isset( $expiry[$action] ) ) {
2330  $expiry[$action] = $dbw->getInfinity();
2331  }
2332  if ( !isset( $limit[$action] ) ) {
2333  $limit[$action] = '';
2334  } elseif ( $limit[$action] != '' ) {
2335  $protect = true;
2336  }
2337 
2338  // Get current restrictions on $action
2339  $current = implode( '', $this->mTitle->getRestrictions( $action ) );
2340  if ( $current != '' ) {
2341  $isProtected = true;
2342  }
2343 
2344  if ( $limit[$action] != $current ) {
2345  $changed = true;
2346  } elseif ( $limit[$action] != '' ) {
2347  // Only check expiry change if the action is actually being
2348  // protected, since expiry does nothing on an not-protected
2349  // action.
2350  if ( $this->mTitle->getRestrictionExpiry( $action ) != $expiry[$action] ) {
2351  $changed = true;
2352  }
2353  }
2354  }
2355 
2356  if ( !$changed && $protect && $this->mTitle->areRestrictionsCascading() != $cascade ) {
2357  $changed = true;
2358  }
2359 
2360  // If nothing has changed, do nothing
2361  if ( !$changed ) {
2362  return Status::newGood();
2363  }
2364 
2365  if ( !$protect ) { // No protection at all means unprotection
2366  $revCommentMsg = 'unprotectedarticle';
2367  $logAction = 'unprotect';
2368  } elseif ( $isProtected ) {
2369  $revCommentMsg = 'modifiedarticleprotection';
2370  $logAction = 'modify';
2371  } else {
2372  $revCommentMsg = 'protectedarticle';
2373  $logAction = 'protect';
2374  }
2375 
2376  // Truncate for whole multibyte characters
2377  $reason = $wgContLang->truncate( $reason, 255 );
2378 
2379  $logRelationsValues = array();
2380  $logRelationsField = null;
2381 
2382  if ( $id ) { // Protection of existing page
2383  if ( !wfRunHooks( 'ArticleProtect', array( &$this, &$user, $limit, $reason ) ) ) {
2384  return Status::newGood();
2385  }
2386 
2387  // Only certain restrictions can cascade...
2388  $editrestriction = isset( $limit['edit'] ) ? array( $limit['edit'] ) : $this->mTitle->getRestrictions( 'edit' );
2389  foreach ( array_keys( $editrestriction, 'sysop' ) as $key ) {
2390  $editrestriction[$key] = 'editprotected'; // backwards compatibility
2391  }
2392  foreach ( array_keys( $editrestriction, 'autoconfirmed' ) as $key ) {
2393  $editrestriction[$key] = 'editsemiprotected'; // backwards compatibility
2394  }
2395 
2396  $cascadingRestrictionLevels = $wgCascadingRestrictionLevels;
2397  foreach ( array_keys( $cascadingRestrictionLevels, 'sysop' ) as $key ) {
2398  $cascadingRestrictionLevels[$key] = 'editprotected'; // backwards compatibility
2399  }
2400  foreach ( array_keys( $cascadingRestrictionLevels, 'autoconfirmed' ) as $key ) {
2401  $cascadingRestrictionLevels[$key] = 'editsemiprotected'; // backwards compatibility
2402  }
2403 
2404  // The schema allows multiple restrictions
2405  if ( !array_intersect( $editrestriction, $cascadingRestrictionLevels ) ) {
2406  $cascade = false;
2407  }
2408 
2409  // insert null revision to identify the page protection change as edit summary
2410  $latest = $this->getLatest();
2411  $nullRevision = $this->insertProtectNullRevision( $revCommentMsg, $limit, $expiry, $cascade, $reason );
2412  if ( $nullRevision === null ) {
2413  return Status::newFatal( 'no-null-revision', $this->mTitle->getPrefixedText() );
2414  }
2415 
2416  $logRelationsField = 'pr_id';
2417 
2418  // Update restrictions table
2419  foreach ( $limit as $action => $restrictions ) {
2420  $dbw->delete(
2421  'page_restrictions',
2422  array(
2423  'pr_page' => $id,
2424  'pr_type' => $action
2425  ),
2426  __METHOD__
2427  );
2428  if ( $restrictions != '' ) {
2429  $dbw->insert(
2430  'page_restrictions',
2431  array(
2432  'pr_id' => $dbw->nextSequenceValue( 'page_restrictions_pr_id_seq' ),
2433  'pr_page' => $id,
2434  'pr_type' => $action,
2435  'pr_level' => $restrictions,
2436  'pr_cascade' => ( $cascade && $action == 'edit' ) ? 1 : 0,
2437  'pr_expiry' => $dbw->encodeExpiry( $expiry[$action] )
2438  ),
2439  __METHOD__
2440  );
2441  $logRelationsValues[] = $dbw->insertId();
2442  }
2443  }
2444 
2445  // Clear out legacy restriction fields
2446  $dbw->update(
2447  'page',
2448  array( 'page_restrictions' => '' ),
2449  array( 'page_id' => $id ),
2450  __METHOD__
2451  );
2452 
2453  wfRunHooks( 'NewRevisionFromEditComplete', array( $this, $nullRevision, $latest, $user ) );
2454  wfRunHooks( 'ArticleProtectComplete', array( &$this, &$user, $limit, $reason ) );
2455  } else { // Protection of non-existing page (also known as "title protection")
2456  // Cascade protection is meaningless in this case
2457  $cascade = false;
2458 
2459  if ( $limit['create'] != '' ) {
2460  $dbw->replace( 'protected_titles',
2461  array( array( 'pt_namespace', 'pt_title' ) ),
2462  array(
2463  'pt_namespace' => $this->mTitle->getNamespace(),
2464  'pt_title' => $this->mTitle->getDBkey(),
2465  'pt_create_perm' => $limit['create'],
2466  'pt_timestamp' => $dbw->timestamp(),
2467  'pt_expiry' => $dbw->encodeExpiry( $expiry['create'] ),
2468  'pt_user' => $user->getId(),
2469  'pt_reason' => $reason,
2470  ), __METHOD__
2471  );
2472  } else {
2473  $dbw->delete( 'protected_titles',
2474  array(
2475  'pt_namespace' => $this->mTitle->getNamespace(),
2476  'pt_title' => $this->mTitle->getDBkey()
2477  ), __METHOD__
2478  );
2479  }
2480  }
2481 
2482  $this->mTitle->flushRestrictions();
2483  InfoAction::invalidateCache( $this->mTitle );
2484 
2485  if ( $logAction == 'unprotect' ) {
2486  $params = array();
2487  } else {
2488  $protectDescriptionLog = $this->protectDescriptionLog( $limit, $expiry );
2489  $params = array( $protectDescriptionLog, $cascade ? 'cascade' : '' );
2490  }
2491 
2492  // Update the protection log
2493  $log = new LogPage( 'protect' );
2494  $logId = $log->addEntry( $logAction, $this->mTitle, $reason, $params, $user );
2495  if ( $logRelationsField !== null && count( $logRelationsValues ) ) {
2496  $log->addRelations( $logRelationsField, $logRelationsValues, $logId );
2497  }
2498 
2499  return Status::newGood();
2500  }
2501 
2512  public function insertProtectNullRevision( $revCommentMsg, array $limit, array $expiry, $cascade, $reason ) {
2514  $dbw = wfGetDB( DB_MASTER );
2515 
2516  // Prepare a null revision to be added to the history
2517  $editComment = $wgContLang->ucfirst(
2518  wfMessage(
2519  $revCommentMsg,
2520  $this->mTitle->getPrefixedText()
2521  )->inContentLanguage()->text()
2522  );
2523  if ( $reason ) {
2524  $editComment .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . $reason;
2525  }
2526  $protectDescription = $this->protectDescription( $limit, $expiry );
2527  if ( $protectDescription ) {
2528  $editComment .= wfMessage( 'word-separator' )->inContentLanguage()->text();
2529  $editComment .= wfMessage( 'parentheses' )->params( $protectDescription )->inContentLanguage()->text();
2530  }
2531  if ( $cascade ) {
2532  $editComment .= wfMessage( 'word-separator' )->inContentLanguage()->text();
2533  $editComment .= wfMessage( 'brackets' )->params(
2534  wfMessage( 'protect-summary-cascade' )->inContentLanguage()->text()
2535  )->inContentLanguage()->text();
2536  }
2537 
2538  $nullRev = Revision::newNullRevision( $dbw, $this->getId(), $editComment, true );
2539  if ( $nullRev ) {
2540  $nullRev->insertOn( $dbw );
2541 
2542  // Update page record and touch page
2543  $oldLatest = $nullRev->getParentId();
2544  $this->updateRevisionOn( $dbw, $nullRev, $oldLatest );
2545  }
2546 
2547  return $nullRev;
2548  }
2549 
2554  protected function formatExpiry( $expiry ) {
2556  $dbr = wfGetDB( DB_SLAVE );
2557 
2558  $encodedExpiry = $dbr->encodeExpiry( $expiry );
2559  if ( $encodedExpiry != 'infinity' ) {
2560  return wfMessage(
2561  'protect-expiring',
2562  $wgContLang->timeanddate( $expiry, false, false ),
2563  $wgContLang->date( $expiry, false, false ),
2564  $wgContLang->time( $expiry, false, false )
2565  )->inContentLanguage()->text();
2566  } else {
2567  return wfMessage( 'protect-expiry-indefinite' )
2568  ->inContentLanguage()->text();
2569  }
2570  }
2571 
2579  public function protectDescription( array $limit, array $expiry ) {
2580  $protectDescription = '';
2581 
2582  foreach ( array_filter( $limit ) as $action => $restrictions ) {
2583  # $action is one of $wgRestrictionTypes = array( 'create', 'edit', 'move', 'upload' ).
2584  # All possible message keys are listed here for easier grepping:
2585  # * restriction-create
2586  # * restriction-edit
2587  # * restriction-move
2588  # * restriction-upload
2589  $actionText = wfMessage( 'restriction-' . $action )->inContentLanguage()->text();
2590  # $restrictions is one of $wgRestrictionLevels = array( '', 'autoconfirmed', 'sysop' ),
2591  # with '' filtered out. All possible message keys are listed below:
2592  # * protect-level-autoconfirmed
2593  # * protect-level-sysop
2594  $restrictionsText = wfMessage( 'protect-level-' . $restrictions )->inContentLanguage()->text();
2595 
2596  $expiryText = $this->formatExpiry( $expiry[$action] );
2597 
2598  if ( $protectDescription !== '' ) {
2599  $protectDescription .= wfMessage( 'word-separator' )->inContentLanguage()->text();
2600  }
2601  $protectDescription .= wfMessage( 'protect-summary-desc' )
2602  ->params( $actionText, $restrictionsText, $expiryText )
2603  ->inContentLanguage()->text();
2604  }
2605 
2606  return $protectDescription;
2607  }
2608 
2620  public function protectDescriptionLog( array $limit, array $expiry ) {
2622 
2623  $protectDescriptionLog = '';
2624 
2625  foreach ( array_filter( $limit ) as $action => $restrictions ) {
2626  $expiryText = $this->formatExpiry( $expiry[$action] );
2627  $protectDescriptionLog .= $wgContLang->getDirMark() . "[$action=$restrictions] ($expiryText)";
2628  }
2629 
2630  return trim( $protectDescriptionLog );
2631  }
2632 
2642  protected static function flattenRestrictions( $limit ) {
2643  if ( !is_array( $limit ) ) {
2644  throw new MWException( 'WikiPage::flattenRestrictions given non-array restriction set' );
2645  }
2646 
2647  $bits = array();
2648  ksort( $limit );
2649 
2650  foreach ( array_filter( $limit ) as $action => $restrictions ) {
2651  $bits[] = "$action=$restrictions";
2652  }
2653 
2654  return implode( ':', $bits );
2655  }
2656 
2673  public function doDeleteArticle(
2674  $reason, $suppress = false, $id = 0, $commit = true, &$error = '', User $user = null
2675  ) {
2676  $status = $this->doDeleteArticleReal( $reason, $suppress, $id, $commit, $error, $user );
2677  return $status->isGood();
2678  }
2679 
2697  public function doDeleteArticleReal(
2698  $reason, $suppress = false, $id = 0, $commit = true, &$error = '', User $user = null
2699  ) {
2700  global $wgUser, $wgContentHandlerUseDB;
2701 
2702  wfDebug( __METHOD__ . "\n" );
2703 
2704  $status = Status::newGood();
2705 
2706  if ( $this->mTitle->getDBkey() === '' ) {
2707  $status->error( 'cannotdelete', wfEscapeWikiText( $this->getTitle()->getPrefixedText() ) );
2708  return $status;
2709  }
2710 
2711  $user = is_null( $user ) ? $wgUser : $user;
2712  if ( ! wfRunHooks( 'ArticleDelete', array( &$this, &$user, &$reason, &$error, &$status ) ) ) {
2713  if ( $status->isOK() ) {
2714  // Hook aborted but didn't set a fatal status
2715  $status->fatal( 'delete-hook-aborted' );
2716  }
2717  return $status;
2718  }
2719 
2720  if ( $id == 0 ) {
2721  $this->loadPageData( 'forupdate' );
2722  $id = $this->getID();
2723  if ( $id == 0 ) {
2724  $status->error( 'cannotdelete', wfEscapeWikiText( $this->getTitle()->getPrefixedText() ) );
2725  return $status;
2726  }
2727  }
2728 
2729  // Bitfields to further suppress the content
2730  if ( $suppress ) {
2731  $bitfield = 0;
2732  // This should be 15...
2733  $bitfield |= Revision::DELETED_TEXT;
2734  $bitfield |= Revision::DELETED_COMMENT;
2735  $bitfield |= Revision::DELETED_USER;
2736  $bitfield |= Revision::DELETED_RESTRICTED;
2737  } else {
2738  $bitfield = 'rev_deleted';
2739  }
2740 
2741  // we need to remember the old content so we can use it to generate all deletion updates.
2742  $content = $this->getContent( Revision::RAW );
2743 
2744  $dbw = wfGetDB( DB_MASTER );
2745  $dbw->begin( __METHOD__ );
2746  // For now, shunt the revision data into the archive table.
2747  // Text is *not* removed from the text table; bulk storage
2748  // is left intact to avoid breaking block-compression or
2749  // immutable storage schemes.
2750  //
2751  // For backwards compatibility, note that some older archive
2752  // table entries will have ar_text and ar_flags fields still.
2753  //
2754  // In the future, we may keep revisions and mark them with
2755  // the rev_deleted field, which is reserved for this purpose.
2756 
2757  $row = array(
2758  'ar_namespace' => 'page_namespace',
2759  'ar_title' => 'page_title',
2760  'ar_comment' => 'rev_comment',
2761  'ar_user' => 'rev_user',
2762  'ar_user_text' => 'rev_user_text',
2763  'ar_timestamp' => 'rev_timestamp',
2764  'ar_minor_edit' => 'rev_minor_edit',
2765  'ar_rev_id' => 'rev_id',
2766  'ar_parent_id' => 'rev_parent_id',
2767  'ar_text_id' => 'rev_text_id',
2768  'ar_text' => '\'\'', // Be explicit to appease
2769  'ar_flags' => '\'\'', // MySQL's "strict mode"...
2770  'ar_len' => 'rev_len',
2771  'ar_page_id' => 'page_id',
2772  'ar_deleted' => $bitfield,
2773  'ar_sha1' => 'rev_sha1',
2774  );
2775 
2776  if ( $wgContentHandlerUseDB ) {
2777  $row['ar_content_model'] = 'rev_content_model';
2778  $row['ar_content_format'] = 'rev_content_format';
2779  }
2780 
2781  $dbw->insertSelect( 'archive', array( 'page', 'revision' ),
2782  $row,
2783  array(
2784  'page_id' => $id,
2785  'page_id = rev_page'
2786  ), __METHOD__
2787  );
2788 
2789  // Now that it's safely backed up, delete it
2790  $dbw->delete( 'page', array( 'page_id' => $id ), __METHOD__ );
2791  $ok = ( $dbw->affectedRows() > 0 ); // $id could be laggy
2792 
2793  if ( !$ok ) {
2794  $dbw->rollback( __METHOD__ );
2795  $status->error( 'cannotdelete', wfEscapeWikiText( $this->getTitle()->getPrefixedText() ) );
2796  return $status;
2797  }
2798 
2799  if ( !$dbw->cascadingDeletes() ) {
2800  $dbw->delete( 'revision', array( 'rev_page' => $id ), __METHOD__ );
2801  }
2802 
2803  $this->doDeleteUpdates( $id, $content );
2804 
2805  // Log the deletion, if the page was suppressed, log it at Oversight instead
2806  $logtype = $suppress ? 'suppress' : 'delete';
2807 
2808  $logEntry = new ManualLogEntry( $logtype, 'delete' );
2809  $logEntry->setPerformer( $user );
2810  $logEntry->setTarget( $this->mTitle );
2811  $logEntry->setComment( $reason );
2812  $logid = $logEntry->insert();
2813  $logEntry->publish( $logid );
2814 
2815  if ( $commit ) {
2816  $dbw->commit( __METHOD__ );
2817  }
2818 
2819  wfRunHooks( 'ArticleDeleteComplete', array( &$this, &$user, $reason, $id, $content, $logEntry ) );
2820  $status->value = $logid;
2821  return $status;
2822  }
2823 
2831  public function doDeleteUpdates( $id, Content $content = null ) {
2832  // update site status
2833  DeferredUpdates::addUpdate( new SiteStatsUpdate( 0, 1, - (int)$this->isCountable(), -1 ) );
2834 
2835  // remove secondary indexes, etc
2836  $updates = $this->getDeletionUpdates( $content );
2837  DataUpdate::runUpdates( $updates );
2838 
2839  // Reparse any pages transcluding this page
2840  LinksUpdate::queueRecursiveJobsForTable( $this->mTitle, 'templatelinks' );
2841 
2842  // Reparse any pages including this image
2843  if ( $this->mTitle->getNamespace() == NS_FILE ) {
2844  LinksUpdate::queueRecursiveJobsForTable( $this->mTitle, 'imagelinks' );
2845  }
2846 
2847  // Clear caches
2848  WikiPage::onArticleDelete( $this->mTitle );
2849 
2850  // Reset this object and the Title object
2851  $this->loadFromRow( false, self::READ_LATEST );
2852 
2853  // Search engine
2854  DeferredUpdates::addUpdate( new SearchUpdate( $id, $this->mTitle ) );
2855  }
2856 
2881  public function doRollback(
2882  $fromP, $summary, $token, $bot, &$resultDetails, User $user
2883  ) {
2884  $resultDetails = null;
2885 
2886  // Check permissions
2887  $editErrors = $this->mTitle->getUserPermissionsErrors( 'edit', $user );
2888  $rollbackErrors = $this->mTitle->getUserPermissionsErrors( 'rollback', $user );
2889  $errors = array_merge( $editErrors, wfArrayDiff2( $rollbackErrors, $editErrors ) );
2890 
2891  if ( !$user->matchEditToken( $token, array( $this->mTitle->getPrefixedText(), $fromP ) ) ) {
2892  $errors[] = array( 'sessionfailure' );
2893  }
2894 
2895  if ( $user->pingLimiter( 'rollback' ) || $user->pingLimiter() ) {
2896  $errors[] = array( 'actionthrottledtext' );
2897  }
2898 
2899  // If there were errors, bail out now
2900  if ( !empty( $errors ) ) {
2901  return $errors;
2902  }
2903 
2904  return $this->commitRollback( $fromP, $summary, $bot, $resultDetails, $user );
2905  }
2906 
2923  public function commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser ) {
2924  global $wgUseRCPatrol, $wgContLang;
2925 
2926  $dbw = wfGetDB( DB_MASTER );
2927 
2928  if ( wfReadOnly() ) {
2929  return array( array( 'readonlytext' ) );
2930  }
2931 
2932  // Get the last editor
2933  $current = $this->getRevision();
2934  if ( is_null( $current ) ) {
2935  // Something wrong... no page?
2936  return array( array( 'notanarticle' ) );
2937  }
2938 
2939  $from = str_replace( '_', ' ', $fromP );
2940  // User name given should match up with the top revision.
2941  // If the user was deleted then $from should be empty.
2942  if ( $from != $current->getUserText() ) {
2943  $resultDetails = array( 'current' => $current );
2944  return array( array( 'alreadyrolled',
2945  htmlspecialchars( $this->mTitle->getPrefixedText() ),
2946  htmlspecialchars( $fromP ),
2947  htmlspecialchars( $current->getUserText() )
2948  ) );
2949  }
2950 
2951  // Get the last edit not by this guy...
2952  // Note: these may not be public values
2953  $user = intval( $current->getRawUser() );
2954  $user_text = $dbw->addQuotes( $current->getRawUserText() );
2955  $s = $dbw->selectRow( 'revision',
2956  array( 'rev_id', 'rev_timestamp', 'rev_deleted' ),
2957  array( 'rev_page' => $current->getPage(),
2958  "rev_user != {$user} OR rev_user_text != {$user_text}"
2959  ), __METHOD__,
2960  array( 'USE INDEX' => 'page_timestamp',
2961  'ORDER BY' => 'rev_timestamp DESC' )
2962  );
2963  if ( $s === false ) {
2964  // No one else ever edited this page
2965  return array( array( 'cantrollback' ) );
2966  } elseif ( $s->rev_deleted & Revision::DELETED_TEXT || $s->rev_deleted & Revision::DELETED_USER ) {
2967  // Only admins can see this text
2968  return array( array( 'notvisiblerev' ) );
2969  }
2970 
2971  // Set patrolling and bot flag on the edits, which gets rollbacked.
2972  // This is done before the rollback edit to have patrolling also on failure (bug 62157).
2973  $set = array();
2974  if ( $bot && $guser->isAllowed( 'markbotedits' ) ) {
2975  // Mark all reverted edits as bot
2976  $set['rc_bot'] = 1;
2977  }
2978 
2979  if ( $wgUseRCPatrol ) {
2980  // Mark all reverted edits as patrolled
2981  $set['rc_patrolled'] = 1;
2982  }
2983 
2984  if ( count( $set ) ) {
2985  $dbw->update( 'recentchanges', $set,
2986  array( /* WHERE */
2987  'rc_cur_id' => $current->getPage(),
2988  'rc_user_text' => $current->getUserText(),
2989  'rc_timestamp > ' . $dbw->addQuotes( $s->rev_timestamp ),
2990  ), __METHOD__
2991  );
2992  }
2993 
2994  // Generate the edit summary if necessary
2995  $target = Revision::newFromId( $s->rev_id );
2996  if ( empty( $summary ) ) {
2997  if ( $from == '' ) { // no public user name
2998  $summary = wfMessage( 'revertpage-nouser' );
2999  } else {
3000  $summary = wfMessage( 'revertpage' );
3001  }
3002  }
3003 
3004  // Allow the custom summary to use the same args as the default message
3005  $args = array(
3006  $target->getUserText(), $from, $s->rev_id,
3007  $wgContLang->timeanddate( wfTimestamp( TS_MW, $s->rev_timestamp ) ),
3008  $current->getId(), $wgContLang->timeanddate( $current->getTimestamp() )
3009  );
3010  if ( $summary instanceof Message ) {
3011  $summary = $summary->params( $args )->inContentLanguage()->text();
3012  } else {
3014  }
3015 
3016  // Trim spaces on user supplied text
3017  $summary = trim( $summary );
3018 
3019  // Truncate for whole multibyte characters.
3020  $summary = $wgContLang->truncate( $summary, 255 );
3021 
3022  // Save
3023  $flags = EDIT_UPDATE;
3024 
3025  if ( $guser->isAllowed( 'minoredit' ) ) {
3026  $flags |= EDIT_MINOR;
3027  }
3028 
3029  if ( $bot && ( $guser->isAllowedAny( 'markbotedits', 'bot' ) ) ) {
3031  }
3032 
3033  // Actually store the edit
3034  $status = $this->doEditContent( $target->getContent(), $summary, $flags, $target->getId(), $guser );
3035 
3036  if ( !$status->isOK() ) {
3037  return $status->getErrorsArray();
3038  }
3039 
3040  // raise error, when the edit is an edit without a new version
3041  if ( empty( $status->value['revision'] ) ) {
3042  $resultDetails = array( 'current' => $current );
3043  return array( array( 'alreadyrolled',
3044  htmlspecialchars( $this->mTitle->getPrefixedText() ),
3045  htmlspecialchars( $fromP ),
3046  htmlspecialchars( $current->getUserText() )
3047  ) );
3048  }
3049 
3050  $revId = $status->value['revision']->getId();
3051 
3052  wfRunHooks( 'ArticleRollbackComplete', array( $this, $guser, $target, $current ) );
3053 
3054  $resultDetails = array(
3055  'summary' => $summary,
3056  'current' => $current,
3057  'target' => $target,
3058  'newid' => $revId
3059  );
3060 
3061  return array();
3062  }
3063 
3075  public static function onArticleCreate( $title ) {
3076  // Update existence markers on article/talk tabs...
3077  if ( $title->isTalkPage() ) {
3078  $other = $title->getSubjectPage();
3079  } else {
3080  $other = $title->getTalkPage();
3081  }
3082 
3083  $other->invalidateCache();
3084  $other->purgeSquid();
3085 
3086  $title->touchLinks();
3087  $title->purgeSquid();
3088  $title->deleteTitleProtection();
3089  }
3090 
3096  public static function onArticleDelete( $title ) {
3097  // Update existence markers on article/talk tabs...
3098  if ( $title->isTalkPage() ) {
3099  $other = $title->getSubjectPage();
3100  } else {
3101  $other = $title->getTalkPage();
3102  }
3103 
3104  $other->invalidateCache();
3105  $other->purgeSquid();
3106 
3107  $title->touchLinks();
3108  $title->purgeSquid();
3109 
3110  // File cache
3113 
3114  // Messages
3115  if ( $title->getNamespace() == NS_MEDIAWIKI ) {
3116  MessageCache::singleton()->replace( $title->getDBkey(), false );
3117  }
3118 
3119  // Images
3120  if ( $title->getNamespace() == NS_FILE ) {
3121  $update = new HTMLCacheUpdate( $title, 'imagelinks' );
3122  $update->doUpdate();
3123  }
3124 
3125  // User talk pages
3126  if ( $title->getNamespace() == NS_USER_TALK ) {
3127  $user = User::newFromName( $title->getText(), false );
3128  if ( $user ) {
3129  $user->setNewtalk( false );
3130  }
3131  }
3132 
3133  // Image redirects
3134  RepoGroup::singleton()->getLocalRepo()->invalidateImageRedirect( $title );
3135  }
3136 
3143  public static function onArticleEdit( $title ) {
3144  // Invalidate caches of articles which include this page
3145  DeferredUpdates::addHTMLCacheUpdate( $title, 'templatelinks' );
3146 
3147  // Invalidate the caches of all pages which redirect here
3149 
3150  // Purge squid for this page only
3151  $title->purgeSquid();
3152 
3153  // Clear file cache for this page only
3156  }
3166  public function getCategories() {
3167  $id = $this->getId();
3168  if ( $id == 0 ) {
3170  }
3171 
3172  $dbr = wfGetDB( DB_SLAVE );
3173  $res = $dbr->select( 'categorylinks',
3174  array( 'cl_to AS page_title, ' . NS_CATEGORY . ' AS page_namespace' ),
3175  // Have to do that since DatabaseBase::fieldNamesWithAlias treats numeric indexes
3176  // as not being aliases, and NS_CATEGORY is numeric
3177  array( 'cl_from' => $id ),
3178  __METHOD__ );
3179 
3181  }
3182 
3189  public function getHiddenCategories() {
3190  $result = array();
3191  $id = $this->getId();
3192 
3193  if ( $id == 0 ) {
3194  return array();
3195  }
3196 
3197  $dbr = wfGetDB( DB_SLAVE );
3198  $res = $dbr->select( array( 'categorylinks', 'page_props', 'page' ),
3199  array( 'cl_to' ),
3200  array( 'cl_from' => $id, 'pp_page=page_id', 'pp_propname' => 'hiddencat',
3201  'page_namespace' => NS_CATEGORY, 'page_title=cl_to' ),
3202  __METHOD__ );
3203 
3204  if ( $res !== false ) {
3205  foreach ( $res as $row ) {
3206  $result[] = Title::makeTitle( NS_CATEGORY, $row->cl_to );
3207  }
3208  }
3209 
3210  return $result;
3211  }
3212 
3222  public static function getAutosummary( $oldtext, $newtext, $flags ) {
3223  // NOTE: stub for backwards-compatibility. assumes the given text is wikitext. will break horribly if it isn't.
3224 
3225  ContentHandler::deprecated( __METHOD__, '1.21' );
3226 
3228  $oldContent = is_null( $oldtext ) ? null : $handler->unserializeContent( $oldtext );
3229  $newContent = is_null( $newtext ) ? null : $handler->unserializeContent( $newtext );
3230 
3231  return $handler->getAutosummary( $oldContent, $newContent, $flags );
3232  }
3233 
3241  public function getAutoDeleteReason( &$hasHistory ) {
3242  return $this->getContentHandler()->getAutoDeleteReason( $this->getTitle(), $hasHistory );
3243  }
3244 
3252  public function updateCategoryCounts( array $added, array $deleted ) {
3253  $that = $this;
3254  $method = __METHOD__;
3255  $dbw = wfGetDB( DB_MASTER );
3256 
3257  // Do this at the end of the commit to reduce lock wait timeouts
3258  $dbw->onTransactionPreCommitOrIdle(
3259  function() use ( $dbw, $that, $method, $added, $deleted ) {
3260  $ns = $that->getTitle()->getNamespace();
3261 
3262  $addFields = array( 'cat_pages = cat_pages + 1' );
3263  $removeFields = array( 'cat_pages = cat_pages - 1' );
3264  if ( $ns == NS_CATEGORY ) {
3265  $addFields[] = 'cat_subcats = cat_subcats + 1';
3266  $removeFields[] = 'cat_subcats = cat_subcats - 1';
3267  } elseif ( $ns == NS_FILE ) {
3268  $addFields[] = 'cat_files = cat_files + 1';
3269  $removeFields[] = 'cat_files = cat_files - 1';
3270  }
3271 
3272  if ( count( $added ) ) {
3273  $insertRows = array();
3274  foreach ( $added as $cat ) {
3275  $insertRows[] = array(
3276  'cat_title' => $cat,
3277  'cat_pages' => 1,
3278  'cat_subcats' => ( $ns == NS_CATEGORY ) ? 1 : 0,
3279  'cat_files' => ( $ns == NS_FILE ) ? 1 : 0,
3280  );
3281  }
3282  $dbw->upsert(
3283  'category',
3284  $insertRows,
3285  array( 'cat_title' ),
3286  $addFields,
3287  $method
3288  );
3289  }
3290 
3291  if ( count( $deleted ) ) {
3292  $dbw->update(
3293  'category',
3294  $removeFields,
3295  array( 'cat_title' => $deleted ),
3296  $method
3297  );
3298  }
3299 
3300  foreach ( $added as $catName ) {
3301  $cat = Category::newFromName( $catName );
3302  wfRunHooks( 'CategoryAfterPageAdded', array( $cat, $that ) );
3303  }
3304 
3305  foreach ( $deleted as $catName ) {
3306  $cat = Category::newFromName( $catName );
3307  wfRunHooks( 'CategoryAfterPageRemoved', array( $cat, $that ) );
3308  }
3309  }
3310  );
3311  }
3312 
3318  public function doCascadeProtectionUpdates( ParserOutput $parserOutput ) {
3319  if ( wfReadOnly() || !$this->mTitle->areRestrictionsCascading() ) {
3320  return;
3321  }
3322 
3323  // templatelinks or imagelinks tables may have become out of sync,
3324  // especially if using variable-based transclusions.
3325  // For paranoia, check if things have changed and if
3326  // so apply updates to the database. This will ensure
3327  // that cascaded protections apply as soon as the changes
3328  // are visible.
3329 
3330  // Get templates from templatelinks and images from imagelinks
3331  $id = $this->getId();
3332 
3333  $dbLinks = array();
3334 
3335  $dbr = wfGetDB( DB_SLAVE );
3336  $res = $dbr->select( array( 'templatelinks' ),
3337  array( 'tl_namespace', 'tl_title' ),
3338  array( 'tl_from' => $id ),
3339  __METHOD__
3340  );
3341 
3342  foreach ( $res as $row ) {
3343  $dbLinks["{$row->tl_namespace}:{$row->tl_title}"] = true;
3344  }
3345 
3346  $dbr = wfGetDB( DB_SLAVE );
3347  $res = $dbr->select( array( 'imagelinks' ),
3348  array( 'il_to' ),
3349  array( 'il_from' => $id ),
3350  __METHOD__
3351  );
3352 
3353  foreach ( $res as $row ) {
3354  $dbLinks[NS_FILE . ":{$row->il_to}"] = true;
3355  }
3356 
3357  // Get templates and images from parser output.
3358  $poLinks = array();
3359  foreach ( $parserOutput->getTemplates() as $ns => $templates ) {
3360  foreach ( $templates as $dbk => $id ) {
3361  $poLinks["$ns:$dbk"] = true;
3362  }
3363  }
3364  foreach ( $parserOutput->getImages() as $dbk => $id ) {
3365  $poLinks[NS_FILE . ":$dbk"] = true;
3366  }
3367 
3368  // Get the diff
3369  $links_diff = array_diff_key( $poLinks, $dbLinks );
3370 
3371  if ( count( $links_diff ) > 0 ) {
3372  // Whee, link updates time.
3373  // Note: we are only interested in links here. We don't need to get other DataUpdate items from the parser output.
3374  $u = new LinksUpdate( $this->mTitle, $parserOutput, false );
3375  $u->doUpdate();
3376  }
3377  }
3378 
3386  public function getUsedTemplates() {
3387  return $this->mTitle->getTemplateLinksFrom();
3388  }
3389 
3402  public function preSaveTransform( $text, User $user = null, ParserOptions $popts = null ) {
3404 
3405  wfDeprecated( __METHOD__, '1.19' );
3406 
3407  $user = is_null( $user ) ? $wgUser : $user;
3408 
3409  if ( $popts === null ) {
3410  $popts = ParserOptions::newFromUser( $user );
3411  }
3412 
3413  return $wgParser->preSaveTransform( $text, $this->mTitle, $user, $popts );
3414  }
3415 
3422  public function isBigDeletion() {
3423  wfDeprecated( __METHOD__, '1.19' );
3424  return $this->mTitle->isBigDeletion();
3425  }
3426 
3433  public function estimateRevisionCount() {
3434  wfDeprecated( __METHOD__, '1.19' );
3435  return $this->mTitle->estimateRevisionCount();
3436  }
3437 
3449  public function updateRestrictions(
3450  $limit = array(), $reason = '', &$cascade = 0, $expiry = array(), User $user = null
3451  ) {
3452  global $wgUser;
3453 
3454  $user = is_null( $user ) ? $wgUser : $user;
3455 
3456  return $this->doUpdateRestrictions( $limit, $expiry, $cascade, $reason, $user )->isOK();
3457  }
3458 
3466  public function getDeletionUpdates( Content $content = null ) {
3467  if ( !$content ) {
3468  // load content object, which may be used to determine the necessary updates
3469  // XXX: the content may not be needed to determine the updates, then this would be overhead.
3470  $content = $this->getContent( Revision::RAW );
3471  }
3472 
3473  if ( !$content ) {
3474  $updates = array();
3475  } else {
3476  $updates = $content->getDeletionUpdates( $this );
3477  }
3478 
3479  wfRunHooks( 'WikiPageDeletionUpdates', array( $this, $content, &$updates ) );
3480  return $updates;
3481  }
3482 
3483 }
3485 class PoolWorkArticleView extends PoolCounterWork {
3486 
3490  private $page;
3491 
3495  private $cacheKey;
3500  private $revid;
3501 
3505  private $parserOptions;
3506 
3510  private $content = null;
3511 
3515  private $parserOutput = false;
3516 
3520  private $isDirty = false;
3521 
3525  private $error = false;
3526 
3536  public function __construct( Page $page, ParserOptions $parserOptions, $revid, $useParserCache, $content = null ) {
3537  if ( is_string( $content ) ) { // BC: old style call
3538  $modelId = $page->getRevision()->getContentModel();
3539  $format = $page->getRevision()->getContentFormat();
3540  $content = ContentHandler::makeContent( $content, $page->getTitle(), $modelId, $format );
3541  }
3542 
3543  $this->page = $page;
3544  $this->revid = $revid;
3545  $this->cacheable = $useParserCache;
3546  $this->parserOptions = $parserOptions;
3547  $this->content = $content;
3548  $this->cacheKey = ParserCache::singleton()->getKey( $page, $parserOptions );
3549  parent::__construct( 'ArticleView', $this->cacheKey . ':revid:' . $revid );
3550  }
3551 
3557  public function getParserOutput() {
3559  }
3560 
3566  public function getIsDirty() {
3567  return $this->isDirty;
3568  }
3569 
3575  public function getError() {
3576  return $this->error;
3577  }
3578 
3582  public function doWork() {
3583  global $wgUseFileCache;
3584 
3585  // @todo several of the methods called on $this->page are not declared in Page, but present
3586  // in WikiPage and delegated by Article.
3587 
3588  $isCurrent = $this->revid === $this->page->getLatest();
3589 
3590  if ( $this->content !== null ) {
3591  $content = $this->content;
3592  } elseif ( $isCurrent ) {
3593  // XXX: why use RAW audience here, and PUBLIC (default) below?
3594  $content = $this->page->getContent( Revision::RAW );
3595  } else {
3596  $rev = Revision::newFromTitle( $this->page->getTitle(), $this->revid );
3597 
3598  if ( $rev === null ) {
3599  $content = null;
3600  } else {
3601  // XXX: why use PUBLIC audience here (default), and RAW above?
3602  $content = $rev->getContent();
3603  }
3604  }
3605 
3606  if ( $content === null ) {
3607  return false;
3608  }
3609 
3610  // Reduce effects of race conditions for slow parses (bug 46014)
3611  $cacheTime = wfTimestampNow();
3612 
3613  $time = - microtime( true );
3614  $this->parserOutput = $content->getParserOutput( $this->page->getTitle(), $this->revid, $this->parserOptions );
3615  $time += microtime( true );
3616 
3617  // Timing hack
3618  if ( $time > 3 ) {
3619  wfDebugLog( 'slow-parse', sprintf( "%-5.2f %s", $time,
3620  $this->page->getTitle()->getPrefixedDBkey() ) );
3621  }
3622 
3623  if ( $this->cacheable && $this->parserOutput->isCacheable() && $isCurrent ) {
3624  ParserCache::singleton()->save(
3625  $this->parserOutput, $this->page, $this->parserOptions, $cacheTime, $this->revid );
3626  }
3627 
3628  // Make sure file cache is not used on uncacheable content.
3629  // Output that has magic words in it can still use the parser cache
3630  // (if enabled), though it will generally expire sooner.
3631  if ( !$this->parserOutput->isCacheable() || $this->parserOutput->containsOldMagic() ) {
3632  $wgUseFileCache = false;
3633  }
3634 
3635  if ( $isCurrent ) {
3636  $this->page->doCascadeProtectionUpdates( $this->parserOutput );
3637  }
3638 
3639  return true;
3640  }
3641 
3645  public function getCachedWork() {
3646  $this->parserOutput = ParserCache::singleton()->get( $this->page, $this->parserOptions );
3647 
3648  if ( $this->parserOutput === false ) {
3649  wfDebug( __METHOD__ . ": parser cache miss\n" );
3650  return false;
3651  } else {
3652  wfDebug( __METHOD__ . ": parser cache hit\n" );
3653  return true;
3654  }
3655  }
3656 
3660  public function fallback() {
3661  $this->parserOutput = ParserCache::singleton()->getDirty( $this->page, $this->parserOptions );
3663  if ( $this->parserOutput === false ) {
3664  wfDebugLog( 'dirty', 'dirty missing' );
3665  wfDebug( __METHOD__ . ": no dirty cache\n" );
3666  return false;
3667  } else {
3668  wfDebug( __METHOD__ . ": sending dirty output\n" );
3669  wfDebugLog( 'dirty', "dirty output {$this->cacheKey}" );
3670  $this->isDirty = true;
3671  return true;
3672  }
3673  }
3674 
3679  public function error( $status ) {
3680  $this->error = $status;
3681  return false;
3682  }
3683 }
WikiPage\getCategories
getCategories()
#-
Definition: WikiPage.php:3157
Content\getContentHandler
getContentHandler()
Convenience method that returns the ContentHandler singleton for handling the content model that this...
Revision\FOR_PUBLIC
const FOR_PUBLIC
Definition: Revision.php:72
ContentHandler\deprecated
static deprecated( $func, $version, $component=false)
Logs a deprecation warning, visible if $wgDevelopmentWarnings, but only if self::$enableDeprecationWa...
Definition: ContentHandler.php:1030
Revision\DELETED_USER
const DELETED_USER
Definition: Revision.php:67
FakeResultWrapper
Overloads the relevant methods of the real ResultsWrapper so it doesn't go anywhere near an actual da...
Definition: DatabaseUtility.php:230
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
Revision\getTimestamp
getTimestamp()
Definition: Revision.php:1133
ContentHandler\getForModelID
static getForModelID( $modelId)
Returns the ContentHandler singleton for the given model ID.
Definition: ContentHandler.php:311
$wgUser
$wgUser
Definition: Setup.php:552
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
object
globals will be eliminated from MediaWiki replaced by an application object which would be passed to constructors Whether that would be an convenient solution remains to be but certainly PHP makes such object oriented programming models easier than they were in previous versions For the time being MediaWiki programmers will have to work in an environment with some global context At the time of globals were initialised on startup by MediaWiki of these were configuration which are documented in DefaultSettings php There is no comprehensive documentation for the remaining however some of the most important ones are listed below They are typically initialised either in index php or in Setup php For a description of the see design txt $wgTitle Title object created from the request URL $wgOut OutputPage object for HTTP response $wgUser User object for the user associated with the current request $wgLang Language object selected by user preferences $wgContLang Language object associated with the wiki being viewed $wgParser Parser object Parser extensions register their hooks here $wgRequest WebRequest object
Definition: globals.txt:25
$result
The index of the header message $result[1]=The index of the body text message $result[2 through n]=Parameters passed to body text message. Please note the header message cannot receive/use parameters. 'ImportHandleLogItemXMLTag':When parsing a XML tag in a log item. $reader:XMLReader object $logInfo:Array of information Return false to stop further processing of the tag 'ImportHandlePageXMLTag':When parsing a XML tag in a page. $reader:XMLReader object $pageInfo:Array of information Return false to stop further processing of the tag 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision. $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information Return false to stop further processing of the tag 'ImportHandleToplevelXMLTag':When parsing a top level XML tag. $reader:XMLReader object Return false to stop further processing of the tag 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload. $reader:XMLReader object $revisionInfo:Array of information Return false to stop further processing of the tag 'InfoAction':When building information to display on the action=info page. $context:IContextSource object & $pageInfo:Array of information 'InitializeArticleMaybeRedirect':MediaWiki check to see if title is a redirect. $title:Title object for the current page $request:WebRequest $ignoreRedirect:boolean to skip redirect check $target:Title/string of redirect target $article:Article object 'InterwikiLoadPrefix':When resolving if a given prefix is an interwiki or not. Return true without providing an interwiki to continue interwiki search. $prefix:interwiki prefix we are looking for. & $iwData:output array describing the interwiki with keys iw_url, iw_local, iw_trans and optionally iw_api and iw_wikiid. 'InternalParseBeforeSanitize':during Parser 's internalParse method just before the parser removes unwanted/dangerous HTML tags and after nowiki/noinclude/includeonly/onlyinclude and other processings. Ideal for syntax-extensions after template/parser function execution which respect nowiki and HTML-comments. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InternalParseBeforeLinks':during Parser 's internalParse method before links but after nowiki/noinclude/includeonly/onlyinclude and other processings. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InvalidateEmailComplete':Called after a user 's email has been invalidated successfully. $user:user(object) whose email is being invalidated 'IRCLineURL':When constructing the URL to use in an IRC notification. Callee may modify $url and $query, URL will be constructed as $url . $query & $url:URL to index.php & $query:Query string $rc:RecentChange object that triggered url generation 'IsFileCacheable':Override the result of Article::isFileCacheable()(if true) $article:article(object) being checked 'IsTrustedProxy':Override the result of wfIsTrustedProxy() $ip:IP being check $result:Change this value to override the result of wfIsTrustedProxy() 'IsUploadAllowedFromUrl':Override the result of UploadFromUrl::isAllowedUrl() $url:URL used to upload from & $allowed:Boolean indicating if uploading is allowed for given URL 'isValidEmailAddr':Override the result of User::isValidEmailAddr(), for instance to return false if the domain name doesn 't match your organization. $addr:The e-mail address entered by the user & $result:Set this and return false to override the internal checks 'isValidPassword':Override the result of User::isValidPassword() $password:The password entered by the user & $result:Set this and return false to override the internal checks $user:User the password is being validated for 'Language::getMessagesFileName':$code:The language code or the language we 're looking for a messages file for & $file:The messages file path, you can override this to change the location. 'LanguageGetNamespaces':Provide custom ordering for namespaces or remove namespaces. Do not use this hook to add namespaces. Use CanonicalNamespaces for that. & $namespaces:Array of namespaces indexed by their numbers 'LanguageGetMagic':DEPRECATED, use $magicWords in a file listed in $wgExtensionMessagesFiles instead. Use this to define synonyms of magic words depending of the language $magicExtensions:associative array of magic words synonyms $lang:language code(string) 'LanguageGetSpecialPageAliases':DEPRECATED, use $specialPageAliases in a file listed in $wgExtensionMessagesFiles instead. Use to define aliases of special pages names depending of the language $specialPageAliases:associative array of magic words synonyms $lang:language code(string) 'LanguageGetTranslatedLanguageNames':Provide translated language names. & $names:array of language code=> language name $code language of the preferred translations 'LanguageLinks':Manipulate a page 's language links. This is called in various places to allow extensions to define the effective language links for a page. $title:The page 's Title. & $links:Associative array mapping language codes to prefixed links of the form "language:title". & $linkFlags:Associative array mapping prefixed links to arrays of flags. Currently unused, but planned to provide support for marking individual language links in the UI, e.g. for featured articles. 'LinkBegin':Used when generating internal and interwiki links in Linker::link(), before processing starts. Return false to skip default processing and return $ret. See documentation for Linker::link() for details on the expected meanings of parameters. $skin:the Skin object $target:the Title that the link is pointing to & $html:the contents that the< a > tag should have(raw HTML) $result
Definition: hooks.txt:1528
$time
see documentation in includes Linker php for Linker::makeImageLink & $time
Definition: hooks.txt:1358
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
DB_MASTER
const DB_MASTER
Definition: Defines.php:56
Revision\getUserText
getUserText( $audience=self::FOR_PUBLIC, User $user=null)
Fetch revision's username if it's available to the specified audience.
Definition: Revision.php:844
WikiPage\insertProtectNullRevision
insertProtectNullRevision( $revCommentMsg, array $limit, array $expiry, $cascade, $reason)
Insert a new null revision for this page.
Definition: WikiPage.php:2503
WikiPage\pageDataFromId
pageDataFromId( $dbr, $id, $options=array())
Fetch a page record matching the requested ID.
Definition: WikiPage.php:338
WikiPage\loadPageData
loadPageData( $from='fromdb')
Set the general counter, title etc data loaded from some source.
Definition: WikiPage.php:354
RepoGroup\singleton
static singleton()
Get a RepoGroup instance.
Definition: RepoGroup.php:53
Content\serialize
serialize( $format=null)
Convenience method for serializing this Content object.
WikiPage\getAutoDeleteReason
getAutoDeleteReason(&$hasHistory)
Auto-generates a deletion reason.
Definition: WikiPage.php:3232
Revision\DELETED_COMMENT
const DELETED_COMMENT
Definition: Revision.php:66
ParserOutput
Definition: ParserOutput.php:24
WikiPage\doDeleteUpdates
doDeleteUpdates( $id, Content $content=null)
Do some database updates after deletion.
Definition: WikiPage.php:2822
WikiPage\doCascadeProtectionUpdates
doCascadeProtectionUpdates(ParserOutput $parserOutput)
Updates cascading protections.
Definition: WikiPage.php:3309
WikiPage\isBigDeletion
isBigDeletion()
Check whether the number of revisions of this page surpasses $wgDeleteRevisionsLimit.
Definition: WikiPage.php:3413
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
WikiPage\getRedirectTarget
getRedirectTarget()
If this page is a redirect, get its target.
Definition: WikiPage.php:889
Revision\newFromId
static newFromId( $id, $flags=0)
Load a page revision from a given revision ID number.
Definition: Revision.php:88
PoolWorkArticleView\doWork
doWork()
Definition: WikiPage.php:3565
content
per default it will return the text for text based content
Definition: contenthandler.txt:107
WikiPage\clearCacheFields
clearCacheFields()
Clear the object cache fields.
Definition: WikiPage.php:240
WikiPage\replaceSectionContent
replaceSectionContent( $section, Content $sectionContent, $sectionTitle='', $edittime=null)
Definition: WikiPage.php:1518
WikiPage\doEditUpdates
doEditUpdates(Revision $revision, User $user, array $options=array())
Do standard deferred updates after page edit.
Definition: WikiPage.php:2112
WikiPage\getUsedTemplates
getUsedTemplates()
Return a list of templates used by this article.
Definition: WikiPage.php:3377
WikiPage\getUndoContent
getUndoContent(Revision $undo, Revision $undoafter=null)
Get the content that needs to be saved in order to undo all revisions between $undo and $undoafter.
Definition: WikiPage.php:1425
WikiPage\updateRevisionOn
updateRevisionOn( $dbw, $revision, $lastRevision=null, $lastRevIsRedirect=null)
Update the page record to point to a newly saved revision.
Definition: WikiPage.php:1289
DataUpdate\runUpdates
static runUpdates( $updates)
Convenience method, calls doUpdate() on every DataUpdate in the array.
Definition: DataUpdate.php:79
Content\isValid
isValid()
Returns whether the content is valid.
Revision\newNullRevision
static newNullRevision( $dbw, $pageId, $summary, $minor)
Create a new null-revision for insertion into a page's history.
Definition: Revision.php:1567
$tables
namespace and then decline to actually register it RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist & $tables
Definition: hooks.txt:815
RecentChange\notifyEdit
static notifyEdit( $timestamp, &$title, $minor, &$user, $comment, $oldId, $lastTimestamp, $bot, $ip='', $oldSize=0, $newSize=0, $newId=0, $patrol=0)
Makes an entry in the database corresponding to an edit.
Definition: RecentChange.php:508
TitleArray\newFromResult
static newFromResult( $res)
Definition: TitleArray.php:38
WikiPage\doQuickEditContent
doQuickEditContent(Content $content, User $user, $comment='', $minor=false, $serialisation_format=null)
Edit an article without doing all that other stuff The article must already exist; link tables etc ar...
Definition: WikiPage.php:2258
HTMLFileCache\clearFileCache
static clearFileCache(Title $title)
Clear the file caches for a page for all actions.
Definition: HTMLFileCache.php:206
WikiPage\getUser
getUser( $audience=Revision::FOR_PUBLIC, User $user=null)
Definition: WikiPage.php:735
EDIT_FORCE_BOT
const EDIT_FORCE_BOT
Definition: Defines.php:193
$wgMemc
globals will be eliminated from MediaWiki replaced by an application object which would be passed to constructors Whether that would be an convenient solution remains to be but certainly PHP makes such object oriented programming models easier than they were in previous versions For the time being MediaWiki programmers will have to work in an environment with some global context At the time of globals were initialised on startup by MediaWiki of these were configuration which are documented in DefaultSettings php There is no comprehensive documentation for the remaining however some of the most important ones are listed below They are typically initialised either in index php or in Setup php For a description of the see design txt $wgTitle Title object created from the request URL $wgOut OutputPage object for HTTP response $wgUser User object for the user associated with the current request $wgLang Language object selected by user preferences $wgContLang Language object associated with the wiki being viewed $wgParser Parser object Parser extensions register their hooks here $wgRequest WebRequest to get request data $wgMemc
Definition: globals.txt:25
WikiPage\doEdit
doEdit( $text, $summary, $flags=0, $baseRevId=false, $user=null)
Change an existing article or create a new article.
Definition: WikiPage.php:1629
wfGetDB
& wfGetDB( $db, $groups=array(), $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:3659
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
WikiPage\hasViewableContent
hasViewableContent()
Check if this page is something we're going to be showing some sort of sensible content for.
Definition: WikiPage.php:463
Revision\setId
setId( $id)
Set the revision ID.
Definition: Revision.php:709
RecentChange\notifyNew
static notifyNew( $timestamp, &$title, $minor, &$user, $comment, $bot, $ip='', $size=0, $newId=0, $patrol=0)
Makes an entry in the database corresponding to page creation Note: the title object must be loaded w...
Definition: RecentChange.php:567
WikiPage\getTouched
getTouched()
Get the page_touched field.
Definition: WikiPage.php:534
WikiPage\getAutosummary
static getAutosummary( $oldtext, $newtext, $flags)
Return an applicable autosummary if one exists for the given edit.
Definition: WikiPage.php:3213
Revision\getContent
getContent( $audience=self::FOR_PUBLIC, User $user=null)
Fetch revision content if it's available to the specified audience.
Definition: Revision.php:999
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:2483
LinksUpdate\queueRecursiveJobsForTable
static queueRecursiveJobsForTable(Title $title, $table)
Queue a RefreshLinks job for any table.
Definition: LinksUpdate.php:238
ParserOutput\getImages
& getImages()
Definition: ParserOutput.php:130
WikiPage\doViewUpdates
doViewUpdates(User $user, $oldid=0)
Do standard deferred updates after page view (existing or missing page)
Definition: WikiPage.php:1172
wfDebugLog
wfDebugLog( $logGroup, $text, $dest='all')
Send a line to a supplementary debug log file, if configured, or main debug log if not.
Definition: GlobalFunctions.php:1040
UserArrayFromResult
return false to override stock group addition can be modified UserArrayFromResult
Definition: hooks.txt:2697
PoolWorkArticleView\$page
Page $page
Definition: WikiPage.php:3480
WikiPage\checkFlags
checkFlags( $flags)
Check flags and add EDIT_NEW or EDIT_UPDATE to them as needed.
Definition: WikiPage.php:1568
wfProfileIn
wfProfileIn( $functionname)
Begin profiling of a function.
Definition: Profiler.php:33
WikiPage\doEditContent
doEditContent(Content $content, $summary, $flags=0, $baseRevId=false, User $user=null, $serialisation_format=null)
Change an existing article or create a new article.
Definition: WikiPage.php:1685
WikiPage\onArticleEdit
static onArticleEdit( $title)
Purge caches on page update etc.
Definition: WikiPage.php:3134
$ret
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 & $ret
Definition: hooks.txt:1530
$from
$from
Definition: importImages.php:90
$serialized
foreach( $res as $row) $serialized
Definition: testCompression.php:77
WikiPage
Class representing a MediaWiki article and history.
Definition: WikiPage.php:37
wfArrayDiff2
if(!defined( 'MEDIAWIKI')) wfArrayDiff2( $a, $b)
Like array_diff( $a, $b ) except that it works with two-dimensional arrays.
Definition: GlobalFunctions.php:113
LinksUpdate
See docs/deferred.txt.
Definition: LinksUpdate.php:28
Status\newGood
static newGood( $value=null)
Factory function for good results.
Definition: Status.php:77
WikiPage\getUserText
getUserText( $audience=Revision::FOR_PUBLIC, User $user=null)
Definition: WikiPage.php:773
WikiPage\$mCounter
int null $mCounter
Definition: WikiPage.php:85
PoolWorkArticleView
Definition: WikiPage.php:3476
NS_FILE
const NS_FILE
Definition: Defines.php:85
$params
$params
Definition: styleTest.css.php:40
$limit
if( $sleep) $limit
Definition: importImages.php:99
WikiPage\makeParserOptions
makeParserOptions( $context)
Get parser options suitable for rendering the primary article wikitext.
Definition: WikiPage.php:2012
PoolWorkArticleView\__construct
__construct(Page $page, ParserOptions $parserOptions, $revid, $useParserCache, $content=null)
Constructor.
Definition: WikiPage.php:3519
WikiPage\getRedirectURL
getRedirectURL( $rt)
Get the Title object or URL to use for a redirect.
Definition: WikiPage.php:971
wfReadOnly
wfReadOnly()
Check whether the wiki is in read-only mode.
Definition: GlobalFunctions.php:1313
wfMsgReplaceArgs
wfMsgReplaceArgs( $message, $args)
Replace message parameter keys on the given formatted output.
Definition: GlobalFunctions.php:1590
User\newFromName
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:388
WikiPage\getRevision
getRevision()
Get the latest revision.
Definition: WikiPage.php:642
Revision\getContentHandler
getContentHandler()
Returns the content handler appropriate for this revision's content model.
Definition: Revision.php:1114
$s
$s
Definition: mergeMessageFileList.php:156
WikiPage\getContributors
getContributors()
Get a list of users who have edited this article, not including the user who made the most recent rev...
Definition: WikiPage.php:1012
Content\getDeletionUpdates
getDeletionUpdates(WikiPage $page, ParserOutput $parserOutput=null)
Returns a list of updates to perform when this content is deleted.
$wgContLang
this class mediates it Skin Encapsulates a look and feel for the wiki All of the functions that render HTML and make choices about how to render it are here and are called from various other places when and is meant to be subclassed with other skins that may override some of its functions The User object contains a reference to a and so rather than having a global skin object we just rely on the global User and get the skin with $wgUser and also has some character encoding functions and other locale stuff The current user interface language is instantiated as and the content language as $wgContLang
Definition: design.txt:56
WikiPage\getComment
getComment( $audience=Revision::FOR_PUBLIC, User $user=null)
Definition: WikiPage.php:791
CONTENT_MODEL_WIKITEXT
const CONTENT_MODEL_WIKITEXT
Definition: Defines.php:283
Revision\newFromPageId
static newFromPageId( $pageId, $revId=0, $flags=0)
Load either the current, or a specified, revision that's attached to a given page ID.
Definition: Revision.php:137
IDBAccessObject\READ_LATEST
const READ_LATEST
Definition: IDBAccessObject.php:49
$flags
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition: hooks.txt:2113
IDBAccessObject
Interface for database access objects.
Definition: IDBAccessObject.php:47
Revision\getId
getId()
Get revision ID.
Definition: Revision.php:699
Revision\getContentModel
getContentModel()
Returns the content model for this revision.
Definition: Revision.php:1077
Revision\insertOn
insertOn( $dbw)
Insert a new revision into the database, returning the new revision ID number on success and dies hor...
Definition: Revision.php:1328
Content\getSize
getSize()
Returns the content's nominal size in bogo-bytes.
WikiPage\$mLatest
$mLatest
Definition: WikiPage.php:49
WikiPage\getActionOverrides
getActionOverrides()
Returns overrides for action handlers.
Definition: WikiPage.php:199
WikiPage\flattenRestrictions
static flattenRestrictions( $limit)
Take an array of page restrictions and flatten it to a string suitable for insertion into the page_re...
Definition: WikiPage.php:2633
WikiPage\pageData
pageData( $dbr, $conditions, $options=array())
Fetch a page record with the given conditions.
Definition: WikiPage.php:303
DeferredUpdates\doUpdates
static doUpdates( $commit='')
Do any deferred updates and clear the list.
Definition: DeferredUpdates.php:82
Content\prepareSave
prepareSave(WikiPage $page, $flags, $baseRevId, User $user)
Prepare Content for saving.
WikiPage\pageDataFromTitle
pageDataFromTitle( $dbr, $title, $options=array())
Fetch a page record matching the Title object's namespace and title using a sanitized title string.
Definition: WikiPage.php:324
WikiPage\$mTitle
Title $mTitle
Definition: WikiPage.php:42
WikiPage\prepareTextForEdit
prepareTextForEdit( $text, $revid=null, User $user=null)
Prepare text which is about to be saved.
Definition: WikiPage.php:2029
ContentHandler\runLegacyHooks
static runLegacyHooks( $event, $args=array(), $warn=null)
Call a legacy hook that uses text instead of Content objects.
Definition: ContentHandler.php:1053
WikiPage\$mTouched
string $mTouched
Definition: WikiPage.php:77
SquidUpdate\newSimplePurge
static newSimplePurge(Title $title)
Definition: SquidUpdate.php:113
WikiPage\protectDescription
protectDescription(array $limit, array $expiry)
Builds the description to serve as comment for the edit.
Definition: WikiPage.php:2570
$dbr
$dbr
Definition: testCompression.php:48
IDBAccessObject\READ_LOCKING
const READ_LOCKING
Definition: IDBAccessObject.php:50
Status
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition: Status.php:40
PoolWorkArticleView\$parserOptions
ParserOptions $parserOptions
Definition: WikiPage.php:3492
ContentHandler\getLocalizedName
static getLocalizedName( $name)
Returns the localized name for a given content model.
Definition: ContentHandler.php:360
Content\getWikitextForTransclusion
getWikitextForTransclusion()
Revision
Definition: Revision.php:26
IDBAccessObject\READ_NORMAL
const READ_NORMAL
Definition: IDBAccessObject.php:53
WikiPage\insertOn
insertOn( $dbw)
Insert a new empty page record for this article.
Definition: WikiPage.php:1244
PoolWorkArticleView\$parserOutput
ParserOutput bool $parserOutput
Definition: WikiPage.php:3500
WikiPage\supportsSections
supportsSections()
Returns true if this page's content model supports sections.
Definition: WikiPage.php:1503
WikiPage\getRawText
getRawText()
Get the text of the current revision.
Definition: WikiPage.php:699
WikiCategoryPage
Special handling for category pages.
Definition: WikiCategoryPage.php:26
$total
$total
Definition: Utf8Test.php:92
MWException
MediaWiki exception.
Definition: MWException.php:26
wfMemcKey
wfMemcKey()
Get a cache key.
Definition: GlobalFunctions.php:3580
WikiPage\factory
static factory(Title $title)
Create a WikiPage object of the appropriate class for the given title.
Definition: WikiPage.php:103
WikiPage\getMinorEdit
getMinorEdit()
Returns true if last revision was marked as "minor edit".
Definition: WikiPage.php:805
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
Definition: GlobalFunctions.php:1127
WikiPage\isParserCacheUsed
isParserCacheUsed(ParserOptions $parserOptions, $oldid)
Should the parser cache be used?
Definition: WikiPage.php:1117
WikiPage\doDeleteArticleReal
doDeleteArticleReal( $reason, $suppress=false, $id=0, $commit=true, &$error='', User $user=null)
Back-end article deletion Deletes the article with database consistency, writes logs,...
Definition: WikiPage.php:2688
WikiPage\updateCategoryCounts
updateCategoryCounts(array $added, array $deleted)
Update all the appropriate counts in the category table, given that we've added the categories $added...
Definition: WikiPage.php:3243
ParserOptions\newFromUserAndLang
static newFromUserAndLang(User $user, Language $lang)
Get a ParserOptions object from a given user and language.
Definition: ParserOptions.php:386
PoolWorkArticleView\$error
Status bool $error
Definition: WikiPage.php:3508
Title\newFromRow
static newFromRow( $row)
Make a Title object from a DB row.
Definition: Title.php:345
wfIncrStats
wfIncrStats( $key, $count=1)
Increment a statistics counter.
Definition: GlobalFunctions.php:1304
WikiPage\selectFields
static selectFields()
Return the list of revision fields that should be selected to create a new page.
Definition: WikiPage.php:271
InfoAction\invalidateCache
static invalidateCache(Title $title)
Clear the info cache for a given Title.
Definition: InfoAction.php:66
WikiPage\clearPreparedEdit
clearPreparedEdit()
Clear the mPreparedEdit cache field, as may be needed by mutable content types.
Definition: WikiPage.php:261
WikiPage\doDeleteArticle
doDeleteArticle( $reason, $suppress=false, $id=0, $commit=true, &$error='', User $user=null)
Same as doDeleteArticleReal(), but returns a simple boolean.
Definition: WikiPage.php:2664
WikiPage\$mDataLoadedFrom
$mDataLoadedFrom
Definition: WikiPage.php:61
WikiPage\getId
getId()
Definition: WikiPage.php:438
WikiPage\insertRedirectEntry
insertRedirectEntry( $rt)
Insert or update the redirect table entry for this page to indicate it redirects to $rt .
Definition: WikiPage.php:941
WikiPage\$mTimestamp
$mTimestamp
Definition: WikiPage.php:73
WikiPage\doUpdateRestrictions
doUpdateRestrictions(array $limit, array $expiry, &$cascade, $reason, User $user)
Update the article's restriction field, and leave a log entry.
Definition: WikiPage.php:2293
WikiPage\onArticleDelete
static onArticleDelete( $title)
Clears caches when article is deleted.
Definition: WikiPage.php:3087
DeferredUpdates\addUpdate
static addUpdate(DeferrableUpdate $update)
Add an update to the deferred list.
Definition: DeferredUpdates.php:51
WikiPage\getTitle
getTitle()
Get the title object of the article.
Definition: WikiPage.php:221
wfTimestampOrNull
wfTimestampOrNull( $outputtype=TS_UNIX, $ts=null)
Return a formatted timestamp, or null if input is null.
Definition: GlobalFunctions.php:2501
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
WikiPage\getContent
getContent( $audience=Revision::FOR_PUBLIC, User $user=null)
Get the content of the current revision.
Definition: WikiPage.php:663
WikiPage\exists
exists()
Definition: WikiPage.php:448
wfRunHooks
wfRunHooks( $event, array $args=array(), $deprecatedVersion=null)
Call hook functions defined in $wgHooks.
Definition: GlobalFunctions.php:4010
User\isIP
static isIP( $name)
Does the string match an anonymous IPv4 address?
Definition: User.php:554
WikiPage\$mRedirectTarget
Title $mRedirectTarget
Definition: WikiPage.php:65
WikiPage\__construct
__construct(Title $title)
Constructor and clear the article.
Definition: WikiPage.php:91
WikiPage\checkTouched
checkTouched()
Loads page_touched and returns a value indicating if it should be used.
Definition: WikiPage.php:523
WikiPage\getLinksTimestamp
getLinksTimestamp()
Get the page_links_updated field.
Definition: WikiPage.php:545
array
the array() calling protocol came about after MediaWiki 1.4rc1.
List of Api Query prop modules.
WikiPage\prepareContentForEdit
prepareContentForEdit(Content $content, $revid=null, User $user=null, $serialization_format=null)
Prepare content which is about to be saved.
Definition: WikiPage.php:2048
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
$comment
$comment
Definition: importImages.php:107
ViewCountUpdate
Update for the 'page_counter' field, when $wgDisableCounters is false.
Definition: ViewCountUpdate.php:30
wfTimestampNow
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
Definition: GlobalFunctions.php:2514
WikiPage\setTimestamp
setTimestamp( $ts)
Set the page timestamp (use only to avoid DB queries)
Definition: WikiPage.php:722
NS_CATEGORY
const NS_CATEGORY
Definition: Defines.php:93
WikiPage\getLatest
getLatest()
Get the page_latest field.
Definition: WikiPage.php:556
ParserOptions\getStubThreshold
getStubThreshold()
Definition: ParserOptions.php:239
SiteStatsUpdate
Class for handling updates to the site_stats table.
Definition: SiteStatsUpdate.php:24
IDBAccessObject\READ_NONE
const READ_NONE
Definition: IDBAccessObject.php:56
WikiPage\doPurge
doPurge()
Perform the actions of a page purging.
Definition: WikiPage.php:1192
WikiPage\isCountable
isCountable( $editInfo=false)
Determine whether a page would be suitable for being counted as an article in the site_stats table ba...
Definition: WikiPage.php:845
false
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:188
MessageCache\singleton
static singleton()
Get the signleton instance of this class.
Definition: MessageCache.php:101
WikiPage\getContentModel
getContentModel()
Returns the page's content model id (see the CONTENT_MODEL_XXX constants).
Definition: WikiPage.php:502
Revision\getPrevious
getPrevious()
Get previous revision for this title.
Definition: Revision.php:1149
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
WikiPage\replaceSection
replaceSection( $section, $text, $sectionTitle='', $edittime=null)
Definition: WikiPage.php:1473
PoolWorkArticleView\$cacheKey
string $cacheKey
Definition: WikiPage.php:3484
$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
WikiPage\updateIfNewerOn
updateIfNewerOn( $dbw, $revision)
If the given revision is newer than the currently set page_latest, update the page record.
Definition: WikiPage.php:1385
$ok
$ok
Definition: UtfNormalTest.php:71
$section
$section
Definition: Utf8Test.php:88
WikiPage\setLastEdit
setLastEdit(Revision $revision)
Set the latest revision.
Definition: WikiPage.php:633
TS_MW
const TS_MW
MediaWiki concatenated string timestamp (YYYYMMDDHHMMSS)
Definition: GlobalFunctions.php:2431
Revision\newFromTitle
static newFromTitle( $title, $id=0, $flags=0)
Load either the current, or a specified, revision that's attached to a given title.
Definition: Revision.php:106
wfDebug
wfDebug( $text, $dest='all')
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:933
HTMLCacheUpdate
Class to invalidate the HTML cache of all the pages linking to a given title.
Definition: HTMLCacheUpdate.php:29
PoolWorkArticleView\getError
getError()
Get a Status object in case of error or false otherwise.
Definition: WikiPage.php:3558
Content\equals
equals(Content $that=null)
Returns true if this Content objects is conceptually equivalent to the given Content object.
WikiPage\getLastNAuthors
getLastNAuthors( $num, $revLatest=0)
Get the last N authors.
Definition: WikiPage.php:1064
PatrolLog\record
static record( $rc, $auto=false, User $user=null)
Record a log event for a change being patrolled.
Definition: PatrolLog.php:39
$sectionContent
do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty & $sectionContent
Definition: hooks.txt:1956
$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
DeferredUpdates\addHTMLCacheUpdate
static addHTMLCacheUpdate( $title, $table)
HTMLCacheUpdates are the most common deferred update people use.
Definition: DeferredUpdates.php:62
WikiPage\protectDescriptionLog
protectDescriptionLog(array $limit, array $expiry)
Builds the description to serve as comment for the log entry.
Definition: WikiPage.php:2611
ParserCache\singleton
static singleton()
Get an instance of this object.
Definition: ParserCache.php:35
WikiPage\insertRedirect
insertRedirect()
Insert an entry for this page into the redirect table.
Definition: WikiPage.php:925
EDIT_UPDATE
const EDIT_UPDATE
Definition: Defines.php:190
NS_MEDIA
const NS_MEDIA
Definition: Defines.php:67
WikiPage\getCreator
getCreator( $audience=Revision::FOR_PUBLIC, User $user=null)
Get the User object of the user who created the page.
Definition: WikiPage.php:754
Content\getSecondaryDataUpdates
getSecondaryDataUpdates(Title $title, Content $old=null, $recursive=true, ParserOutput $parserOutput=null)
Returns a list of DataUpdate objects for recording information about this Content in some secondary d...
WikiPage\newFromID
static newFromID( $id, $from='fromdb')
Constructor from a page id.
Definition: WikiPage.php:136
Revision\newFromRow
static newFromRow( $row)
Definition: Revision.php:206
WikiPage\updateRestrictions
updateRestrictions( $limit=array(), $reason='', &$cascade=0, $expiry=array(), User $user=null)
Update the article's restriction field, and leave a log entry.
Definition: WikiPage.php:3440
wfEscapeWikiText
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
Definition: GlobalFunctions.php:2077
EDIT_DEFER_UPDATES
const EDIT_DEFER_UPDATES
Definition: Defines.php:194
Revision\RAW
const RAW
Definition: Revision.php:74
WikiPage\getHiddenCategories
getHiddenCategories()
Returns a list of hidden categories this page is a member of.
Definition: WikiPage.php:3180
WikiPage\newFromRow
static newFromRow( $row, $from='fromdb')
Constructor from a database row.
Definition: WikiPage.php:163
WikiPage\getOldestRevision
getOldestRevision()
Get the Revision object of the oldest revision.
Definition: WikiPage.php:567
$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
ParserOutput\getTemplates
& getTemplates()
Definition: ParserOutput.php:128
WikiPage\loadLastEdit
loadLastEdit()
Loads everything except the text This isn't necessary for all uses, so it's only done if needed.
Definition: WikiPage.php:607
WikiPage\getText
getText( $audience=Revision::FOR_PUBLIC, User $user=null)
Get the text of the current revision.
Definition: WikiPage.php:683
WikiPage\followRedirect
followRedirect()
Get the Title object or URL this page redirects to.
Definition: WikiPage.php:960
Content\preSaveTransform
preSaveTransform(Title $title, User $user, ParserOptions $parserOptions)
Returns a Content object with pre-save transformations applied (or this object if no transformations ...
$summary
$summary
Definition: importImages.php:120
Content
Base interface for content objects.
Definition: Content.php:34
EDIT_NEW
const EDIT_NEW
Definition: Defines.php:189
WikiPage\loadFromRow
loadFromRow( $data, $from)
Load the object from a database row.
Definition: WikiPage.php:397
WikiPage\formatExpiry
formatExpiry( $expiry)
Definition: WikiPage.php:2545
$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
WikiPage\doQuickEdit
doQuickEdit( $text, User $user, $comment='', $minor=0)
Edit an article without doing all that other stuff The article must already exist; link tables etc ar...
Definition: WikiPage.php:2240
DB_SLAVE
const DB_SLAVE
Definition: Defines.php:55
Title
Represents a title within MediaWiki.
Definition: Title.php:35
EDIT_AUTOSUMMARY
const EDIT_AUTOSUMMARY
Definition: Defines.php:195
wfRandom
wfRandom()
Get a random decimal value between 0 and 1, in a way not likely to give duplicate values for any real...
Definition: GlobalFunctions.php:281
ContentHandler\getContentText
static getContentText(Content $content=null)
Convenience function for getting flat text from a Content object.
Definition: ContentHandler.php:94
User\isAllowedAny
isAllowedAny()
Check if user is allowed to access a feature / make an action.
Definition: User.php:3000
wfReadOnlyReason
wfReadOnlyReason()
Get the value of $wgReadOnly or the contents of $wgReadOnlyFile.
Definition: GlobalFunctions.php:1322
WikiPage\int
int
one of the READ_* constants
Definition: WikiPage.php:61
$wgParser
$wgParser
Definition: Setup.php:567
WikiPage\commitRollback
commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser)
Backend implementation of doRollback(), please refer there for parameter and return value documentati...
Definition: WikiPage.php:2914
WikiPage\getUndoText
getUndoText(Revision $undo, Revision $undoafter=null)
Get the text that needs to be saved in order to undo all revisions between $undo and $undoafter.
Definition: WikiPage.php:1439
WikiPage\$mId
int $mId
Definition: WikiPage.php:57
WikiPage\$mIsRedirect
$mIsRedirect
Definition: WikiPage.php:48
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
PoolWorkArticleView\$revid
integer $revid
Definition: WikiPage.php:3488
RecentChange\purgeExpiredChanges
static purgeExpiredChanges()
Purge expired changes from the recentchanges table.
Definition: RecentChange.php:832
Revision\loadFromTimestamp
static loadFromTimestamp( $db, $title, $timestamp)
Load the revision for the given title with the given timestamp.
Definition: Revision.php:277
WikiPage\convertSelectType
static convertSelectType( $type)
Convert 'fromdb', 'fromdbmaster' and 'forupdate' to READ_* constants.
Definition: WikiPage.php:175
$source
if(PHP_SAPI !='cli') $source
Definition: mwdoc-filter.php:18
PoolCounterWork
Class for dealing with PoolCounters using class members.
Definition: PoolCounterWork.php:27
Content\getModel
getModel()
Returns the ID of the content model used by this Content object.
ManualLogEntry
Class for creating log entries manually, for example to inject them into the database.
Definition: LogEntry.php:339
WikiPage\getCachedLastEditTime
getCachedLastEditTime()
Get the cached timestamp for the last time the page changed.
Definition: WikiPage.php:819
WikiPage\string
string
timestamp of the current revision or empty string if not loaded
Definition: WikiPage.php:73
PoolWorkArticleView\getParserOutput
getParserOutput()
Get the ParserOutput from this object, or false in case of failure.
Definition: WikiPage.php:3540
WikiFilePage
Special handling for file pages.
Definition: WikiFilePage.php:28
Revision\isMinor
isMinor()
Definition: Revision.php:906
PoolWorkArticleView\error
error( $status)
Definition: WikiPage.php:3662
wfWarn
wfWarn( $msg, $callerOffset=1, $level=E_USER_NOTICE)
Send a warning either to the debug log or in a PHP error depending on $wgDevelopmentWarnings.
Definition: GlobalFunctions.php:1141
NS_MEDIAWIKI
const NS_MEDIAWIKI
Definition: Defines.php:87
Category\newFromName
static newFromName( $name)
Factory function.
Definition: Category.php:114
EDIT_MINOR
const EDIT_MINOR
Definition: Defines.php:191
EDIT_SUPPRESS_RC
const EDIT_SUPPRESS_RC
Definition: Defines.php:192
WikiPage\preSaveTransform
preSaveTransform( $text, User $user=null, ParserOptions $popts=null)
This function is called right before saving the wikitext, so we can do things like signatures and lin...
Definition: WikiPage.php:3393
WikiPage\estimateRevisionCount
estimateRevisionCount()
Get the approximate revision count of this page.
Definition: WikiPage.php:3424
PoolWorkArticleView\$isDirty
bool $isDirty
Definition: WikiPage.php:3504
WikiPage\doRollback
doRollback( $fromP, $summary, $token, $bot, &$resultDetails, User $user)
Roll back the most recent consecutive set of edits to a page from the same user; fails if there are n...
Definition: WikiPage.php:2872
Content\getParserOutput
getParserOutput(Title $title, $revId=null, ParserOptions $options=null, $generateHtml=true)
Parse the Content object and generate a ParserOutput from the result.
WikiPage\getTimestamp
getTimestamp()
Definition: WikiPage.php:708
WikiPage\updateRedirectOn
updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect=null)
Add row to the redirect table if this is a redirect, remove otherwise.
Definition: WikiPage.php:1350
$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:2573
WikiPage\$mLinksUpdated
string $mLinksUpdated
Definition: WikiPage.php:81
Revision\selectFields
static selectFields()
Return the list of revision fields that should be selected to create a new revision.
Definition: Revision.php:405
WikiPage\$mPreparedEdit
$mPreparedEdit
Definition: WikiPage.php:53
$e
if( $useReadline) $e
Definition: eval.php:66
WikiPage\isRedirect
isRedirect()
Tests if the article content represents a redirect.
Definition: WikiPage.php:483
WikiPage\$mLastRevision
Revision $mLastRevision
Definition: WikiPage.php:69
WikiPage\setCachedLastEditTime
setCachedLastEditTime( $timestamp)
Set the cached timestamp for the last time the page changed.
Definition: WikiPage.php:831
WikiPage\$mDataLoaded
$mDataLoaded
Definition: WikiPage.php:47
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:59
$res
$res
Definition: database.txt:21
WikiPage\getDeletionUpdates
getDeletionUpdates(Content $content=null)
Returns a list of updates to be performed when this page is deleted.
Definition: WikiPage.php:3457
LinkCache\singleton
static & singleton()
Get an instance of this class.
Definition: LinkCache.php:49
WikiPage\getContentHandler
getContentHandler()
Returns the ContentHandler instance to be used to deal with the content of this WikiPage.
Definition: WikiPage.php:213
$retval
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 incomplete not yet checked for validity & $retval
Definition: hooks.txt:237
PoolWorkArticleView\fallback
fallback()
Definition: WikiPage.php:3643
PoolWorkArticleView\getCachedWork
getCachedWork()
Definition: WikiPage.php:3628
Revision\DELETED_TEXT
const DELETED_TEXT
Definition: Revision.php:65
PoolWorkArticleView\getIsDirty
getIsDirty()
Get whether the ParserOutput is a dirty one (i.e.
Definition: WikiPage.php:3549
WikiPage\getCount
getCount()
Definition: WikiPage.php:470
Title\purgeExpiredRestrictions
static purgeExpiredRestrictions()
Purge expired restrictions from the page_restrictions table.
Definition: Title.php:2988
WikiPage\getParserOutput
getParserOutput(ParserOptions $parserOptions, $oldid=null)
Get a ParserOutput for the given ParserOptions and revision ID.
Definition: WikiPage.php:1138
ParserOptions\newFromUser
static newFromUser( $user)
Get a ParserOptions object from a given user.
Definition: ParserOptions.php:375
User\isAllowed
isAllowed( $action='')
Internal mechanics of testing a permission.
Definition: User.php:3030
WikiPage\clear
clear()
Clear the object.
Definition: WikiPage.php:229
Status\newFatal
static newFatal( $message)
Factory function for fatal errors.
Definition: Status.php:63
PoolWorkArticleView\$content
Content null $content
Definition: WikiPage.php:3496
$changed
$changed
Definition: UtfNormalGenerate.php:130
page
do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values my talk page
Definition: hooks.txt:1956
$type
$type
Definition: testCompression.php:46