Skip to content

Commit

Permalink
MDL-18014 Atto: Autosave text every N seconds.
Browse files Browse the repository at this point in the history
  • Loading branch information
Damyon Wiese committed Aug 14, 2014
1 parent 3376dae commit 2ba6706
Show file tree
Hide file tree
Showing 14 changed files with 929 additions and 6 deletions.
136 changes: 136 additions & 0 deletions lib/editor/atto/autosave-ajax.php
@@ -0,0 +1,136 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle 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 3 of the License, or
// (at your option) any later version.
//
// Moodle 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.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

/**
* Save and load draft text while a user is still editing a form.
*
* @package editor_atto
* @copyright 2014 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

define('AJAX_SCRIPT', true);

require_once(dirname(__FILE__) . '/../../../config.php');

$contextid = required_param('contextid', PARAM_INT);
$elementid = required_param('elementid', PARAM_ALPHANUMEXT);
$pagedomid = required_param('pagedomid', PARAM_ALPHANUMEXT);
$pageinstance = required_param('pageinstance', PARAM_ALPHANUMEXT);

list($context, $course, $cm) = get_context_info_array($contextid);
$PAGE->set_url('/lib/editor/atto/autosave-ajax.php');
$PAGE->set_context($context);

require_login($course, false, $cm);
require_sesskey();

$action = required_param('action', PARAM_ALPHA);

if ($action === 'save') {
$drafttext = required_param('drafttext', PARAM_RAW);
$params = array('elementid' => $elementid,
'userid' => $USER->id,
'pagedomid' => $pagedomid,
'contextid' => $contextid);

$record = $DB->get_record('editor_atto_autosave', $params);
if ($record && $record->pageinstance != $pageinstance) {
print_error('concurrent access from the same user is not supported');
die();
}

if (!$record) {
$record = new stdClass();
$record->elementid = $elementid;
$record->userid = $USER->id;
$record->pagedomid = $pagedomid;
$record->contextid = $contextid;
$record->drafttext = $drafttext;
$record->pageinstance = $pageinstance;

$DB->insert_record('editor_atto_autosave', $record);

// No response means no error.
die();
} else {
$record->drafttext = $drafttext;
$DB->update_record('editor_atto_autosave', $record);

// No response means no error.
die();
}
} else if ($action == 'resume') {
$params = array('elementid' => $elementid,
'userid' => $USER->id,
'pagedomid' => $pagedomid,
'contextid' => $contextid);
$newdraftid = required_param('draftid', PARAM_INT);

$record = $DB->get_record('editor_atto_autosave', $params);

if (!$record) {
$record = new stdClass();
$record->elementid = $elementid;
$record->userid = $USER->id;
$record->pagedomid = $pagedomid;
$record->contextid = $contextid;
$record->pageinstance = $pageinstance;
$record->pagedomid = $pagedomid;
$record->draftid = $newdraftid;
$record->drafttext = '';

$DB->insert_record('editor_atto_autosave', $record);

// No response means no error.
die();
} else {
// Copy all draft files from the old draft area.
$usercontext = context_user::instance($USER->id);
require_once($CFG->libdir . '/filelib.php');

// This function copies all the files in one draft area, to another area (in this case it's
// another draft area). It also rewrites the text to @@PLUGINFILE@@ links.
$newdrafttext = file_save_draft_area_files($record->draftid, $usercontext->id, 'user', 'draft', $newdraftid, array(), $record->drafttext);

// Final rewrite to the new draft area (convert the @@PLUGINFILES@@ again).
$newdrafttext = file_rewrite_pluginfile_urls($newdrafttext,
'draftfile.php',
$usercontext->id,
'user',
'draft',
$newdraftid);
$record->drafttext = $newdrafttext;

$record->pageinstance = $pageinstance;
$record->draftid = $newdraftid;
$DB->update_record('editor_atto_autosave', $record);

// A response means the draft has been restored and here is the auto-saved text.
echo $record->drafttext;
die();
}
} else if ($action == 'reset') {
$params = array('elementid' => $elementid,
'userid' => $USER->id,
'pagedomid' => $pagedomid,
'contextid' => $contextid);

$DB->delete_records('editor_atto_autosave', $params);
die();
}

print_error('invalidarguments');
24 changes: 24 additions & 0 deletions lib/editor/atto/db/install.xml
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="lib/editor/atto/db" VERSION="20140703" COMMENT="XMLDB file for Moodle lib/editor/atto"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd"
>
<TABLES>
<TABLE NAME="editor_atto_autosave" COMMENT="Draft text that is auto-saved every 5 seconds while an editor is open.">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="elementid" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="The unique id for the text editor in the form."/>
<FIELD NAME="contextid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The contextid that the form was loaded with."/>
<FIELD NAME="pagedomid" TYPE="char" LENGTH="64" NOTNULL="true" SEQUENCE="false" COMMENT="The HTML DOM id of the page that loaded the form."/>
<FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The id of the user that loaded the form."/>
<FIELD NAME="drafttext" TYPE="text" NOTNULL="true" SEQUENCE="false" COMMENT="The draft text"/>
<FIELD NAME="draftid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Optional draft area id containing draft files."/>
<FIELD NAME="pageinstance" TYPE="char" LENGTH="64" NOTNULL="true" SEQUENCE="false" COMMENT="The browser tab instance that last saved the draft text. This is to prevent multiple tabs from the same user saving different text alternately."/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="autosave_uniq_key" TYPE="unique" FIELDS="elementid, contextid, userid, pagedomid" COMMENT="Unique key for the user in the form in the page."/>
</KEYS>
</TABLE>
</TABLES>
</XMLDB>
28 changes: 28 additions & 0 deletions lib/editor/atto/db/upgrade.php
Expand Up @@ -53,6 +53,34 @@ function xmldb_editor_atto_upgrade($oldversion) {

// Moodle v2.7.0 release upgrade line.
// Put any upgrade step following this.
if ($oldversion < 2014070301) {

// Define table editor_atto_autosave to be created.
$table = new xmldb_table('editor_atto_autosave');

// Adding fields to table editor_atto_autosave.
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
$table->add_field('elementid', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
$table->add_field('contextid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
$table->add_field('pagedomid', XMLDB_TYPE_CHAR, '64', null, XMLDB_NOTNULL, null, null);
$table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
$table->add_field('drafttext', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
$table->add_field('draftid', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
$table->add_field('pageinstance', XMLDB_TYPE_CHAR, '64', null, XMLDB_NOTNULL, null, null);

// Adding keys to table editor_atto_autosave.
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
$table->add_key('autosave_uniq_key', XMLDB_KEY_UNIQUE, array('elementid', 'contextid', 'userid', 'pagedomid'));

// Conditionally launch create table for editor_atto_autosave.
if (!$dbman->table_exists($table)) {
$dbman->create_table($table);
}

// Atto savepoint reached.
upgrade_plugin_savepoint(true, 2014070301, 'editor', 'atto');
}


return true;
}
4 changes: 4 additions & 0 deletions lib/editor/atto/lang/en/editor_atto.php
Expand Up @@ -36,3 +36,7 @@
$string['editor_command_keycode'] = 'Cmd + {$a}';
$string['editor_control_keycode'] = 'Ctrl + {$a}';
$string['plugin_title_shortcut'] = '{$a->title} [{$a->shortcut}]';
$string['confirm'] = 'Confirm';
$string['cancel'] = 'Cancel';
$string['recover'] = 'Recover';
$string['confirmrecover'] = 'A previously unsaved version of the text for field "{$a->label}" was found. Do you want to recover it?';
10 changes: 10 additions & 0 deletions lib/editor/atto/lib.php
Expand Up @@ -125,6 +125,10 @@ public function use_editor($elementid, array $options=null, $fpoptions=null) {
'editor_command_keycode',
'editor_control_keycode',
'plugin_title_shortcut',
'confirm',
'recover',
'cancel',
'confirmrecover'
), 'editor_atto');
$PAGE->requires->yui_module($modules,
'Y.M.editor_atto.Editor.init',
Expand All @@ -146,11 +150,17 @@ protected function get_init_params($elementid, array $options = null, array $fpo
$strtime = get_string('strftimetime');
$strdate = get_string('strftimedaydate');
$lang = current_language();
$autosave = true;
if (isset($options['autosave'])) {
$autosave = $options['autosave'];
}
$contentcss = $PAGE->theme->editor_css_url()->out(false);

$params = array(
'elementid' => $elementid,
'content_css' => $contentcss,
'contextid' => $options['context']->id,
'autosaveEnabled' => $autosave,
'language' => $lang,
'directionality' => $directionality,
'filepickeroptions' => array(),
Expand Down
2 changes: 1 addition & 1 deletion lib/editor/atto/version.php
Expand Up @@ -24,6 +24,6 @@

defined('MOODLE_INTERNAL') || die();

$plugin->version = 2014051200; // The current plugin version (Date: YYYYMMDDXX).
$plugin->version = 2014070301; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2014050800; // Requires this Moodle version.
$plugin->component = 'editor_atto'; // Full name of the plugin (used for diagnostics).

0 comments on commit 2ba6706

Please sign in to comment.