3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
19 * @ingroup RevisionDelete
22 use MediaWiki\Cache\HTMLCacheUpdater
;
23 use MediaWiki\Context\IContextSource
;
24 use MediaWiki\HookContainer\HookContainer
;
25 use MediaWiki\HookContainer\HookRunner
;
26 use MediaWiki\MediaWikiServices
;
27 use MediaWiki\Page\PageIdentity
;
28 use MediaWiki\Revision\RevisionRecord
;
29 use MediaWiki\Revision\RevisionStore
;
30 use MediaWiki\Status\Status
;
31 use MediaWiki\Title\Title
;
32 use Wikimedia\Rdbms\FakeResultWrapper
;
33 use Wikimedia\Rdbms\IResultWrapper
;
34 use Wikimedia\Rdbms\LBFactory
;
37 * List for revision table items
39 * This will check both the 'revision' table for live revisions and the
40 * 'archive' table for traditionally-deleted revisions that have an
43 * See RevDelRevisionItem and RevDelArchivedRevisionItem for items.
45 class RevDelRevisionList
extends RevDelList
{
50 /** @var HookRunner */
53 /** @var HTMLCacheUpdater */
54 private $htmlCacheUpdater;
56 /** @var RevisionStore */
57 private $revisionStore;
63 * @param IContextSource $context
64 * @param PageIdentity $page
66 * @param LBFactory $lbFactory
67 * @param HookContainer $hookContainer
68 * @param HTMLCacheUpdater $htmlCacheUpdater
69 * @param RevisionStore $revisionStore
71 public function __construct(
72 IContextSource
$context,
76 HookContainer
$hookContainer,
77 HTMLCacheUpdater
$htmlCacheUpdater,
78 RevisionStore
$revisionStore
80 parent
::__construct( $context, $page, $ids, $lbFactory );
81 $this->lbFactory
= $lbFactory;
82 $this->hookRunner
= new HookRunner( $hookContainer );
83 $this->htmlCacheUpdater
= $htmlCacheUpdater;
84 $this->revisionStore
= $revisionStore;
87 public function getType() {
91 public static function getRelationType() {
95 public static function getRestriction() {
96 return 'deleterevision';
99 public static function getRevdelConstant() {
100 return RevisionRecord
::DELETED_TEXT
;
103 public static function suggestTarget( $target, array $ids ) {
104 $revisionRecord = MediaWikiServices
::getInstance()
105 ->getRevisionLookup()
106 ->getRevisionById( $ids[0] );
108 if ( $revisionRecord ) {
109 return Title
::newFromLinkTarget( $revisionRecord->getPageAsLinkTarget() );
115 * @param \Wikimedia\Rdbms\IReadableDatabase $db
116 * @return IResultWrapper
118 public function doQuery( $db ) {
119 $ids = array_map( 'intval', $this->ids
);
120 $queryBuilder = $this->revisionStore
->newSelectQueryBuilder( $db )
124 ->where( [ 'rev_page' => $this->page
->getId(), 'rev_id' => $ids ] )
125 ->orderBy( 'rev_id', \Wikimedia\Rdbms\SelectQueryBuilder
::SORT_DESC
)
126 // workaround for MySQL bug (T104313)
127 ->useIndex( [ 'revision' => 'PRIMARY' ] );
129 MediaWikiServices
::getInstance()->getChangeTagsStore()->modifyDisplayQueryBuilder( $queryBuilder, 'revision' );
131 $live = $queryBuilder->caller( __METHOD__
)->fetchResultSet();
132 if ( $live->numRows() >= count( $ids ) ) {
133 // All requested revisions are live, keeps things simple!
137 $queryBuilder = $this->revisionStore
->newArchiveSelectQueryBuilder( $db )
139 ->where( [ 'ar_rev_id' => $ids ] )
140 ->orderBy( 'ar_rev_id', \Wikimedia\Rdbms\SelectQueryBuilder
::SORT_DESC
);
142 MediaWikiServices
::getInstance()->getChangeTagsStore()->modifyDisplayQueryBuilder( $queryBuilder, 'archive' );
144 // Check if any requested revisions are available fully deleted.
145 $archived = $queryBuilder->caller( __METHOD__
)->fetchResultSet();
147 if ( $archived->numRows() == 0 ) {
149 } elseif ( $live->numRows() == 0 ) {
152 // Combine the two! Whee
154 foreach ( $live as $row ) {
155 $rows[$row->rev_id
] = $row;
157 foreach ( $archived as $row ) {
158 $rows[$row->ar_rev_id
] = $row;
161 return new FakeResultWrapper( array_values( $rows ) );
165 public function newItem( $row ) {
166 if ( isset( $row->rev_id
) ) {
167 return new RevDelRevisionItem( $this, $row );
168 } elseif ( isset( $row->ar_rev_id
) ) {
169 return new RevDelArchivedRevisionItem( $this, $row, $this->lbFactory
);
171 // This shouldn't happen. :)
172 throw new InvalidArgumentException( 'Invalid row type in RevDelRevisionList' );
176 public function getCurrent() {
177 if ( $this->currentRevId
=== null ) {
178 $dbw = $this->lbFactory
->getPrimaryDatabase();
179 $this->currentRevId
= $dbw->newSelectQueryBuilder()
180 ->select( 'page_latest' )
182 ->where( [ 'page_namespace' => $this->page
->getNamespace(), 'page_title' => $this->page
->getDBkey() ] )
183 ->caller( __METHOD__
)->fetchField();
185 return $this->currentRevId
;
188 public function doPreCommitUpdates() {
189 Title
::newFromPageIdentity( $this->page
)->invalidateCache();
190 return Status
::newGood();
193 public function doPostCommitUpdates( array $visibilityChangeMap ) {
194 $this->htmlCacheUpdater
->purgeTitleUrls(
196 HTMLCacheUpdater
::PURGE_INTENT_TXROUND_REFLECTED
198 // Extensions that require referencing previous revisions may need this
199 $this->hookRunner
->onArticleRevisionVisibilitySet(
200 Title
::newFromPageIdentity( $this->page
),
205 return Status
::newGood();