/** * @return string HTML */ protected function buildCheckBoxes() { $html = '<table>'; // If there is just one item, use checkboxes $list = $this->getList(); if ($list->length() == 1) { $list->reset(); $bitfield = $list->current()->getBits(); // existing field if ($this->submitClicked) { $bitfield = RevisionDeleter::extractBitfield($this->extractBitParams(), $bitfield); } foreach ($this->checks as $item) { // Messages: revdelete-hide-text, revdelete-hide-image, revdelete-hide-name, // revdelete-hide-comment, revdelete-hide-user, revdelete-hide-restricted list($message, $name, $field) = $item; $innerHTML = Xml::checkLabel($this->msg($message)->text(), $name, $name, $bitfield & $field); if ($field == Revision::DELETED_RESTRICTED) { $innerHTML = "<b>{$innerHTML}</b>"; } $line = Xml::tags('td', array('class' => 'mw-input'), $innerHTML); $html .= "<tr>{$line}</tr>\n"; } } else { // Otherwise, use tri-state radios $html .= '<tr>'; $html .= '<th class="mw-revdel-checkbox">' . $this->msg('revdelete-radio-same')->escaped() . '</th>'; $html .= '<th class="mw-revdel-checkbox">' . $this->msg('revdelete-radio-unset')->escaped() . '</th>'; $html .= '<th class="mw-revdel-checkbox">' . $this->msg('revdelete-radio-set')->escaped() . '</th>'; $html .= "<th></th></tr>\n"; foreach ($this->checks as $item) { // Messages: revdelete-hide-text, revdelete-hide-image, revdelete-hide-name, // revdelete-hide-comment, revdelete-hide-user, revdelete-hide-restricted list($message, $name, $field) = $item; // If there are several items, use third state by default... if ($this->submitClicked) { $selected = $this->getRequest()->getInt($name, 0); } else { $selected = -1; // use existing field } $line = '<td class="mw-revdel-checkbox">' . Xml::radio($name, -1, $selected == -1) . '</td>'; $line .= '<td class="mw-revdel-checkbox">' . Xml::radio($name, 0, $selected == 0) . '</td>'; $line .= '<td class="mw-revdel-checkbox">' . Xml::radio($name, 1, $selected == 1) . '</td>'; $label = $this->msg($message)->escaped(); if ($field == Revision::DELETED_RESTRICTED) { $label = "<b>{$label}</b>"; } $line .= "<td>{$label}</td>"; $html .= "<tr>{$line}</tr>\n"; } } $html .= '</table>'; return $html; }
/** * Set the visibility for the revisions in this list. Logging and * transactions are done here. * * @param array $params Associative array of parameters. Members are: * value: ExtractBitParams() bitfield array * comment: The log comment. * perItemStatus: Set if you want per-item status reports * @return Status * @since 1.23 Added 'perItemStatus' param */ public function setVisibility($params) { $bitPars = $params['value']; $comment = $params['comment']; $perItemStatus = isset($params['perItemStatus']) ? $params['perItemStatus'] : false; // CAS-style checks are done on the _deleted fields so the select // does not need to use FOR UPDATE nor be in the atomic section $dbw = wfGetDB(DB_MASTER); $this->res = $this->doQuery($dbw); $dbw->startAtomic(__METHOD__); $status = Status::newGood(); $missing = array_flip($this->ids); $this->clearFileOps(); $idsForLog = array(); $authorIds = $authorIPs = array(); if ($perItemStatus) { $status->itemStatuses = array(); } // @codingStandardsIgnoreStart Generic.CodeAnalysis.ForLoopWithTestFunctionCall.NotAllowed for ($this->reset(); $this->current(); $this->next()) { // @codingStandardsIgnoreEnd /** @var $item RevDelItem */ $item = $this->current(); unset($missing[$item->getId()]); if ($perItemStatus) { $itemStatus = Status::newGood(); $status->itemStatuses[$item->getId()] = $itemStatus; } else { $itemStatus = $status; } $oldBits = $item->getBits(); // Build the actual new rev_deleted bitfield $newBits = RevisionDeleter::extractBitfield($bitPars, $oldBits); if ($oldBits == $newBits) { $itemStatus->warning('revdelete-no-change', $item->formatDate(), $item->formatTime()); $status->failCount++; continue; } elseif ($oldBits == 0 && $newBits != 0) { $opType = 'hide'; } elseif ($oldBits != 0 && $newBits == 0) { $opType = 'show'; } else { $opType = 'modify'; } if ($item->isHideCurrentOp($newBits)) { // Cannot hide current version text $itemStatus->error('revdelete-hide-current', $item->formatDate(), $item->formatTime()); $status->failCount++; continue; } if (!$item->canView()) { // Cannot access this revision $msg = $opType == 'show' ? 'revdelete-show-no-access' : 'revdelete-modify-no-access'; $itemStatus->error($msg, $item->formatDate(), $item->formatTime()); $status->failCount++; continue; } // Cannot just "hide from Sysops" without hiding any fields if ($newBits == Revision::DELETED_RESTRICTED) { $itemStatus->warning('revdelete-only-restricted', $item->formatDate(), $item->formatTime()); $status->failCount++; continue; } // Update the revision $ok = $item->setBits($newBits); if ($ok) { $idsForLog[] = $item->getId(); $status->successCount++; if ($item->getAuthorId() > 0) { $authorIds[] = $item->getAuthorId(); } elseif (IP::isIPAddress($item->getAuthorName())) { $authorIPs[] = $item->getAuthorName(); } } else { $itemStatus->error('revdelete-concurrent-change', $item->formatDate(), $item->formatTime()); $status->failCount++; } } // Handle missing revisions foreach ($missing as $id => $unused) { if ($perItemStatus) { $status->itemStatuses[$id] = Status::newFatal('revdelete-modify-missing', $id); } else { $status->error('revdelete-modify-missing', $id); } $status->failCount++; } if ($status->successCount == 0) { $dbw->rollback(__METHOD__); return $status; } // Save success count $successCount = $status->successCount; // Move files, if there are any $status->merge($this->doPreCommitUpdates()); if (!$status->isOK()) { // Fatal error, such as no configured archive directory $dbw->rollback(__METHOD__); return $status; } // Log it // @FIXME: $newBits/$oldBits set in for loop, makes IDE warnings too $this->updateLog(array('title' => $this->title, 'count' => $successCount, 'newBits' => $newBits, 'oldBits' => $oldBits, 'comment' => $comment, 'ids' => $idsForLog, 'authorIds' => $authorIds, 'authorIPs' => $authorIPs)); // Clear caches $that = $this; $dbw->onTransactionIdle(function () use($that) { $that->doPostCommitUpdates(); }); $dbw->endAtomic(__METHOD__); return $status; }
/** * Set the visibility for the revisions in this list. Logging and * transactions are done here. * * @param array $params Associative array of parameters. Members are: * value: The integer value to set the visibility to * comment: The log comment. * perItemStatus: Set if you want per-item status reports * @return Status * @since 1.23 Added 'perItemStatus' param */ public function setVisibility($params) { $bitPars = $params['value']; $comment = $params['comment']; $perItemStatus = isset($params['perItemStatus']) ? $params['perItemStatus'] : false; $this->res = false; $dbw = wfGetDB(DB_MASTER); $this->doQuery($dbw); $dbw->begin(__METHOD__); $status = Status::newGood(); $missing = array_flip($this->ids); $this->clearFileOps(); $idsForLog = array(); $authorIds = $authorIPs = array(); if ($perItemStatus) { $status->itemStatuses = array(); } for ($this->reset(); $this->current(); $this->next()) { $item = $this->current(); unset($missing[$item->getId()]); if ($perItemStatus) { $itemStatus = Status::newGood(); $status->itemStatuses[$item->getId()] = $itemStatus; } else { $itemStatus = $status; } $oldBits = $item->getBits(); // Build the actual new rev_deleted bitfield $newBits = RevisionDeleter::extractBitfield($bitPars, $oldBits); if ($oldBits == $newBits) { $itemStatus->warning('revdelete-no-change', $item->formatDate(), $item->formatTime()); $status->failCount++; continue; } elseif ($oldBits == 0 && $newBits != 0) { $opType = 'hide'; } elseif ($oldBits != 0 && $newBits == 0) { $opType = 'show'; } else { $opType = 'modify'; } if ($item->isHideCurrentOp($newBits)) { // Cannot hide current version text $itemStatus->error('revdelete-hide-current', $item->formatDate(), $item->formatTime()); $status->failCount++; continue; } if (!$item->canView()) { // Cannot access this revision $msg = $opType == 'show' ? 'revdelete-show-no-access' : 'revdelete-modify-no-access'; $itemStatus->error($msg, $item->formatDate(), $item->formatTime()); $status->failCount++; continue; } // Cannot just "hide from Sysops" without hiding any fields if ($newBits == Revision::DELETED_RESTRICTED) { $itemStatus->warning('revdelete-only-restricted', $item->formatDate(), $item->formatTime()); $status->failCount++; continue; } // Update the revision $ok = $item->setBits($newBits); if ($ok) { $idsForLog[] = $item->getId(); $status->successCount++; if ($item->getAuthorId() > 0) { $authorIds[] = $item->getAuthorId(); } elseif (IP::isIPAddress($item->getAuthorName())) { $authorIPs[] = $item->getAuthorName(); } } else { $itemStatus->error('revdelete-concurrent-change', $item->formatDate(), $item->formatTime()); $status->failCount++; } } // Handle missing revisions foreach ($missing as $id => $unused) { if ($perItemStatus) { $status->itemStatuses[$id] = Status::newFatal('revdelete-modify-missing', $id); } else { $status->error('revdelete-modify-missing', $id); } $status->failCount++; } if ($status->successCount == 0) { $dbw->rollback(__METHOD__); return $status; } // Save success count $successCount = $status->successCount; // Move files, if there are any $status->merge($this->doPreCommitUpdates()); if (!$status->isOK()) { // Fatal error, such as no configured archive directory $dbw->rollback(__METHOD__); return $status; } // Log it $this->updateLog(array('title' => $this->title, 'count' => $successCount, 'newBits' => $newBits, 'oldBits' => $oldBits, 'comment' => $comment, 'ids' => $idsForLog, 'authorIds' => $authorIds, 'authorIPs' => $authorIPs)); $dbw->commit(__METHOD__); // Clear caches $status->merge($this->doPostCommitUpdates()); return $status; }
/** * Put together a rev_deleted bitfield * @deprecated since 1.22, use RevisionDeleter::extractBitfield instead * @param array $bitPars extractBitParams() params * @param int $oldfield current bitfield * @return array */ public static function extractBitfield( $bitPars, $oldfield ) { return RevisionDeleter::extractBitfield( $bitPars, $oldfield ); }
/** * Set the visibility for the revisions in this list. Logging and * transactions are done here. * * @param array $params Associative array of parameters. Members are: * value: ExtractBitParams() bitfield array * comment: The log comment. * perItemStatus: Set if you want per-item status reports * @return Status * @since 1.23 Added 'perItemStatus' param */ public function setVisibility(array $params) { $status = Status::newGood(); $bitPars = $params['value']; $comment = $params['comment']; $perItemStatus = isset($params['perItemStatus']) ? $params['perItemStatus'] : false; // CAS-style checks are done on the _deleted fields so the select // does not need to use FOR UPDATE nor be in the atomic section $dbw = wfGetDB(DB_MASTER); $this->res = $this->doQuery($dbw); $status->merge($this->acquireItemLocks()); if (!$status->isGood()) { return $status; } $dbw->startAtomic(__METHOD__); $dbw->onTransactionResolution(function () { // Release locks on commit or error $this->releaseItemLocks(); }, __METHOD__); $missing = array_flip($this->ids); $this->clearFileOps(); $idsForLog = []; $authorIds = $authorIPs = []; if ($perItemStatus) { $status->itemStatuses = []; } // For multi-item deletions, set the old/new bitfields in log_params such that "hid X" // shows in logs if field X was hidden from ANY item and likewise for "unhid Y". Note the // form does not let the same field get hidden and unhidden in different items at once. $virtualOldBits = 0; $virtualNewBits = 0; $logType = 'delete'; // Will be filled with id => [old, new bits] information and // passed to doPostCommitUpdates(). $visibilityChangeMap = []; /** @var $item RevDelItem */ foreach ($this as $item) { unset($missing[$item->getId()]); if ($perItemStatus) { $itemStatus = Status::newGood(); $status->itemStatuses[$item->getId()] = $itemStatus; } else { $itemStatus = $status; } $oldBits = $item->getBits(); // Build the actual new rev_deleted bitfield $newBits = RevisionDeleter::extractBitfield($bitPars, $oldBits); if ($oldBits == $newBits) { $itemStatus->warning('revdelete-no-change', $item->formatDate(), $item->formatTime()); $status->failCount++; continue; } elseif ($oldBits == 0 && $newBits != 0) { $opType = 'hide'; } elseif ($oldBits != 0 && $newBits == 0) { $opType = 'show'; } else { $opType = 'modify'; } if ($item->isHideCurrentOp($newBits)) { // Cannot hide current version text $itemStatus->error('revdelete-hide-current', $item->formatDate(), $item->formatTime()); $status->failCount++; continue; } elseif (!$item->canView()) { // Cannot access this revision $msg = $opType == 'show' ? 'revdelete-show-no-access' : 'revdelete-modify-no-access'; $itemStatus->error($msg, $item->formatDate(), $item->formatTime()); $status->failCount++; continue; // Cannot just "hide from Sysops" without hiding any fields } elseif ($newBits == Revision::DELETED_RESTRICTED) { $itemStatus->warning('revdelete-only-restricted', $item->formatDate(), $item->formatTime()); $status->failCount++; continue; } // Update the revision $ok = $item->setBits($newBits); if ($ok) { $idsForLog[] = $item->getId(); // If any item field was suppressed or unsupressed if (($oldBits | $newBits) & $this->getSuppressBit()) { $logType = 'suppress'; } // Track which fields where (un)hidden for each item $addedBits = ($oldBits ^ $newBits) & $newBits; $removedBits = ($oldBits ^ $newBits) & $oldBits; $virtualNewBits |= $addedBits; $virtualOldBits |= $removedBits; $status->successCount++; if ($item->getAuthorId() > 0) { $authorIds[] = $item->getAuthorId(); } elseif (IP::isIPAddress($item->getAuthorName())) { $authorIPs[] = $item->getAuthorName(); } // Save the old and new bits in $visibilityChangeMap for // later use. $visibilityChangeMap[$item->getId()] = ['oldBits' => $oldBits, 'newBits' => $newBits]; } else { $itemStatus->error('revdelete-concurrent-change', $item->formatDate(), $item->formatTime()); $status->failCount++; } } // Handle missing revisions foreach ($missing as $id => $unused) { if ($perItemStatus) { $status->itemStatuses[$id] = Status::newFatal('revdelete-modify-missing', $id); } else { $status->error('revdelete-modify-missing', $id); } $status->failCount++; } if ($status->successCount == 0) { $dbw->endAtomic(__METHOD__); return $status; } // Save success count $successCount = $status->successCount; // Move files, if there are any $status->merge($this->doPreCommitUpdates()); if (!$status->isOK()) { // Fatal error, such as no configured archive directory or I/O failures wfGetLBFactory()->rollbackMasterChanges(__METHOD__); return $status; } // Log it $this->updateLog($logType, ['title' => $this->title, 'count' => $successCount, 'newBits' => $virtualNewBits, 'oldBits' => $virtualOldBits, 'comment' => $comment, 'ids' => $idsForLog, 'authorIds' => $authorIds, 'authorIPs' => $authorIPs]); // Clear caches after commit DeferredUpdates::addCallableUpdate(function () use($visibilityChangeMap) { $this->doPostCommitUpdates($visibilityChangeMap); }, DeferredUpdates::PRESEND, $dbw); $dbw->endAtomic(__METHOD__); return $status; }