# HG changeset patch
# User Dan
# Date 1204861541 18000
# Node ID 07bf15b066bcad193eb493bc3bfcda5a200a3141
# Parent d5376271f96bd28008cc203f0dbd6da63aa50625
Hopefully completed rewrite and localization of rollback backend and interface
diff -r d5376271f96b -r 07bf15b066bc ajax.php
--- a/ajax.php Thu Mar 06 20:53:26 2008 -0500
+++ b/ajax.php Thu Mar 06 22:45:41 2008 -0500
@@ -398,6 +398,13 @@
break;
case "protect":
// echo PageUtils::protect($paths->page_id, $paths->namespace, (int)$_POST['level'], $_POST['reason']);
+
+ if ( @$_POST['reason'] === '__ROLLBACK__' )
+ {
+ // __ROLLBACK__ is a keyword for log entries.
+ die('"__ROLLBACK__" ain\'t gonna do it, buddy. Try to _not_ use reserved keywords next time, ok?');
+ }
+
$page = new PageProcessor($paths->page_id, $paths->namespace);
header('Content-type: application/json');
diff -r d5376271f96b -r 07bf15b066bc includes/clientside/static/ajax.js
--- a/includes/clientside/static/ajax.js Thu Mar 06 20:53:26 2008 -0500
+++ b/includes/clientside/static/ajax.js Thu Mar 06 22:45:41 2008 -0500
@@ -216,8 +216,20 @@
ajaxPost(stdAjaxPrefix+'&_mode=protect', 'reason='+ajaxEscape(r)+'&level='+l, function() {
if ( ajax.readyState == 4 && ajax.status == 200 ) {
unsetAjaxLoading();
- if(ajax.responseText != 'good')
- alert(ajax.responseText);
+ if(ajax.responseText == 'good')
+ return true;
+ // check for JSON error response
+ var response = String(ajax.responseText + '');
+ if ( response.substr(0, 1) == '{' )
+ {
+ response = parseJSON(response);
+ if ( response.mode == 'error' )
+ {
+ alert(response.error);
+ return true;
+ }
+ }
+ alert(ajax.responseText);
}
}, true);
}
@@ -403,13 +415,13 @@
});
}
-function ajaxHistView(oldid, tit) {
+function ajaxHistView(oldid, ttl) {
// IE <6 pseudo-compatibility
if ( KILL_SWITCH )
return true;
- if(!tit) tit=title;
+ if(!ttl) ttl=title;
setAjaxLoading();
- ajaxGet(append_sid(scriptPath+'/ajax.php?title='+tit+'&_mode=getpage&oldid='+oldid), function() {
+ ajaxGet(append_sid(scriptPath+'/ajax.php?title='+ttl+'&_mode=getpage&oldid='+oldid), function() {
if ( ajax.readyState == 4 && ajax.status == 200 ) {
unsetAjaxLoading();
edit_open = false;
@@ -426,7 +438,30 @@
ajaxGet(stdAjaxPrefix+'&_mode=rollback&id='+id, function() {
if ( ajax.readyState == 4 && ajax.status == 200 ) {
unsetAjaxLoading();
- alert(ajax.responseText);
+
+ var response = String(ajax.responseText + '');
+ if ( response.substr(0, 1) != '{' )
+ {
+ handle_invalid_json(response);
+ return false;
+ }
+
+ response = parseJSON(response);
+ if ( response.success )
+ {
+ alert( $lang.get('page_msg_rb_success_' + response.action, { dateline: response.dateline }) )
+ }
+ else
+ {
+ if ( response.action )
+ {
+ alert( $lang.get('page_err_' + response.error, { action: response.action }) );
+ }
+ else
+ {
+ alert( $lang.get('page_err_' + response.error) );
+ }
+ }
}
});
}
diff -r d5376271f96b -r 07bf15b066bc includes/functions.php
--- a/includes/functions.php Thu Mar 06 20:53:26 2008 -0500
+++ b/includes/functions.php Thu Mar 06 22:45:41 2008 -0500
@@ -814,7 +814,12 @@
{
$selfn = substr($paths->page_id, strlen($paths->nslist['File']), strlen($paths->page_id));
}
- $q = $db->sql_query('SELECT mimetype,time_id,size FROM '.table_prefix.'files WHERE page_id=\''.$selfn.'\' ORDER BY time_id DESC;');
+ $selfn = $db->escape($selfn);
+ $q = $db->sql_query('SELECT f.mimetype,f.time_id,f.size,l.log_id FROM ' . table_prefix . "files AS f\n"
+ . " LEFT JOIN " . table_prefix . "logs AS l\n"
+ . " ON ( l.time_id = f.time_id AND ( l.action = 'reupload' OR l.action IS NULL ) )\n"
+ . " WHERE f.page_id = '$selfn'\n"
+ . " ORDER BY f.time_id DESC;");
if ( !$q )
{
$db->_die('The file type could not be fetched.');
@@ -845,7 +850,7 @@
{
$size .= ' (' . ( round($r['size'] / 1024, 1) ) . ' ' . $lang->get('etc_unit_kilobytes_short') . ')';
}
-
+
echo $lang->get('onpage_filebox_lbl_size', array('size' => $size));
echo '
' . $lang->get('onpage_filebox_lbl_uploaded') . ' ' . $datestring . '
'; + $last_rollback_id = false; while ( $r = $db->fetchrow() ) { echo '(' . $lang->get('onpage_filebox_btn_this_version') . ') '; - if ( $session->get_permissions('history_rollback') ) - echo ' (' . $lang->get('onpage_filebox_btn_revert') . ') '; + if ( $session->get_permissions('history_rollback') && $last_rollback_id ) + echo ' (' . $lang->get('onpage_filebox_btn_revert') . ') '; + else if ( $session->get_permissions('history_rollback') && !$last_rollback_id ) + echo ' (' . $lang->get('onpage_filebox_btn_current') . ') '; + $last_rollback_id = $r['log_id']; $mimetype = $r['mimetype']; $datestring = enano_date('F d, Y h:i a', (int)$r['time_id']); @@ -4189,6 +4208,51 @@ return ( get_char_count($string, "\n") ) + 1; } +if ( !function_exists('sys_get_temp_dir') ) +{ + // Based on http://www.phpit.net/ + // article/creating-zip-tar-archives-dynamically-php/2/ + /** + * Attempt to get the system's temp directory. + * @return string or bool false on failure + */ + + function sys_get_temp_dir() + { + // Try to get from environment variable + if ( !empty($_ENV['TMP']) ) + { + return realpath( $_ENV['TMP'] ); + } + else if ( !empty($_ENV['TMPDIR']) ) + { + return realpath( $_ENV['TMPDIR'] ); + } + else if ( !empty($_ENV['TEMP']) ) + { + return realpath( $_ENV['TEMP'] ); + } + + // Detect by creating a temporary file + else + { + // Try to use system's temporary directory + // as random name shouldn't exist + $temp_file = tempnam( md5(uniqid(rand(), TRUE)), '' ); + if ( $temp_file ) + { + $temp_dir = realpath( dirname($temp_file) ); + unlink( $temp_file ); + return $temp_dir; + } + else + { + return FALSE; + } + } + } +} + //die('
Original: 01010101010100101010100101010101011010'."\nProcessed: ".uncompress_bitfield(compress_bitfield('01010101010100101010100101010101011010')).''); ?> diff -r d5376271f96b -r 07bf15b066bc includes/pageprocess.php --- a/includes/pageprocess.php Thu Mar 06 20:53:26 2008 -0500 +++ b/includes/pageprocess.php Thu Mar 06 22:45:41 2008 -0500 @@ -221,7 +221,8 @@ if ( !$this->page_exists ) { $func_name = "page_{$this->namespace}_{$this->page_id}"; - die_semicritical($lang->get('page_msg_admin_404_title'), $lang->get('page_msg_admin_404_body', array('func_name' => $func_name))); + + die_semicritical($lang->get('page_msg_admin_404_title'), $lang->get('page_msg_admin_404_body', array('func_name' => $func_name)), (!$this->send_headers)); } $func_name = "page_{$this->namespace}_{$this->page_id}"; if ( function_exists($func_name) ) @@ -519,7 +520,7 @@ } // Guess the proper title - $name = ( !empty($title) ) ? $title : dirtify_page_id($this->page_id); + $name = ( !empty($title) ) ? $title : str_replace('_', ' ', dirtify_page_id($this->page_id)); // Check for the restricted Project: prefix if ( substr($this->page_id, 0, 8) == 'Project:' ) @@ -621,19 +622,134 @@ $log_entry = $db->fetchrow(); $db->free_result(); + $dateline = enano_date('d M Y h:i a', $log_entry['time_id']); + // Let's see, what do we have here... switch ( $log_entry['action'] ) { case 'rename': // Page was renamed, let the rename method handle this - return $this->rename($log_entry['edit_summary']); + return array_merge($this->rename($log_entry['edit_summary']), array('dateline' => $dateline, 'action' => $log_entry['action'])); break; case 'prot': case 'unprot': case 'semiprot': - return $this->protect_page(intval($log_entry['page_text']), '__REVERSION__'); + return array_merge($this->protect_page(intval($log_entry['page_text']), '__REVERSION__'), array('dateline' => $dateline, 'action' => $log_entry['action'])); + break; + case 'delete': + + // Raising a previously dead page has implications... + + // FIXME: l10n + // rollback_extra is required because usually only moderators can undo page deletion AND restore the content. + if ( !$this->perms->get_permissions('history_rollback_extra') ) + return 'Administrative privileges are required for page undeletion.'; + + // Rolling back the deletion of a page that was since created? + $pathskey = $paths->nslist[ $this->namespace ] . $this->page_id; + if ( isset($paths->pages[$pathskey]) ) + return array( + 'success' => false, + // This is a clean Christian in-joke. + 'error' => 'seeking_living_among_dead' + ); + + // Generate a crappy page name + $name = $db->escape( str_replace('_', ' ', dirtify_page_id($this->page_id)) ); + + // Stage 1 - re-insert page + $e = $db->sql_query('INSERT INTO ' . table_prefix.'pages(name,urlname,namespace) VALUES( \'' . $name . '\', \'' . $this->page_id . '\',\'' . $this->namespace . '\' )'); + if ( !$e ) + $db->die_json(); + + // Select the latest published revision + $q = $db->sql_query('SELECT page_text FROM ' . table_prefix . "logs WHERE\n" + . " log_type = 'page'\n" + . " AND action = 'edit'\n" + . " AND page_id = '$this->page_id'\n" + . " AND namespace = '$this->namespace'\n" + . " AND is_draft != 1\n" + . "ORDER BY time_id DESC LIMIT 1;"); + if ( !$q ) + $db->die_json(); + list($page_text) = $db->fetchrow_num(); + $db->free_result($q); + + // Apply the latest revision as the current page text + $page_text = $db->escape($page_text); + $e = $db->sql_query('INSERT INTO ' . table_prefix."page_text(page_id, namespace, page_text) VALUES\n" + . " ( '$this->page_id', '$this->namespace', '$page_text' );"); + if ( !$e ) + $db->die_json(); + + return array( + 'success' => true, + 'dateline' => $dateline, + 'action' => $log_entry['action'] + ); + + break; + case 'reupload': + + // given a log id and some revision info, restore the old file. + // get the timestamp of the file before this one + $q = $db->sql_query('SELECT time_id, file_key, file_extension, filename, size, mimetype FROM ' . table_prefix . "files WHERE time_id < {$log_entry['time_id']} ORDER BY time_id DESC LIMIT 1;"); + if ( !$q ) + $db->_die(); + + $row = $db->fetchrow(); + $db->free_result(); + + // If the file hasn't been renamed to the new format (omitting timestamp), do that now. + $fname = ENANO_ROOT . "/files/{$row['file_key']}_{$row['time_id']}{$row['file_extension']}"; + if ( @file_exists($fname) ) + { + // it's stored in the old format - rename + $fname_new = ENANO_ROOT . "/files/{$row['file_key']}{$row['file_extension']}"; + if ( !@rename($fname, $fname_new) ) + { + return array( + 'success' => false, + 'error' => 'rb_file_rename_failed', + 'action' => $log_entry['action'] + ); + } + } + + // Insert a new file entry + $time = time(); + $filename = $db->escape($row['filename']); + $mimetype = $db->escape($row['mimetype']); + $ext = $db->escape($row['file_extension']); + $key = $db->escape($row['file_key']); + + $q = $db->sql_query('INSERT INTO ' . table_prefix . "files ( time_id, page_id, filename, size, mimetype, file_extension, file_key ) VALUES\n" + . " ( $time, '$this->page_id', '$filename', {$row['size']}, '$mimetype', '$ext', '$key' );"); + if ( !$q ) + $db->die_json(); + + // add reupload log entry + $username = $db->escape($session->username); + $q = $db->sql_query('INSERT INTO ' . table_prefix . "logs ( log_type, action, time_id, page_id, namespace, author, edit_summary ) VALUES\n" + . " ( 'page', 'reupload', $time, '$this->page_id', '$this->namespace', '$username', '__ROLLBACK__' )"); + if ( !$q ) + $db->die_json(); + + return array( + 'success' => true, + 'dateline' => $dateline, + 'action' => $log_entry['action'] + ); + break; default: + + return array( + 'success' => false, + 'error' => 'rb_action_not_supported', + 'action' => $log_entry['action'] + ); + break; } } @@ -743,6 +859,14 @@ $existing_protection = intval($metadata['protected']); $reason = $db->escape($reason); + if ( $existing_protection == $protection_level ) + { + return array( + 'success' => false, + 'error' => 'protection_already_there' + ); + } + $action = '[ insanity ]'; switch($protection_level) { @@ -755,13 +879,13 @@ . " ( 'page', '$action', '{$this->page_id}', '{$this->namespace}', '$username', '$reason', '$time', '$existing_protection', 'DATE_STRING COLUMN OBSOLETE, USE time_id' );"; if ( !$db->sql_query($sql) ) { - $db->_die(); + $db->die_json(); } // Perform the actual protection $q = $db->sql_query('UPDATE ' . table_prefix . "pages SET protected = $protection_level WHERE urlname = '{$this->page_id}' AND namespace = '{$this->namespace}';"); if ( !$q ) - $db->_die(); + $db->die_json(); return array( 'success' => true @@ -1666,7 +1790,7 @@ echo '
' . $lang->get('page_msg_404_was_deleted', array( 'delete_time' => enano_date('d M Y h:i a', $r['time_id']), 'delete_reason' => htmlspecialchars($r['edit_summary']), - 'rollback_flags' => 'href="'.makeUrl($paths->page, 'do=rollback&id='.$r['time_id']).'" onclick="ajaxRollback(\''.$r['time_id'].'\'); return false;"' + 'rollback_flags' => 'href="'.makeUrl($paths->page, 'do=rollback&id='.$r['log_id']).'" onclick="ajaxRollback(\''.$r['log_id'].'\'); return false;"' )) . '
'; if ( $session->user_level >= USER_LEVEL_ADMIN ) diff -r d5376271f96b -r 07bf15b066bc includes/pageutils.php --- a/includes/pageutils.php Thu Mar 06 20:53:26 2008 -0500 +++ b/includes/pageutils.php Thu Mar 06 22:45:41 2008 -0500 @@ -498,7 +498,7 @@ elseif($r['action']=='rename') echo $lang->get('history_log_rename') . 'The URL parameter "id" is not an integer. Exiting to prevent nasties like SQL injection, etc.
'); - $rb = PageUtils::rollback( (int) $id ); + + $id = intval($id); + + $page = new PageProcessor($paths->page_id, $paths->namespace); + $result = $page->rollback_log_entry($id); + + if ( $result['success'] ) + { + $result = $lang->get("page_msg_rb_success_{$result['action']}", array('dateline' => $result['dateline'])); + } + else + { + $result = $lang->get("page_err_{$result['error']}", array('action' => @$result['action'])); + } + $template->header(); - echo ''.$rb.' Return to the page.
'; + echo ''.$result.' ' . $lang->get('etc_return_to_page') . '
'; $template->footer(); break; case 'catedit': diff -r d5376271f96b -r 07bf15b066bc language/english/core.json --- a/language/english/core.json Thu Mar 06 20:53:26 2008 -0500 +++ b/language/english/core.json Thu Mar 06 22:45:41 2008 -0500 @@ -128,6 +128,13 @@If you would like to inquire further about this message, you may contact the %site_administration%.
', err_access_denied_siteadmin: 'site administrator', + err_seeking_living_among_dead: 'You are trying to un-delete a page that has since been restored.\n\n"But the men said to them, \'Why do you look for the living among the dead?\'" (Luke 24:5b/NIV)', + err_access_denied: 'Access to that action is denied.', + err_invalid_parameter: 'An invalid value (parameter) was sent to this action.', + err_rb_action_not_supported: 'Rolling back actions of type "%action%" isn\'t supported.', + err_rb_file_rename_failed: 'Could not rename the file to its new name (1.1.x format)', + err_protection_already_there: 'The protection level you selected is already in effect for this page.', + msg_this_is_a_redirector: 'This page is a redirector.' . $lang->get('etc_access_denied') . '
'); } - $fname = ENANO_ROOT . '/files/' . $row['file_key'] . '_' . $row['time_id'] . $row['file_extension']; + $fname = ENANO_ROOT . '/files/' . $row['file_key'] . $row['file_extension']; + if ( !file_exists($fname) ) + { + $fname = ENANO_ROOT . '/files/' . $row['file_key'] . '_' . $row['time_id'] . $row['file_extension']; + } + if ( !file_exists($fname) ) + { + die("Uploaded file $fname not found."); + } if ( isset($_GET['preview']) && substr($row['mimetype'], 0, 6) == 'image/' ) { @@ -262,7 +270,7 @@ { // Get a temporary file // In this case, the file will not be cached and will be scaled each time it's requested - $temp_dir = ( is_dir('/tmp') ) ? '/tmp' : ( isset($_ENV['TEMP']) ) ? $_ENV['TEMP'] : 'SOME RANDOM NAME'; + $temp_dir = sys_get_temp_dir(); // if tempnam() cannot use the specified directory name, it will fall back on the system default $tempname = tempnam($temp_dir, $filename); if ( $tempname && is_writeable($tempname) )