MediaWiki  master
RecentChange.php
Go to the documentation of this file.
1 <?php
32 use Wikimedia\Assert\Assert;
33 use Wikimedia\IPUtils;
34 
80 class RecentChange implements Taggable {
82 
83  // Constants for the rc_source field. Extensions may also have
84  // their own source constants.
85  public const SRC_EDIT = 'mw.edit';
86  public const SRC_NEW = 'mw.new';
87  public const SRC_LOG = 'mw.log';
88  public const SRC_EXTERNAL = 'mw.external'; // obsolete
89  public const SRC_CATEGORIZE = 'mw.categorize';
90 
91  public const PRC_UNPATROLLED = 0;
92  public const PRC_PATROLLED = 1;
93  public const PRC_AUTOPATROLLED = 2;
94 
98  public const SEND_NONE = true;
99 
103  public const SEND_FEED = false;
104 
106  public $mAttribs = [];
107  public $mExtra = [];
108 
112  private $mPage = null;
113 
117  private $mPerformer = null;
118 
119  public $numberofWatchingusers = 0; # Dummy to prevent error message in SpecialRecentChangesLinked
121 
126 
130  public $counter = -1;
131 
135  private $tags = [];
136 
140  private $editResult = null;
141 
142  private const CHANGE_TYPES = [
143  'edit' => RC_EDIT,
144  'new' => RC_NEW,
145  'log' => RC_LOG,
146  'external' => RC_EXTERNAL,
147  'categorize' => RC_CATEGORIZE,
148  ];
149 
150  # Factory methods
151 
156  public static function newFromRow( $row ) {
157  $rc = new RecentChange;
158  $rc->loadFromRow( $row );
159 
160  return $rc;
161  }
162 
170  public static function parseToRCType( $type ) {
171  if ( is_array( $type ) ) {
172  $retval = [];
173  foreach ( $type as $t ) {
174  $retval[] = self::parseToRCType( $t );
175  }
176 
177  return $retval;
178  }
179 
180  if ( !array_key_exists( $type, self::CHANGE_TYPES ) ) {
181  throw new MWException( "Unknown type '$type'" );
182  }
183  return self::CHANGE_TYPES[$type];
184  }
185 
192  public static function parseFromRCType( $rcType ) {
193  return array_search( $rcType, self::CHANGE_TYPES, true ) ?: "$rcType";
194  }
195 
203  public static function getChangeTypes() {
204  return array_keys( self::CHANGE_TYPES );
205  }
206 
213  public static function newFromId( $rcid ) {
214  return self::newFromConds( [ 'rc_id' => $rcid ], __METHOD__ );
215  }
216 
226  public static function newFromConds(
227  $conds,
228  $fname = __METHOD__,
229  $dbType = DB_REPLICA
230  ) {
231  $db = wfGetDB( $dbType );
232  $rcQuery = self::getQueryInfo();
233  $row = $db->selectRow(
234  $rcQuery['tables'], $rcQuery['fields'], $conds, $fname, [], $rcQuery['joins']
235  );
236  if ( $row !== false ) {
237  return self::newFromRow( $row );
238  } else {
239  return null;
240  }
241  }
242 
257  public static function getQueryInfo() {
258  $commentQuery = CommentStore::getStore()->getJoin( 'rc_comment' );
259  return [
260  'tables' => [
261  'recentchanges',
262  'recentchanges_actor' => 'actor'
263  ] + $commentQuery['tables'],
264  'fields' => [
265  'rc_id',
266  'rc_timestamp',
267  'rc_namespace',
268  'rc_title',
269  'rc_minor',
270  'rc_bot',
271  'rc_new',
272  'rc_cur_id',
273  'rc_this_oldid',
274  'rc_last_oldid',
275  'rc_type',
276  'rc_source',
277  'rc_patrolled',
278  'rc_ip',
279  'rc_old_len',
280  'rc_new_len',
281  'rc_deleted',
282  'rc_logid',
283  'rc_log_type',
284  'rc_log_action',
285  'rc_params',
286  'rc_actor',
287  'rc_user' => 'recentchanges_actor.actor_user',
288  'rc_user_text' => 'recentchanges_actor.actor_name',
289  ] + $commentQuery['fields'],
290  'joins' => [
291  'recentchanges_actor' => [ 'JOIN', 'actor_id=rc_actor' ]
292  ] + $commentQuery['joins'],
293  ];
294  }
295 
296  public function __construct() {
298  'mTitle',
299  '1.37',
300  function () {
301  return Title::castFromPageReference( $this->mPage );
302  },
303  function ( ?Title $title ) {
304  $this->mPage = $title;
305  }
306  );
307  }
308 
309  # Accessors
310 
314  public function setAttribs( $attribs ) {
315  $this->mAttribs = $attribs;
316  }
317 
321  public function setExtra( $extra ) {
322  $this->mExtra = $extra;
323  }
324 
329  public function getTitle() {
330  $this->mPage = Title::castFromPageReference( $this->getPage() );
331  return $this->mPage ?: Title::makeTitle( NS_SPECIAL, 'BadTitle' );
332  }
333 
338  public function getPage(): ?PageReference {
339  if ( !$this->mPage ) {
340  // NOTE: As per the 1.36 release, we always provide rc_title,
341  // even in cases where it doesn't really make sense.
342  // In the future, rc_title may be nullable, or we may use
343  // empty strings in entries that do not refer to a page.
344  if ( ( $this->mAttribs['rc_title'] ?? '' ) === '' ) {
345  return null;
346  }
347 
348  // XXX: We could use rc_cur_id to create a PageIdentityValue,
349  // at least if it's not a special page.
350  // However, newForCategorization() puts the ID of the categorized page into
351  // rc_cur_id, but the title of the category page into rc_title.
352  $this->mPage = new PageReferenceValue(
353  (int)$this->mAttribs['rc_namespace'],
354  $this->mAttribs['rc_title'],
355  PageReference::LOCAL
356  );
357  }
358 
359  return $this->mPage;
360  }
361 
368  public function getPerformer(): User {
369  wfDeprecated( __METHOD__, '1.36' );
370  if ( !$this->mPerformer instanceof User ) {
371  $this->mPerformer = User::newFromIdentity( $this->getPerformerIdentity() );
372  }
373 
374  return $this->mPerformer;
375  }
376 
384  public function getPerformerIdentity(): UserIdentity {
385  if ( !$this->mPerformer ) {
386  $this->mPerformer = $this->getUserIdentityFromAnyId(
387  $this->mAttribs['rc_user'] ?? null,
388  $this->mAttribs['rc_user_text'] ?? null,
389  $this->mAttribs['rc_actor'] ?? null
390  );
391  }
392 
393  return $this->mPerformer;
394  }
395 
405  public function save( $send = self::SEND_FEED ) {
407 
408  $dbw = wfGetDB( DB_PRIMARY );
409  if ( !is_array( $this->mExtra ) ) {
410  $this->mExtra = [];
411  }
412 
413  if ( !$wgPutIPinRC ) {
414  $this->mAttribs['rc_ip'] = '';
415  }
416 
417  # Strict mode fixups (not-NULL fields)
418  foreach ( [ 'minor', 'bot', 'new', 'patrolled', 'deleted' ] as $field ) {
419  $this->mAttribs["rc_$field"] = (int)$this->mAttribs["rc_$field"];
420  }
421  # ...more fixups (NULL fields)
422  foreach ( [ 'old_len', 'new_len' ] as $field ) {
423  $this->mAttribs["rc_$field"] = isset( $this->mAttribs["rc_$field"] )
424  ? (int)$this->mAttribs["rc_$field"]
425  : null;
426  }
427 
428  # If our database is strict about IP addresses, use NULL instead of an empty string
429  $strictIPs = $dbw->getType() === 'postgres'; // legacy
430  if ( $strictIPs && $this->mAttribs['rc_ip'] == '' ) {
431  unset( $this->mAttribs['rc_ip'] );
432  }
433 
434  $row = $this->mAttribs;
435 
436  # Trim spaces on user supplied text
437  $row['rc_comment'] = trim( $row['rc_comment'] );
438 
439  # Fixup database timestamps
440  $row['rc_timestamp'] = $dbw->timestamp( $row['rc_timestamp'] );
441 
442  # # If we are using foreign keys, an entry of 0 for the page_id will fail, so use NULL
443  if ( $row['rc_cur_id'] == 0 ) {
444  unset( $row['rc_cur_id'] );
445  }
446 
447  # Convert mAttribs['rc_comment'] for CommentStore
448  $comment = $row['rc_comment'];
449  unset( $row['rc_comment'], $row['rc_comment_text'], $row['rc_comment_data'] );
450  $row += CommentStore::getStore()->insert( $dbw, 'rc_comment', $comment );
451 
452  # Normalize UserIdentity to actor ID
453  $user = $this->getPerformerIdentity();
454  $actorStore = MediaWikiServices::getInstance()->getActorStore();
455  $row['rc_actor'] = $actorStore->acquireActorId( $user, $dbw );
456  unset( $row['rc_user'], $row['rc_user_text'] );
457 
458  # Don't reuse an existing rc_id for the new row, if one happens to be
459  # set for some reason.
460  unset( $row['rc_id'] );
461 
462  # Insert new row
463  $dbw->insert( 'recentchanges', $row, __METHOD__ );
464 
465  # Set the ID
466  $this->mAttribs['rc_id'] = $dbw->insertId();
467 
468  # Notify extensions
469  Hooks::runner()->onRecentChange_save( $this );
470 
471  // Apply revert tags (if needed)
472  if ( $this->editResult !== null && count( $this->editResult->getRevertTags() ) ) {
474  $this->editResult->getRevertTags(),
475  $this->mAttribs['rc_id'],
476  $this->mAttribs['rc_this_oldid'],
477  $this->mAttribs['rc_logid'],
478  FormatJson::encode( $this->editResult ),
479  $this
480  );
481  }
482 
483  if ( count( $this->tags ) ) {
484  // $this->tags may contain revert tags we already applied above, they will
485  // just be ignored.
487  $this->tags,
488  $this->mAttribs['rc_id'],
489  $this->mAttribs['rc_this_oldid'],
490  $this->mAttribs['rc_logid'],
491  null,
492  $this
493  );
494  }
495 
496  if ( $send === self::SEND_FEED ) {
497  // Emit the change to external applications via RCFeeds.
498  $this->notifyRCFeeds();
499  }
500 
501  # E-mail notifications
502  if ( $wgUseEnotif || $wgShowUpdatedMarker ) {
503  $userFactory = MediaWikiServices::getInstance()->getUserFactory();
504  $editor = $userFactory->newFromUserIdentity( $this->getPerformerIdentity() );
505  $page = $this->getPage();
507 
508  // Never send an RC notification email about categorization changes
509  if (
510  $title &&
511  Hooks::runner()->onAbortEmailNotification( $editor, $title, $this ) &&
512  $this->mAttribs['rc_type'] != RC_CATEGORIZE
513  ) {
514  // @FIXME: This would be better as an extension hook
515  // Send emails or email jobs once this row is safely committed
516  $dbw->onTransactionCommitOrIdle(
517  function () use ( $editor, $title ) {
518  $enotif = new EmailNotification();
519  $enotif->notifyOnPageChange(
520  $editor,
521  $title,
522  $this->mAttribs['rc_timestamp'],
523  $this->mAttribs['rc_comment'],
524  $this->mAttribs['rc_minor'],
525  $this->mAttribs['rc_last_oldid'],
526  $this->mExtra['pageStatus']
527  );
528  },
529  __METHOD__
530  );
531  }
532  }
533 
534  $jobs = [];
535  // Flush old entries from the `recentchanges` table
536  if ( mt_rand( 0, 9 ) == 0 ) {
538  }
539  // Update the cached list of active users
540  if ( $this->mAttribs['rc_user'] > 0 ) {
542  }
543  JobQueueGroup::singleton()->lazyPush( $jobs );
544  }
545 
550  public function notifyRCFeeds( array $feeds = null ) {
551  global $wgRCFeeds;
552  if ( $feeds === null ) {
553  $feeds = $wgRCFeeds;
554  }
555 
556  $performer = $this->getPerformerIdentity();
557 
558  foreach ( $feeds as $params ) {
559  $params += [
560  'omit_bots' => false,
561  'omit_anon' => false,
562  'omit_user' => false,
563  'omit_minor' => false,
564  'omit_patrolled' => false,
565  ];
566 
567  if (
568  ( $params['omit_bots'] && $this->mAttribs['rc_bot'] ) ||
569  ( $params['omit_anon'] && !$performer->isRegistered() ) ||
570  ( $params['omit_user'] && $performer->isRegistered() ) ||
571  ( $params['omit_minor'] && $this->mAttribs['rc_minor'] ) ||
572  ( $params['omit_patrolled'] && $this->mAttribs['rc_patrolled'] ) ||
573  $this->mAttribs['rc_type'] == RC_EXTERNAL
574  ) {
575  continue;
576  }
577 
578  $actionComment = $this->mExtra['actionCommentIRC'] ?? null;
579 
580  $feed = RCFeed::factory( $params );
581  $feed->notify( $this, $actionComment );
582  }
583  }
584 
593  public static function getEngine( $uri, $params = [] ) {
594  // TODO: Merge into RCFeed::factory().
595  global $wgRCEngines;
596  $scheme = parse_url( $uri, PHP_URL_SCHEME );
597  if ( !$scheme ) {
598  throw new MWException( "Invalid RCFeed uri: '$uri'" );
599  }
600  if ( !isset( $wgRCEngines[$scheme] ) ) {
601  throw new MWException( "Unknown RCFeedEngine scheme: '$scheme'" );
602  }
603  if ( defined( 'MW_PHPUNIT_TEST' ) && is_object( $wgRCEngines[$scheme] ) ) {
604  return $wgRCEngines[$scheme];
605  }
606  // TODO For non test a object could be here?
607  // @phan-suppress-next-line PhanTypeExpectedObjectOrClassName
608  return new $wgRCEngines[$scheme]( $params );
609  }
610 
622  public function doMarkPatrolled( Authority $performer, $auto = false, $tags = null ) {
624 
625  // Fix up $tags so that the MarkPatrolled hook below always gets an array
626  if ( $tags === null ) {
627  $tags = [];
628  } elseif ( is_string( $tags ) ) {
629  $tags = [ $tags ];
630  }
631 
632  $status = PermissionStatus::newEmpty();
633  // If recentchanges patrol is disabled, only new pages or new file versions
634  // can be patrolled, provided the appropriate config variable is set
635  if ( !$wgUseRCPatrol && ( !$wgUseNPPatrol || $this->getAttribute( 'rc_type' ) != RC_NEW ) &&
636  ( !$wgUseFilePatrol || !( $this->getAttribute( 'rc_type' ) == RC_LOG &&
637  $this->getAttribute( 'rc_log_type' ) == 'upload' ) ) ) {
638  $status->fatal( 'rcpatroldisabled' );
639  }
640  // Automatic patrol needs "autopatrol", ordinary patrol needs "patrol"
641  $performer->authorizeWrite( $auto ? 'autopatrol' : 'patrol', $this->getTitle(), $status );
642  $user = MediaWikiServices::getInstance()->getUserFactory()->newFromAuthority( $performer );
643  if ( !Hooks::runner()->onMarkPatrolled(
644  $this->getAttribute( 'rc_id' ), $user, false, $auto, $tags )
645  ) {
646  $status->fatal( 'hookaborted' );
647  }
648  // Users without the 'autopatrol' right can't patrol their own revisions
649  if ( $performer->getUser()->getName() === $this->getAttribute( 'rc_user_text' ) &&
650  !$performer->isAllowed( 'autopatrol' )
651  ) {
652  $status->fatal( 'markedaspatrollederror-noautopatrol' );
653  }
654  if ( !$status->isGood() ) {
655  return $status->toLegacyErrorArray();
656  }
657  // If the change was patrolled already, do nothing
658  if ( $this->getAttribute( 'rc_patrolled' ) ) {
659  return [];
660  }
661  // Actually set the 'patrolled' flag in RC
662  $this->reallyMarkPatrolled();
663  // Log this patrol event
664  PatrolLog::record( $this, $auto, $performer->getUser(), $tags );
665 
666  Hooks::runner()->onMarkPatrolledComplete(
667  $this->getAttribute( 'rc_id' ), $user, false, $auto );
668 
669  return [];
670  }
671 
676  public function reallyMarkPatrolled() {
677  $dbw = wfGetDB( DB_PRIMARY );
678  $dbw->update(
679  'recentchanges',
680  [
681  'rc_patrolled' => self::PRC_PATROLLED
682  ],
683  [
684  'rc_id' => $this->getAttribute( 'rc_id' )
685  ],
686  __METHOD__
687  );
688  // Invalidate the page cache after the page has been patrolled
689  // to make sure that the Patrol link isn't visible any longer!
690  $this->getTitle()->invalidateCache();
691 
692  // Enqueue a reverted tag update (in case the edit was a revert)
693  $revisionId = $this->getAttribute( 'rc_this_oldid' );
694  if ( $revisionId ) {
695  $revertedTagUpdateManager =
696  MediaWikiServices::getInstance()->getRevertedTagUpdateManager();
697  $revertedTagUpdateManager->approveRevertedTagForRevision( $revisionId );
698  }
699 
700  return $dbw->affectedRows();
701  }
702 
727  public static function notifyEdit(
728  $timestamp, $page, $minor, $user, $comment, $oldId, $lastTimestamp,
729  $bot, $ip = '', $oldSize = 0, $newSize = 0, $newId = 0, $patrol = 0,
730  $tags = [], EditResult $editResult = null
731  ) {
732  Assert::parameter( $page->exists(), '$page', 'must represent an existing page' );
733 
734  $rc = new RecentChange;
735  $rc->mPage = $page;
736  $rc->mPerformer = $user;
737  $rc->mAttribs = [
738  'rc_timestamp' => $timestamp,
739  'rc_namespace' => $page->getNamespace(),
740  'rc_title' => $page->getDBkey(),
741  'rc_type' => RC_EDIT,
742  'rc_source' => self::SRC_EDIT,
743  'rc_minor' => $minor ? 1 : 0,
744  'rc_cur_id' => $page->getId(),
745  'rc_user' => $user->getId(),
746  'rc_user_text' => $user->getName(),
747  'rc_comment' => &$comment,
748  'rc_comment_text' => &$comment,
749  'rc_comment_data' => null,
750  'rc_this_oldid' => (int)$newId,
751  'rc_last_oldid' => $oldId,
752  'rc_bot' => $bot ? 1 : 0,
753  'rc_ip' => self::checkIPAddress( $ip ),
754  'rc_patrolled' => intval( $patrol ),
755  'rc_new' => 0, # obsolete
756  'rc_old_len' => $oldSize,
757  'rc_new_len' => $newSize,
758  'rc_deleted' => 0,
759  'rc_logid' => 0,
760  'rc_log_type' => null,
761  'rc_log_action' => '',
762  'rc_params' => ''
763  ];
764 
765  // TODO: deprecate the 'prefixedDBkey' entry, let callers do the formatting.
766  $formatter = MediaWikiServices::getInstance()->getTitleFormatter();
767 
768  $rc->mExtra = [
769  'prefixedDBkey' => $formatter->getPrefixedDBkey( $page ),
770  'lastTimestamp' => $lastTimestamp,
771  'oldSize' => $oldSize,
772  'newSize' => $newSize,
773  'pageStatus' => 'changed'
774  ];
775 
777  static function () use ( $rc, $tags, $editResult ) {
778  $rc->addTags( $tags );
779  $rc->setEditResult( $editResult );
780  $rc->save();
781  },
782  DeferredUpdates::POSTSEND,
784  );
785 
786  return $rc;
787  }
788 
808  public static function notifyNew(
809  $timestamp,
810  $page, $minor, $user, $comment, $bot,
811  $ip = '', $size = 0, $newId = 0, $patrol = 0, $tags = []
812  ) {
813  Assert::parameter( $page->exists(), '$page', 'must represent an existing page' );
814 
815  $rc = new RecentChange;
816  $rc->mPage = $page;
817  $rc->mPerformer = $user;
818  $rc->mAttribs = [
819  'rc_timestamp' => $timestamp,
820  'rc_namespace' => $page->getNamespace(),
821  'rc_title' => $page->getDBkey(),
822  'rc_type' => RC_NEW,
823  'rc_source' => self::SRC_NEW,
824  'rc_minor' => $minor ? 1 : 0,
825  'rc_cur_id' => $page->getId(),
826  'rc_user' => $user->getId(),
827  'rc_user_text' => $user->getName(),
828  'rc_comment' => &$comment,
829  'rc_comment_text' => &$comment,
830  'rc_comment_data' => null,
831  'rc_this_oldid' => (int)$newId,
832  'rc_last_oldid' => 0,
833  'rc_bot' => $bot ? 1 : 0,
834  'rc_ip' => self::checkIPAddress( $ip ),
835  'rc_patrolled' => intval( $patrol ),
836  'rc_new' => 1, # obsolete
837  'rc_old_len' => 0,
838  'rc_new_len' => $size,
839  'rc_deleted' => 0,
840  'rc_logid' => 0,
841  'rc_log_type' => null,
842  'rc_log_action' => '',
843  'rc_params' => ''
844  ];
845 
846  // TODO: deprecate the 'prefixedDBkey' entry, let callers do the formatting.
847  $formatter = MediaWikiServices::getInstance()->getTitleFormatter();
848 
849  $rc->mExtra = [
850  'prefixedDBkey' => $formatter->getPrefixedDBkey( $page ),
851  'lastTimestamp' => 0,
852  'oldSize' => 0,
853  'newSize' => $size,
854  'pageStatus' => 'created'
855  ];
856 
858  static function () use ( $rc, $tags ) {
859  $rc->addTags( $tags );
860  $rc->save();
861  },
862  DeferredUpdates::POSTSEND,
864  );
865 
866  return $rc;
867  }
868 
885  public static function notifyLog( $timestamp,
886  $logPage, $user, $actionComment, $ip, $type,
887  $action, $target, $logComment, $params, $newId = 0, $actionCommentIRC = ''
888  ) {
889  global $wgLogRestrictions;
890 
891  # Don't add private logs to RC!
892  if ( isset( $wgLogRestrictions[$type] ) && $wgLogRestrictions[$type] != '*' ) {
893  return false;
894  }
895  $rc = self::newLogEntry( $timestamp,
896  $logPage, $user, $actionComment, $ip, $type, $action,
897  $target, $logComment, $params, $newId, $actionCommentIRC );
898  $rc->save();
899 
900  return true;
901  }
902 
921  public static function newLogEntry( $timestamp,
922  $logPage, $user, $actionComment, $ip,
923  $type, $action, $target, $logComment, $params, $newId = 0, $actionCommentIRC = '',
924  $revId = 0, $isPatrollable = false ) {
925  global $wgRequest;
926  $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
927 
928  # # Get pageStatus for email notification
929  switch ( $type . '-' . $action ) {
930  case 'delete-delete':
931  case 'delete-delete_redir':
932  case 'delete-delete_redir2':
933  $pageStatus = 'deleted';
934  break;
935  case 'move-move':
936  case 'move-move_redir':
937  $pageStatus = 'moved';
938  break;
939  case 'delete-restore':
940  $pageStatus = 'restored';
941  break;
942  case 'upload-upload':
943  $pageStatus = 'created';
944  break;
945  case 'upload-overwrite':
946  default:
947  $pageStatus = 'changed';
948  break;
949  }
950 
951  // Allow unpatrolled status for patrollable log entries
952  $canAutopatrol = $permissionManager->userHasRight( $user, 'autopatrol' );
953  $markPatrolled = $isPatrollable ? $canAutopatrol : true;
954 
955  if ( $target instanceof PageIdentity && $target->canExist() ) {
956  $pageId = $target->getId();
957  } else {
958  $pageId = 0;
959  }
960 
961  $rc = new RecentChange;
962  $rc->mPage = $target;
963  $rc->mPerformer = $user;
964  $rc->mAttribs = [
965  'rc_timestamp' => $timestamp,
966  'rc_namespace' => $target->getNamespace(),
967  'rc_title' => $target->getDBkey(),
968  'rc_type' => RC_LOG,
969  'rc_source' => self::SRC_LOG,
970  'rc_minor' => 0,
971  'rc_cur_id' => $pageId,
972  'rc_user' => $user->getId(),
973  'rc_user_text' => $user->getName(),
974  'rc_comment' => &$logComment,
975  'rc_comment_text' => &$logComment,
976  'rc_comment_data' => null,
977  'rc_this_oldid' => (int)$revId,
978  'rc_last_oldid' => 0,
979  'rc_bot' => $permissionManager->userHasRight( $user, 'bot' ) ?
980  (int)$wgRequest->getBool( 'bot', true ) : 0,
981  'rc_ip' => self::checkIPAddress( $ip ),
982  'rc_patrolled' => $markPatrolled ? self::PRC_AUTOPATROLLED : self::PRC_UNPATROLLED,
983  'rc_new' => 0, # obsolete
984  'rc_old_len' => null,
985  'rc_new_len' => null,
986  'rc_deleted' => 0,
987  'rc_logid' => $newId,
988  'rc_log_type' => $type,
989  'rc_log_action' => $action,
990  'rc_params' => $params
991  ];
992 
993  // TODO: deprecate the 'prefixedDBkey' entry, let callers do the formatting.
994  $formatter = MediaWikiServices::getInstance()->getTitleFormatter();
995 
996  $rc->mExtra = [
997  // XXX: This does not correspond to rc_namespace/rc_title/rc_cur_id.
998  // Is that intentional? For all other kinds of RC entries, prefixedDBkey
999  // matches rc_namespace/rc_title. Do we even need $logPage?
1000  'prefixedDBkey' => $formatter->getPrefixedDBkey( $logPage ),
1001  'lastTimestamp' => 0,
1002  'actionComment' => $actionComment, // the comment appended to the action, passed from LogPage
1003  'pageStatus' => $pageStatus,
1004  'actionCommentIRC' => $actionCommentIRC
1005  ];
1006 
1007  return $rc;
1008  }
1009 
1031  public static function newForCategorization(
1032  $timestamp,
1033  PageIdentity $categoryTitle,
1034  ?UserIdentity $user,
1035  $comment,
1036  PageIdentity $pageTitle,
1037  $oldRevId,
1038  $newRevId,
1039  $lastTimestamp,
1040  $bot,
1041  $ip = '',
1042  $deleted = 0,
1043  $added = null
1044  ) {
1045  // Done in a backwards compatible way.
1046  $categoryWikiPage = MediaWikiServices::getInstance()->getWikiPageFactory()
1047  ->newFromTitle( $categoryTitle );
1048 
1049  '@phan-var WikiCategoryPage $categoryWikiPage';
1050  $params = [
1051  'hidden-cat' => $categoryWikiPage->isHidden()
1052  ];
1053  if ( $added !== null ) {
1054  $params['added'] = $added;
1055  }
1056 
1057  if ( !$user ) {
1058  // XXX: when and why do we need this?
1059  $user = MediaWikiServices::getInstance()->getActorStore()->getUnknownActor();
1060  }
1061 
1062  $rc = new RecentChange;
1063  $rc->mPage = $categoryTitle;
1064  $rc->mPerformer = $user;
1065  $rc->mAttribs = [
1066  'rc_timestamp' => MWTimestamp::convert( TS_MW, $timestamp ),
1067  'rc_namespace' => $categoryTitle->getNamespace(),
1068  'rc_title' => $categoryTitle->getDBkey(),
1069  'rc_type' => RC_CATEGORIZE,
1070  'rc_source' => self::SRC_CATEGORIZE,
1071  'rc_minor' => 0,
1072  // XXX: rc_cur_id does not correspond to rc_namespace/rc_title.
1073  // They refer to different pages. Is that intentional?
1074  'rc_cur_id' => $pageTitle->getId(),
1075  'rc_user' => $user->getId(),
1076  'rc_user_text' => $user->getName(),
1077  'rc_comment' => &$comment,
1078  'rc_comment_text' => &$comment,
1079  'rc_comment_data' => null,
1080  'rc_this_oldid' => (int)$newRevId,
1081  'rc_last_oldid' => $oldRevId,
1082  'rc_bot' => $bot ? 1 : 0,
1083  'rc_ip' => self::checkIPAddress( $ip ),
1084  'rc_patrolled' => self::PRC_AUTOPATROLLED, // Always patrolled, just like log entries
1085  'rc_new' => 0, # obsolete
1086  'rc_old_len' => null,
1087  'rc_new_len' => null,
1088  'rc_deleted' => $deleted,
1089  'rc_logid' => 0,
1090  'rc_log_type' => null,
1091  'rc_log_action' => '',
1092  'rc_params' => serialize( $params )
1093  ];
1094 
1095  // TODO: deprecate the 'prefixedDBkey' entry, let callers do the formatting.
1096  $formatter = MediaWikiServices::getInstance()->getTitleFormatter();
1097 
1098  $rc->mExtra = [
1099  'prefixedDBkey' => $formatter->getPrefixedDBkey( $categoryTitle ),
1100  'lastTimestamp' => $lastTimestamp,
1101  'oldSize' => 0,
1102  'newSize' => 0,
1103  'pageStatus' => 'changed'
1104  ];
1105 
1106  return $rc;
1107  }
1108 
1117  public function getParam( $name ) {
1118  $params = $this->parseParams();
1119  return $params[$name] ?? null;
1120  }
1121 
1127  public function loadFromRow( $row ) {
1128  $this->mAttribs = get_object_vars( $row );
1129  $this->mAttribs['rc_timestamp'] = wfTimestamp( TS_MW, $this->mAttribs['rc_timestamp'] );
1130  // rc_deleted MUST be set
1131  $this->mAttribs['rc_deleted'] = $row->rc_deleted;
1132 
1133  if ( isset( $this->mAttribs['rc_ip'] ) ) {
1134  // Clean up CIDRs for Postgres per T164898. ("127.0.0.1" casts to "127.0.0.1/32")
1135  $n = strpos( $this->mAttribs['rc_ip'], '/' );
1136  if ( $n !== false ) {
1137  $this->mAttribs['rc_ip'] = substr( $this->mAttribs['rc_ip'], 0, $n );
1138  }
1139  }
1140 
1141  $comment = CommentStore::getStore()
1142  // Legacy because $row may have come from self::selectFields()
1143  ->getCommentLegacy( wfGetDB( DB_REPLICA ), 'rc_comment', $row, true )
1144  ->text;
1145  $this->mAttribs['rc_comment'] = &$comment;
1146  $this->mAttribs['rc_comment_text'] = &$comment;
1147  $this->mAttribs['rc_comment_data'] = null;
1148 
1149  $this->mPerformer = $this->getUserIdentityFromAnyId(
1150  $row->rc_user ?? null,
1151  $row->rc_user_text ?? null,
1152  $row->rc_actor ?? null
1153  );
1154  $this->mAttribs['rc_user'] = $this->mPerformer->getId();
1155  $this->mAttribs['rc_user_text'] = $this->mPerformer->getName();
1156 
1157  // Watchlist expiry.
1158  if ( isset( $row->we_expiry ) && $row->we_expiry ) {
1159  $this->watchlistExpiry = wfTimestamp( TS_MW, $row->we_expiry );
1160  }
1161  }
1162 
1169  public function getAttribute( $name ) {
1170  if ( $name === 'rc_comment' ) {
1171  return CommentStore::getStore()
1172  ->getComment( 'rc_comment', $this->mAttribs, true )->text;
1173  }
1174 
1175  if ( $name === 'rc_user' || $name === 'rc_user_text' || $name === 'rc_actor' ) {
1176  $user = $this->getPerformerIdentity();
1177 
1178  if ( $name === 'rc_user' ) {
1179  return $user->getId();
1180  }
1181  if ( $name === 'rc_user_text' ) {
1182  return $user->getName();
1183  }
1184  if ( $name === 'rc_actor' ) {
1185  // NOTE: rc_actor exists in the database, but application logic should not use it.
1186  wfDeprecatedMsg( 'Accessing deprecated field rc_actor', '1.36' );
1187  $actorStore = MediaWikiServices::getInstance()->getActorStore();
1188  $db = wfGetDB( DB_REPLICA );
1189  return $actorStore->findActorId( $user, $db );
1190  }
1191  }
1192 
1193  return $this->mAttribs[$name] ?? null;
1194  }
1195 
1199  public function getAttributes() {
1200  return $this->mAttribs;
1201  }
1202 
1209  public function diffLinkTrail( $forceCur ) {
1210  if ( $this->mAttribs['rc_type'] == RC_EDIT ) {
1211  $trail = "curid=" . (int)( $this->mAttribs['rc_cur_id'] ) .
1212  "&oldid=" . (int)( $this->mAttribs['rc_last_oldid'] );
1213  if ( $forceCur ) {
1214  $trail .= '&diff=0';
1215  } else {
1216  $trail .= '&diff=' . (int)( $this->mAttribs['rc_this_oldid'] );
1217  }
1218  } else {
1219  $trail = '';
1220  }
1221 
1222  return $trail;
1223  }
1224 
1232  public function getCharacterDifference( $old = 0, $new = 0 ) {
1233  if ( $old === 0 ) {
1234  $old = $this->mAttribs['rc_old_len'];
1235  }
1236  if ( $new === 0 ) {
1237  $new = $this->mAttribs['rc_new_len'];
1238  }
1239  if ( $old === null || $new === null ) {
1240  return '';
1241  }
1242 
1243  return ChangesList::showCharacterDifference( $old, $new );
1244  }
1245 
1246  private static function checkIPAddress( $ip ) {
1247  global $wgRequest;
1248  if ( $ip ) {
1249  if ( !IPUtils::isIPAddress( $ip ) ) {
1250  throw new MWException( "Attempt to write \"" . $ip .
1251  "\" as an IP address into recent changes" );
1252  }
1253  } else {
1254  $ip = $wgRequest->getIP();
1255  if ( !$ip ) {
1256  $ip = '';
1257  }
1258  }
1259 
1260  return $ip;
1261  }
1262 
1272  public static function isInRCLifespan( $timestamp, $tolerance = 0 ) {
1273  global $wgRCMaxAge;
1274 
1275  return wfTimestamp( TS_UNIX, $timestamp ) > time() - $tolerance - $wgRCMaxAge;
1276  }
1277 
1285  public function parseParams() {
1286  $rcParams = $this->getAttribute( 'rc_params' );
1287 
1288  Wikimedia\suppressWarnings();
1289  $unserializedParams = unserialize( $rcParams );
1290  Wikimedia\restoreWarnings();
1291 
1292  return $unserializedParams;
1293  }
1294 
1303  public function addTags( $tags ) {
1304  if ( is_string( $tags ) ) {
1305  $this->tags[] = $tags;
1306  } else {
1307  $this->tags = array_merge( $tags, $this->tags );
1308  }
1309  }
1310 
1318  public function setEditResult( ?EditResult $editResult ) {
1319  $this->editResult = $editResult;
1320  }
1321 
1329  private function getUserIdentityFromAnyId(
1330  $userId,
1331  $userName,
1332  $actorId = null
1333  ): UserIdentity {
1334  // XXX: Is this logic needed elsewhere? Should it be reusable?
1335 
1336  $userId = isset( $userId ) ? (int)$userId : null;
1337  $actorId = isset( $actorId ) ? (int)$actorId : 0;
1338 
1339  $actorStore = MediaWikiServices::getInstance()->getActorStore();
1340  if ( $userName && $actorId ) {
1341  // Likely the fields are coming from a join on actor table,
1342  // so can definitely build a UserIdentityValue.
1343  return $actorStore->newActorFromRowFields( $userId, $userName, $actorId );
1344  }
1345  if ( $userId !== null ) {
1346  if ( $userName !== null ) {
1347  // NOTE: For IPs and external users, $userId will be 0.
1348  $user = new UserIdentityValue( $userId, $userName );
1349  } else {
1350  $user = $actorStore->getUserIdentityByUserId( $userId );
1351 
1352  if ( !$user ) {
1353  throw new RuntimeException( "User not found by ID: $userId" );
1354  }
1355  }
1356  } elseif ( $actorId > 0 ) {
1357  $db = wfGetDB( DB_REPLICA );
1358  $user = $actorStore->getActorById( $actorId, $db );
1359 
1360  if ( !$user ) {
1361  throw new RuntimeException( "User not found by actor ID: $actorId" );
1362  }
1363  } elseif ( $userName !== null ) {
1364  $user = $actorStore->getUserIdentityByName( $userName );
1365 
1366  if ( !$user ) {
1367  throw new RuntimeException( "User not found by name: $userName" );
1368  }
1369  } else {
1370  throw new RuntimeException( 'At least one of user ID, actor ID or user name must be given' );
1371  }
1372 
1373  return $user;
1374  }
1375 }
RecentChange\getCharacterDifference
getCharacterDifference( $old=0, $new=0)
Returns the change size (HTML).
Definition: RecentChange.php:1232
MediaWiki\User\UserIdentityValue
Value object representing a user's identity.
Definition: UserIdentityValue.php:35
Page\PageIdentity
Interface for objects (potentially) representing an editable wiki page.
Definition: PageIdentity.php:64
RecentChange\save
save( $send=self::SEND_FEED)
Writes the data in this object to the database.
Definition: RecentChange.php:405
RecentChange\getQueryInfo
static getQueryInfo()
Return the tables, fields, and join conditions to be selected to create a new recentchanges object.
Definition: RecentChange.php:257
RecentChange\$editResult
EditResult null $editResult
EditResult associated with the edit.
Definition: RecentChange.php:140
RecentChange\newLogEntry
static newLogEntry( $timestamp, $logPage, $user, $actionComment, $ip, $type, $action, $target, $logComment, $params, $newId=0, $actionCommentIRC='', $revId=0, $isPatrollable=false)
Definition: RecentChange.php:921
RecentChange\notifyEdit
static notifyEdit( $timestamp, $page, $minor, $user, $comment, $oldId, $lastTimestamp, $bot, $ip='', $oldSize=0, $newSize=0, $newId=0, $patrol=0, $tags=[], EditResult $editResult=null)
Makes an entry in the database corresponding to an edit.
Definition: RecentChange.php:727
RecentChange\$tags
array $tags
List of tags to apply.
Definition: RecentChange.php:135
RC_EDIT
const RC_EDIT
Definition: Defines.php:115
RecentChange\setEditResult
setEditResult(?EditResult $editResult)
Sets the EditResult associated with the edit.
Definition: RecentChange.php:1318
RecentChange\setExtra
setExtra( $extra)
Definition: RecentChange.php:321
RecentChange\getUserIdentityFromAnyId
getUserIdentityFromAnyId( $userId, $userName, $actorId=null)
Definition: RecentChange.php:1329
RecentChange\$mPage
PageReference null $mPage
Definition: RecentChange.php:112
SpecialRecentChangesLinked
This is to display changes made to all articles linked in an article.
Definition: SpecialRecentChangesLinked.php:32
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:193
RecentChange\newFromConds
static newFromConds( $conds, $fname=__METHOD__, $dbType=DB_REPLICA)
Find the first recent change matching some specific conditions.
Definition: RecentChange.php:226
$wgRequest
$wgRequest
Definition: Setup.php:705
$wgShowUpdatedMarker
$wgShowUpdatedMarker
Show "Updated (since my last visit)" marker in RC view, watchlist and history view for watched pages ...
Definition: DefaultSettings.php:8147
RecentChange\getAttributes
getAttributes()
Definition: RecentChange.php:1199
RecentChange
Utility class for creating new RC entries.
Definition: RecentChange.php:80
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:1668
RecentChange\getEngine
static getEngine( $uri, $params=[])
Definition: RecentChange.php:593
RecentChange\loadFromRow
loadFromRow( $row)
Initialises the members of this object from a mysql row object.
Definition: RecentChange.php:1127
RecentChange\getChangeTypes
static getChangeTypes()
Get an array of all change types.
Definition: RecentChange.php:203
Page\PageIdentity\getId
getId( $wikiId=self::LOCAL)
Returns the page ID.
RecentChange\reallyMarkPatrolled
reallyMarkPatrolled()
Mark this RecentChange patrolled, without error checking.
Definition: RecentChange.php:676
RecentChange\$counter
int $counter
Line number of recent change.
Definition: RecentChange.php:130
RecentChange\setAttribs
setAttribs( $attribs)
Definition: RecentChange.php:314
User\newFromIdentity
static newFromIdentity(UserIdentity $identity)
Returns a User object corresponding to the given UserIdentity.
Definition: User.php:683
MediaWiki\User\UserIdentity\getId
getId( $wikiId=self::LOCAL)
RecentChange\getPerformerIdentity
getPerformerIdentity()
Get the UserIdentity of the client that performed this change.
Definition: RecentChange.php:384
MediaWiki\ChangeTags\Taggable
Interface that defines how to tag objects.
Definition: Taggable.php:32
RecentChange\SRC_CATEGORIZE
const SRC_CATEGORIZE
Definition: RecentChange.php:89
RecentChange\parseToRCType
static parseToRCType( $type)
Parsing text to RC_* constants.
Definition: RecentChange.php:170
RC_NEW
const RC_NEW
Definition: Defines.php:116
serialize
serialize()
Definition: ApiMessageTrait.php:138
$wgUseRCPatrol
$wgUseRCPatrol
Use RC Patrolling to check for vandalism (from recent changes and watchlists) New pages and new files...
Definition: DefaultSettings.php:8040
$wgUseNPPatrol
$wgUseNPPatrol
Use new page patrolling to check new pages on Special:Newpages.
Definition: DefaultSettings.php:8056
MediaWiki\Permissions\Authority\getUser
getUser()
Returns the performer of the actions associated with this authority.
RecentChange\SRC_LOG
const SRC_LOG
Definition: RecentChange.php:87
RecentChangesUpdateJob\newCacheUpdateJob
static newCacheUpdateJob()
Definition: RecentChangesUpdateJob.php:54
Page\PageReference
Interface for objects (potentially) representing a page that can be viewable and linked to on a wiki.
Definition: PageReference.php:49
MediaWiki\User\UserIdentity
Interface for objects representing user identity.
Definition: UserIdentity.php:39
RecentChange\getTitle
getTitle()
Definition: RecentChange.php:329
RecentChange\parseParams
parseParams()
Parses and returns the rc_params attribute.
Definition: RecentChange.php:1285
RecentChange\CHANGE_TYPES
const CHANGE_TYPES
Definition: RecentChange.php:142
$wgPutIPinRC
$wgPutIPinRC
Log IP addresses in the recentchanges table; can be accessed only by extensions (e....
Definition: DefaultSettings.php:6629
NS_SPECIAL
const NS_SPECIAL
Definition: Defines.php:53
RC_LOG
const RC_LOG
Definition: Defines.php:117
FormatJson\encode
static encode( $value, $pretty=false, $escaping=0)
Returns the JSON representation of a value.
Definition: FormatJson.php:96
wfDeprecatedMsg
wfDeprecatedMsg( $msg, $version=false, $component=false, $callerOffset=2)
Log a deprecation warning with arbitrary message text.
Definition: GlobalFunctions.php:1028
MWException
MediaWiki exception.
Definition: MWException.php:29
deprecatePublicPropertyFallback
deprecatePublicPropertyFallback(string $property, string $version, callable $getter, ?callable $setter=null, $class=null, $component=null)
Mark a removed public property as deprecated and provide fallback getter and setter callables.
Definition: DeprecationHelper.php:123
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
Definition: GlobalFunctions.php:997
Page\PageReference\getNamespace
getNamespace()
Returns the page's namespace number.
RecentChange\$notificationtimestamp
$notificationtimestamp
Definition: RecentChange.php:120
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2203
RecentChange\$mPerformer
UserIdentity null $mPerformer
Definition: RecentChange.php:117
RecentChange\$watchlistExpiry
string null $watchlistExpiry
The expiry time, if this is a temporary watchlist item.
Definition: RecentChange.php:125
$wgRCFeeds
$wgRCFeeds
Configuration for feeds to which notifications about recent changes will be sent.
Definition: DefaultSettings.php:8007
RecentChange\newFromRow
static newFromRow( $row)
Definition: RecentChange.php:156
PatrolLog\record
static record( $rc, $auto, UserIdentity $user, $tags=null)
Record a log event for a change being patrolled.
Definition: PatrolLog.php:44
MediaWiki\User\UserIdentity\getName
getName()
$title
$title
Definition: testCompression.php:38
RecentChange\SRC_EDIT
const SRC_EDIT
Definition: RecentChange.php:85
RecentChange\getPage
getPage()
Definition: RecentChange.php:338
Title\makeTitle
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:651
RecentChange\diffLinkTrail
diffLinkTrail( $forceCur)
Gets the end part of the diff URL associated with this object Blank if no diff link should be display...
Definition: RecentChange.php:1209
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
MediaWiki\Permissions\Authority\authorizeWrite
authorizeWrite(string $action, PageIdentity $target, PermissionStatus $status=null)
Authorize write access.
RecentChange\SRC_NEW
const SRC_NEW
Definition: RecentChange.php:86
$wgUseFilePatrol
$wgUseFilePatrol
Use file patrolling to check new files on Special:Newfiles.
Definition: DefaultSettings.php:8067
RecentChange\notifyLog
static notifyLog( $timestamp, $logPage, $user, $actionComment, $ip, $type, $action, $target, $logComment, $params, $newId=0, $actionCommentIRC='')
Definition: RecentChange.php:885
MediaWiki\Storage\EditResult
Object for storing information about the effects of an edit.
Definition: EditResult.php:38
MediaWiki\Permissions\Authority
This interface represents the authority associated the current execution context, such as a web reque...
Definition: Authority.php:37
RecentChange\PRC_PATROLLED
const PRC_PATROLLED
Definition: RecentChange.php:92
RecentChange\newFromId
static newFromId( $rcid)
Obtain the recent change with a given rc_id value.
Definition: RecentChange.php:213
RecentChange\$mExtra
$mExtra
Definition: RecentChange.php:107
RC_EXTERNAL
const RC_EXTERNAL
Definition: Defines.php:118
Page\PageReference\getDBkey
getDBkey()
Get the page title in DB key form.
$wgLogRestrictions
$wgLogRestrictions
This restricts log access to those who have a certain right Users without this will not see it in the...
Definition: DefaultSettings.php:8921
DB_PRIMARY
const DB_PRIMARY
Definition: defines.php:27
Hooks\runner
static runner()
Get a HookRunner instance for calling hooks using the new interfaces.
Definition: Hooks.php:173
$wgRCMaxAge
$wgRCMaxAge
Recentchanges items are periodically purged; entries older than this many seconds will go.
Definition: DefaultSettings.php:7907
ChangesList\showCharacterDifference
static showCharacterDifference( $old, $new, IContextSource $context=null)
Show formatted char difference.
Definition: ChangesList.php:323
RecentChange\PRC_AUTOPATROLLED
const PRC_AUTOPATROLLED
Definition: RecentChange.php:93
RecentChange\addTags
addTags( $tags)
Tags to append to the recent change, and associated revision/log.
Definition: RecentChange.php:1303
MediaWiki\Permissions\PermissionStatus
A StatusValue for permission errors.
Definition: PermissionStatus.php:35
RecentChange\getAttribute
getAttribute( $name)
Get an attribute value.
Definition: RecentChange.php:1169
$wgUseEnotif
$wgUseEnotif
Definition: Setup.php:484
RecentChange\checkIPAddress
static checkIPAddress( $ip)
Definition: RecentChange.php:1246
unserialize
unserialize( $serialized)
Definition: ApiMessageTrait.php:146
RecentChange\newForCategorization
static newForCategorization( $timestamp, PageIdentity $categoryTitle, ?UserIdentity $user, $comment, PageIdentity $pageTitle, $oldRevId, $newRevId, $lastTimestamp, $bot, $ip='', $deleted=0, $added=null)
Constructs a RecentChange object for the given categorization This does not call save() on the object...
Definition: RecentChange.php:1031
Title
Represents a title within MediaWiki.
Definition: Title.php:48
MediaWiki\Permissions\Authority\isAllowed
isAllowed(string $permission)
Checks whether this authority has the given permission in general.
JobQueueGroup\singleton
static singleton( $domain=false)
Definition: JobQueueGroup.php:114
RC_CATEGORIZE
const RC_CATEGORIZE
Definition: Defines.php:119
RecentChange\parseFromRCType
static parseFromRCType( $rcType)
Parsing RC_* constants to human-readable test.
Definition: RecentChange.php:192
$wgRCEngines
$wgRCEngines
Used by RecentChange::getEngine to find the correct engine for a given URI scheme.
Definition: DefaultSettings.php:8014
Page\PageReferenceValue
Immutable value object representing a page reference.
Definition: PageReferenceValue.php:42
RecentChange\notifyNew
static notifyNew( $timestamp, $page, $minor, $user, $comment, $bot, $ip='', $size=0, $newId=0, $patrol=0, $tags=[])
Makes an entry in the database corresponding to page creation.
Definition: RecentChange.php:808
RecentChange\PRC_UNPATROLLED
const PRC_UNPATROLLED
Definition: RecentChange.php:91
RCFeed\factory
static factory(array $params)
Definition: RCFeed.php:46
RecentChange\isInRCLifespan
static isInRCLifespan( $timestamp, $tolerance=0)
Check whether the given timestamp is new enough to have a RC row with a given tolerance as the recent...
Definition: RecentChange.php:1272
RecentChangesUpdateJob\newPurgeJob
static newPurgeJob()
Definition: RecentChangesUpdateJob.php:44
RecentChange\doMarkPatrolled
doMarkPatrolled(Authority $performer, $auto=false, $tags=null)
Mark this RecentChange as patrolled.
Definition: RecentChange.php:622
Title\castFromPageReference
static castFromPageReference(?PageReference $pageReference)
Return a Title for a given Reference.
Definition: Title.php:345
RecentChange\notifyRCFeeds
notifyRCFeeds(array $feeds=null)
Notify all the feeds about the change.
Definition: RecentChange.php:550
$t
$t
Definition: testCompression.php:74
RecentChange\$mAttribs
array $mAttribs
Definition: RecentChange.php:106
RecentChange\getPerformer
getPerformer()
Get the User object of the person who performed this change.
Definition: RecentChange.php:368
EmailNotification
This module processes the email notifications when the current page is changed.
Definition: EmailNotification.php:53
RecentChange\__construct
__construct()
Definition: RecentChange.php:296
DeprecationHelper
trait DeprecationHelper
Use this trait in classes which have properties for which public access is deprecated or implementati...
Definition: DeprecationHelper.php:60
Page\PageIdentity\canExist
canExist()
Checks whether this PageIdentity represents a "proper" page, meaning that it could exist as an editab...
CommentStore\getStore
static getStore()
Definition: CommentStore.php:120
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:68
DeferredUpdates\addCallableUpdate
static addCallableUpdate( $callable, $stage=self::POSTSEND, $dbw=null)
Add an update to the pending update queue that invokes the specified callback when run.
Definition: DeferredUpdates.php:145
RecentChange\getParam
getParam( $name)
Get a parameter value.
Definition: RecentChange.php:1117
RecentChange\$numberofWatchingusers
$numberofWatchingusers
Definition: RecentChange.php:119
ChangeTags\addTags
static addTags( $tags, $rc_id=null, $rev_id=null, $log_id=null, $params=null, RecentChange $rc=null)
Add tags to a change given its rc_id, rev_id and/or log_id.
Definition: ChangeTags.php:328
RecentChange\SRC_EXTERNAL
const SRC_EXTERNAL
Definition: RecentChange.php:88
$type
$type
Definition: testCompression.php:52