MediaWiki REL1_34
RevDelList.php
Go to the documentation of this file.
1<?php
24
37abstract class RevDelList extends RevisionListBase {
39 parent::__construct( $context, $title );
40 $this->ids = $ids;
41 }
42
49 public static function getRelationType() {
50 return null;
51 }
52
59 public static function getRestriction() {
60 return null;
61 }
62
69 public static function getRevdelConstant() {
70 return null;
71 }
72
81 public static function suggestTarget( $target, array $ids ) {
82 return $target;
83 }
84
90 public function areAnySuppressed() {
91 $bit = $this->getSuppressBit();
92
94 foreach ( $this as $item ) {
95 if ( $item->getBits() & $bit ) {
96 return true;
97 }
98 }
99
100 return false;
101 }
102
115 public function setVisibility( array $params ) {
116 $status = Status::newGood();
117
118 $bitPars = $params['value'];
119 $comment = $params['comment'];
120 $perItemStatus = $params['perItemStatus'] ?? false;
121
122 // CAS-style checks are done on the _deleted fields so the select
123 // does not need to use FOR UPDATE nor be in the atomic section
124 $dbw = wfGetDB( DB_MASTER );
125 $this->res = $this->doQuery( $dbw );
126
127 $status->merge( $this->acquireItemLocks() );
128 if ( !$status->isGood() ) {
129 return $status;
130 }
131
132 $dbw->startAtomic( __METHOD__ );
133 $dbw->onTransactionResolution(
134 function () {
135 // Release locks on commit or error
136 $this->releaseItemLocks();
137 },
138 __METHOD__
139 );
140
141 $missing = array_flip( $this->ids );
142 $this->clearFileOps();
143 $idsForLog = [];
144 $authorActors = [];
145
146 if ( $perItemStatus ) {
147 $status->itemStatuses = [];
148 }
149
150 // For multi-item deletions, set the old/new bitfields in log_params such that "hid X"
151 // shows in logs if field X was hidden from ANY item and likewise for "unhid Y". Note the
152 // form does not let the same field get hidden and unhidden in different items at once.
153 $virtualOldBits = 0;
154 $virtualNewBits = 0;
155 $logType = 'delete';
156
157 // Will be filled with id => [old, new bits] information and
158 // passed to doPostCommitUpdates().
159 $visibilityChangeMap = [];
160
162 foreach ( $this as $item ) {
163 unset( $missing[$item->getId()] );
164
165 if ( $perItemStatus ) {
166 $itemStatus = Status::newGood();
167 $status->itemStatuses[$item->getId()] = $itemStatus;
168 } else {
169 $itemStatus = $status;
170 }
171
172 $oldBits = $item->getBits();
173 // Build the actual new rev_deleted bitfield
174 $newBits = RevisionDeleter::extractBitfield( $bitPars, $oldBits );
175
176 if ( $oldBits == $newBits ) {
177 $itemStatus->warning(
178 'revdelete-no-change', $item->formatDate(), $item->formatTime() );
179 $status->failCount++;
180 continue;
181 } elseif ( $oldBits == 0 && $newBits != 0 ) {
182 $opType = 'hide';
183 } elseif ( $oldBits != 0 && $newBits == 0 ) {
184 $opType = 'show';
185 } else {
186 $opType = 'modify';
187 }
188
189 if ( $item->isHideCurrentOp( $newBits ) ) {
190 // Cannot hide current version text
191 $itemStatus->error(
192 'revdelete-hide-current', $item->formatDate(), $item->formatTime() );
193 $status->failCount++;
194 continue;
195 } elseif ( !$item->canView() ) {
196 // Cannot access this revision
197 $msg = ( $opType == 'show' ) ?
198 'revdelete-show-no-access' : 'revdelete-modify-no-access';
199 $itemStatus->error( $msg, $item->formatDate(), $item->formatTime() );
200 $status->failCount++;
201 continue;
202 // Cannot just "hide from Sysops" without hiding any fields
203 } elseif ( $newBits == RevisionRecord::DELETED_RESTRICTED ) {
204 $itemStatus->warning(
205 'revdelete-only-restricted', $item->formatDate(), $item->formatTime() );
206 $status->failCount++;
207 continue;
208 }
209
210 // Update the revision
211 $ok = $item->setBits( $newBits );
212
213 if ( $ok ) {
214 $idsForLog[] = $item->getId();
215 // If any item field was suppressed or unsuppressed
216 if ( ( $oldBits | $newBits ) & $this->getSuppressBit() ) {
217 $logType = 'suppress';
218 }
219 // Track which fields where (un)hidden for each item
220 $addedBits = ( $oldBits ^ $newBits ) & $newBits;
221 $removedBits = ( $oldBits ^ $newBits ) & $oldBits;
222 $virtualNewBits |= $addedBits;
223 $virtualOldBits |= $removedBits;
224
225 $status->successCount++;
226 $authorActors[] = $item->getAuthorActor();
227
228 // Save the old and new bits in $visibilityChangeMap for
229 // later use.
230 $visibilityChangeMap[$item->getId()] = [
231 'oldBits' => $oldBits,
232 'newBits' => $newBits,
233 ];
234 } else {
235 $itemStatus->error(
236 'revdelete-concurrent-change', $item->formatDate(), $item->formatTime() );
237 $status->failCount++;
238 }
239 }
240
241 // Handle missing revisions
242 foreach ( $missing as $id => $unused ) {
243 if ( $perItemStatus ) {
244 $status->itemStatuses[$id] = Status::newFatal( 'revdelete-modify-missing', $id );
245 } else {
246 $status->error( 'revdelete-modify-missing', $id );
247 }
248 $status->failCount++;
249 }
250
251 if ( $status->successCount == 0 ) {
252 $dbw->endAtomic( __METHOD__ );
253 return $status;
254 }
255
256 // Save success count
257 $successCount = $status->successCount;
258
259 // Move files, if there are any
260 $status->merge( $this->doPreCommitUpdates() );
261 if ( !$status->isOK() ) {
262 // Fatal error, such as no configured archive directory or I/O failures
263 $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
264 $lbFactory->rollbackMasterChanges( __METHOD__ );
265 return $status;
266 }
267
268 // Log it
269 $authorFields = [];
270 $authorFields['authorActors'] = $authorActors;
271 $this->updateLog(
272 $logType,
273 [
274 'title' => $this->title,
275 'count' => $successCount,
276 'newBits' => $virtualNewBits,
277 'oldBits' => $virtualOldBits,
278 'comment' => $comment,
279 'ids' => $idsForLog,
280 'tags' => $params['tags'] ?? [],
281 ] + $authorFields
282 );
283
284 // Clear caches after commit
285 DeferredUpdates::addCallableUpdate(
286 function () use ( $visibilityChangeMap ) {
287 $this->doPostCommitUpdates( $visibilityChangeMap );
288 },
289 DeferredUpdates::PRESEND,
290 $dbw
291 );
292
293 $dbw->endAtomic( __METHOD__ );
294
295 return $status;
296 }
297
298 final protected function acquireItemLocks() {
299 $status = Status::newGood();
301 foreach ( $this as $item ) {
302 $status->merge( $item->lock() );
303 }
304
305 return $status;
306 }
307
308 final protected function releaseItemLocks() {
309 $status = Status::newGood();
311 foreach ( $this as $item ) {
312 $status->merge( $item->unlock() );
313 }
314
315 return $status;
316 }
317
322 function reloadFromMaster() {
323 $dbw = wfGetDB( DB_MASTER );
324 $this->res = $this->doQuery( $dbw );
325 }
326
340 private function updateLog( $logType, $params ) {
341 // Get the URL param's corresponding DB field
342 $field = RevisionDeleter::getRelationType( $this->getType() );
343 if ( !$field ) {
344 throw new MWException( "Bad log URL param type!" );
345 }
346 // Add params for affected page and ids
347 $logParams = $this->getLogParams( $params );
348 // Actually add the deletion log entry
349 $logEntry = new ManualLogEntry( $logType, $this->getLogAction() );
350 $logEntry->setTarget( $params['title'] );
351 $logEntry->setComment( $params['comment'] );
352 $logEntry->setParameters( $logParams );
353 $logEntry->setPerformer( $this->getUser() );
354 // Allow for easy searching of deletion log items for revision/log items
355 $relations = [
356 $field => $params['ids'],
357 ];
358 if ( isset( $params['authorActors'] ) ) {
359 $relations += [
360 'target_author_actor' => $params['authorActors'],
361 ];
362 }
363 $logEntry->setRelations( $relations );
364 // Apply change tags to the log entry
365 $logEntry->addTags( $params['tags'] );
366 $logId = $logEntry->insert();
367 $logEntry->publish( $logId );
368 }
369
374 public function getLogAction() {
375 return 'revision';
376 }
377
383 public function getLogParams( $params ) {
384 return [
385 '4::type' => $this->getType(),
386 '5::ids' => $params['ids'],
387 '6::ofield' => $params['oldBits'],
388 '7::nfield' => $params['newBits'],
389 ];
390 }
391
396 public function clearFileOps() {
397 }
398
404 public function doPreCommitUpdates() {
405 return Status::newGood();
406 }
407
414 public function doPostCommitUpdates( array $visibilityChangeMap ) {
415 return Status::newGood();
416 }
417
421 abstract public function getSuppressBit();
422}
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
IContextSource $context
MediaWiki exception.
Class for creating new log entries and inserting them into the database.
MediaWikiServices is the service locator for the application scope of MediaWiki.
Page revision base class.
Abstract base class for deletable items.
setVisibility(array $params)
Set the visibility for the revisions in this list.
areAnySuppressed()
Indicate whether any item in this list is suppressed.
static suggestTarget( $target, array $ids)
Suggest a target for the revision deletion Optionally override this function.
doPreCommitUpdates()
A hook for setVisibility(): do batch updates pre-commit.
__construct(IContextSource $context, Title $title, array $ids)
static getRestriction()
Get the user right required for this list type Override this function.
getLogAction()
Get the log action for this list type.
clearFileOps()
Clear any data structures needed for doPreCommitUpdates() and doPostCommitUpdates() STUB.
static getRelationType()
Get the DB field name associated with the ID list.
doPostCommitUpdates(array $visibilityChangeMap)
A hook for setVisibility(): do any necessary updates post-commit.
getSuppressBit()
Get the integer value of the flag used for suppression.
getLogParams( $params)
Get log parameter array.
static getRevdelConstant()
Get the revision deletion constant for this list type Override this function.
updateLog( $logType, $params)
Record a log entry on the action.
reloadFromMaster()
Reload the list data from the master DB.
static getRelationType( $typeName)
Get DB field name for URL param... Future code for other things may also track other types of revisio...
static extractBitfield(array $bitPars, $oldfield)
Put together a rev_deleted bitfield.
List for revision table items for a single page.
getType()
Get the internal type name of this list.
doQuery( $db)
Do the DB query to iterate through the objects.
Represents a title within MediaWiki.
Definition Title.php:42
Interface for objects which can provide a MediaWiki context on request.
const DB_MASTER
Definition defines.php:26