Add bulkRevert() to EditHistoryManager
authorTim Düsterhus <duesterhus@woltlab.com>
Tue, 22 Jul 2014 20:13:10 +0000 (22:13 +0200)
committerTim Düsterhus <duesterhus@woltlab.com>
Tue, 22 Jul 2014 23:41:36 +0000 (01:41 +0200)
wcfsetup/install/files/lib/system/cronjob/DailyCleanUpCronjob.class.php
wcfsetup/install/files/lib/system/edit/EditHistoryManager.class.php
wcfsetup/setup/db/install.sql

index 2c6dbfc57b21e22afadcf95b894a28e42305bf44..7cf64836a7f9828eedaa8090eb2da3e9068c30c4 100644 (file)
@@ -128,7 +128,7 @@ class DailyCleanUpCronjob extends AbstractCronjob {
                // clean up expired edit history entries
                if (EDIT_HISTORY_EXPIRATION) {
                        $sql = "DELETE FROM     wcf".WCF_N."_edit_history_entry
-                               WHERE           insertionTime < ?";
+                               WHERE           obsoletedAt < ?";
                                $statement = WCF::getDB()->prepareStatement($sql);
                                $statement->execute(array(
                                        (TIME_NOW - 86400 * EDIT_HISTORY_EXPIRATION)
index b08d4a4cba70bf361e881d1c4950be78cd2e0726..cbfb8a2926a4a73967423ab4b624f35a20cf5ad5 100644 (file)
@@ -1,5 +1,6 @@
 <?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;
@@ -54,14 +55,15 @@ class EditHistoryManager extends 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));
        }
        
        /**
@@ -83,4 +85,61 @@ class EditHistoryManager extends SingletonFactory {
                }
                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);
+               }
+       }
 }
index b836a43d7789be54a8ba05c22abe1a47a9b0e3d8..9e1bfc47ffb7602ab748ecd3bdcf52fb7ad0c6c6 100644 (file)
@@ -358,11 +358,13 @@ CREATE TABLE wcf1_edit_history_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;
@@ -1482,6 +1484,7 @@ ALTER TABLE wcf1_cronjob_log ADD FOREIGN KEY (cronjobID) REFERENCES wcf1_cronjob
 
 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;