MediaWiki REL1_37
RevDelList.php
Go to the documentation of this file.
1<?php
25
37abstract class RevDelList extends RevisionListBase {
38
40 private $lbFactory;
41
48 public function __construct(
50 PageIdentity $page,
51 array $ids,
53 ) {
54 parent::__construct( $context, $page );
55
56 // ids is a protected variable in RevisionListBase
57 $this->ids = $ids;
58 $this->lbFactory = $lbFactory;
59 }
60
67 public static function getRelationType() {
68 return null;
69 }
70
77 public static function getRestriction() {
78 return null;
79 }
80
87 public static function getRevdelConstant() {
88 return null;
89 }
90
99 public static function suggestTarget( $target, array $ids ) {
100 return $target;
101 }
102
108 public function areAnySuppressed() {
109 $bit = $this->getSuppressBit();
110
112 foreach ( $this as $item ) {
113 if ( $item->getBits() & $bit ) {
114 return true;
115 }
116 }
117
118 return false;
119 }
120
133 public function setVisibility( array $params ) {
134 $status = Status::newGood();
135
136 $bitPars = $params['value'];
137 $comment = $params['comment'];
138 $perItemStatus = $params['perItemStatus'] ?? false;
139
140 // CAS-style checks are done on the _deleted fields so the select
141 // does not need to use FOR UPDATE nor be in the atomic section
142 $dbw = $this->lbFactory->getMainLB()->getConnectionRef( DB_PRIMARY );
143 $this->res = $this->doQuery( $dbw );
144
145 $status->merge( $this->acquireItemLocks() );
146 if ( !$status->isGood() ) {
147 return $status;
148 }
149
150 $dbw->startAtomic( __METHOD__ );
151 $dbw->onTransactionResolution(
152 function () {
153 // Release locks on commit or error
154 $this->releaseItemLocks();
155 },
156 __METHOD__
157 );
158
159 $missing = array_fill_keys( $this->ids, true );
160 $this->clearFileOps();
161 $idsForLog = [];
162 $authorActors = [];
163
164 if ( $perItemStatus ) {
165 $status->itemStatuses = [];
166 }
167
168 // For multi-item deletions, set the old/new bitfields in log_params such that "hid X"
169 // shows in logs if field X was hidden from ANY item and likewise for "unhid Y". Note the
170 // form does not let the same field get hidden and unhidden in different items at once.
171 $virtualOldBits = 0;
172 $virtualNewBits = 0;
173 $logType = 'delete';
174
175 // Will be filled with id => [old, new bits] information and
176 // passed to doPostCommitUpdates().
177 $visibilityChangeMap = [];
178
180 foreach ( $this as $item ) {
181 unset( $missing[$item->getId()] );
182
183 if ( $perItemStatus ) {
184 $itemStatus = Status::newGood();
185 $status->itemStatuses[$item->getId()] = $itemStatus;
186 } else {
187 $itemStatus = $status;
188 }
189
190 $oldBits = $item->getBits();
191 // Build the actual new rev_deleted bitfield
192 $newBits = RevisionDeleter::extractBitfield( $bitPars, $oldBits );
193
194 if ( $oldBits == $newBits ) {
195 $itemStatus->warning(
196 'revdelete-no-change', $item->formatDate(), $item->formatTime() );
197 $status->failCount++;
198 continue;
199 } elseif ( $oldBits == 0 && $newBits != 0 ) {
200 $opType = 'hide';
201 } elseif ( $oldBits != 0 && $newBits == 0 ) {
202 $opType = 'show';
203 } else {
204 $opType = 'modify';
205 }
206
207 if ( $item->isHideCurrentOp( $newBits ) ) {
208 // Cannot hide current version text
209 $itemStatus->error(
210 'revdelete-hide-current', $item->formatDate(), $item->formatTime() );
211 $status->failCount++;
212 continue;
213 } elseif ( !$item->canView() ) {
214 // Cannot access this revision
215 $msg = ( $opType == 'show' ) ?
216 'revdelete-show-no-access' : 'revdelete-modify-no-access';
217 $itemStatus->error( $msg, $item->formatDate(), $item->formatTime() );
218 $status->failCount++;
219 continue;
220 // Cannot just "hide from Sysops" without hiding any fields
221 } elseif ( $newBits == RevisionRecord::DELETED_RESTRICTED ) {
222 $itemStatus->warning(
223 'revdelete-only-restricted', $item->formatDate(), $item->formatTime() );
224 $status->failCount++;
225 continue;
226 }
227
228 // Update the revision
229 $ok = $item->setBits( $newBits );
230
231 if ( $ok ) {
232 $idsForLog[] = $item->getId();
233 // If any item field was suppressed or unsuppressed
234 if ( ( $oldBits | $newBits ) & $this->getSuppressBit() ) {
235 $logType = 'suppress';
236 }
237 // Track which fields where (un)hidden for each item
238 $addedBits = ( $oldBits ^ $newBits ) & $newBits;
239 $removedBits = ( $oldBits ^ $newBits ) & $oldBits;
240 $virtualNewBits |= $addedBits;
241 $virtualOldBits |= $removedBits;
242
243 $status->successCount++;
244 $authorActors[] = $item->getAuthorActor();
245
246 // Save the old and new bits in $visibilityChangeMap for
247 // later use.
248 $visibilityChangeMap[$item->getId()] = [
249 'oldBits' => $oldBits,
250 'newBits' => $newBits,
251 ];
252 } else {
253 $itemStatus->error(
254 'revdelete-concurrent-change', $item->formatDate(), $item->formatTime() );
255 $status->failCount++;
256 }
257 }
258
259 // Handle missing revisions
260 foreach ( $missing as $id => $unused ) {
261 if ( $perItemStatus ) {
262 $status->itemStatuses[$id] = Status::newFatal( 'revdelete-modify-missing', $id );
263 } else {
264 $status->error( 'revdelete-modify-missing', $id );
265 }
266 $status->failCount++;
267 }
268
269 if ( $status->successCount == 0 ) {
270 $dbw->endAtomic( __METHOD__ );
271 return $status;
272 }
273
274 // Save success count
275 $successCount = $status->successCount;
276
277 // Move files, if there are any
278 $status->merge( $this->doPreCommitUpdates() );
279 if ( !$status->isOK() ) {
280 // Fatal error, such as no configured archive directory or I/O failures
281 $this->lbFactory->rollbackPrimaryChanges( __METHOD__ );
282 return $status;
283 }
284
285 // Log it
286 $authorFields = [];
287 $authorFields['authorActors'] = $authorActors;
288 $this->updateLog(
289 $logType,
290 [
291 'title' => $this->title,
292 'count' => $successCount,
293 'newBits' => $virtualNewBits,
294 'oldBits' => $virtualOldBits,
295 'comment' => $comment,
296 'ids' => $idsForLog,
297 'tags' => $params['tags'] ?? [],
298 ] + $authorFields
299 );
300
301 // Clear caches after commit
302 DeferredUpdates::addCallableUpdate(
303 function () use ( $visibilityChangeMap ) {
304 $this->doPostCommitUpdates( $visibilityChangeMap );
305 },
306 DeferredUpdates::PRESEND,
307 $dbw
308 );
309
310 $dbw->endAtomic( __METHOD__ );
311
312 return $status;
313 }
314
315 final protected function acquireItemLocks() {
316 $status = Status::newGood();
318 foreach ( $this as $item ) {
319 $status->merge( $item->lock() );
320 }
321
322 return $status;
323 }
324
325 final protected function releaseItemLocks() {
326 $status = Status::newGood();
328 foreach ( $this as $item ) {
329 $status->merge( $item->unlock() );
330 }
331
332 return $status;
333 }
334
340 public function reloadFromPrimary() {
341 $dbw = $this->lbFactory->getMainLB()->getConnectionRef( DB_PRIMARY );
342 $this->res = $this->doQuery( $dbw );
343 }
344
348 public function reloadFromMaster() {
349 wfDeprecated( __METHOD__, '1.37' );
350 $this->reloadFromPrimary();
351 }
352
366 private function updateLog( $logType, $params ) {
367 // Get the URL param's corresponding DB field
368 $field = RevisionDeleter::getRelationType( $this->getType() );
369 if ( !$field ) {
370 throw new MWException( "Bad log URL param type!" );
371 }
372 // Add params for affected page and ids
373 $logParams = $this->getLogParams( $params );
374 // Actually add the deletion log entry
375 $logEntry = new ManualLogEntry( $logType, $this->getLogAction() );
376 $logEntry->setTarget( $params['title'] );
377 $logEntry->setComment( $params['comment'] );
378 $logEntry->setParameters( $logParams );
379 $logEntry->setPerformer( $this->getUser() );
380 // Allow for easy searching of deletion log items for revision/log items
381 $relations = [
382 $field => $params['ids'],
383 ];
384 if ( isset( $params['authorActors'] ) ) {
385 $relations += [
386 'target_author_actor' => $params['authorActors'],
387 ];
388 }
389 $logEntry->setRelations( $relations );
390 // Apply change tags to the log entry
391 $logEntry->addTags( $params['tags'] );
392 $logId = $logEntry->insert();
393 $logEntry->publish( $logId );
394 }
395
400 public function getLogAction() {
401 return 'revision';
402 }
403
409 public function getLogParams( $params ) {
410 return [
411 '4::type' => $this->getType(),
412 '5::ids' => $params['ids'],
413 '6::ofield' => $params['oldBits'],
414 '7::nfield' => $params['newBits'],
415 ];
416 }
417
422 public function clearFileOps() {
423 }
424
430 public function doPreCommitUpdates() {
431 return Status::newGood();
432 }
433
440 public function doPostCommitUpdates( array $visibilityChangeMap ) {
441 return Status::newGood();
442 }
443
447 abstract public function getSuppressBit();
448}
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
IContextSource $context
MediaWiki exception.
Class for creating new log entries and inserting them into the database.
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.
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.
LBFactory $lbFactory
static getRevdelConstant()
Get the revision deletion constant for this list type Override this function.
reloadFromPrimary()
Reload the list data from the primary DB.
updateLog( $logType, $params)
Record a log entry on the action.
__construct(IContextSource $context, PageIdentity $page, array $ids, LBFactory $lbFactory)
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.
An interface for generating database load balancers.
Definition LBFactory.php:42
Interface for objects which can provide a MediaWiki context on request.
Interface for objects (potentially) representing an editable wiki page.
const DB_PRIMARY
Definition defines.php:27