From daf51d54771e2d2e56383d0c0cb527f396b21406 Mon Sep 17 00:00:00 2001 From: "green-ponies (jgen)" Date: Sun, 15 Jan 2012 11:57:32 -0500 Subject: [PATCH] Feature to Revert Tag changes by IP address. Allows you to revert all edits made by a specific ip during a specified timeframe. --- contrib/tag_history/main.php | 134 ++++++++++++++++++++++++++++++---- contrib/tag_history/theme.php | 69 ++++++++++++++--- core/util.inc.php | 32 ++++++++ 3 files changed, 209 insertions(+), 26 deletions(-) diff --git a/contrib/tag_history/main.php b/contrib/tag_history/main.php index ce2b9249..fcb23d8e 100644 --- a/contrib/tag_history/main.php +++ b/contrib/tag_history/main.php @@ -1,8 +1,8 @@ - * Description: Keep a record of tag changes + * Author: Bzchan , modified by jgen + * Description: Keep a record of tag changes, and allows you to revert changes. */ class Tag_History implements Extension { @@ -20,8 +20,44 @@ class Tag_History implements Extension { $this->install(); } } - - if(($event instanceof PageRequestEvent) && $event->page_matches("tag_history")) + + if(($event instanceof AdminBuildingEvent)) + { + if(isset($_POST['revert_ip']) && $user->is_admin() && $user->check_auth_token()) + { + $revert_ip = filter_var($_POST['revert_ip'], FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE); + + if ($revert_ip === false) { + // invalid ip given. + $this->theme->display_admin_block('Invalid IP'); + return; + } + + if (isset($_POST['revert_date']) && !empty($_POST['revert_date'])) { + if (isValidDate($_POST['revert_date'])){ + $revert_date = addslashes($_POST['revert_date']); // addslashes is really unnecessary since we just checked if valid, but better safe. + } else { + $this->theme->display_admin_block('Invalid Date'); + return; + } + } else { + $revert_date = null; + } + + set_time_limit(0); // reverting changes can take a long time, disable php's timelimit if possible. + + // Call the revert function. + $this->process_revert_all_changes_by_ip($revert_ip, $revert_date); + // output results + $this->theme->display_revert_ip_results(); + } + else + { + $this->theme->display_admin_block(); // add a block to the admin panel + } + } + + if (($event instanceof PageRequestEvent) && ($event->page_matches("tag_history"))) { if($event->get_arg(0) == "revert") { @@ -40,6 +76,7 @@ class Tag_History implements Extension { $this->theme->display_global_page($page, $this->get_global_tag_history()); } } + if(($event instanceof DisplayingImageEvent)) { // handle displaying a link on the view page @@ -107,7 +144,7 @@ class Tag_History implements Extension { { global $page; // check for the nothing case - if($revert_id=="nothing") + if(empty($revert_id) || $revert_id=="nothing") { // tried to set it too the same thing so ignore it (might be a bot) // go back to the index page with you @@ -121,7 +158,7 @@ class Tag_History implements Extension { // lets get this revert id assuming it exists $result = $this->get_tag_history_from_revert($revert_id); - if($result==null) + if(empty($result)) { // there is no history entry with that id so either the image was deleted // while the user was viewing the history, someone is playing with form @@ -135,13 +172,43 @@ class Tag_History implements Extension { $stored_image_id = $result['image_id']; $stored_tags = $result['tags']; - log_debug("tag_history", "Reverting tags of $stored_image_id to [$stored_tags]"); + log_debug("tag_history", 'Reverting tags of Image #'.$stored_image_id.' to ['.$stored_tags.']'); // all should be ok so we can revert by firing the SetUserTags event. send_event(new TagSetEvent(Image::by_id($stored_image_id), $stored_tags)); // all should be done now so redirect the user back to the image $page->set_mode("redirect"); - $page->set_redirect(make_link("post/view/$stored_image_id")); + $page->set_redirect(make_link('post/view/'.$stored_image_id)); + } + + /* + * This function is used by process_revert_all_changes_by_ip() + * to just revert an image's tag history. + */ + private function process_revert_request_only($revert_id) + { + if(empty($revert_id)) { + return; + } + $id = (int) $revert_id; + $result = $this->get_tag_history_from_revert($id); + + if(empty($result)) { + // there is no history entry with that id so either the image was deleted + // while the user was viewing the history, or something messed up + /* calling die() is probably not a good idea, we should throw an Exception */ + die('Error: No tag history with specified id ('.$id.') was found in the database.'."\n\n". + 'Perhaps the image was deleted while processing this request.'); + } + + // lets get the values out of the result + $stored_result_id = $result['id']; + $stored_image_id = $result['image_id']; + $stored_tags = $result['tags']; + + log_debug("tag_history", 'Reverting tags of Image #'.$stored_image_id.' to ['.$stored_tags.']'); + // all should be ok so we can revert by firing the SetUserTags event. + send_event(new TagSetEvent(Image::by_id($stored_image_id), $stored_tags)); } public function get_tag_history_from_revert($revert_id) @@ -192,16 +259,54 @@ class Tag_History implements Extension { return ($row ? $row : array()); } - public function process_revert_all_changes_by_ip($ip) + /* + * This function attempts to revert all changes by a given IP within an (optional) timeframe. + */ + public function process_revert_all_changes_by_ip($ip, $date=null) { global $database; - /* + $date_select = ''; -SELECT * FROM `tag_histories` WHERE image_id IN -( select image_id from `tag_histories` where user_ip="216.240.14.185" and date_set >= 2011-10-23) -ORDER BY image_id, date_set + if (!empty($date)) { + $date_select = 'and date_set >= '.$date; + } else { + $date = 'forever'; + } - */ + log_info("tag_history", 'Attempting to revert edits by ip='.$ip.' (from '.$date.' to now).'); + + // Get all the images that the given IP has changed tags on (within the timeframe) that were last editied by the given IP + $result = $database->get_all(' + SELECT t1.image_id FROM tag_histories t1 LEFT JOIN tag_histories t2 + ON (t1.image_id = t2.image_id AND t1.date_set < t2.date_set) + WHERE t2.image_id IS NULL AND t1.user_ip="'.$ip.'" AND t1.image_id IN + ( select image_id from `tag_histories` where user_ip="'.$ip.'" '.$date_select.') + ORDER BY t1.image_id;'); + + if (empty($result)) { + log_info("tag_history", 'Nothing to revert! for ip='.$ip.' (from '.$date.' to now).'); + $this->theme->add_status('Nothing to Revert','Nothing to revert for ip='.$ip.' (from '.$date.' to now)'); + return; // nothing to do. + } + + for ($i = 0 ; $i < count($result) ; $i++) + { + $image_id = (int) $result[$i]['image_id']; + + // Get the first tag history that was done before the given IP edit + $row = $database->get_row(' + SELECT id,tags FROM `tag_histories` WHERE image_id="'.$image_id.'" AND user_ip!="'.$ip.'" '.$date_select.' ORDER BY date_set DESC LIMIT 1'); + + if (empty($row)) { + // we can not revert this image based on the date restriction. + // Output a message perhaps? + } else { + $id = (int) $row['id']; + $this->process_revert_request_only($id); + $this->theme->add_status('Reverted Change','Reverted Image #'.$image_id.' to Tag History #'.$id.' ('.$row['tags'].')'); + } + } + log_info("tag_history", 'Reverted '.count($result).' edits by ip='.$ip.' (from '.$date.' to now).'); } /* @@ -232,6 +337,7 @@ ORDER BY image_id, date_set // if the image has no history, make one with the old tags $entries = $database->get_one("SELECT COUNT(*) FROM tag_histories WHERE image_id = ?", array($image->id)); if($entries == 0){ + /* these two queries could probably be combined */ $database->execute(" INSERT INTO tag_histories(image_id, tags, user_id, user_ip, date_set) VALUES (?, ?, ?, ?, now())", diff --git a/contrib/tag_history/theme.php b/contrib/tag_history/theme.php index 46ed3564..842ed8bd 100644 --- a/contrib/tag_history/theme.php +++ b/contrib/tag_history/theme.php @@ -1,6 +1,12 @@ , modified by jgen + */ class Tag_HistoryTheme extends Themelet { + var $messages = array(); + public function display_history_page(Page $page, $image_id, $history) { global $user; $start_string = " @@ -22,11 +28,12 @@ class Tag_HistoryTheme extends Themelet { $setter .= " / " . $fields['user_ip']; } $selected = ($n == 2) ? " checked" : ""; - $history_list .= " + $history_list .= '
  • - - -
  • \n"; + + + + '; } $end_string = " @@ -37,8 +44,8 @@ class Tag_HistoryTheme extends Themelet { "; $history_html = $start_string . $history_list . $end_string; - $page->set_title("Image $image_id Tag History"); - $page->set_heading("Tag History: $image_id"); + $page->set_title('Image '.$image_id.' Tag History'); + $page->set_heading('Tag History: '.$image_id); $page->add_block(new NavBlock()); $page->add_block(new Block("Tag History", $history_html, "main", 10)); } @@ -68,13 +75,13 @@ class Tag_HistoryTheme extends Themelet { if($user->is_admin()) { $setter .= " / " . $fields['user_ip']; } - $history_list .= " + $history_list .= '
  • - - $image_id: - $current_tags (Set by $setter) + + '.$image_id.': + '.$current_tags.' (Set by '.$setter.')
  • - "; + '; } $history_html = $start_string . $history_list . $end_string; @@ -85,8 +92,46 @@ class Tag_HistoryTheme extends Themelet { } public function display_history_link(Page $page, $image_id) { - $link = "Tag History\n"; + $link = 'Tag History'; $page->add_block(new Block(null, $link, "main", 5)); } + + /* + * Add a section to the admin page. + */ + public function display_admin_block($validation_msg='') { + global $page; + + if (!empty($validation_msg)) { + $validation_msg = '
    '. $validation_msg .''; + } + + $html = ' + Revert tag changes/edit by a specific IP address.
    + You can restrict the time frame to revert these edits as well. +
    (Date format: 2011-10-23) + '.$validation_msg.' + +

    '.make_form(make_link("admin/revert_ip"),'POST',false,'revert_ip_form')." + IP Address:
    + Date range:

    + +
    + "; + $page->add_block(new Block("Revert By IP", $html)); + } + + /* + * Show a standard page for results to be put into + */ + public function display_revert_ip_results() { + global $page; + $html = implode($this->messages, "\n"); + $page->add_block(new Block("Revert by IP", $html)); + } + + public function add_status($title, $body) { + $this->messages[] = '

    '. $title .'
    '. $body .'

    '; + } } ?> diff --git a/core/util.inc.php b/core/util.inc.php index 25e020f9..97146d25 100644 --- a/core/util.inc.php +++ b/core/util.inc.php @@ -134,6 +134,38 @@ function autodate($date, $html=true) { return ($html ? "" : $hum); } +/** + * Check if a given string is a valid date-time. ( Format: yyyy-mm-dd hh:mm:ss ) + * + * @retval boolean + */ +function isValidDateTime($dateTime) +{ + if (preg_match("/^(\d{4})-(\d{2})-(\d{2}) ([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$/", $dateTime, $matches)) { + if (checkdate($matches[2], $matches[3], $matches[1])) { + return true; + } + } + + return false; +} + +/** + * Check if a given string is a valid date. ( Format: yyyy-mm-dd ) + * + * @retval boolean + */ +function isValidDate($date) +{ + if (preg_match("/^(\d{4})-(\d{2})-(\d{2})$/", $date, $matches)) { + // checkdate wants (month, day, year) + if (checkdate($matches[2], $matches[3], $matches[1])) { + return true; + } + } + + return false; +} /** * Return a pluraliser if necessary