# HG changeset patch # User Dan # Date 1202794666 18000 # Node ID 6607cd646d6d14eda1b9f5a75f9c030c862ce4c0 # Parent 4746dd07cc48467b325a44fbc8ad1f8669065ba3 Added autosave functionality and resurrected the old toolbar code that was added about a year ago but never uesd. diff -r 4746dd07cc48 -r 6607cd646d6d ajax.php --- a/ajax.php Mon Feb 11 14:33:49 2008 -0500 +++ b/ajax.php Tue Feb 12 00:37:46 2008 -0500 @@ -96,14 +96,26 @@ echo PageUtils::checkusername($_GET['name']); break; case "getsource": - header('Content-type: application/json'); + header('Content-type: text/plain'); $password = ( isset($_GET['pagepass']) ) ? $_GET['pagepass'] : false; $revid = ( isset($_GET['revid']) ) ? intval($_GET['revid']) : 0; $page = new PageProcessor($paths->page_id, $paths->namespace, $revid); $page->password = $password; + $have_draft = false; if ( $src = $page->fetch_source() ) { $allowed = true; + $q = $db->sql_query('SELECT author, time_id, page_text FROM ' . table_prefix . 'logs WHERE log_type = \'page\' AND action = \'edit\' + AND page_id = \'' . $db->escape($paths->page_id) . '\' + AND namespace = \'' . $db->escape($paths->namespace) . '\' + AND is_draft = 1;'); + if ( !$q ) + $db->die_json(); + + if ( $db->numrows() > 0 ) + { + $have_draft = true; + } } else if ( $src !== false ) { @@ -127,9 +139,22 @@ 'time' => time(), 'require_captcha' => false, 'allow_wysiwyg' => $auth_wysiwyg, - 'revid' => $revid + 'revid' => $revid, + 'have_draft' => false ); + if ( $have_draft ) + { + $row = $db->fetchrow($q); + $return['have_draft'] = true; + $return['draft_author'] = $row['author']; + $return['draft_time'] = enano_date('d M Y h:i a', intval($row['time_id'])); + if ( isset($_GET['get_draft']) && @$_GET['get_draft'] === '1' ) + { + $return['src'] = $row['page_text']; + } + } + if ( $revid > 0 ) { // Retrieve information about this revision and the current one @@ -138,25 +163,35 @@ ON ( l2.time_id = ' . $revid . ' AND l2.log_type = \'page\' AND l2.action = \'edit\' - AND l2.page_id = \'ACL_Tests\' - AND l2.namespace = \'Article\' + AND l2.page_id = \'' . $db->escape($paths->page_id) . '\' + AND l2.namespace = \'' . $db->escape($paths->namespace) . '\' ) WHERE l1.log_type = \'page\' AND l1.action = \'edit\' - AND l1.page_id = \'ACL_Tests\' - AND l1.namespace = \'Article\' + AND l1.page_id = \'' . $db->escape($paths->page_id) . '\' + AND l1.namespace = \'' . $db->escape($paths->namespace) . '\' AND l1.time_id >= ' . $revid . ' ORDER BY l1.time_id DESC;'); if ( !$q ) $db->die_json(); $rev_count = $db->numrows() - 1; - $row = $db->fetchrow(); - $return['undo_info'] = array( - 'old_author' => $row['oldrev_author'], - 'current_author' => $row['currentrev_author'], - 'undo_count' => $rev_count - ); + if ( $rev_count == -1 ) + { + $return = array( + 'mode' => 'error', + 'error' => '[Internal] No rows returned by revision info query. SQL:
' . $db->latest_query . '
' + ); + } + else + { + $row = $db->fetchrow(); + $return['undo_info'] = array( + 'old_author' => $row['oldrev_author'], + 'current_author' => $row['currentrev_author'], + 'undo_count' => $rev_count + ); + } } if ( $auth_edit && !$session->user_logged_in && getConfig('guest_edit_require_captcha') == '1' ) @@ -165,6 +200,9 @@ $return['captcha_id'] = $session->make_captcha(); } + $template->load_theme(); + $return['toolbar_templates'] = $template->extract_vars('toolbar.tpl'); + echo enano_json_encode($return); break; case "getpage": @@ -181,7 +219,7 @@ $summ = ( isset($_POST['summary']) ) ? $_POST['summary'] : ''; $minor = isset($_POST['minor']); $e = PageUtils::savepage($paths->page_id, $paths->namespace, $_POST['text'], $summ, $minor); - if($e=='good') + if ( $e == 'good' ) { $page = new PageProcessor($paths->page_id, $paths->namespace); $page->send(); @@ -194,77 +232,126 @@ case "savepage_json": header('Content-type: application/json'); if ( !isset($_POST['r']) ) - die('Invalid request'); + die('Invalid request [1]'); $request = enano_json_decode($_POST['r']); - if ( !isset($request['src']) || !isset($request['summary']) || !isset($request['minor_edit']) || !isset($request['time']) ) - die('Invalid request'); + if ( !isset($request['src']) || !isset($request['summary']) || !isset($request['minor_edit']) || !isset($request['time']) || !isset($request['draft']) ) + die('Invalid request [2]
' . htmlspecialchars(print_r($request, true)) . '
'); $time = intval($request['time']); - // Verify that no edits have been made since the editor was requested - $q = $db->sql_query('SELECT time_id, author FROM ' . table_prefix . "logs WHERE log_type = 'page' AND action = 'edit' AND page_id = '{$paths->page_id}' AND namespace = '{$paths->namespace}' ORDER BY time_id DESC LIMIT 1;"); - if ( !$q ) - $db->die_json(); - - $row = $db->fetchrow(); - $db->free_result(); - - if ( $row['time_id'] > $time ) - { - $return = array( - 'mode' => 'obsolete', - 'author' => $row['author'], - 'date_string' => enano_date('d M Y h:i a', $row['time_id']), - 'time' => $row['time_id'] // time() ??? - ); - echo enano_json_encode($return); - break; - } - - // Verify captcha, if needed - if ( !$session->user_logged_in && getConfig('guest_edit_require_captcha') == '1' ) + if ( $request['draft'] ) { - if ( !isset($request['captcha_id']) || !isset($request['captcha_code']) ) - { - die('Invalid request, need captcha metadata'); - } - $code_correct = strtolower($session->get_captcha($request['captcha_id'])); - $code_input = strtolower($request['captcha_code']); - if ( $code_correct !== $code_input ) - { - $return = array( - 'mode' => 'errors', - 'errors' => array($lang->get('editor_err_captcha_wrong')), - 'new_captcha' => $session->make_captcha() - ); - echo enano_json_encode($return); - break; - } - } - - // Verification complete. Start the PageProcessor and let it do the dirty work for us. - $page = new PageProcessor($paths->page_id, $paths->namespace); - if ( $page->update_page($request['src'], $request['summary'], ( $request['minor_edit'] == 1 )) ) - { + // + // The user wants to save a draft version of the page. + // + + // Delete any draft copies if they exist + $q = $db->sql_query('DELETE FROM ' . table_prefix . 'logs WHERE log_type = \'page\' AND action = \'edit\' + AND page_id = \'' . $db->escape($paths->page_id) . '\' + AND namespace = \'' . $db->escape($paths->namespace) . '\' + AND is_draft = 1;'); + if ( !$q ) + $db->die_json(); + + $src = RenderMan::preprocess_text($request['src'], false, false); + + // Save the draft + $q = $db->sql_query('INSERT INTO ' . table_prefix . 'logs ( log_type, action, page_id, namespace, author, edit_summary, page_text, is_draft, time_id ) + VALUES ( + \'page\', + \'edit\', + \'' . $db->escape($paths->page_id) . '\', + \'' . $db->escape($paths->namespace) . '\', + \'' . $db->escape($session->username) . '\', + \'' . $db->escape($request['summary']) . '\', + \'' . $db->escape($src) . '\', + 1, + ' . time() . ' + );'); + + // Done! $return = array( - 'mode' => 'success' + 'mode' => 'success', + 'is_draft' => true ); } else { - $errors = array(); - while ( $err = $page->pop_error() ) + // Verify that no edits have been made since the editor was requested + $q = $db->sql_query('SELECT time_id, author FROM ' . table_prefix . "logs WHERE log_type = 'page' AND action = 'edit' AND page_id = '{$paths->page_id}' AND namespace = '{$paths->namespace}' ORDER BY time_id DESC LIMIT 1;"); + if ( !$q ) + $db->die_json(); + + $row = $db->fetchrow(); + $db->free_result(); + + if ( $row['time_id'] > $time ) { - $errors[] = $err; + $return = array( + 'mode' => 'obsolete', + 'author' => $row['author'], + 'date_string' => enano_date('d M Y h:i a', $row['time_id']), + 'time' => $row['time_id'] // time() ??? + ); + echo enano_json_encode($return); + break; } - $return = array( - 'mode' => 'errors', - 'errors' => array_values($errors) - ); + + // Verify captcha, if needed if ( !$session->user_logged_in && getConfig('guest_edit_require_captcha') == '1' ) { - $return['new_captcha'] = $session->make_captcha(); + if ( !isset($request['captcha_id']) || !isset($request['captcha_code']) ) + { + die('Invalid request, need captcha metadata'); + } + $code_correct = strtolower($session->get_captcha($request['captcha_id'])); + $code_input = strtolower($request['captcha_code']); + if ( $code_correct !== $code_input ) + { + $return = array( + 'mode' => 'errors', + 'errors' => array($lang->get('editor_err_captcha_wrong')), + 'new_captcha' => $session->make_captcha() + ); + echo enano_json_encode($return); + break; + } + } + + // Verification complete. Start the PageProcessor and let it do the dirty work for us. + $page = new PageProcessor($paths->page_id, $paths->namespace); + if ( $page->update_page($request['src'], $request['summary'], ( $request['minor_edit'] == 1 )) ) + { + $return = array( + 'mode' => 'success', + 'is_draft' => false + ); + } + else + { + $errors = array(); + while ( $err = $page->pop_error() ) + { + $errors[] = $err; + } + $return = array( + 'mode' => 'errors', + 'errors' => array_values($errors) + ); + if ( !$session->user_logged_in && getConfig('guest_edit_require_captcha') == '1' ) + { + $return['new_captcha'] = $session->make_captcha(); + } + } + + // If this is based on a draft version, delete the draft - we no longer need it. + if ( @$request['used_draft'] ) + { + $q = $db->sql_query('DELETE FROM ' . table_prefix . 'logs WHERE log_type = \'page\' AND action = \'edit\' + AND page_id = \'' . $db->escape($paths->page_id) . '\' + AND namespace = \'' . $db->escape($paths->namespace) . '\' + AND is_draft = 1;'); } } diff -r 4746dd07cc48 -r 6607cd646d6d images/editor/diff.gif Binary file images/editor/diff.gif has changed diff -r 4746dd07cc48 -r 6607cd646d6d images/editor/discard.gif Binary file images/editor/discard.gif has changed diff -r 4746dd07cc48 -r 6607cd646d6d images/editor/preview.gif Binary file images/editor/preview.gif has changed diff -r 4746dd07cc48 -r 6607cd646d6d images/editor/revert.gif Binary file images/editor/revert.gif has changed diff -r 4746dd07cc48 -r 6607cd646d6d images/editor/save.gif Binary file images/editor/save.gif has changed diff -r 4746dd07cc48 -r 6607cd646d6d includes/clientside/css/enano-shared.css --- a/includes/clientside/css/enano-shared.css Mon Feb 11 14:33:49 2008 -0500 +++ b/includes/clientside/css/enano-shared.css Tue Feb 12 00:37:46 2008 -0500 @@ -52,7 +52,7 @@ opacity: 0.6; /*filter: alpha(opacity=60);*/ } -div.toolbar a:hover img { +div.toolbar a:hover img, div.toolbar a:focus img { opacity: 1; /*filter: alpha(opacity=100);*/ } @@ -67,7 +67,7 @@ max-height: 16px; text-decoration: none; } -div.toolbar a:hover { +div.toolbar a:hover, div.toolbar a:focus { border: 1px solid #202090; background-color: #ceceed; color: #000000; @@ -649,3 +649,10 @@ font-weight: bold; } +div.noborderbottom * { + border-bottom-width: 0px; +} + +div.nobordertop * { + border-top-width: 0px; +} diff -r 4746dd07cc48 -r 6607cd646d6d includes/clientside/static/ajax.js --- a/includes/clientside/static/ajax.js Mon Feb 11 14:33:49 2008 -0500 +++ b/includes/clientside/static/ajax.js Tue Feb 12 00:37:46 2008 -0500 @@ -97,7 +97,7 @@ else { customerror = 'We unexpectedly received the following response from the server. The response should have been in the JSON '; - customerror += 'serialization format, but the response wasn\'t composed only of the JSON response. There are three possible triggers'; + customerror += 'serialization format, but the response wasn\'t composed only of the JSON response. There are three possible triggers '; customerror += 'for this problem:'; var el = document.createElement('p'); el.appendChild(document.createTextNode(customerror)); diff -r 4746dd07cc48 -r 6607cd646d6d includes/clientside/static/dynano.js --- a/includes/clientside/static/dynano.js Mon Feb 11 14:33:49 2008 -0500 +++ b/includes/clientside/static/dynano.js Tue Feb 12 00:37:46 2008 -0500 @@ -22,6 +22,7 @@ this.switchToMCE = DN_switchToMCE; this.destroyMCE = DN_destroyMCE; this.getContent = DN_mceFetchContent; + this.setContent = DN_mceSetContent; } } function __DNObjGetHeight(o) { @@ -148,6 +149,23 @@ } } +function DN_mceSetContent(text) +{ + if ( this.object.name ) + { + this.object.value = text; + if ( tinyMCE.get(this.object.id) ) + { + var editor = tinyMCE.get(this.object.id); + editor.setContent(text); + } + } + else + { + this.object.value = text; + } +} + // A basic Wikitext to XHTML converter function DN_WikitextToXHTML(text) { diff -r 4746dd07cc48 -r 6607cd646d6d includes/clientside/static/editor.js --- a/includes/clientside/static/editor.js Mon Feb 11 14:33:49 2008 -0500 +++ b/includes/clientside/static/editor.js Tue Feb 12 00:37:46 2008 -0500 @@ -11,6 +11,11 @@ var do_popups = ( is_Safari ) ? '' : ',inlinepopups'; var _skin = ( typeof(tinymce_skin) == 'string' ) ? tinymce_skin : 'default'; +var editor_img_path = scriptPath + '/images/editor'; + +// Idle time required for autosave, in seconds +var AUTOSAVE_TIMEOUT = 15; +var AutosaveTimeoutObj = null; var enano_tinymce_options = { mode : "none", @@ -84,12 +89,12 @@ // do we need to enter a captcha before saving the page? var captcha_hash = ( response.require_captcha ) ? response.captcha_id : false; - ajaxBuildEditor(response.src, (!response.auth_edit), response.time, response.allow_wysiwyg, captcha_hash, response.revid, response.undo_info); + ajaxBuildEditor((!response.auth_edit), response.time, response.allow_wysiwyg, captcha_hash, response.revid, response.undo_info, response); } }); } -function ajaxBuildEditor(content, readonly, timestamp, allow_wysiwyg, captcha_hash, revid, undo_info) +function ajaxBuildEditor(readonly, timestamp, allow_wysiwyg, captcha_hash, revid, undo_info, response) { // Set flags // We don't want the fancy confirmation framework to trigger if the user is only viewing the page source @@ -97,7 +102,7 @@ { editor_open = true; disableUnload(); - } + } // Destroy existing contents of page container var edcon = document.getElementById('ajaxEditContainer'); @@ -106,6 +111,8 @@ edcon.removeChild(edcon.childNodes[i]); } + var content = response.src; + // // BUILD EDITOR // @@ -174,13 +181,22 @@ return false; } + // Draft notice + if ( response.have_draft ) + { + var dn = document.createElement('div'); + dn.className = 'warning-box'; + dn.id = 'ajax_edit_draft_notice'; + dn.innerHTML = '' + $lang.get('editor_msg_have_draft_title') + '
'; + dn.innerHTML += $lang.get('editor_msg_have_draft_body', { author: response.draft_author, time: response.draft_time }); + } + // Old-revision notice if ( revid > 0 ) { var oldrev_box = document.createElement('div'); oldrev_box.className = 'usermessage'; oldrev_box.appendChild(document.createTextNode($lang.get('editor_msg_editing_old_revision'))); - form.appendChild(oldrev_box); } // Preview holder @@ -196,18 +212,12 @@ ta_wrapper.style.margin = '10px 0'; // ta_wrapper.style.clear = 'both'; var textarea = document.createElement('textarea'); - textarea.value = content; - textarea._edTimestamp = timestamp; + ta_wrapper.appendChild(textarea); + textarea.id = 'ajaxEditArea'; textarea.rows = '20'; textarea.cols = '60'; textarea.style.width = '98.7%'; - if ( readonly ) - { - textarea.className = 'mce_readonly'; - textarea.setAttribute('readonly', 'readonly'); - } - ta_wrapper.appendChild(textarea); // Revision metadata controls var tblholder = document.createElement('div'); @@ -220,19 +230,22 @@ if ( readonly ) { // Close Viewer button - var tr3 = document.createElement('tr'); - var td3 = document.createElement('th'); - td3.setAttribute('colspan', '2'); - td3.className = 'subhead'; + var toolbar = ''; + var head = new templateParser(response.toolbar_templates.toolbar_start); + var button = new templateParser(response.toolbar_templates.toolbar_button); + var tail = new templateParser(response.toolbar_templates.toolbar_end); - var btn_cancel = document.createElement('input'); - btn_cancel.type = 'button'; - btn_cancel.value = $lang.get('editor_btn_closeviewer'); - btn_cancel.onclick = function() { ajaxReset(true); return false; }; - td3.appendChild(btn_cancel); - tr3.appendChild(td3); + button.assign_bool({ + show_title: true + }); - metatable.appendChild(tr3); + // Button: close + button.assign_vars({ + TITLE: $lang.get('editor_btn_closeviewer'), + IMAGE: editor_img_path + '/discard.gif', + FLAGS: 'href="#" onclick="ajaxReset(true); return false;"' + }); + toolbar += button.run(); } else { @@ -332,49 +345,74 @@ } // Third row: controls - var tr3 = document.createElement('tr'); - var td3 = document.createElement('th'); - td3.setAttribute('colspan', '2'); - td3.className = 'subhead'; + + var toolbar = ''; + var head = new templateParser(response.toolbar_templates.toolbar_start); + var button = new templateParser(response.toolbar_templates.toolbar_button); + var label = new templateParser(response.toolbar_templates.toolbar_label); + var tail = new templateParser(response.toolbar_templates.toolbar_end); + + button.assign_bool({ + show_title: true + }); + + toolbar += head.run(); - var btn_save = document.createElement('input'); - btn_save.type = 'button'; - btn_save.value = $lang.get('editor_btn_save'); - btn_save.onclick = function() { ajaxEditorSave(); return false; }; - td3.appendChild(btn_save); - - td3.appendChild(document.createTextNode(' ')); + // Button: Save + button.assign_vars({ + TITLE: $lang.get('editor_btn_save'), + IMAGE: editor_img_path + '/save.gif', + FLAGS: 'href="#" onclick="ajaxEditorSave(); return false;"' + }); + toolbar += button.run(); - var btn_preview = document.createElement('input'); - btn_preview.type = 'button'; - btn_preview.value = $lang.get('editor_btn_preview'); - btn_preview.onclick = function() { ajaxEditorGenPreview(); return false; }; - td3.appendChild(btn_preview); - - td3.appendChild(document.createTextNode(' ')); + // Button: preview + button.assign_vars({ + TITLE: $lang.get('editor_btn_preview'), + IMAGE: editor_img_path + '/preview.gif', + FLAGS: 'href="#" onclick="ajaxEditorGenPreview(); return false;"' + }); + toolbar += button.run(); - var btn_revert = document.createElement('input'); - btn_revert.type = 'button'; - btn_revert.value = $lang.get('editor_btn_revert'); - btn_revert.onclick = function() { ajaxEditorRevertToLatest(); return false; }; - td3.appendChild(btn_revert); + // Button: revert + button.assign_vars({ + TITLE: $lang.get('editor_btn_revert'), + IMAGE: editor_img_path + '/revert.gif', + FLAGS: 'href="#" onclick="ajaxEditorRevertToLatest(); return false;"' + }); + toolbar += button.run(); - td3.appendChild(document.createTextNode(' ')); + // Button: diff + button.assign_vars({ + TITLE: $lang.get('editor_btn_diff'), + IMAGE: editor_img_path + '/diff.gif', + FLAGS: 'href="#" onclick="ajaxEditorShowDiffs(); return false;"' + }); + toolbar += button.run(); - var btn_diff = document.createElement('input'); - btn_diff.type = 'button'; - btn_diff.value = $lang.get('editor_btn_diff'); - btn_diff.onclick = function() { ajaxEditorShowDiffs(); return false; }; - td3.appendChild(btn_diff); + // Button: cancel + button.assign_vars({ + TITLE: $lang.get('editor_btn_cancel'), + IMAGE: editor_img_path + '/discard.gif', + FLAGS: 'href="#" onclick="ajaxEditorCancel(); return false;"' + }); + toolbar += button.run(); - td3.appendChild(document.createTextNode(' ')); + // Separator + label.assign_vars({ + TITLE: ' ' + }); + toolbar += label.run(); - var btn_cancel = document.createElement('input'); - btn_cancel.type = 'button'; - btn_cancel.value = $lang.get('editor_btn_cancel'); - btn_cancel.onclick = function() { ajaxEditorCancel(); return false; }; - td3.appendChild(btn_cancel); - tr3.appendChild(td3); + // Button: Save draft + button.assign_vars({ + TITLE: $lang.get('editor_btn_savedraft'), + IMAGE: editor_img_path + '/savedraft.gif', + FLAGS: 'href="#" onclick="ajaxPerformAutosave(); return false;" id="ajax_edit_savedraft_btn"' + }); + toolbar += button.run(); + + toolbar += tail.run(); metatable.appendChild(tr1); metatable.appendChild(tr2); @@ -382,7 +420,7 @@ { metatable.appendChild(tr4); } - metatable.appendChild(tr3); + // metatable.appendChild(tr3); } tblholder.appendChild(metatable); @@ -399,10 +437,18 @@ form.appendChild(heading); if ( allow_wysiwyg ) form.appendChild(toggler); + + if ( dn ) + form.appendChild(dn); + + if ( oldrev_box ) + form.appendChild(oldrev_box); + form.appendChild(preview_anchor); form.appendChild(preview_container); form.appendChild(ta_wrapper); form.appendChild(tblholder); + form.innerHTML += '
' + toolbar + '
'; edcon.appendChild(form); if ( editNotice && !readonly ) @@ -410,37 +456,85 @@ edcon.appendChild(en_div); } + // more textarea attribs/init + var textarea = document.getElementById('ajaxEditArea'); + textarea.as_last_save = 0; + textarea.content_orig = content; + textarea.used_draft = false; + textarea.onkeyup = function() + { + if ( this.needReset ) + { + var img = $('ajax_edit_savedraft_btn').object.getElementsByTagName('img')[0]; + var lbl = $('ajax_edit_savedraft_btn').object.getElementsByTagName('span')[0]; + img.src = editor_img_path + '/savedraft.gif'; + lbl.innerHTML = $lang.get('editor_btn_savedraft'); + } + if ( AutosaveTimeoutObj ) + clearTimeout(AutosaveTimeoutObj); + AutosaveTimeoutObj = setTimeout('ajaxAutosaveDraft();', ( AUTOSAVE_TIMEOUT * 1000 )); + } + + if ( readonly ) + { + textarea.className = 'mce_readonly'; + textarea.setAttribute('readonly', 'readonly'); + } + // If the editor preference is tinymce, switch the editor to TinyMCE now if ( readCookie('enano_editor_mode') == 'tinymce' && allow_wysiwyg ) { $dynano('ajaxEditArea').switchToMCE(); } $dynano('ajaxEditArea').object.focus(); + $dynano('ajaxEditArea').object._edTimestamp = timestamp; + $dynano('ajaxEditArea').setContent(content); + + // Autosave every 5 minutes (m * s * ms) + setInterval('ajaxPerformAutosave();', ( 5 * 60 * 1000 )); } -function ajaxEditorSave() +function ajaxEditorSave(is_draft) { - ajaxSetEditorLoading(); + if ( !is_draft ) + ajaxSetEditorLoading(); var ta_content = $('ajaxEditArea').getContent(); - if ( ta_content == '' || ta_content == '

