<?php
namespace wcf\system\edit;
+use wcf\data\edit\history\entry\EditHistoryEntryList;
use wcf\data\object\type\ObjectTypeCache;
use wcf\system\exception\SystemException;
use wcf\system\SingletonFactory;
* @param integer $userID
* @param string $username
* @param string $editReason
+ * @param integer $obsoletedByUserID The userID of the user that forced this entry to become outdated
*/
- public function add($objectType, $objectID, $message, $time, $userID, $username, $editReason) {
+ public function add($objectType, $objectID, $message, $time, $userID, $username, $editReason, $obsoletedByUserID) {
// save new entry
$sql = "INSERT INTO wcf".WCF_N."_edit_history_entry
- (objectTypeID, objectID, message, time, insertionTime, userID, username, editReason)
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
+ (objectTypeID, objectID, message, time, obsoletedAt, userID, username, editReason, obsoletedByUserID)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
$statement = WCF::getDB()->prepareStatement($sql);
- $statement->execute(array($this->getObjectTypeID($objectType), $objectID, $message, $time, TIME_NOW, $userID, $username, $editReason));
+ $statement->execute(array($this->getObjectTypeID($objectType), $objectID, $message, $time, TIME_NOW, $userID, $username, $editReason, $obsoletedByUserID));
}
/**
}
WCF::getDB()->commitTransaction();
}
+
+ /**
+ * Performs mass reverting of edits by the given users in the given timeframe.
+ *
+ * @param array<integer> $userIDs
+ * @param integer $timeframe
+ */
+ public function bulkRevert(array $userIDs, $timeframe = 86400) {
+ if (empty($userIDs)) return;
+
+ // 1: Select the newest edit history item for each object ("newestEntries")
+ // 2: Check whether the edit was made by the offending users ("vandalizedEntries")
+ // 3: Fetch the newest version that is either:
+ // a) older than $timeframe days
+ // b) by a non offending user
+ $userIDPlaceholders = '?'.str_repeat(',?', count($userIDs) - 1);
+ $sql = "SELECT MAX(entryID)
+ FROM wcf".WCF_N."_edit_history_entry revertTo
+ INNER JOIN(
+ SELECT vandalizedEntries.objectID,
+ vandalizedEntries.objectTypeID
+ FROM wcf".WCF_N."_edit_history_entry vandalizedEntries
+ INNER JOIN (
+ SELECT MAX(newestEntries.entryID) AS entryID
+ FROM wcf".WCF_N."_edit_history_entry newestEntries
+ WHERE newestEntries.obsoletedAt > ?
+ GROUP BY newestEntries.objectTypeID, newestEntries.objectID
+ ) newestEntries2
+ WHERE newestEntries2.entryID = vandalizedEntries.entryID
+ AND vandalizedEntries.obsoletedByUserID IN (".$userIDPlaceholders.")
+ ) AS vandalizedEntries2
+ WHERE revertTo.objectID = vandalizedEntries2.objectID
+ AND revertTo.objectTypeID = vandalizedEntries2.objectTypeID
+ AND ( revertTo.obsoletedAt <= ?
+ OR revertTo.userID NOT IN(".$userIDPlaceholders."))
+ GROUP BY revertTo.objectTypeID, revertTo.objectID";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array_merge(
+ array(TIME_NOW - $timeframe),
+ $userIDs,
+ array(TIME_NOW - $timeframe),
+ $userIDs
+ ));
+
+ $entryIDs = array();
+ while ($entryID = $statement->fetchColumn()) {
+ $entryIDs[] = $entryID;
+ }
+ if (empty($entryIDs)) return;
+
+ $list = new EditHistoryEntryList();
+ $list->getConditionBuilder()->add('entryID IN(?)', array($entryIDs));
+ $list->readObjects();
+ foreach ($list as $entry) {
+ $entry->getObject()->revertVersion($entry);
+ }
+ }
}
userID INT(10),
username VARCHAR(255) NOT NULL DEFAULT '',
time INT(10) NOT NULL DEFAULT 0, -- time the version was created, displayed to the user
- insertionTime INT(10) NOT NULL DEFAULT 0, -- time the version was inserted into the edit history, used for clean up
+ obsoletedAt INT(10) NOT NULL DEFAULT 0, -- time the version was inserted into the edit history, used for clean up
+ obsoletedByUserID INT(10),
message MEDIUMTEXT,
editReason TEXT,
- KEY (objectTypeID, objectID)
+ KEY (objectTypeID, objectID),
+ KEY (obsoletedAt, obsoletedByUserID)
);
DROP TABLE IF EXISTS wcf1_event_listener;
ALTER TABLE wcf1_edit_history_entry ADD FOREIGN KEY (objectTypeID) REFERENCES wcf1_object_type (objectTypeID) ON DELETE CASCADE;
ALTER TABLE wcf1_edit_history_entry ADD FOREIGN KEY (userID) REFERENCES wcf1_user (userID) ON DELETE SET NULL;
+ALTER TABLE wcf1_edit_history_entry ADD FOREIGN KEY (obsoletedByUserID) REFERENCES wcf1_user (userID) ON DELETE SET NULL;
ALTER TABLE wcf1_event_listener ADD FOREIGN KEY (packageID) REFERENCES wcf1_package (packageID) ON DELETE CASCADE;