50 private const DIR_PREV = 0;
51 private const DIR_NEXT = 1;
69 return $this->
msg(
'history-title' )->plaintextParams( $this->
getTitle()->getPrefixedText() );
74 $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
75 $subtitle = $linkRenderer->makeKnownLink(
76 SpecialPage::getTitleFor(
'Log' ),
77 $this->
msg(
'viewpagelogs' )->text(),
79 [
'page' => $this->
getTitle()->getPrefixedText() ]
87 . $this->
msg(
'word-separator' )->escaped()
88 . $this->
msg(
'parentheses' )
89 ->rawParams( $this->
getLanguage()->pipeList( $links ) )
92 return Html::rawElement(
'div', [
'class' =>
'mw-history-subtitle' ], $subtitle );
99 private function preCacheMessages() {
101 if ( !isset( $this->message ) ) {
104 'cur',
'tooltip-cur',
'last',
'tooltip-last',
'pipe-separator',
105 'changeslist-nocomment',
'updatedmarker',
107 foreach ( $msgs as $msg ) {
108 $this->message[$msg] = $this->msg( $msg )->escaped();
117 private function getTimestampFromRequest(
WebRequest $request ) {
119 $year = $request->
getInt(
'year' );
120 $month = $request->
getInt(
'month' );
122 if ( $year !== 0 || $month !== 0 ) {
124 $year = MWTimestamp::getLocalInstance()->format(
'Y' );
126 if ( $month < 1 || $month > 12 ) {
131 $day = cal_days_in_month( CAL_GREGORIAN, $month, $year );
134 $month = str_pad( (
string)$month, 2,
"0", STR_PAD_LEFT );
135 $day = str_pad( (
string)$day, 2,
"0", STR_PAD_LEFT );
138 $before = $request->
getVal(
'date-range-to' );
140 $parts = explode(
'-', $before );
143 if ( count( $parts ) === 3 ) {
148 return $year && $month && $day ? $year .
'-' . $month .
'-' . $day :
'';
158 $config = $this->context->getConfig();
159 $services = MediaWikiServices::getInstance();
168 $watchlistManager = $services->getWatchlistManager();
169 $hasUnseenRevisionMarkers = $config->get( MainConfigNames::ShowUpdatedMarker ) &&
175 !$hasUnseenRevisionMarkers &&
176 $out->checkLastModified( $this->getWikiPage()->getTouched() )
181 $this->preCacheMessages();
183 # Fill in the file cache if not set already
184 if ( HTMLFileCache::useFileCache( $this->
getContext() ) ) {
186 if ( !$cache->isCacheGood( ) ) {
187 ob_start( [ &$cache,
'saveToFileCache' ] );
192 $out->setFeedAppendQuery(
'action=history' );
193 $out->addModules(
'mediawiki.action.history' );
194 $out->addModuleStyles( [
195 'mediawiki.interface.helpers.styles',
196 'mediawiki.action.history.styles',
197 'mediawiki.special.changeslist',
201 $feedType = $request->
getRawVal(
'feed' );
202 if ( $feedType !==
null ) {
203 $this->feed( $feedType );
208 'https://www.mediawiki.org/wiki/Special:MyLanguage/Help:History',
212 $dbr = $services->getConnectionProvider()->getReplicaDatabase();
215 $send404Code = $config->get( MainConfigNames::Send404Code );
216 if ( $send404Code ) {
217 $out->setStatusCode( 404 );
219 $out->addWikiMsg(
'nohistory' );
221 # show deletion/move log if there is an entry
222 LogEventsList::showLogExtract(
224 [
'delete',
'move',
'protect',
'merge' ],
228 'conds' => [ $dbr->expr(
'log_action',
'!=',
'revision' ) ],
229 'showIfEmpty' =>
false,
230 'msgKey' => [
'moveddeleted-notice' ]
237 $ts = $this->getTimestampFromRequest( $request );
238 $tagFilter = $request->
getVal(
'tagfilter' );
243 if ( $request->
getBool(
'deleted' ) ) {
244 $conds = [ $dbr->expr(
'rev_deleted',
'!=', 0 ) ];
254 'default' =>
'history',
259 'label' => $this->
msg(
'date-range-to' )->text(),
260 'name' =>
'date-range-to',
263 'label-message' =>
'tag-filter',
264 'type' =>
'tagfilter',
266 'name' =>
'tagfilter',
267 'value' => $tagFilter,
271 'name' =>
'tagInvert',
272 'label-message' =>
'invert',
273 'hide-if' => [
'===',
'tagfilter',
'' ],
276 if ( $this->
getAuthority()->isAllowed(
'deletedhistory' ) ) {
279 'label' => $this->
msg(
'history-show-deleted' )->text(),
280 'default' => $request->
getBool(
'deleted' ),
290 ->setCollapsibleOptions(
true )
291 ->setId(
'mw-history-searchform' )
292 ->setSubmitTextMsg(
'historyaction-submit' )
293 ->setWrapperAttributes( [
'id' =>
'mw-history-search' ] )
294 ->setWrapperLegendMsg(
'history-fieldset-title' )
297 $out->addHTML( $htmlForm->getHTML(
false ) );
305 $dateComponents = explode(
'-', $ts );
306 if ( count( $dateComponents ) > 1 ) {
307 $y = (int)$dateComponents[0];
308 $m = (int)$dateComponents[1];
309 $d = (int)$dateComponents[2];
323 $services->getLinkBatchFactory(),
325 $services->getCommentFormatter(),
326 $services->getHookContainer(),
327 $services->getChangeTagsStore()
330 $pager->getNavigationBar() .
332 $pager->getNavigationBar()
334 $out->getMetadata()->setPreventClickjacking( $pager->getPreventClickjacking() );
349 private function fetchRevisions( $limit, $offset, $direction ) {
351 if ( !$this->
getTitle()->exists() ) {
355 $dbr = MediaWikiServices::getInstance()->getConnectionProvider()->getReplicaDatabase();
357 if ( $direction === self::DIR_PREV ) {
358 [ $dirs, $oper ] = [
"ASC",
">=" ];
360 [ $dirs, $oper ] = [
"DESC",
"<=" ];
363 $queryBuilder = MediaWikiServices::getInstance()->getRevisionStore()->newSelectQueryBuilder( $dbr )
365 ->where( [
'rev_page' => $this->
getWikiPage()->getId() ] )
366 ->useIndex( [
'revision' =>
'rev_page_timestamp' ] )
367 ->orderBy( [
'rev_timestamp' ], $dirs )
370 $queryBuilder->andWhere( $dbr->expr(
'rev_timestamp', $oper, $dbr->timestamp( $offset ) ) );
373 return $queryBuilder->caller( __METHOD__ )->fetchResultSet();
381 private function feed( $type ) {
382 if ( !FeedUtils::checkFeedOutput( $type, $this->
getOutput() ) ) {
387 $feedClasses = $this->context->getConfig()->get( MainConfigNames::FeedClasses );
389 $feed =
new $feedClasses[$type](
390 $this->
getTitle()->getPrefixedText() .
' - ' .
391 $this->
msg(
'history-feed-title' )->inContentLanguage()->text(),
392 $this->
msg(
'history-feed-description' )->inContentLanguage()->text(),
393 $this->
getTitle()->getFullURL(
'action=history' )
398 $limit = $request->
getInt(
'limit', 10 );
401 $this->context->getConfig()->get( MainConfigNames::FeedLimit )
404 $items = $this->fetchRevisions( $limit, 0, self::DIR_NEXT );
407 $formattedComments = MediaWikiServices::getInstance()->getRowCommentFormatter()
408 ->formatRows( $items,
'rev_comment' );
412 if ( $items->numRows() ) {
413 foreach ( $items as $i => $row ) {
414 $feed->outItem( $this->feedItem( $row, $formattedComments[$i] ) );
417 $feed->outItem( $this->feedEmpty() );
422 private function feedEmpty() {
424 $this->
msg(
'nohistory' )->inContentLanguage()->text(),
425 $this->
msg(
'history-feed-empty' )->inContentLanguage()->parseAsBlock(),
429 $this->
getTitle()->getTalkPage()->getFullURL()
442 private function feedItem( $row, $formattedComment ) {
443 $revisionStore = MediaWikiServices::getInstance()->getRevisionStore();
444 $rev = $revisionStore->newRevisionFromRow( $row, 0, $this->
getTitle() );
445 $prevRev = $revisionStore->getPreviousRevision( $rev );
446 $revComment = $rev->getComment() ===
null ? null : $rev->getComment()->text;
447 $text = FeedUtils::formatDiffRow2(
449 $prevRev ? $prevRev->getId() : false,
454 $revUserText = $rev->getUser() ? $rev->getUser()->getName() :
'';
455 if ( $revComment ==
'' ) {
456 $contLang = MediaWikiServices::getInstance()->getContentLanguage();
457 $title = $this->
msg(
'history-feed-item-nocomment',
459 $contLang->timeanddate( $rev->getTimestamp() ),
460 $contLang->date( $rev->getTimestamp() ),
461 $contLang->time( $rev->getTimestamp() )
462 )->inContentLanguage()->text();
464 $title = $revUserText .
465 $this->
msg(
'colon-separator' )->inContentLanguage()->text() .
466 FeedItem::stripComment( $revComment );
472 $this->
getTitle()->getFullURL(
'diff=' . $rev->getId() .
'&oldid=prev' ),
473 $rev->getTimestamp(),
475 $this->getTitle()->getTalkPage()->getFullURL()
This class handles printing the history page for an article.
onView()
Print the history page for an article.
array $message
Array of message keys and strings.
getName()
Return the name of the action this object responds to.
requiresWrite()
Indicates whether this action page write access to the wiki.
getPageTitle()
Returns the name that goes in the <h1> page title.
getDescription()
Returns the description that goes below the <h1> element.
requiresUnblock()
Whether this action can still be executed by a blocked user.