' || ta_content == '

 

' ) + if ( !is_draft && ( ta_content == '' || ta_content == '

' || ta_content == '

 

' ) ) { new messagebox(MB_OK|MB_ICONSTOP, $lang.get('editor_err_no_text_title'), $lang.get('editor_err_no_text_body')); ajaxUnSetEditorLoading(); return false; } + if ( is_draft ) + { + // ajaxSetEditorLoading(); + var img = $('ajax_edit_savedraft_btn').object.getElementsByTagName('img')[0]; + var lbl = $('ajax_edit_savedraft_btn').object.getElementsByTagName('span')[0]; + img.src = scriptPath + '/images/loading.gif'; + var d = new Date(); + var m = String(d.getMinutes()); + if ( m.length < 2 ) + m = '0' + m; + var time = d.getHours() + ':' + m; + lbl.innerHTML = $lang.get('editor_msg_draft_saving'); + } + var edit_summ = $('enano_editor_field_summary').object.value; if ( !edit_summ ) edit_summ = ''; var is_minor = ( $('enano_editor_field_minor').object.checked ) ? 1 : 0; var timestamp = $('ajaxEditArea').object._edTimestamp; + var used_draft = $('ajaxEditArea').object.used_draft; var json_packet = { src: ta_content, summary: edit_summ, minor_edit: is_minor, - time: timestamp + time: timestamp, + draft: ( is_draft == true ), + used_draft: used_draft }; // Do we need to add captcha info? @@ -462,7 +556,6 @@ { if ( ajax.readyState == 4 && ajax.status == 200 ) { - ajaxUnSetEditorLoading(); var response = String(ajax.responseText + ''); if ( response.substr(0, 1) != '{' ) { @@ -509,27 +602,44 @@ } if ( response.mode == 'success' ) { - // The save was successful; reset flags and make another request for the new page content - setAjaxLoading(); - editor_open = false; - enableUnload(); - changeOpac(0, 'ajaxEditContainer'); - ajaxGet(stdAjaxPrefix + '&_mode=getpage&noheaders', function() - { - if ( ajax.readyState == 4 && ajax.status == 200 ) + if ( response.is_draft ) + { + document.getElementById('ajaxEditArea').used_draft = true; + document.getElementById('ajaxEditArea').needReset = true; + var img = $('ajax_edit_savedraft_btn').object.getElementsByTagName('img')[0]; + var lbl = $('ajax_edit_savedraft_btn').object.getElementsByTagName('span')[0]; + img.src = scriptPath + '/images/mini-info.png'; + var d = new Date(); + var m = String(d.getMinutes()); + if ( m.length < 2 ) + m = '0' + m; + var time = d.getHours() + ':' + m; + lbl.innerHTML = $lang.get('editor_msg_draft_saved', { time: time }); + } + else + { + // The save was successful; reset flags and make another request for the new page content + ajaxUnSetEditorLoading(); + setAjaxLoading(); + editor_open = false; + enableUnload(); + changeOpac(0, 'ajaxEditContainer'); + ajaxGet(stdAjaxPrefix + '&_mode=getpage&noheaders', function() { - unsetAjaxLoading(); - selectButtonMajor('article'); - unselectAllButtonsMinor(); - - document.getElementById('ajaxEditContainer').innerHTML = '
' + $lang.get('editor_msg_saved') + '
' + ajax.responseText; - opacity('ajaxEditContainer', 0, 100, 1000); - } - }); + if ( ajax.readyState == 4 && ajax.status == 200 ) + { + unsetAjaxLoading(); + selectButtonMajor('article'); + unselectAllButtonsMinor(); + + document.getElementById('ajaxEditContainer').innerHTML = '
' + $lang.get('editor_msg_saved') + '
' + ajax.responseText; + opacity('ajaxEditContainer', 0, 100, 1000); + } + }); + } } } }, true); - } function ajaxEditorGenPreview() @@ -594,15 +704,7 @@ return false; } - var ed = tinyMCE.get('ajaxEditArea'); - if ( ed ) - { - ed.setContent(response.src); - } - else - { - $('ajaxEditArea').object.value = response.src; - } + $('ajaxEditArea').setContent(response.src); } }, true); } @@ -747,3 +849,70 @@ } } +function ajaxAutosaveDraft() +{ + var aed = document.getElementById('ajaxEditArea'); + if ( !aed ) + return false; + var last_save = aed.as_last_save; + var now = unix_time(); + if ( ( last_save + 120 ) < now && aed.value != aed.content_orig ) + { + ajaxPerformAutosave(); + } +} + +function ajaxPerformAutosave() +{ + var aed = document.getElementById('ajaxEditArea'); + if ( !aed ) + return false; + var now = unix_time(); + aed.as_last_save = now; + + var ta_content = $('ajaxEditArea').getContent(); + + if ( ta_content == '' || ta_content == '

' || ta_content == '

 

' ) + { + return false; + } + + ajaxEditorSave(true); +} + +function ajaxEditorUseDraft() +{ + var aed = document.getElementById('ajaxEditArea'); + if ( !aed ) + return false; + ajaxSetEditorLoading(); + ajaxGet(stdAjaxPrefix + '&_mode=getsource&get_draft=1', function() + { + if ( ajax.readyState == 4 && ajax.status == 200 ) + { + ajaxUnSetEditorLoading(); + + var response = String(ajax.responseText + ''); + if ( response.substr(0, 1) != '{' ) + { + handle_invalid_json(response); + return false; + } + + response = parseJSON(response); + if ( response.mode == 'error' ) + { + unselectAllButtonsMinor(); + new messagebox(MB_OK | MB_ICONSTOP, $lang.get('editor_err_server'), response.error); + return false; + } + + $('ajaxEditArea').setContent(response.src); + $('ajaxEditArea').object.used_draft = true; + + var dn = $('ajax_edit_draft_notice').object; + dn.parentNode.removeChild(dn); + } + }, true); +} + diff -r 4746dd07cc48 -r 6607cd646d6d includes/clientside/static/misc.js --- a/includes/clientside/static/misc.js Mon Feb 11 14:33:49 2008 -0500 +++ b/includes/clientside/static/misc.js Tue Feb 12 00:37:46 2008 -0500 @@ -397,7 +397,7 @@ { // let's hope this gets the image cached var _ = new Image(32, 32); - _.src = scriptPath + "/images/good.gif"; + _.src = scriptPath + "/images/check.png"; ajaxGet(makeUrlNS('Special', 'Login', 'act=getkey'), function() { if ( ajax.readyState == 4 && ajax.status == 200 ) @@ -626,7 +626,7 @@ case 'success': var success_win = '
\

' + $lang.get('user_login_success_short') + '

\ -

\ +

\
'; ajax_auth_mb_cache.updateContent(success_win); if ( typeof(ajax_auth_prompt_cache) == 'function' ) diff -r 4746dd07cc48 -r 6607cd646d6d includes/pageutils.php --- a/includes/pageutils.php Mon Feb 11 14:33:49 2008 -0500 +++ b/includes/pageutils.php Tue Feb 12 00:37:46 2008 -0500 @@ -431,9 +431,9 @@ echo ''. (( $r['minor_edit'] ) ? 'M' : '' ) .''."\n"; // Actions! - echo '' . $lang->get('history_action_view') . ''."\n"; - echo '' . $lang->get('history_action_contrib') . ''."\n"; - echo '' . $lang->get('history_action_restore') . ''."\n"; + echo '' . $lang->get('history_action_view') . ''."\n"; + echo '' . $lang->get('history_action_contrib') . ''."\n"; + echo '' . $lang->get('history_action_restore') . ''."\n"; echo ''."\n"."\n"; @@ -502,8 +502,8 @@ echo ''; // Actions! - echo '' . $lang->get('history_action_contrib') . ''; - echo '' . $lang->get('history_action_revert') . ''; + echo '' . $lang->get('history_action_contrib') . ''; + echo '' . $lang->get('history_action_revert') . ''; echo ''; } diff -r 4746dd07cc48 -r 6607cd646d6d includes/template.php --- a/includes/template.php Mon Feb 11 14:33:49 2008 -0500 +++ b/includes/template.php Tue Feb 12 00:37:46 2008 -0500 @@ -874,7 +874,7 @@ $f = microtime_float(); $f = $f - $_starttime; - $f = round($f, 4); + $f = round($f, 2); $t_loc = $lang->get('page_msg_stats_gentime_short', array('time' => $f)); $t_loc_long = $lang->get('page_msg_stats_gentime_long', array('time' => $f)); diff -r 4746dd07cc48 -r 6607cd646d6d install/schemas/mysql_stage2.sql --- a/install/schemas/mysql_stage2.sql Mon Feb 11 14:33:49 2008 -0500 +++ b/install/schemas/mysql_stage2.sql Tue Feb 12 00:37:46 2008 -0500 @@ -43,6 +43,7 @@ author varchar(63), edit_summary text, minor_edit tinyint(1), + is_draft tinyint(1) NOT NULL DEFAULT 0, PRIMARY KEY ( log_id ) ) CHARACTER SET `utf8` COLLATE `utf8_bin`; diff -r 4746dd07cc48 -r 6607cd646d6d install/schemas/postgresql_stage2.sql --- a/install/schemas/postgresql_stage2.sql Mon Feb 11 14:33:49 2008 -0500 +++ b/install/schemas/postgresql_stage2.sql Tue Feb 12 00:37:46 2008 -0500 @@ -23,7 +23,7 @@ subject text, comment_data text, name text, - approved smallint default 1, + approved smallint DEFAULT 1, user_id int NOT NULL DEFAULT -1, time int NOT NULL DEFAULT 0, ip_address varchar(39), @@ -34,7 +34,7 @@ log_id SERIAL, log_type varchar(16), action varchar(16), - time_id int NOT NULL default '0', + time_id int NOT NULL DEFAULT '0', date_string varchar(63), page_id text, namespace text, @@ -43,12 +43,13 @@ author varchar(63), edit_summary text, minor_edit smallint, + is_draft smallint NOT NULL DEFAULT 0, PRIMARY KEY ( log_id ) ); CREATE TABLE {{TABLE_PREFIX}}page_text( page_id varchar(255), - namespace varchar(16) NOT NULL default 'Article', + namespace varchar(16) NOT NULL DEFAULT 'Article', page_text text, char_tag varchar(63) ); @@ -57,13 +58,13 @@ page_order int, name varchar(255), urlname varchar(255), - namespace varchar(16) NOT NULL default 'Article', - special smallint default '0', - visible smallint default '1', - comments_on smallint default '1', + namespace varchar(16) NOT NULL DEFAULT 'Article', + special smallint DEFAULT '0', + visible smallint DEFAULT '1', + comments_on smallint DEFAULT '1', protected smallint NOT NULL DEFAULT 0, wiki_mode smallint NOT NULL DEFAULT 2, - delvotes int NOT NULL default 0, + delvotes int NOT NULL DEFAULT 0, password varchar(40) NOT NULL DEFAULT '', delvote_ips text DEFAULT NULL ); @@ -72,17 +73,17 @@ session_key varchar(32), salt varchar(32), user_id int, - auth_level smallint NOT NULL default '0', - source_ip varchar(10) default '0x7f000001', - time bigint default '0' + auth_level smallint NOT NULL DEFAULT '0', + source_ip varchar(10) DEFAULT '0x7f000001', + time bigint DEFAULT '0' ); CREATE TABLE {{TABLE_PREFIX}}themes( theme_id varchar(63), theme_name text, - theme_order smallint NOT NULL default '1', - default_style varchar(63) NOT NULL DEFAULT '', - enabled smallint NOT NULL default '1' + theme_order smallint NOT NULL DEFAULT '1', + DEFAULT_style varchar(63) NOT NULL DEFAULT '', + enabled smallint NOT NULL DEFAULT '1' ); CREATE TABLE {{TABLE_PREFIX}}users( @@ -91,9 +92,9 @@ password varchar(255), email text, real_name text, - user_level smallint NOT NULL default 2, - theme varchar(64) NOT NULL default 'bleu.css', - style varchar(64) NOT NULL default 'default', + user_level smallint NOT NULL DEFAULT 2, + theme varchar(64) NOT NULL DEFAULT 'bleu.css', + style varchar(64) NOT NULL DEFAULT 'DEFAULT', signature text, reg_time int NOT NULL DEFAULT 0, account_active smallint NOT NULL DEFAULT 0, @@ -139,10 +140,10 @@ file_id SERIAL, time_id int NOT NULL, page_id varchar(63) NOT NULL, - filename varchar(127) default NULL, + filename varchar(127) DEFAULT NULL, size bigint NOT NULL, - mimetype varchar(63) default NULL, - file_extension varchar(8) default NULL, + mimetype varchar(63) DEFAULT NULL, + file_extension varchar(8) DEFAULT NULL, file_key varchar(32) NOT NULL, PRIMARY KEY (file_id) ); @@ -151,7 +152,7 @@ buddy_id SERIAL, user_id int, buddy_user_id int, - is_friend smallint NOT NULL default '1', + is_friend smallint NOT NULL DEFAULT '1', PRIMARY KEY (buddy_id) ); @@ -266,7 +267,7 @@ CREATE TABLE {{TABLE_PREFIX}}language( lang_id SERIAL, lang_code varchar(16) NOT NULL, - lang_name_default varchar(64) NOT NULL, + lang_name_DEFAULT varchar(64) NOT NULL, lang_name_native varchar(64) NOT NULL, last_changed int NOT NULL DEFAULT 0 ); @@ -337,7 +338,7 @@ INSERT INTO {{TABLE_PREFIX}}pages(page_order, name, urlname, namespace, special, visible, comments_on, protected, delvotes, delvote_ips) VALUES (NULL, 'Main Page', 'Main_Page', 'Article', 0, 1, 1, 1, 0, ''); -INSERT INTO {{TABLE_PREFIX}}themes(theme_id, theme_name, theme_order, default_style, enabled) VALUES +INSERT INTO {{TABLE_PREFIX}}themes(theme_id, theme_name, theme_order, DEFAULT_style, enabled) VALUES ('oxygen', 'Oxygen', 1, 'bleu.css', 1), ('stpatty', 'St. Patty', 2, 'shamrock.css', 1); diff -r 4746dd07cc48 -r 6607cd646d6d install/schemas/upgrade/1.1.1-1.1.2-mysql.sql --- a/install/schemas/upgrade/1.1.1-1.1.2-mysql.sql Mon Feb 11 14:33:49 2008 -0500 +++ b/install/schemas/upgrade/1.1.1-1.1.2-mysql.sql Tue Feb 12 00:37:46 2008 -0500 @@ -2,6 +2,7 @@ -- Upgrade schema - Enano 1.1.1 - 1.1.2 ALTER TABLE {{TABLE_PREFIX}}logs ADD COLUMN log_id int(15) NOT NULL auto_increment, ADD PRIMARY KEY ( log_id ); +ALTER TABLE {{TABLE_PREFIX}}logs ADD COLUMN is_draft tinyint(1) NOT NULL DEFAULT 0; ALTER TABLE {{TABLE_PREFIX}}users ADD COLUMN user_rank int(12) UNSIGNED NOT NULL DEFAULT 1, ADD COLUMN user_timezone int(12) UNSIGNED NOT NULL DEFAULT 0; diff -r 4746dd07cc48 -r 6607cd646d6d install/schemas/upgrade/1.1.1-1.1.2-postgresql.sql --- a/install/schemas/upgrade/1.1.1-1.1.2-postgresql.sql Mon Feb 11 14:33:49 2008 -0500 +++ b/install/schemas/upgrade/1.1.1-1.1.2-postgresql.sql Tue Feb 12 00:37:46 2008 -0500 @@ -2,6 +2,7 @@ -- Upgrade schema - Enano 1.1.1 - 1.1.2 ALTER TABLE {{TABLE_PREFIX}}logs ADD COLUMN log_id SERIAL, ADD PRIMARY KEY ( log_id ); +ALTER TABLE {{TABLE_PREFIX}}logs ADD COLUMN is_draft smallint NOT NULL DEFAULT 0; ALTER TABLE {{TABLE_PREFIX}}users ADD COLUMN user_rank int NOT NULL DEFAULT 1, ADD COLUMN user_timezone int NOT NULL DEFAULT 0; diff -r 4746dd07cc48 -r 6607cd646d6d language/english/core.json --- a/language/english/core.json Mon Feb 11 14:33:49 2008 -0500 +++ b/language/english/core.json Tue Feb 12 00:37:46 2008 -0500 @@ -319,12 +319,14 @@ msg_confirm_ajax: 'Do you really want to do this?\nYour changes to this page have not been saved. If you continue, your changes will be lost.', msg_unload: 'If you do, any changes that you have made to this page will be lost.', msg_revert_confirm_title: 'Do you really want to revert your changes?', - msg_revert_confirm_body: 'If you choose to continue, all of the changes you have made to this page will be lost, and the editor will be set with the latest copy of the page.', + msg_revert_confirm_body: 'If you choose to continue, all of the changes you have made to this page will be lost, and the editor will be set with the latest published (not draft) copy of the page.', msg_cancel_confirm_title: 'Do you want to cancel your changes?', msg_cancel_confirm_body: 'Are you sure you want to discard the changes you made? If you continue, your changes cannot be recovered.', msg_diff: 'Below is a summarization of the differences between the current revision of the page (left), and your version (right).', msg_diff_empty: 'There are no differences between the text in the editor and the current revision of the page.', msg_editing_old_revision: 'You are editing an old revision of this page. If you click Save, newer revisions of this page will be undone.', + msg_have_draft_title: 'A draft copy of this page is available.', + msg_have_draft_body: '%author% saved a draft version of this page on %time%. You can use the draft copy, or edit the current published version (below). If you edit the published version, the draft copy will remain available, but will not reflect your changes. It is recommended that you edit the draft version instead of editing the published version.', btn_graphical: 'graphical editor', btn_wikitext: 'wikitext editor', lbl_edit_summary: 'Brief summary of your changes:', @@ -332,12 +334,15 @@ lbl_minor_edit: 'Mark revision as trivial:', lbl_minor_edit_field: 'This is a minor edit', lbl_minor_edit_explain: 'Select this if you\'re only making a minor change to the page', - btn_save: 'Save changes', - btn_preview: 'Show preview', - btn_revert: 'Revert changes', - btn_cancel: 'Cancel and return to page', + btn_save: 'Save', + btn_savedraft: 'Save draft', + btn_preview: 'Preview', + btn_revert: 'Revert', + btn_cancel: 'Cancel', + btn_diff: 'Show changes', btn_closeviewer: 'Close viewer', - btn_diff: 'Show changes', + msg_draft_saving: 'Saving...', + msg_draft_saved: 'Saved at %time%', preview_blurb: 'Reminder: This is only a preview - your changes to this page have not yet been saved.', msg_save_success_title: 'Changes saved', msg_save_success_body: 'Your changes to this page have been saved. Redirecting...', diff -r 4746dd07cc48 -r 6607cd646d6d plugins/admin/SecurityLog.php --- a/plugins/admin/SecurityLog.php Mon Feb 11 14:33:49 2008 -0500 +++ b/plugins/admin/SecurityLog.php Tue Feb 12 00:37:46 2008 -0500 @@ -102,7 +102,7 @@ $l = 'SELECT action,date_string,author,edit_summary,time_id,page_text FROM '.table_prefix.'logs WHERE log_type=\'security\' ORDER BY time_id DESC, action ASC;'; } $q = $db->sql_query($l); - while($r = $db->fetchrow()) + while($r = $db->fetchrow($q)) { $return .= seclog_format_inner($r); } diff -r 4746dd07cc48 -r 6607cd646d6d themes/oxygen/css/bleu.css --- a/themes/oxygen/css/bleu.css Mon Feb 11 14:33:49 2008 -0500 +++ b/themes/oxygen/css/bleu.css Tue Feb 12 00:37:46 2008 -0500 @@ -289,9 +289,10 @@ /* toolbar */ div.toolbar { - border-bottom: 1px solid #909090; + border: 1px solid #3b619c; background-color: #D0D0D0; - padding: 2px 0; + background-image: url(../images/tb-bkg.gif); + padding: 1px 0; height: 22px; font-family: arial, sans-serif; font-size: 8pt; @@ -309,7 +310,7 @@ opacity: 0.6; /*filter: alpha(opacity=60);*/ } -div.toolbar a:hover img { +div.toolbar a:hover img, div.toolbar a:focus img { opacity: 1; /*filter: alpha(opacity=100);*/ } @@ -324,15 +325,16 @@ max-height: 16px; text-decoration: none; } -div.toolbar a:hover { - border: 1px solid #202090; +div.toolbar a:hover, div.toolbar a:focus { + border: 1px solid #000080; background-color: #ceceed; + background-image: url(../images/tb-frg.gif); color: #000000; text-decoration: none; } div.toolbar a:active { - border: 1px solid #A0A0A0; background-color: #E0E0E0; + background-image: url(../images/tb-psh.gif); } div.toolbar img { margin: 0; @@ -342,11 +344,20 @@ } div.toolbar a span { position: relative; - top: -4px; + top: -3px !important; +} +div.toolbar a span.noimage { + position: relative; + top: 0px !important; + height: 16px !important; + display: block; + padding-left: 2px !important; } div.toolbar li span { - padding-left: 2px; - padding-right: 5px; + padding-left: 4px; + padding-right: 2px; + position: relative; + top: 4px; } /* vertical toolbar */ diff -r 4746dd07cc48 -r 6607cd646d6d themes/oxygen/images/tb-bkg.gif Binary file themes/oxygen/images/tb-bkg.gif has changed diff -r 4746dd07cc48 -r 6607cd646d6d themes/oxygen/images/tb-frg.gif Binary file themes/oxygen/images/tb-frg.gif has changed diff -r 4746dd07cc48 -r 6607cd646d6d themes/oxygen/images/tb-psh.gif Binary file themes/oxygen/images/tb-psh.gif has changed diff -r 4746dd07cc48 -r 6607cd646d6d themes/oxygen/toolbar.tpl --- a/themes/oxygen/toolbar.tpl Mon Feb 11 14:33:49 2008 -0500 +++ b/themes/oxygen/toolbar.tpl Tue Feb 12 00:37:46 2008 -0500 @@ -1,5 +1,6 @@ @@ -9,9 +10,15 @@
  • + {TITLE} + - {TITLE} + + {TITLE} + + {TITLE} +