Commit 684e59ea authored by David Monllaó's avatar David Monllaó Committed by Eloy Lafuente
Browse files

Merge branch 'MDL-51179-28' of git://github.com/damyon/moodle into MOODLE_28_STABLE

Conflicts:
	lib/upgrade.txt
parents 0b9cf149 42e3e297
...@@ -2319,6 +2319,7 @@ class admin_setting_confightmleditor extends admin_setting_configtext { ...@@ -2319,6 +2319,7 @@ class admin_setting_confightmleditor extends admin_setting_configtext {
} }
$editor = editors_get_preferred_editor(FORMAT_HTML); $editor = editors_get_preferred_editor(FORMAT_HTML);
$editor->set_text($data);
$editor->use_editor($this->get_id(), array('noclean'=>true)); $editor->use_editor($this->get_id(), array('noclean'=>true));
return format_admin_setting($this, $this->visiblename, return format_admin_setting($this, $this->visiblename,
......
...@@ -1394,6 +1394,7 @@ function print_textarea($unused, $rows, $cols, $width, $height, $name, $value='' ...@@ -1394,6 +1394,7 @@ function print_textarea($unused, $rows, $cols, $width, $height, $name, $value=''
editors_head_setup(); editors_head_setup();
$editor = editors_get_preferred_editor(FORMAT_HTML); $editor = editors_get_preferred_editor(FORMAT_HTML);
$editor->set_text($value);
$editor->use_editor($id, array('legacy'=>true)); $editor->use_editor($id, array('legacy'=>true));
$str .= "\n".'<textarea class="form-textarea" id="'. $id .'" name="'. $name .'" rows="'. $rows .'" cols="'. $cols .'" spellcheck="true">'."\n"; $str .= "\n".'<textarea class="form-textarea" id="'. $id .'" name="'. $name .'" rows="'. $rows .'" cols="'. $cols .'" spellcheck="true">'."\n";
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
define('AJAX_SCRIPT', true); define('AJAX_SCRIPT', true);
require_once(dirname(__FILE__) . '/../../../config.php'); require_once(dirname(__FILE__) . '/../../../config.php');
require_once($CFG->libdir . '/filestorage/file_storage.php');
$contextid = required_param('contextid', PARAM_INT); $contextid = required_param('contextid', PARAM_INT);
$elementid = required_param('elementid', PARAM_ALPHANUMEXT); $elementid = required_param('elementid', PARAM_ALPHANUMEXT);
...@@ -114,34 +115,49 @@ if ($action === 'save') { ...@@ -114,34 +115,49 @@ if ($action === 'save') {
$stale = $record->timemodified < $before; $stale = $record->timemodified < $before;
require_once($CFG->libdir . '/filelib.php'); require_once($CFG->libdir . '/filelib.php');
// This function copies all the files in one draft area, to another area (in this case it's $fs = get_file_storage();
// another draft area). It also rewrites the text to @@PLUGINFILE@@ links. $files = $fs->get_directory_files($usercontext->id, 'user', 'draft', $newdraftid, '/', true, true);
$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; $lastfilemodified = 0;
$record->draftid = $newdraftid; foreach ($files as $file) {
$record->timemodified = time(); $lastfilemodified = max($lastfilemodified, $file->get_timemodified());
$DB->update_record('editor_atto_autosave', $record); }
if ($record->timemodified < $lastfilemodified) {
$stale = true;
}
// A response means the draft has been restored and here is the auto-saved text.
if (!$stale) { if (!$stale) {
// 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;
$record->timemodified = time();
$DB->update_record('editor_atto_autosave', $record);
// A response means the draft has been restored and here is the auto-saved text.
$response['result'] = $record->drafttext; $response['result'] = $record->drafttext;
echo json_encode($response); echo json_encode($response);
} else {
$DB->delete_records('editor_atto_autosave', array('id' => $record->id));
// No response means no error.
} }
die(); die();
} }
......
<?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/>.
/**
* A scheduled task.
*
* @package editor_atto
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace editor_atto\task;
use \core\task\scheduled_task;
/**
* Simple task to run the autosave cleanup task.
*/
class autosave_cleanup_task extends scheduled_task {
/**
* Get a descriptive name for this task (shown to admins).
*
* @return string
*/
public function get_name() {
return get_string('taskautosavecleanup', 'editor_atto');
}
/**
* Do the job.
* Throw exceptions on errors (the job will be retried).
*/
public function execute() {
global $DB;
$now = time();
// This is the oldest time any autosave text will be recovered from.
// This is so that there is a good chance the draft files will still exist (there are many variables so
// this is impossible to guarantee).
$before = $now - 60*60*24*4;
$DB->delete_records_select('editor_atto_autosave', 'timemodified < :before', array('before' => $before));
}
}
<?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/>.
/**
* Definition of core scheduled tasks.
*
* The handlers defined on this file are processed and registered into
* the Moodle DB after any install or upgrade operation. All plugins
* support this.
*
* @package core
* @category task
* @copyright 2013 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/* List of handlers */
$tasks = array(
array(
'classname' => 'editor_atto\task\autosave_cleanup_task',
'blocking' => 0,
'minute' => 'R',
'hour' => 'R',
'day' => '*',
'dayofweek' => 'R',
'month' => '*'
)
);
...@@ -36,6 +36,7 @@ $string['pluginname'] = 'Atto HTML editor'; ...@@ -36,6 +36,7 @@ $string['pluginname'] = 'Atto HTML editor';
$string['subplugintype_atto'] = 'Atto plugin'; $string['subplugintype_atto'] = 'Atto plugin';
$string['subplugintype_atto_plural'] = 'Atto plugins'; $string['subplugintype_atto_plural'] = 'Atto plugins';
$string['settings'] = 'Atto toolbar settings'; $string['settings'] = 'Atto toolbar settings';
$string['taskautosavecleanup'] = 'Delete expired autosave drafts from the database.';
$string['textrecovered'] = 'A draft version of this text was automatically restored.'; $string['textrecovered'] = 'A draft version of this text was automatically restored.';
$string['toolbarconfig'] = 'Toolbar config'; $string['toolbarconfig'] = 'Toolbar config';
$string['toolbarconfig_desc'] = 'The list of plugins and the order they are displayed can be configured here. The configuration consists of groups (one per line) followed by the ordered list of plugins for that group. The group is separated from the plugins with an equals sign and the plugins are separated with commas. The group names must be unique and should indicate what the buttons have in common. Button and group names should not be repeated and may only contain alphanumeric characters.'; $string['toolbarconfig_desc'] = 'The list of plugins and the order they are displayed can be configured here. The configuration consists of groups (one per line) followed by the ordered list of plugins for that group. The group is separated from the plugins with an equals sign and the plugins are separated with commas. The group names must be unique and should indicate what the buttons have in common. Button and group names should not be repeated and may only contain alphanumeric characters.';
......
...@@ -161,6 +161,8 @@ class atto_texteditor extends texteditor { ...@@ -161,6 +161,8 @@ class atto_texteditor extends texteditor {
} }
$contentcss = $PAGE->theme->editor_css_url()->out(false); $contentcss = $PAGE->theme->editor_css_url()->out(false);
// Note <> is a safe separator, because it will not appear in the output of s().
$pagehash = sha1($PAGE->url . '<>' . s($this->get_text()));
$params = array( $params = array(
'elementid' => $elementid, 'elementid' => $elementid,
'content_css' => $contentcss, 'content_css' => $contentcss,
...@@ -171,7 +173,7 @@ class atto_texteditor extends texteditor { ...@@ -171,7 +173,7 @@ class atto_texteditor extends texteditor {
'directionality' => $directionality, 'directionality' => $directionality,
'filepickeroptions' => array(), 'filepickeroptions' => array(),
'plugins' => $plugins, 'plugins' => $plugins,
'pageHash' => sha1($PAGE->url) 'pageHash' => $pagehash,
); );
if ($fpoptions) { if ($fpoptions) {
$params['filepickeroptions'] = $fpoptions; $params['filepickeroptions'] = $fpoptions;
......
@editor @editor_atto @atto @_file_upload
Feature: Atto Autosave
To reduce frustration, atto should save drafts of my work.
Background:
Given the following "courses" exist:
| fullname | shortname | category | groupmode |
| Course 1 | C1 | 0 | 1 |
And the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| teacher2 | Teacher | 2 | teacher2@example.com |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| teacher2 | C1 | editingteacher |
And I log in as "admin"
And I navigate to "Atto toolbar settings" node in "Site administration > Plugins > Text editors > Atto HTML editor"
And I set the field with xpath "//input[@id='id_s_editor_atto_autosavefrequencyv']" to "3"
And I set the field with xpath "//select[@id='id_s_editor_atto_autosavefrequencyu']" to "seconds"
And I click on "Save changes" "button"
And I am on homepage
And I follow "Course 1"
And I navigate to "Edit settings" node in "Course administration"
And I set the field with xpath "//select[@name='summary_editor[format]']" to "1"
And I click on "Save changes" "button"
And I log out
@javascript
Scenario: Restore a draft
Given I log in as "teacher1"
And I follow "Course 1"
And I navigate to "Edit settings" node in "Course administration"
And I set the field "Course summary" to "This is my draft"
# Wait for the autosave
And I wait "5" seconds
And I log out
When I log in as "teacher1"
And I follow "Course 1"
And I navigate to "Edit settings" node in "Course administration"
# Wait for the autorestore
And I wait "2" seconds
Then I should see "This is my draft"
@javascript
Scenario: Do not restore a draft if files have been modified
Given I log in as "teacher1"
And I follow "Course 1"
And I navigate to "Edit settings" node in "Course administration"
And I set the field "Course summary" to "This is my draft"
# Wait for the autosave
And I wait "5" seconds
And I log out
And I log in as "teacher2"
And I navigate to "My private files" node in "My profile"
And I upload "lib/editor/atto/tests/fixtures/moodle-logo.png" file to "Files" filemanager
And I click on "Save changes" "button"
And I am on homepage
And I follow "Course 1"
And I navigate to "Edit settings" node in "Course administration"
And I set the field "Course summary" to "<p>Image test</p>"
And I select the text in the "Course summary" Atto editor
And I click on "Image" "button"
And I click on "Browse repositories..." "button"
And I click on "Private files" "link"
And I click on "moodle-logo.png" "link"
And I click on "Select this file" "button"
And I set the field "Describe this image" to "It's the Moodle"
# Wait for the page to "settle".
And I wait until the page is ready
And I click on "Save image" "button"
And I click on "Save changes" "button"
And I log out
When I log in as "teacher1"
And I follow "Course 1"
And I navigate to "Edit settings" node in "Course administration"
Then I should not see "This is my draft"
@javascript
Scenario: Do not restore a draft if text has been modified
Given I log in as "teacher1"
And I follow "Course 1"
And I navigate to "Edit settings" node in "Course administration"
And I set the field "Course summary" to "This is my draft"
# Wait for the autosave
And I wait "5" seconds
And I log out
And I log in as "teacher2"
And I follow "Course 1"
And I navigate to "Edit settings" node in "Course administration"
And I set the field "Course summary" to "Modified text"
And I click on "Save and display" "button"
And I log out
When I log in as "teacher1"
And I follow "Course 1"
And I navigate to "Edit settings" node in "Course administration"
Then I should not see "This is my draft"
And I should see "Modified text"
...@@ -24,6 +24,6 @@ ...@@ -24,6 +24,6 @@
defined('MOODLE_INTERNAL') || die(); defined('MOODLE_INTERNAL') || die();
$plugin->version = 2014111000; // The current plugin version (Date: YYYYMMDDXX). $plugin->version = 2014111001; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2014110400; // Requires this Moodle version. $plugin->requires = 2014110400; // Requires this Moodle version.
$plugin->component = 'editor_atto'; // Full name of the plugin (used for diagnostics). $plugin->component = 'editor_atto'; // Full name of the plugin (used for diagnostics).
...@@ -201,6 +201,28 @@ abstract class texteditor { ...@@ -201,6 +201,28 @@ abstract class texteditor {
*/ */
public abstract function supports_repositories(); public abstract function supports_repositories();
/**
* @var string $text The text set to the editor in the form.
* @since 2.8.8, 2.9.2, 3.0
*/
protected $text = '';
/**
* Set the text set for this form field. Will be called before "use_editor".
* @param string $text The text for the form field.
*/
public function set_text($text) {
$this->text = $text;
}
/**
* Get the text set for this form field. Can be called from "use_editor".
* @return string
*/
public function get_text() {
return $this->text;
}
/** /**
* Add required JS needed for editor * Add required JS needed for editor
* @param string $elementid id of text area to be converted to editor * @param string $elementid id of text area to be converted to editor
......
...@@ -377,6 +377,7 @@ class MoodleQuickForm_editor extends HTML_QuickForm_element { ...@@ -377,6 +377,7 @@ class MoodleQuickForm_editor extends HTML_QuickForm_element {
} }
// print text area - TODO: add on-the-fly switching, size configuration, etc. // print text area - TODO: add on-the-fly switching, size configuration, etc.
$editor->set_text($text);
$editor->use_editor($id, $this->_options, $fpoptions); $editor->use_editor($id, $this->_options, $fpoptions);
$rows = empty($this->_attributes['rows']) ? 15 : $this->_attributes['rows']; $rows = empty($this->_attributes['rows']) ? 15 : $this->_attributes['rows'];
......
...@@ -6,6 +6,7 @@ information provided here is intended especially for developers. ...@@ -6,6 +6,7 @@ information provided here is intended especially for developers.
* The actionmenu hideMenu() function now expects an EventFacade object to be passed to it, * The actionmenu hideMenu() function now expects an EventFacade object to be passed to it,
i.e. a call to M.core.actionmenu.instance.hideMenu() can be changed to M.core.actionmenu.instance.hideMenu(e) to ensure good i.e. a call to M.core.actionmenu.instance.hideMenu() can be changed to M.core.actionmenu.instance.hideMenu(e) to ensure good
behaviour when using custom action menus. behaviour when using custom action menus.
* Users of the text editor API to manually create a text editor should call set_text before calling use_editor. See MDL-51179.
=== 2.8.7 === === 2.8.7 ===
......
...@@ -119,6 +119,7 @@ class data_field_textarea extends data_field_base { ...@@ -119,6 +119,7 @@ class data_field_textarea extends data_field_base {
foreach ($formats as $fid) { foreach ($formats as $fid) {
$formats[$fid] = $strformats[$fid]; $formats[$fid] = $strformats[$fid];
} }
$editor->set_text($text);
$editor->use_editor($field, $options, $fpoptions); $editor->use_editor($field, $options, $fpoptions);
$str .= '<input type="hidden" name="'.$field.'_itemid" value="'.$draftitemid.'" />'; $str .= '<input type="hidden" name="'.$field.'_itemid" value="'.$draftitemid.'" />';
$str .= '<div><textarea id="'.$field.'" name="'.$field.'" rows="'.$this->field->param3.'" cols="'.$this->field->param2.'" spellcheck="true">'.s($text).'</textarea></div>'; $str .= '<div><textarea id="'.$field.'" name="'.$field.'" rows="'.$this->field->param3.'" cols="'.$this->field->param2.'" spellcheck="true">'.s($text).'</textarea></div>';
......
...@@ -218,6 +218,7 @@ if ($mode == 'listtemplate'){ ...@@ -218,6 +218,7 @@ if ($mode == 'listtemplate'){
echo '<div class="template_heading"><label for="edit-listtemplateheader">'.get_string('header','data').'</label></div>'; echo '<div class="template_heading"><label for="edit-listtemplateheader">'.get_string('header','data').'</label></div>';
$field = 'listtemplateheader'; $field = 'listtemplateheader';
$editor->set_text($data->listtemplateheader);
$editor->use_editor($field, $options); $editor->use_editor($field, $options);
echo '<div><textarea id="'.$field.'" name="'.$field.'" rows="15" cols="80">'.s($data->listtemplateheader).'</textarea></div>'; echo '<div><textarea id="'.$field.'" name="'.$field.'" rows="15" cols="80">'.s($data->listtemplateheader).'</textarea></div>';
...@@ -314,6 +315,7 @@ if ($mode == 'listtemplate'){ ...@@ -314,6 +315,7 @@ if ($mode == 'listtemplate'){
} }
$field = 'template'; $field = 'template';
$editor->set_text($data->{$mode});
$editor->use_editor($field, $options); $editor->use_editor($field, $options);
echo '<div><textarea id="'.$field.'" name="'.$field.'" rows="15" cols="80">'.s($data->{$mode}).'</textarea></div>'; echo '<div><textarea id="'.$field.'" name="'.$field.'" rows="15" cols="80">'.s($data->{$mode}).'</textarea></div>';
echo '</td>'; echo '</td>';
...@@ -326,6 +328,7 @@ if ($mode == 'listtemplate'){ ...@@ -326,6 +328,7 @@ if ($mode == 'listtemplate'){
echo '<div class="template_heading"><label for="edit-listtemplatefooter">'.get_string('footer','data').'</label></div>'; echo '<div class="template_heading"><label for="edit-listtemplatefooter">'.get_string('footer','data').'</label></div>';
$field = 'listtemplatefooter'; $field = 'listtemplatefooter';
$editor->set_text($data->listtemplatefooter);
$editor->use_editor($field, $options); $editor->use_editor($field, $options);
echo '<div><textarea id="'.$field.'" name="'.$field.'" rows="15" cols="80">'.s($data->listtemplatefooter).'</textarea></div>'; echo '<div><textarea id="'.$field.'" name="'.$field.'" rows="15" cols="80">'.s($data->listtemplatefooter).'</textarea></div>';
echo '</td>'; echo '</td>';
...@@ -337,6 +340,7 @@ if ($mode == 'listtemplate'){ ...@@ -337,6 +340,7 @@ if ($mode == 'listtemplate'){
echo '<div class="template_heading"><label for="edit-rsstitletemplate">'.get_string('rsstitletemplate','data').'</label></div>'; echo '<div class="template_heading"><label for="edit-rsstitletemplate">'.get_string('rsstitletemplate','data').'</label></div>';
$field = 'rsstitletemplate'; $field = 'rsstitletemplate';
$editor->set_text($data->rsstitletemplate);
$editor->use_editor($field, $options); $editor->use_editor($field, $options);
echo '<div><textarea id="'.$field.'" name="'.$field.'" rows="15" cols="80">'.s($data->rsstitletemplate).'</textarea></div>'; echo '<div><textarea id="'.$field.'" name="'.$field.'" rows="15" cols="80">'.s($data->rsstitletemplate).'</textarea></div>';
echo '</td>'; echo '</td>';
......
...@@ -82,6 +82,7 @@ abstract class qbehaviour_renderer extends plugin_renderer_base { ...@@ -82,6 +82,7 @@ abstract class qbehaviour_renderer extends plugin_renderer_base {
$commenttext = format_text($commenttext, $commentformat, array('para' => false)); $commenttext = format_text($commenttext, $commentformat, array('para' => false));
$editor->set_text($commenttext);
$editor->use_editor($id, array('context' => $options->context)); $editor->use_editor($id, array('context' => $options->context));
$commenteditor = html_writer::tag('div', html_writer::tag('textarea', s($commenttext), $commenteditor = html_writer::tag('div', html_writer::tag('textarea', s($commenttext),
......
...@@ -236,6 +236,7 @@ class qtype_essay_format_editor_renderer extends plugin_renderer_base { ...@@ -236,6 +236,7 @@ class qtype_essay_format_editor_renderer extends plugin_renderer_base {
list($draftitemid, $response) = $this->prepare_response_for_editing( list($draftitemid, $response) = $this->prepare_response_for_editing(
$name, $step, $context); $name, $step, $context);
$editor->set_text($response);
$editor->use_editor($id, $this->get_editor_options($context), $editor->use_editor($id, $this->get_editor_options($context),
$this->get_filepicker_options($context, $draftitemid)); $this->get_filepicker_options($context, $draftitemid));
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment