diff --git a/lib/ajax/autosave.js b/lib/ajax/autosave.js new file mode 100644 index 0000000..843df6e --- /dev/null +++ b/lib/ajax/autosave.js @@ -0,0 +1,211 @@ +// $Id$ + +/////////////////////////////////////////////////////////////////////////// +// // +// NOTICE OF COPYRIGHT // +// // +// Moodle - Modular Object-Oriented Dynamic Learning Environment // +// http://moodle.org // +// // +// Copyleft (c) 1999 onwards Martin Dougiamas http://dougiamas.com // +// (c) 2008 onwards David Mudrak http://mudrak.name // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation; either version 2 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License for more details: // +// // +// http://www.gnu.org/copyleft/gpl.html // +// // +/////////////////////////////////////////////////////////////////////////// + + +/** + * Initialize client for autosaving support + * + * param string pageid - the identifier of the current page + * param string fieldid - the identifier of an input field to be initialized + */ +function autosave_init(pageid, fieldid) { + YAHOO.util.Event.onDOMReady(function() { autosave_check(pageid, fieldid); } ); +} + +/** + * Ask server if there is a previously autosaved draft and enable the autosave support if there is no one. + * + * param string pageid - the identifier of the current page + * param string fieldid - the identifier of an input field to be checked + */ +function autosave_check(pageid, fieldid) { + + /** firstly disable the autosave so it is not replaced by the current (possibly empty) content */ + var enabler = YAHOO.util.Dom.get('autosaveenabled-'+fieldid); + enabler.value = 0; + + /** define response behaviour **/ + var callback = { + success: function(o) { + fieldid = this.args; + var info = YAHOO.util.Dom.get('autosaveinfo-'+fieldid); + var response = new Object(); + try { + response = YAHOO.lang.JSON.parse(o.responseText); + if (response.status == 'OK_NOTFOUND') { + // no previously saved draft found - let us enable autosaving + info.style.background = "url('" + moodle_cfg.pixpath + "/i/tick_amber_big.gif') no-repeat top left"; + autosave_display_response(fieldid, response); + var enabler = YAHOO.util.Dom.get('autosaveenabled-'+fieldid); + enabler.value = 1; + } + if (response.status == 'OK_FOUND') { + // recently autosaved draft is available - let us ask user what she wants to do with it + info.style.background = "url('" + moodle_cfg.pixpath + "/i/info.gif') no-repeat top left"; + autosave_display_response(fieldid, response); + autosave_highlight(info); + } + } catch (e) { /** Fake response **/ + info.style.background = "url('" + moodle_cfg.pixpath + "/i/cross_red_big.gif') no-repeat top left"; + response.status = 'ERROR'; + response.message = 'Unable to parse server response'; + autosave_display_response(fieldid, response); + } + }, + failure: function(o) {}, + args:fieldid + }; + + /** who is going to process the request **/ + var url = moodle_cfg.wwwroot + '/user/autosave.php'; + + var data = ''; + data += '&sesskey=' + moodle_cfg.sesskey; + data += '&pageid=' + pageid; + data += '&fieldid=' + fieldid; + data += '&action=check'; + YAHOO.util.Connect.asyncRequest('POST', url, callback, data); + + response = new Object(); + response.status = 'WAIT'; + response.message = 'Checking for previously autosaved drafts...'; + autosave_display_response(fieldid, response); + var info = YAHOO.util.Dom.get('autosaveinfo-'+fieldid); + info.style.background = "url('" + moodle_cfg.pixpath + "/i/loading_small.gif') no-repeat top left"; +} + +/** + * Save the content of the given field on the background + * + * This should be called periodically. + * param string pageid - the identifier of the current page + * param string fieldid - the identifier of an input field to be saved + */ +function autosave(pageid, fieldid) { + + var enabler = YAHOO.util.Dom.get('autosaveenabled-'+fieldid); + if (enabler.value != 1) { + // autosave not enabled yet for this field - we are still waiting for the server response confirming + // that we can send drafts to the server + return; + } + + /** define response behaviour **/ + var callback = { + success: function(o) { + /** + * Code to execute when we receive a success in the AJAX response + * It should print updated info about autosave (replacing previous one) + */ + fieldid = this.args; + var info = YAHOO.util.Dom.get('autosaveinfo-'+fieldid); + var response = new Object(); + try { + response = YAHOO.lang.JSON.parse(o.responseText); + info.style.background = "url('" + moodle_cfg.pixpath + "/i/tick_green_big.gif') no-repeat top left"; + } catch (e) { /** Fake response **/ + info.style.background = "url('" + moodle_cfg.pixpath + "/i/cross_red_big.gif') no-repeat top left"; + response.status = 'ERROR'; + response.message= 'Unable to parse server response'; + } + autosave_display_response(fieldid, response); + }, + + failure: function(o) { + /** + * Code to execute when we receive a failure in the AJAX response + * It should print some error message + */ + fieldid = this.args; + + /** Stop animation **/ + var info = YAHOO.util.Dom.get('autosaveinfo-'+fieldid); + info.style.background = "url('" + moodle_cfg.pixpath + "/i/cross_red_big.gif') no-repeat top left"; + + /** Process error response **/ + var response = new Object(); + response.status = 'ERROR'; + response.message = 'Draft autosave failed'; + autosave_display_response(fieldid, response); + }, + + args:fieldid + }; + + /** who is going to process the request **/ + var url = moodle_cfg.wwwroot + '/user/autosave.php'; + + /** move the contents from the TinyMCE editor to the form field **/ + tinyMCE.triggerSave(true, true); + + var content = YAHOO.util.Dom.get(fieldid).value; + var data = ''; + data += '&sesskey=' + moodle_cfg.sesskey; + data += '&pageid=' + pageid; + data += '&fieldid=' + fieldid; + data += '&data=save'; + data += '&content=' + content; + YAHOO.util.Connect.asyncRequest('POST', url, callback, data); + + response = new Object(); + response.status = 'WAIT'; + response.message = 'Autosaving draft...'; + autosave_display_response(fieldid, response); + var info = YAHOO.util.Dom.get('autosaveinfo-'+fieldid); + info.style.background = "url('" + moodle_cfg.pixpath + "/i/loading_small.gif') no-repeat top left"; +} + + +/** + * This function will display the correct response received from server + */ +function autosave_display_response(fieldid, response) { + + /** Process ok response, displaying it **/ + var info = YAHOO.util.Dom.get('autosaveinfo-'+fieldid); + var infospan = YAHOO.util.Dom.getFirstChildBy(info, function(el){return YAHOO.util.Dom.hasClass(el,'text');}); + + /** span doesn't exist, add it (should not happen) **/ + if (!infospan) { + infospan = document.createElement('span'); + YAHOO.util.Dom.addClass(infopan, 'text'); + info.appendChild(infospan); + } + + /** finally replace span HTML **/ + infospan.innerHTML = response.message ? response.message : ''; /** Prevent null to be printed in IE7 **/ +} + + +/** + * Highlight the element to get an attention of user + */ +function autosave_highlight(el) { + (new YAHOO.util.ColorAnim(el, + {backgroundColor:{from:'#FFFF66', + to:'#FFFFFF'} + }, 5)).animate(); +} diff --git a/lib/db/install.xml b/lib/db/install.xml index 1781501..5e8a1d0 100644 --- a/lib/db/install.xml +++ b/lib/db/install.xml @@ -1,5 +1,5 @@ - @@ -2140,7 +2140,7 @@ - +
@@ -2160,5 +2160,19 @@
+ + + + + + + + + + + + + +
\ No newline at end of file diff --git a/lib/weblib.php b/lib/weblib.php index 150483b..b273ed5 100644 --- a/lib/weblib.php +++ b/lib/weblib.php @@ -5025,6 +5025,22 @@ document.write(\''.addslashes_js(editorshortcutshelpbutton()).'\'); $str .= ''; } + if (ajaxenabled() && !empty($CFG->autosave) && (!empty($CFG->autosaveperiod)) && (!empty($CFG->pagepath))) { + require_js(array('yui_yahoo', 'yui_event', 'yui_dom', 'yui_connection', 'yui_json', 'yui_animation')); + require_js('lib/ajax/autosave.js'); + page_id_and_class($pageid, $pageclass); + print_js_call('autosave_init', array($pageid, $id)); + $str .= '
'; + $str .= ' '; + $str .= '
'; + $str .= ' '; + $str .= '
'; + } + if ($return) { return $str; } diff --git a/theme/standard/styles_fonts.css b/theme/standard/styles_fonts.css index c9403cd..cdfa660 100644 --- a/theme/standard/styles_fonts.css +++ b/theme/standard/styles_fonts.css @@ -722,6 +722,13 @@ body#grade-index .grades .header { } /*** + *** Draft autosaving + ***/ +.autosaveinfobox { + font-size: 0.7em; +} + +/*** *** Question ***/ diff --git a/user/autosave.php b/user/autosave.php new file mode 100644 index 0000000..ae5419f --- /dev/null +++ b/user/autosave.php @@ -0,0 +1,150 @@ +id, $pageid, $fieldid, $content)) { + $status = 'OK'; + $msg = 'Draft autosaved at '.userdate(time(), get_string('strftimetime')); // todo + // $msg .= " into '$pageid' as '$fieldid' for user {$USER->id}"; + } else { + $status = 'ERROR'; + $msg = 'Error during draft autosaving'; // todo + } + break; + +case 'check': + /** check if there is an autosaved draft available, return the message with a link to restore */ + $found = autosave_draft_exists($USER->id, $pageid, $fieldid); + if ($found == 0) { + $status = 'OK_NOTFOUND'; + $msg = 'No previously autosaved draft found'; //todo + } else { + $status = 'OK_FOUND'; + $msg = 'A recently autosaved draft is available. Click here to restore it.'; + $msg .= '
Note: this will replace the current content of the field.'; + } + break; + +case 'restore': + /** load the autosaved draft and send it to the client */ + break; +} + +sleep(1); // xxx +$response = array('status' => $status, 'message' => $msg); +echo json_encode($response); + + +/*****************************************************************************/ +/* Autosave related functions **********************************************/ +/*****************************************************************************/ + +/** + * Check if an autosaved draft for given user, page and field is available + * + * Return 0 (no draft found) or integer (id of the found draft) + * + * @param int $userid + * @param int $pageid + * @param int $fieldid + * @return int id of found draft or 0 if not found + */ +function autosave_draft_exists($userid, $pageid, $fieldid) { + global $DB; + + $found = $DB->get_record('autosave', + array('userid'=>$userid, 'pageid'=>$pageid, 'fieldid'=>$fieldid), + 'id', true); + if (empty($found)) { + return 0; + } else { + return $found->id; + } +} + + +/** + * Create or update existing autosaved draft + * + * @param int $userid + * @param int $pageid + * @param int $fieldid + * @param str $content + * @return int id of the new/updated record + */ +function autosave_save_draft($userid, $pageid, $fieldid, $content) { + global $DB; + + // todo performance - we should do some sort of caching here as we have two queries per every + // autosave request (plus one at the beginning) + $id = autosave_draft_exists($userid, $pageid, $fieldid); + + if ($id == 0) { + // no previously saved draft found - let us insert a new one + $data = new stdClass(); + $data->userid = $userid; + $data->pageid = $pageid; + $data->fieldid = $fieldid; + $data->content = $content; + $data->timecreated = time(); + $data->timemodified = time(); + return $DB->insert_record('autosave', $data, true); + } else { + // update existing draft + $data = new stdClass(); + $data->id = $id; + $data->content = $content; + $data->timemodified = time(); + $DB->update_record('autosave', $data); + return $id; + } +} + +?>