diff --git a/admin/auth.php b/admin/auth.php
index 934acd371ef..43109017fb8 100644
--- a/admin/auth.php
+++ b/admin/auth.php
@@ -10,6 +10,7 @@
require_once('../config.php');
require_once($CFG->libdir.'/adminlib.php');
require_once($CFG->libdir.'/tablelib.php');
+require_once($CFG->libdir.'/pluginlib.php');
require_login();
require_capability('moodle/site:config', context_system::instance());
@@ -51,7 +52,8 @@ switch ($action) {
if ($auth == $CFG->registerauth) {
set_config('registerauth', '');
}
- session_gc(); // remove stale sessions
+ \core\session\manager::gc(); // Remove stale sessions.
+ plugin_manager::reset_caches();
break;
case 'enable':
@@ -61,7 +63,8 @@ switch ($action) {
$authsenabled = array_unique($authsenabled);
set_config('auth', implode(',', $authsenabled));
}
- session_gc(); // remove stale sessions
+ \core\session\manager::gc(); // Remove stale sessions.
+ plugin_manager::reset_caches();
break;
case 'down':
diff --git a/admin/block.php b/admin/block.php
deleted file mode 100644
index 59938ec1fef..00000000000
--- a/admin/block.php
+++ /dev/null
@@ -1,76 +0,0 @@
-libdir.'/adminlib.php');
-
- $blockid = required_param('block', PARAM_INT);
-
- if(!$blockrecord = blocks_get_record($blockid)) {
- print_error('blockdoesnotexist', 'error');
- }
-
- admin_externalpage_setup('blocksetting'.$blockrecord->name);
-
- $block = block_instance($blockrecord->name);
- if($block === false) {
- print_error('blockcannotinistantiate', 'error');
- }
-
- // Define the data we're going to silently include in the instance config form here,
- // so we can strip them from the submitted data BEFORE handling it.
- $hiddendata = array(
- 'block' => $blockid,
- 'sesskey' => sesskey()
- );
-
- /// If data submitted, then process and store.
-
- if ($config = data_submitted()) {
-
- if (!confirm_sesskey()) {
- print_error('confirmsesskeybad', 'error');
- }
- if(!$block->has_config()) {
- print_error('blockcannotconfig', 'error');
- }
- $remove = array_keys($hiddendata);
- foreach($remove as $item) {
- unset($config->$item);
- }
- $block->config_save($config);
- redirect("$CFG->wwwroot/$CFG->admin/blocks.php", get_string("changessaved"), 1);
- exit;
- }
-
- /// Otherwise print the form.
-
- $strmanageblocks = get_string('manageblocks');
- $strblockname = $block->get_title();
-
- echo $OUTPUT->header();
-
- echo $OUTPUT->heading($strblockname);
-
- echo $OUTPUT->notification('This block still uses an old-style config_global.html file. ' .
- 'It must be updated by a developer to use a settings.php file.');
-
- echo $OUTPUT->box(get_string('configwarning', 'admin'), 'generalbox boxwidthnormal boxaligncenter');
- echo '
';
-
- echo '
Nothing much to say, since this is just a test...
'; - $post->format = 1; - $post->attachments = null; - $post->itemid = 752157083; - $post->parent = $post_ids[array_rand($post_ids)]; - - if ($post_ids[] = forum_add_new_post($post, $mform, $message)) { - $posts_count++; - } - $USER = $global_user; - } - } - - $USER = $super_global_user; - - if ($forum->type == 'single') { - break; - } - } - } - if ($discussions_count > 0 && !$this->get('quiet')) { - echo "$discussions_count forum discussions have been generated.{$this->eolchar}"; - } - if ($posts_count > 0 && !$this->get('quiet')) { - echo "$posts_count forum posts have been generated.{$this->eolchar}"; - } - - return true; - } - return null; - - } - - public function generate_grades($course_users, $courses, $modules) { - global $CFG, $DB, $USER; - - /** - * ASSIGNMENT GRADES GENERATION - */ - if ($this->get('assignment_grades') && isset($modules['assignment'])) { - $grades_count = 0; - foreach ($course_users as $courseid => $userid_array) { - foreach ($userid_array as $userid) { - foreach ($modules['assignment'] as $assignment) { - if (in_array($assignment->course, $courses)) { - $maxgrade = $assignment->grade; - $random_grade = rand(0, $maxgrade); - $grade = new stdClass(); - $grade->assignment = $assignment->id; - $grade->userid = $userid; - $grade->grade = $random_grade; - $grade->rawgrade = $random_grade; - $grade->teacher = $USER->id; - $grade->submissioncomment = 'comment'; - $DB->insert_record('assignment_submissions', $grade); - grade_update('mod/assignment', $assignment->course, 'mod', 'assignment', $assignment->id, 0, $grade); - $this->verbose("A grade ($random_grade) has been given to user $userid " - . "for assignment $assignment->id"); - $grades_count++; - } - } - } - } - if ($grades_count > 0) { - $this->verbose("$grades_count assignment grades have been generated.{$this->eolchar}"); - } - } - - /** - * QUIZ GRADES GENERATION - */ - if ($this->get('quiz_grades') && isset($modules['quiz'])) { - $grades_count = 0; - foreach ($course_users as $userid => $courses) { - foreach ($modules['quiz'] as $quiz) { - if (in_array($quiz->course, $courses)) { - $maxgrade = $quiz->grade; - $random_grade = rand(0, $maxgrade); - $grade = new stdClass(); - $grade->quiz = $quiz->id; - $grade->userid = $userid; - $grade->grade = $random_grade; - $grade->rawgrade = $random_grade; - $DB->insert_record('quiz_grades', $grade); - grade_update('mod/quiz', $courseid, 'mod', 'quiz', $quiz->id, 0, $grade); - $this->verbose("A grade ($random_grade) has been given to user $userid for quiz $quiz->id"); - $grades_count++; - } - } - } - if ($grades_count > 0 && !$this->get('quiet')) { - echo "$grades_count quiz grades have been generated.{$this->eolchar}"; - } - } - return null; - } - - public function generate_module_content($course_users, $courses, $modules) { - global $USER, $DB, $CFG; - $result = null; - - $entries_count = 0; - if ($this->get('entries_per_glossary') && !empty($modules['glossary'])) { - foreach ($modules['glossary'] as $glossary) { - for ($i = 0; $i < $this->get('entries_per_glossary'); $i++) { - $entry = new stdClass(); - $entry->glossaryid = $glossary->id; - $entry->userid = $USER->id; - $entry->concept = "Test concept"; - $entry->definition = "A test concept is nothing to write home about: just a test concept."; - $entry->format = 1; - $entry->timecreated = time(); - $entry->timemodified = time(); - $entry->teacherentry = 0; - $entry->approved = 1; - $DB->insert_record('glossary_entries', $entry); - $entries_count++; - } - } - if ($entries_count > 0 && !$this->get('quiet')) { - echo "$entries_count glossary definitions have been generated.{$this->eolchar}"; - } - $result = true; - } - - $fields_count = 0; - if (!empty($modules['data']) && $this->get('fields_per_database') && $this->get('database_records_per_student')) { - $database_field_types = array('checkbox', - 'date', - 'file', - 'latlong', - 'menu', - 'multimenu', - 'number', - 'picture', - 'radiobutton', - 'text', - 'textarea', - 'url'); - - - $fields = array(); - - foreach ($modules['data'] as $data) { - - for ($i = 0; $i < $this->get('fields_per_database'); $i++) { - $type = $database_field_types[array_rand($database_field_types)]; - require_once($CFG->dirroot.'/mod/data/field/'.$type.'/field.class.php'); - $newfield = 'data_field_'.$type; - $cm = get_coursemodule_from_instance('data', $data->id); - $newfield = new $newfield(0, $data, $cm); - $fields[$data->id][] = $newfield; - $newfield->insert_field(); - } - - // Generate fields for each database (same fields for all, no arguing) - for ($i = 0; $i < $this->get('fields_per_database'); $i++) { - - } - - // Generate database records for each student, if needed - for ($i = 0; $i < $this->get('database_records_per_student'); $i++) { - - } - } - if ($fields_count > 0 && !$this->get('quiet')) { - $datacount = count($modules['data']); - echo "$fields_count database fields have been generated for each of the " - . "$datacount generated databases.{$this->eolchar}"; - } - $result = true; - } - - $messages_count = 0; - if (!empty($modules['chat']) && $this->get('messages_per_chat')) { - - // Insert all users into chat_users table, then a message from each user - foreach ($modules['chat'] as $chat) { - - foreach ($course_users as $courseid => $users_array) { - - foreach ($users_array as $userid) { - if ($messages_count < $this->get('messages_per_chat')) { - $chat_user = new stdClass(); - $chat_user->chatid = $chat->id; - $chat_user->userid = $userid; - $chat_user->course = $courseid; - $DB->insert_record('chat_users', $chat_user); - - $chat_message = new stdClass(); - $chat_message->chatid = $chat->id; - $chat_message->userid = $userid; - $chat_message->message = "Hi, everyone!"; - $DB->insert_record('chat_messages', $chat_message); - - $messages_count++; - } - } - } - } - - if ($messages_count > 0 && !$this->get('quiet')) { - $datacount = count($modules['chat']); - echo "$messages_count messages have been generated for each of the " - . "$datacount generated chats.{$this->eolchar}"; - } - $result = true; - } - - return $result; - } - - - /** - * If verbose is switched on, prints a string terminated by the global eolchar string. - * @param string $string The string to STDOUT - */ - public function verbose($string) { - if ($this->get('verbose') && !$this->get('quiet')) { - echo $string . $this->eolchar; - } - } - - - /** - * Attempts to delete all generated test data. - * WARNING: THIS WILL COMPLETELY MESS UP A "REAL" SITE, AND IS INTENDED ONLY FOR DEVELOPMENT PURPOSES - */ - function data_cleanup() { - global $DB; - - if ($this->get('quiet')) { - ob_start(); - } - - // TODO Cleanup code - - if ($this->get('quiet')) { - ob_end_clean(); - } - } - - public function get($setting) { - if (isset($this->settings[$setting])) { - return $this->settings[$setting]->value; - } else { - return null; - } - } - - public function set($setting, $value) { - if (isset($this->settings[$setting])) { - $this->settings[$setting]->value = $value; - } else { - return false; - } - } - - public function get_module_type($modulename) { - $return_val = false; - - $type = $this->get($modulename.'_type'); - - if (is_object($type) && isset($type->type) && isset($type->options)) { - - if ($type->type == GENERATOR_RANDOM) { - $return_val = $type->options[array_rand($type->options)]; - - } elseif ($type->type == GENERATOR_SEQUENCE) { - $return_val = $type->options[$this->{$modulename.'_type_counter'}]; - $this->{$modulename.'_type_counter'}++; - - if ($this->{$modulename.'_type_counter'} == count($type->options)) { - $this->{$modulename.'_type_counter'} = 0; - } - } - - } elseif (is_array($type)) { - $return_val = $type[array_rand($type)]; - - } elseif (is_string($type)) { - $return_val = $type; - } - - return $return_val; - } -} - -class generator_argument { - public $short; - public $long; - public $help; - public $type; - public $default = null; - public $value; - - public function __construct($params) { - foreach ($params as $key => $val) { - $this->$key = $val; - } - $this->value = $this->default; - } -} - -class generator_cli extends generator { - public $eolchar = "\n"; - - public function __construct($settings, $argc) { - parent::__construct(); - - // Building the USAGE output of the command line version - $help = "Moodle Data Generator. Generates Data for Moodle sites. Good for benchmarking and other tests.\n\n" - . "FOR DEVELOPMENT PURPOSES ONLY! DO NOT USE ON A PRODUCTION SITE!\n\n" - . "Note: By default the script attempts to fill DB tables prefixed with tst_\n" - . "To override the prefix, use the -P (--database_prefix) setting.\n\n" - . "Usage: {$settings[0]}; [OPTION] ...\n" - . "Options:\n" - . " -h, -?, -help, --help This output\n"; - - foreach ($this->settings as $argument) { - $equal = ''; - if (!empty($argument->type)) { - $equal = "={$argument->type}"; - } - - $padding1 = 5 - strlen($argument->short); - $padding2 = 30 - (strlen($argument->long) + strlen($equal)); - $paddingstr1 = ''; - for ($i = 0; $i < $padding1; $i++) { - $paddingstr1 .= ' '; - } - $paddingstr2 = ''; - for ($i = 0; $i < $padding2; $i++) { - $paddingstr2 .= ' '; - } - - $help .= " -{$argument->short},$paddingstr1--{$argument->long}$equal$paddingstr2{$argument->help}\n"; - } - - $help .= "\nUse http://tracker.moodle.org for any suggestions or bug reports.\n"; - - if ($argc == 1 || in_array($settings[1], array('--help', '-help', '-h', '-?'))) { - echo $help; - die(); - - } else { - $this->do_generation = true; - $settings = $this->_arguments($settings); - $argscount = 0; - - foreach ($this->settings as $argument) { - $value = null; - - if (in_array($argument->short, array_keys($settings))) { - $value = $settings[$argument->short]; - unset($settings[$argument->short]); - - } elseif (in_array($argument->long, array_keys($settings))) { - $value = $settings[$argument->long]; - unset($settings[$argument->long]); - } - - if (!is_null($value)) { - - if (!empty($argument->type) && ($argument->type == 'mod1,mod2...' || $argument->type == 'SELECT')) { - $value = explode(',', $value); - } - - $this->set($argument->long, $value); - $argscount++; - } - } - - // If some params are left in argv, it means they are not supported - if ($argscount == 0 || count($settings) > 0) { - echo $help; - die(); - } - } - - $this->connect(); - } - - public function generate_data() { - if (is_null($this->get('username')) || $this->get('username') == '') { - echo "You must enter a valid username for a moodle administrator account on this site.{$this->eolchar}"; - die(); - } elseif (is_null($this->get('password')) || $this->get('password') == '') { - echo "You must enter a valid password for a moodle administrator account on this site.{$this->eolchar}"; - die(); - } else { - if (!$user = authenticate_user_login($this->get('username'), $this->get('password'))) { - echo "Invalid username or password!{$this->eolchar}"; - die(); - } - if (!is_siteadmin($user)) {//TODO: add some proper access control check here!! - echo "You do not have administration privileges on this Moodle site. " - ."These are required for running the generation script.{$this->eolchar}"; - die(); - } - complete_user_login($user); - } - - parent::generate_data(); - } - - /** - * Converts the standard $argv into an associative array taking var=val arguments into account - * @param array $argv - * @return array $_ARG - */ - private function _arguments($argv) { - $_ARG = array(); - foreach ($argv as $arg) { - if (preg_match('/--?([^=]+)=(.*)/',$arg,$reg)) { - $_ARG[$reg[1]] = $reg[2]; - } elseif(preg_match('/-([a-zA-Z0-9]+)/',$arg,$reg)) { - $_ARG[$reg[1]] = 'true'; - } - } - return $_ARG; - } -} - -class generator_web extends generator { - public $eolchar = 'true
+ Courses can be created automatically if there are enrolments to a course that doesn\'t yet exist in Moodle
If you are using automatic course creation, it is recommended that you remove the following capabilities: moodle/course:changeidnumber, moodle/course:changeshortname, moodle/course:changefullname and moodle/course:changesummary, from the relevant roles to prevent modifications of the four course fields specified above (ID number, shortname, fullname and summary).
'; $string['autocreate_key'] = 'Auto create'; $string['autocreation_settings'] = 'Automatic course creation settings'; +$string['autoupdate_settings'] = 'Automatic course update settings'; +$string['autoupdate_settings_desc'] = 'Select fields to update when synchronization script is running (enrol/ldap/cli/sync.php).
When at least one field is selected an update will occur.
'; $string['bind_dn'] = 'If you want to use a bind user to search users, specify it here. Someting like \'cn=ldapuser,ou=public,o=org\''; $string['bind_dn_key'] = 'Bind user distinguished name'; $string['bind_pw'] = 'Password for the bind user'; $string['bind_pw_key'] = 'Password'; $string['bind_settings'] = 'Bind settings'; $string['cannotcreatecourse'] = 'Cannot create course: missing required data from the LDAP record!'; +$string['cannotupdatecourse'] = "Cannot update course: missing required data from the LDAP record! Course idnumber: '{\$a->idnumber}'"; +$string['cannotupdatecourse_duplicateshortname'] = "Cannot update course: Duplicate short name. Skipping course with idnumber '{\$a->idnumber}'..."; +$string['courseupdated'] = "Course with idnumber '{\$a->idnumber}' was successfully updated."; +$string['courseupdateskipped'] = "Course with idnumber '{\$a->idnumber}' does not require updating. Skipping..."; $string['category'] = 'The category for auto-created courses'; $string['category_key'] = 'Category'; $string['contexts'] = 'LDAP contexts'; @@ -41,6 +47,8 @@ $string['couldnotfinduser'] = "Could not find user '{\$a}', skipping\n"; $string['coursenotexistskip'] = "Course '{\$a}' does not exist and autocreation disabled, skipping\n"; $string['course_fullname'] = 'Optional: LDAP attribute to get the full name from'; $string['course_fullname_key'] = 'Full name'; +$string['course_fullname_updateonsync'] = 'Update full name during synchronization script'; +$string['course_fullname_updateonsync_key'] = 'Update full name'; $string['course_idnumber'] = 'LDAP attribute to get the course ID number from. Usually \'cn\' or \'uid\'.'; $string['course_idnumber_key'] = 'ID number'; $string['course_search_sub'] = 'Search group memberships from subcontexts'; @@ -48,8 +56,12 @@ $string['course_search_sub_key'] = 'Search subcontexts'; $string['course_settings'] = 'Course enrolment settings'; $string['course_shortname'] = 'Optional: LDAP attribute to get the shortname from'; $string['course_shortname_key'] = 'Short name'; +$string['course_shortname_updateonsync'] = 'Update short name during synchronization script'; +$string['course_shortname_updateonsync_key'] = 'Update short name'; $string['course_summary'] = 'Optional: LDAP attribute to get the summary from'; $string['course_summary_key'] = 'Summary'; +$string['course_summary_updateonsync'] = 'Update summary during synchronization script'; +$string['course_summary_updateonsync_key'] = 'Update summary'; $string['createcourseextid'] = 'CREATE User enrolled to a nonexistant course \'{$a->courseextid}\''; $string['createnotcourseextid'] = 'User enrolled to a nonexistant course \'{$a->courseextid}\''; $string['creatingcourse'] = 'Creating course \'{$a}\'...'; diff --git a/enrol/ldap/lib.php b/enrol/ldap/lib.php index f37e12ac8fb..fa84fe36f8b 100644 --- a/enrol/ldap/lib.php +++ b/enrol/ldap/lib.php @@ -430,6 +430,8 @@ class enrol_ldap_plugin extends enrol_plugin { $trace->output(get_string('createnotcourseextid', 'enrol_ldap', array('courseextid'=>$idnumber))); continue; // Next; skip this one! } + } else { // Check if course needs update & update as needed. + $this->update_course($course_obj, $course, $trace); } // Enrol & unenrol @@ -1000,6 +1002,78 @@ class enrol_ldap_plugin extends enrol_plugin { return $newcourse->id; } + /** + * Will update a moodle course with new values from LDAP + * A field will be updated only if it is marked to be updated + * on sync in plugin settings + * + * @param object $course + * @param array $externalcourse + * @param progress_trace $trace + * @return bool + */ + protected function update_course($course, $externalcourse, progress_trace $trace) { + global $CFG, $DB; + + $coursefields = array ('shortname', 'fullname', 'summary'); + static $shouldupdate; + + // Initialize $shouldupdate variable. Set to true if one or more fields are marked for update. + if (!isset($shouldupdate)) { + $shouldupdate = false; + foreach ($coursefields as $field) { + $shouldupdate = $shouldupdate || $this->get_config('course_'.$field.'_updateonsync'); + } + } + + // If we should not update return immediately. + if (!$shouldupdate) { + return false; + } + + require_once("$CFG->dirroot/course/lib.php"); + $courseupdated = false; + $updatedcourse = new stdClass(); + $updatedcourse->id = $course->id; + + // Update course fields if necessary. + foreach ($coursefields as $field) { + // If field is marked to be updated on sync && field data was changed update it. + if ($this->get_config('course_'.$field.'_updateonsync') + && isset($externalcourse[$this->get_config('course_'.$field)][0]) + && $course->{$field} != $externalcourse[$this->get_config('course_'.$field)][0]) { + $updatedcourse->{$field} = $externalcourse[$this->get_config('course_'.$field)][0]; + $courseupdated = true; + } + } + + if (!$courseupdated) { + $trace->output(get_string('courseupdateskipped', 'enrol_ldap', $course)); + return false; + } + + // Do not allow empty fullname or shortname. + if ((isset($updatedcourse->fullname) && empty($updatedcourse->fullname)) + || (isset($updatedcourse->shortname) && empty($updatedcourse->shortname))) { + // We are in trouble! + $trace->output(get_string('cannotupdatecourse', 'enrol_ldap', $course)); + return false; + } + + // Check if the shortname already exists if it does - skip course updating. + if (isset($updatedcourse->shortname) + && $DB->record_exists('course', array('shortname' => $updatedcourse->shortname))) { + $trace->output(get_string('cannotupdatecourse_duplicateshortname', 'enrol_ldap', $course)); + return false; + } + + // Finally - update course in DB. + update_course($updatedcourse); + $trace->output(get_string('courseupdated', 'enrol_ldap', $course)); + + return true; + } + /** * Automatic enrol sync executed during restore. * Useful for automatic sync by course->idnumber or course category. diff --git a/enrol/ldap/settings.php b/enrol/ldap/settings.php index a0838622932..8b3f329a382 100644 --- a/enrol/ldap/settings.php +++ b/enrol/ldap/settings.php @@ -76,10 +76,13 @@ if ($ADMIN->fulltree) { //--- course mapping settings --- $settings->add(new admin_setting_heading('enrol_ldap_course_settings', get_string('course_settings', 'enrol_ldap'), '')); $settings->add(new admin_setting_configtext_trim_lower('enrol_ldap/objectclass', get_string('objectclass_key', 'enrol_ldap'), get_string('objectclass', 'enrol_ldap'), '')); - $coursefields = array ('idnumber', 'shortname', 'fullname', 'summary'); + $settings->add(new admin_setting_configtext_trim_lower('enrol_ldap/course_idnumber', get_string('course_idnumber_key', 'enrol_ldap'), get_string('course_idnumber', 'enrol_ldap'), '', true, true)); + + $coursefields = array ('shortname', 'fullname', 'summary'); foreach ($coursefields as $field) { $settings->add(new admin_setting_configtext_trim_lower('enrol_ldap/course_'.$field, get_string('course_'.$field.'_key', 'enrol_ldap'), get_string('course_'.$field, 'enrol_ldap'), '', true, true)); } + $settings->add(new admin_setting_configcheckbox('enrol_ldap/ignorehiddencourses', get_string('ignorehiddencourses', 'enrol_database'), get_string('ignorehiddencourses_desc', 'enrol_database'), 0)); $options = array(ENROL_EXT_REMOVED_UNENROL => get_string('extremovedunenrol', 'enrol'), ENROL_EXT_REMOVED_KEEP => get_string('extremovedkeep', 'enrol'), @@ -97,6 +100,13 @@ if ($ADMIN->fulltree) { } $settings->add(new admin_setting_configtext_trim_lower('enrol_ldap/template', get_string('template_key', 'enrol_ldap'), get_string('template', 'enrol_ldap'), '')); + //--- course update settings --- + $settings->add(new admin_setting_heading('enrol_ldap_autoupdate_settings', get_string('autoupdate_settings', 'enrol_ldap'), get_string('autoupdate_settings_desc', 'enrol_ldap'))); + $options = $yesno; + foreach ($coursefields as $field) { + $settings->add(new admin_setting_configselect('enrol_ldap/course_'.$field.'_updateonsync', get_string('course_'.$field.'_updateonsync_key', 'enrol_ldap'), get_string('course_'.$field.'_updateonsync', 'enrol_ldap'), 0, $options)); + } + //--- nested groups settings --- $settings->add(new admin_setting_heading('enrol_ldap_nested_groups_settings', get_string('nested_groups_settings', 'enrol_ldap'), '')); $options = $yesno; diff --git a/file.php b/file.php index fce3f788ec1..bfbe8701961 100644 --- a/file.php +++ b/file.php @@ -111,7 +111,7 @@ if ($file->get_filename() == '.') { // ======================================== // finally send the file // ======================================== -session_get_instance()->write_close(); // unlock session during fileserving +\core\session\manager::write_close(); // Unlock session during file serving. send_stored_file($file, $lifetime, $CFG->filteruploadedfiles, $forcedownload); diff --git a/filter/manage.php b/filter/manage.php index 49d80ce1c27..15b493cde35 100644 --- a/filter/manage.php +++ b/filter/manage.php @@ -25,6 +25,7 @@ require_once(dirname(__FILE__) . '/../config.php'); require_once($CFG->libdir . '/adminlib.php'); +require_once($CFG->libdir . '/pluginlib.php'); $contextid = required_param('contextid',PARAM_INT); $forfilter = optional_param('filter', '', PARAM_SAFEDIR); @@ -36,9 +37,6 @@ require_login($course, false, $cm); require_capability('moodle/filter:manage', $context); $PAGE->set_context($context); -// Purge all caches related to filter administration. -cache::make('core', 'plugininfo_filter')->purge(); - $args = array('contextid'=>$contextid); $baseurl = new moodle_url('/filter/manage.php', $args); if (!empty($forfilter)) { diff --git a/install.php b/install.php index 899ebd35e38..0c250a05f30 100644 --- a/install.php +++ b/install.php @@ -161,6 +161,7 @@ if (!empty($_POST)) { } // Fake some settings so that we can use selected functions from moodlelib.php, weblib.php and filelib.php. +global $CFG; $CFG = new stdClass(); $CFG->lang = $config->lang; $CFG->dirroot = dirname(__FILE__); @@ -221,20 +222,36 @@ ini_set('include_path', $CFG->libdir.'/pear' . PATH_SEPARATOR . ini_get('include //point zend include path to moodles lib/zend so that includes and requires will search there for files before anywhere else ini_set('include_path', $CFG->libdir.'/zend' . PATH_SEPARATOR . ini_get('include_path')); +// Register our classloader, in theory somebody might want to replace it to load other hacked core classes. +// Required because the database checks below lead to session interaction which is going to lead us to requiring autoloaded classes. +if (defined('COMPONENT_CLASSLOADER')) { + spl_autoload_register(COMPONENT_CLASSLOADER); +} else { + spl_autoload_register('core_component::classloader'); +} + require('version.php'); $CFG->target_release = $release; -$SESSION = new stdClass(); -$SESSION->lang = $CFG->lang; +$_SESSION = array(); +$_SESSION['SESSION'] = new stdClass(); +$_SESSION['SESSION']->lang = $CFG->lang; +$_SESSION['USER'] = new stdClass(); +$_SESSION['USER']->id = 0; +$_SESSION['USER']->mnethostid = 1; -$USER = new stdClass(); -$USER->id = 0; +global $SESSION; +global $USER; +$SESSION = &$_SESSION['SESSION']; +$USER = &$_SESSION['USER']; +global $COURSE; $COURSE = new stdClass(); -$COURSE->id = 0; +$COURSE->id = 1; +global $SITE; $SITE = $COURSE; -define('SITEID', 0); +define('SITEID', 1); $hint_dataroot = ''; $hint_admindir = ''; diff --git a/lang/en/admin.php b/lang/en/admin.php index 235a81c93b9..71a73d89c36 100644 --- a/lang/en/admin.php +++ b/lang/en/admin.php @@ -424,13 +424,9 @@ $string['defaulthomepage'] = 'Default home page for users'; $string['defaultrequestcategory'] = 'Default category for course requests'; $string['defaultsettinginfo'] = 'Default: {$a}'; $string['defaultuserroleid'] = 'Default role for all users'; -$string['deletefilterareyousure'] = 'Are you sure you want to delete the filter \'{$a}\''; -$string['deletefilterareyousuremessage'] = 'You are about to completely delete the filter \'{$a}\'. Are you sure you want to uninstall it?'; -$string['deletefilterfiles'] = 'All data associated with the filter \'{$a->filter}\' has been deleted from the database. To complete the deletion (and to prevent the filter from re-installing itself), you should now delete this directory from your server: {$a->directory}'; $string['deleteincompleteusers'] = 'Delete incomplete users after'; $string['deleteunconfirmed'] = 'Delete not fully setup users after'; $string['deleteuser'] = 'Delete user'; -$string['deletingfilter'] = 'Deleting filter \'{$a}\''; $string['density'] = 'Density'; $string['denyemailaddresses'] = 'Denied email domains'; $string['development'] = 'Development'; @@ -1014,6 +1010,7 @@ $string['tablenosave'] = 'Changes in table above are saved automatically.'; $string['tablesnosave'] = 'Changes in tables above are saved automatically.'; $string['tabselectedtofront'] = 'On tables with tabs, should the row with the currently selected tab be placed at the front'; $string['tabselectedtofronttext'] = 'Bring selected tab row to front'; +$string['testsiteupgradewarning'] = 'You are currently using the {$a} test site, to upgrade it properly use the command line interface tool'; $string['themedesignermode'] = 'Theme designer mode'; $string['themelist'] = 'Theme list'; $string['themenoselected'] = 'No theme selected'; diff --git a/lang/en/backup.php b/lang/en/backup.php index d2de209bbd0..1a092c9cdcb 100644 --- a/lang/en/backup.php +++ b/lang/en/backup.php @@ -140,6 +140,7 @@ $string['generalroleassignments'] = 'Include role assignments'; $string['generalsettings'] = 'General backup settings'; $string['generaluserscompletion'] = 'Include user completion information'; $string['generalusers'] = 'Include users'; +$string['hidetypes'] = 'Hide type options'; $string['importgeneralsettings'] = 'General import defaults'; $string['importgeneralmaxresults'] = 'Maximum number of courses listed for import'; $string['importgeneralmaxresults_desc'] = 'This controls the number of courses that are listed during the first step of the import process'; @@ -174,6 +175,8 @@ $string['nomatchingcourses'] = 'There are no courses to display'; $string['norestoreoptions'] = 'There are no categories or existing courses you can restore to.'; $string['originalwwwroot'] = 'URL of backup'; $string['previousstage'] = 'Previous'; +$string['preparingui'] = 'Preparing to display page'; +$string['preparingdata'] = 'Preparing data'; $string['qcategory2coursefallback'] = 'The questions category "{$a->name}", originally at system/course category context in backup file, will be created at course context by restore'; $string['qcategorycannotberestored'] = 'The questions category "{$a->name}" cannot be created by restore'; $string['question2coursefallback'] = 'The questions category "{$a->name}", originally at system/course category context in backup file, will be created at course context by restore'; @@ -244,6 +247,7 @@ $string['setting_course_shortname'] = 'Course short name'; $string['setting_course_startdate'] = 'Course start date'; $string['setting_keep_roles_and_enrolments'] = 'Keep current roles and enrolments'; $string['setting_keep_groups_and_groupings'] = 'Keep current groups and groupings'; +$string['showtypes'] = 'Show type options'; $string['skiphidden'] = 'Skip hidden courses'; $string['skiphiddenhelp'] = 'Choose whether or not to skip hidden courses'; $string['skipmodifdays'] = 'Skip courses not modified since'; diff --git a/lang/en/badges.php b/lang/en/badges.php index 09cc67510ff..3065685c2d7 100644 --- a/lang/en/badges.php +++ b/lang/en/badges.php @@ -82,9 +82,11 @@ $string['backpackconnection_help'] = 'This page allows you to set up connection Currently, only Mozilla OpenBadges Backpack is supported. You need to sign up for a backpack service before trying to set up backpack connection on this page.'; $string['backpackdetails'] = 'Backpack settings'; $string['backpackemail'] = 'Email address'; -$string['backpackemail_help'] = 'Email address associated with your backpack. +$string['backpackemail_help'] = 'The email address associated with your backpack. While you are connected, any badges earned on this site will be associated with this email address.'; +$string['personaconnection'] = 'Sign in with your email'; +$string['personaconnection_help'] = 'Persona is a system for identifying yourself across the web, using an email address that you own. The Open Badges backpack uses Persona as a login system, so to be able to connect to a backpack you with need a Persona account. -If backpack connection is established, this email address is used instead of your internal email address to push badges to your backpack.'; +For more information about Persona visit https://login.persona.org/about.'; $string['backpackimport'] = 'Badge import settings'; $string['backpackimport_help'] = 'After backpack connection is successfully established, badges from your backpack can be displayed on your "My Badges" page and your profile page. @@ -128,6 +130,7 @@ $string['configenablebadges'] = 'When enabled, this feature lets you create badg $string['configuremessage'] = 'Badge message'; $string['connect'] = 'Connect'; $string['connected'] = 'Connected'; +$string['connecting'] = 'Connecting...'; $string['contact'] = 'Contact'; $string['contact_help'] = 'An email address associated with the badge issuer.'; $string['copyof'] = 'Copy of {$a}'; @@ -139,6 +142,9 @@ $string['createbutton'] = 'Create badge'; $string['creatorbody'] = '{$a->user} has completed all badge requirements and has been awarded the badge. View issued badge at {$a->link}
'; $string['creatorsubject'] = '\'{$a}\' has been awarded!'; $string['criteriasummary'] = 'Criteria summary'; +$string['criteriacreated'] = 'Badge criteria successfully created'; +$string['criteriadeleted'] = 'Badge criteria successfully deleted'; +$string['criteriaupdated'] = 'Badge criteria successfully updated'; $string['criteria_descr'] = 'Users are awarded this badge when they complete the following requirement:'; $string['criteria_descr_bydate'] = ' by {$a} '; $string['criteria_descr_grade'] = ' with minimum grade of {$a} '; @@ -199,10 +205,15 @@ $string['disconnect'] = 'Disconnect'; $string['donotaward'] = 'Currently, this badge is not active, so it cannot be awarded to users. If you would like to award this badge, please set its status to active.'; $string['editsettings'] = 'Edit settings'; $string['enablebadges'] = 'Enable badges'; +$string['error:backpackdatainvalid'] = 'The data return from the backpack was invalid.'; +$string['error:backpackemailnotfound'] = 'The email \'{$a}\' is not associated with a backpack. You need to create a backpack for that account or sign in with another email address.'; $string['error:backpacknotavailable'] = 'Your site is not accessible from the Internet, so any badges issued from this site cannot be verified by external backpack services.'; +$string['error:backpackloginfailed'] = 'You could not be connected to an external backpack for the following reason: {$a}'; $string['error:backpackproblem'] = 'There was a problem connecting to your backpack service provider. Please try again later.'; +$string['error:badjson'] = 'The connection attempt returned invalid data.'; $string['error:cannotact'] = 'Cannot activate the badge. '; $string['error:cannotawardbadge'] = 'Cannot award badge to a user.'; +$string['error:connectionunknownreason'] = 'The connection was unsuccessful but no reason was given.'; $string['error:clone'] = 'Cannot clone the badge.'; $string['error:duplicatename'] = 'Badge with such name already exists in the system.'; $string['error:externalbadgedoesntexist'] = 'Badge not found'; @@ -211,6 +222,7 @@ $string['error:invalidcriteriatype'] = 'Invalid criteria type.'; $string['error:invalidexpiredate'] = 'Expiry date has to be in the future.'; $string['error:invalidexpireperiod'] = 'Expiry period cannot be negative or equal 0.'; $string['error:noactivities'] = 'There are no activities with completion criteria enabled in this course.'; +$string['error:noassertion'] = 'No assertion was returned by Persona. You may have closed the dialog before completing the login process.'; $string['error:nocourses'] = 'Course completion is not enabled for any of the courses in this site, so none can be displayed. Course completion may be enabled in the course settings.'; $string['error:nogroups'] = 'There are no public collections of badges available in your backpack.
Only public collections are shown, visit your backpack to create some public collections.
'; @@ -223,6 +235,9 @@ $string['error:nosuchrole'] = 'Warning: This role is no longer available.'; $string['error:nosuchuser'] = 'User with this email address does not have an account with the current backpack provider.'; $string['error:notifycoursedate'] = 'Warning: Badges associated with course and activity completions will not be issued until the course start date.'; $string['error:parameter'] = 'Warning: At least one parameter should be selected to ensure correct badge issuing workflow.'; +$string['error:personaneedsjs'] = 'Currently, Javascript is required to connect to your backpack. If you can, enable Javascript and reload the page.'; +$string['error:requesttimeout'] = 'The connection request timed out before it could complete.'; +$string['error:requesterror'] = 'The connection request failed (error code {$a}).'; $string['error:save'] = 'Cannot save the badge.'; $string['evidence'] = 'Evidence'; $string['existingrecipients'] = 'Existing badge recipients'; @@ -313,9 +328,14 @@ $string['recipientidentificationproblem'] = 'Cannot find a recipient of this bad $string['recipientvalidationproblem'] = 'Current user cannot be verified as a recipient of this badge.'; $string['relative'] = 'Relative date'; $string['requiredcourse'] = 'At least one course should be added to the courseset criterion.'; -$string['reviewbadge'] = 'Review badge criteria'; -$string['reviewconfirm'] = 'This action will perform a check if any of the users have already completed all the requirements for \'{$a}\' badge?
-Would you like to proceed?
'; +$string['reviewbadge'] = 'Changes in badge access'; +$string['reviewconfirm'] = 'This will make your badge visible to users and allow them to start earning it.
+ +It is possible that some users already meet this badge\'s criteria and will be issued this badge immediately after you enable it.
+ +Once a badge has been issued it will be locked - certain settings including the criteria and expiry settings can no longer be changed.
+ +Are you sure you want to enable access to the badge \'{$a}\'?
'; $string['save'] = 'Save'; $string['searchname'] = 'Search by name'; $string['selectaward'] = 'Please select the role you would like to use to award this badge: '; @@ -323,6 +343,7 @@ $string['selectgroup_end'] = 'Only public collections are shown, id from section $coursemod->section"); - } - } - } - - // Increment course.cacherev for courses that used this module. - // This will force cache rebuilding on the next request. - increment_revision_number('course', 'cacherev', - "id IN (SELECT DISTINCT course - FROM {course_modules} - WHERE module=?)", - array($module->id)); - - // delete all the course module records - $DB->delete_records('course_modules', array('module' => $module->id)); - - // delete module contexts - if ($coursemods) { - foreach ($coursemods as $coursemod) { - context_helper::delete_instance(CONTEXT_MODULE, $coursemod->id); - } - } - - // delete the module entry itself - $DB->delete_records('modules', array('name' => $module->name)); - - // cleanup the gradebook - require_once($CFG->libdir.'/gradelib.php'); - grade_uninstalled_module($module->name); - - // Perform any custom uninstall tasks - if (file_exists($CFG->dirroot . '/mod/' . $module->name . '/lib.php')) { - require_once($CFG->dirroot . '/mod/' . $module->name . '/lib.php'); - $uninstallfunction = $module->name . '_uninstall'; - if (function_exists($uninstallfunction)) { - debugging("{$uninstallfunction}() has been deprecated. Use the plugin's db/uninstall.php instead", DEBUG_DEVELOPER); - if (!$uninstallfunction()) { - echo $OUTPUT->notification('Encountered a problem running uninstall function for '. $module->name.'!'); - } - } - } - - } else if ($type === 'enrol') { - // NOTE: this is a bit brute force way - it will not trigger events and hooks properly - // nuke all role assignments - role_unassign_all(array('component'=>$component)); - // purge participants - $DB->delete_records_select('user_enrolments', "enrolid IN (SELECT id FROM {enrol} WHERE enrol = ?)", array($name)); - // purge enrol instances - $DB->delete_records('enrol', array('enrol'=>$name)); - // tweak enrol settings - if (!empty($CFG->enrol_plugins_enabled)) { - $enabledenrols = explode(',', $CFG->enrol_plugins_enabled); - $enabledenrols = array_unique($enabledenrols); - $enabledenrols = array_flip($enabledenrols); - unset($enabledenrols[$name]); - $enabledenrols = array_flip($enabledenrols); - if (is_array($enabledenrols)) { - set_config('enrol_plugins_enabled', implode(',', $enabledenrols)); - } - } - - } else if ($type === 'block') { - if ($block = $DB->get_record('block', array('name'=>$name))) { - // Inform block it's about to be deleted - if (file_exists("$CFG->dirroot/blocks/$block->name/block_$block->name.php")) { - $blockobject = block_instance($block->name); - if ($blockobject) { - $blockobject->before_delete(); //only if we can create instance, block might have been already removed - } - } - - // First delete instances and related contexts - $instances = $DB->get_records('block_instances', array('blockname' => $block->name)); - foreach($instances as $instance) { - blocks_delete_instance($instance); - } - - // Delete block - $DB->delete_records('block', array('id'=>$block->id)); - } - } else if ($type === 'format') { - if (($defaultformat = get_config('moodlecourse', 'format')) && $defaultformat !== $name) { - $courses = $DB->get_records('course', array('format' => $name), 'id'); - $data = (object)array('id' => null, 'format' => $defaultformat); - foreach ($courses as $record) { - $data->id = $record->id; - update_course($data); - } - } - $DB->delete_records('course_format_options', array('format' => $name)); + // Specific plugin type cleanup. + $plugininfo = plugin_manager::instance()->get_plugin_info($component); + if ($plugininfo) { + $plugininfo->uninstall_cleanup(); + plugin_manager::reset_caches(); } + $plugininfo = null; // perform clean-up task common for all the plugin/subplugin types @@ -298,16 +203,14 @@ function uninstall_plugin($type, $name) { $DB->delete_records('log_display', array('component' => $component)); // delete the module configuration records - unset_all_config_for_plugin($pluginname); + unset_all_config_for_plugin($component); + if ($type === 'mod') { + unset_all_config_for_plugin($pluginname); + } // delete message provider message_provider_uninstall($component); - // delete message processor - if ($type === 'message') { - message_processor_uninstall($name); - } - // delete the plugin tables $xmldbfilepath = $plugindirectory . '/db/install.xml'; drop_plugin_tables($component, $xmldbfilepath, false); @@ -369,15 +272,22 @@ function get_component_version($component, $source='installed') { // activity module if ($type === 'mod') { if ($source === 'installed') { - return $DB->get_field('modules', 'version', array('name'=>$name)); + if ($CFG->version < 2013092001.02) { + return $DB->get_field('modules', 'version', array('name'=>$name)); + } else { + return get_config('mod_'.$name, 'version'); + } + } else { $mods = core_component::get_plugin_list('mod'); if (empty($mods[$name]) or !is_readable($mods[$name].'/version.php')) { return false; } else { - $module = new stdclass(); + $plugin = new stdClass(); + $plugin->version = null; + $module = $plugin; include($mods[$name].'/version.php'); - return $module->version; + return $plugin->version; } } } @@ -6414,8 +6324,10 @@ function admin_get_root($reload=false, $requirefulltree=true) { */ function admin_apply_default_settings($node=NULL, $unconditional=true) { global $CFG; + require_once($CFG->libdir.'/pluginlib.php'); if (is_null($node)) { + plugin_manager::reset_caches(); $node = admin_get_root(true, true); } @@ -6440,6 +6352,8 @@ function admin_apply_default_settings($node=NULL, $unconditional=true) { $setting->write_setting_flags(null); } } + // Just in case somebody modifies the list of active plugins directly. + plugin_manager::reset_caches(); } /** diff --git a/lib/authlib.php b/lib/authlib.php index f8fac6ea501..736374214a2 100644 --- a/lib/authlib.php +++ b/lib/authlib.php @@ -618,9 +618,7 @@ function login_is_lockedout($user) { function login_attempt_valid($user) { global $CFG; - $event = \core\event\user_loggedin::create(array('objectid' => $user->id, 'other' => array('username' => $user->username))); - $event->add_record_snapshot('user', $user); - $event->trigger(); + // Note: user_loggedin event is triggered in complete_user_login(). if ($user->mnethostid != $CFG->mnet_localhost_id) { return; diff --git a/lib/classes/component.php b/lib/classes/component.php index 51db6e9f4ce..e193399b316 100644 --- a/lib/classes/component.php +++ b/lib/classes/component.php @@ -24,6 +24,20 @@ defined('MOODLE_INTERNAL') || die(); +// Constants used in version.php files, these must exist when core_component executes. + +/** Software maturity level - internals can be tested using white box techniques. */ +define('MATURITY_ALPHA', 50); +/** Software maturity level - feature complete, ready for preview and testing. */ +define('MATURITY_BETA', 100); +/** Software maturity level - tested, will be released unless there are fatal bugs. */ +define('MATURITY_RC', 150); +/** Software maturity level - ready for production deployment. */ +define('MATURITY_STABLE', 200); +/** Any version - special value that can be used in $plugin->dependencies in version.php files. */ +define('ANY_VERSION', 'any'); + + /** * Collection of components related methods. */ @@ -268,6 +282,7 @@ $cache = '.var_export($cache, true).'; protected static function fetch_core_version() { global $CFG; if (self::$version === null) { + $version = null; // Prevent IDE complaints. require($CFG->dirroot . '/version.php'); self::$version = $version; } @@ -912,17 +927,11 @@ $cache = '.var_export($cache, true).'; $plugs = self::fetch_plugins($type, $typedir); } foreach ($plugs as $plug => $fullplug) { - if ($type === 'mod') { - $module = new stdClass(); - $module->version = null; - include($fullplug.'/version.php'); - $versions[$type.'_'.$plug] = $module->version; - } else { - $plugin = new stdClass(); - $plugin->version = null; - @include($fullplug.'/version.php'); - $versions[$type.'_'.$plug] = $plugin->version; - } + $plugin = new stdClass(); + $plugin->version = null; + $module = $plugin; + @include($fullplug.'/version.php'); + $versions[$type.'_'.$plug] = $plugin->version; } } diff --git a/lib/classes/event/course_module_instances_list_viewed.php b/lib/classes/event/course_module_instances_list_viewed.php new file mode 100644 index 00000000000..b1b6db37b66 --- /dev/null +++ b/lib/classes/event/course_module_instances_list_viewed.php @@ -0,0 +1,81 @@ +. + +/** + * Course module instances list_viewed event. + * + * @package core + * @copyright 2013 Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core\event; +defined('MOODLE_INTERNAL') || die(); + +/** + * Course module instances list viewed event class. + * + * This is an abstract to guide the developers in using this event name for their events. + * It is intended to be used when the user viewes the list of all the instances of a module + * in a course. This replaces the historical 'view all' log entry generated in mod/somemod/index.php. + * + * Example: + * + * \mod_chat\event\instances_list_viewed extends \core\event\course_module_instances_list_viewed + * + * @package core + * @copyright 2013 Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +abstract class course_module_instances_list_viewed extends \core\event\content_viewed { + + /** + * Init method. + * + * @return void + */ + protected function init() { + $this->data['crud'] = 'r'; + $this->data['level'] = self::LEVEL_OTHER; + } + + /** + * Set page detail. + * + * Override to prevent its use. + */ + public function set_page_detail() { + } + + /** + * Custom validation. + * + * The parent validation is ignored on purpose. + * + * @throws \coding_exception + * @return void + */ + protected function validate_data() { + if ($this->context->contextlevel !== CONTEXT_COURSE) { + throw new \coding_exception('The context must be a course level context.'); + } + // Hack to by pass the requirement of the parent class. 'view_all' was the old fashioned-way + // to describe the page listing all the instances of a module in a course. + $this->data['other']['content'] = 'view_all'; + parent::validate_data(); + } + +} diff --git a/lib/classes/event/course_reset_ended.php b/lib/classes/event/course_reset_ended.php new file mode 100644 index 00000000000..56e10091225 --- /dev/null +++ b/lib/classes/event/course_reset_ended.php @@ -0,0 +1,86 @@ +. + +/** + * core course reset ended event. + * + * @package core + * @copyright 2013 Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core\event; +defined('MOODLE_INTERNAL') || die(); + +/** + * core course reset ended event class. + * + * @package core + * @copyright 2013 Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class course_reset_ended extends \core\event\base { + + /** + * Returns description of what happened. + * + * @return string + */ + public function get_description() { + return "The reset of course $this->courseid has ended."; + } + + /** + * Return localised event name. + * + * @return string + */ + public static function get_name() { + return get_string('eventcourseresetended', 'core'); + } + + /** + * Get URL related to the action + * + * @return \moodle_url + */ + public function get_url() { + return new \moodle_url('/course/view.php', array('id' => $this->courseid)); + } + + /** + * Init method. + * + * @return void + */ + protected function init() { + $this->data['crud'] = 'd'; + $this->data['level'] = self::LEVEL_OTHER; + } + + /** + * Custom validation. + * + * @throws \coding_exception + * @return void + */ + protected function validate_data() { + if (!isset($this->other['reset_options'])) { + throw new \coding_exception('The key reset_options must be set in $other.'); + } + } + +} diff --git a/lib/classes/event/course_reset_started.php b/lib/classes/event/course_reset_started.php new file mode 100644 index 00000000000..8a34a75e073 --- /dev/null +++ b/lib/classes/event/course_reset_started.php @@ -0,0 +1,86 @@ +. + +/** + * core course reset started event. + * + * @package core + * @copyright 2013 Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core\event; +defined('MOODLE_INTERNAL') || die(); + +/** + * core course reset started event class. + * + * @package core + * @copyright 2013 Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class course_reset_started extends \core\event\base { + + /** + * Returns description of what happened. + * + * @return string + */ + public function get_description() { + return "The user $this->userid has started the reset of course $this->courseid."; + } + + /** + * Return localised event name. + * + * @return string + */ + public static function get_name() { + return get_string('eventcourseresetstarted', 'core'); + } + + /** + * Get URL related to the action + * + * @return \moodle_url + */ + public function get_url() { + return new \moodle_url('/course/view.php', array('id' => $this->courseid)); + } + + /** + * Init method. + * + * @return void + */ + protected function init() { + $this->data['crud'] = 'd'; + $this->data['level'] = self::LEVEL_OTHER; + } + + /** + * Custom validation. + * + * @throws \coding_exception + * @return void + */ + protected function validate_data() { + if (!isset($this->other['reset_options'])) { + throw new \coding_exception('The key reset_options must be set in $other.'); + } + } + +} diff --git a/lib/classes/event/user_loggedin.php b/lib/classes/event/user_loggedin.php index 006485f75f9..9065aa77363 100644 --- a/lib/classes/event/user_loggedin.php +++ b/lib/classes/event/user_loggedin.php @@ -96,7 +96,7 @@ class user_loggedin extends \core\event\base { /** * Custom validation. * - * @throws coding_exception when validation does not pass. + * @throws \coding_exception when validation does not pass. * @return void */ protected function validate_data() { diff --git a/lib/classes/event/user_loggedout.php b/lib/classes/event/user_loggedout.php index f881b471884..8f2c6aacfdd 100644 --- a/lib/classes/event/user_loggedout.php +++ b/lib/classes/event/user_loggedout.php @@ -38,6 +38,7 @@ class user_loggedout extends base { * Initialise required event data properties. */ protected function init() { + $this->context = \context_system::instance(); $this->data['objecttable'] = 'user'; $this->data['crud'] = 'r'; $this->data['level'] = self::LEVEL_OTHER; diff --git a/lib/classes/session/database.php b/lib/classes/session/database.php new file mode 100644 index 00000000000..4216980dff2 --- /dev/null +++ b/lib/classes/session/database.php @@ -0,0 +1,315 @@ +. + +/** + * Database based session handler. + * + * @package core + * @copyright 2013 Petr Skoda {@link http://skodak.org} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core\session; + +defined('MOODLE_INTERNAL') || die(); + +/** + * Database based session handler. + * + * @package core + * @copyright 2013 Petr Skoda {@link http://skodak.org} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class database extends handler { + /** @var \stdClass $record session record */ + protected $recordid = null; + + /** @var \moodle_database $database session database */ + protected $database = null; + + /** @var bool $failed session read/init failed, do not write back to DB */ + protected $failed = false; + + /** @var string $lasthash hash of the session data content */ + protected $lasthash = null; + + /** @var int $acquiretimeout how long to wait for session lock */ + protected $acquiretimeout = 120; + + /** + * Create new instance of handler. + */ + public function __construct() { + global $DB, $CFG; + // Note: we store the reference here because we need to modify database in shutdown handler. + $this->database = $DB; + + if (!empty($CFG->session_database_acquire_lock_timeout)) { + $this->acquiretimeout = (int)$CFG->session_database_acquire_lock_timeout; + } + } + + /** + * Init session handler. + */ + public function init() { + if (!$this->database->session_lock_supported()) { + throw new exception('sessionhandlerproblem', 'error', '', null, 'Database does not support session locking'); + } + + $result = session_set_save_handler(array($this, 'handler_open'), + array($this, 'handler_close'), + array($this, 'handler_read'), + array($this, 'handler_write'), + array($this, 'handler_destroy'), + array($this, 'handler_gc')); + if (!$result) { + throw new exception('dbsessionhandlerproblem', 'error'); + } + + register_shutdown_function(array($this, 'handler_shutdown')); + } + + /** + * Check for existing session with id $sid. + * + * Note: this verifies the storage backend only, not the actual session records. + * + * @param string $sid + * @return bool true if session found. + */ + public function session_exists($sid) { + try { + return $this->database->record_exists('sessions', array('sid'=>$sid, 'state'=>0)); + } catch (\dml_exception $ex) { + return false; + } + } + + /** + * Kill all active sessions, the core sessions table is + * purged afterwards. + */ + public function kill_all_sessions() { + // Nothing to do, the sessions table is cleared from core. + return; + } + + /** + * Kill one session, the session record is removed afterwards. + * @param string $sid + */ + public function kill_session($sid) { + // Nothing to do, the sessions table is purged afterwards. + return; + } + + /** + * Open session handler. + * + * {@see http://php.net/manual/en/function.session-set-save-handler.php} + * + * @param string $save_path + * @param string $session_name + * @return bool success + */ + public function handler_open($save_path, $session_name) { + // Note: we use the already open database. + return true; + } + + /** + * Close session handler. + * + * {@see http://php.net/manual/en/function.session-set-save-handler.php} + * + * @return bool success + */ + public function handler_close() { + if ($this->recordid) { + try { + $this->database->release_session_lock($this->recordid); + } catch (\Exception $ex) { + // Ignore any problems. + } + } + $this->recordid = null; + $this->lasthash = null; + return true; + } + + /** + * Read session handler. + * + * {@see http://php.net/manual/en/function.session-set-save-handler.php} + * + * @param string $sid + * @return string + */ + public function handler_read($sid) { + try { + if (!$record = $this->database->get_record('sessions', array('sid'=>$sid), 'id')) { + // Let's cheat and skip locking if this is the first access, + // do not create the record here, let the manager do it after session init. + $this->failed = false; + $this->recordid = null; + $this->lasthash = sha1(''); + return ''; + } + if ($this->recordid and $this->recordid != $record->id) { + error_log('Second session read with different record id detected, cannot read session'); + $this->failed = true; + $this->recordid = null; + return ''; + } + if (!$this->recordid) { + // Lock session if exists and not already locked. + $this->database->get_session_lock($record->id, $this->acquiretimeout); + $this->recordid = $record->id; + } + } catch (\dml_sessionwait_exception $ex) { + // This is a fatal error, better inform users. + // It should not happen very often - all pages that need long time to execute + // should close session immediately after access control checks. + error_log('Cannot obtain session lock for sid: '.$sid); + $this->failed = true; + throw $ex; + + } catch (\Exception $ex) { + // Do not rethrow exceptions here, this should not happen. + error_log('Unknown exception when starting database session : '.$sid.' - '.$ex->getMessage()); + $this->failed = true; + $this->recordid = null; + return ''; + } + + // Finally read the full session data because we know we have the lock now. + if (!$record = $this->database->get_record('sessions', array('id'=>$record->id), 'id, sessdata')) { + // Ignore - something else just deleted the session record. + $this->failed = true; + $this->recordid = null; + return ''; + } + $this->failed = false; + + if (is_null($record->sessdata)) { + $data = ''; + $this->lasthash = sha1(''); + } else { + $data = base64_decode($record->sessdata); + $this->lasthash = sha1($record->sessdata); + } + + return $data; + } + + /** + * Write session handler. + * + * {@see http://php.net/manual/en/function.session-set-save-handler.php} + * + * NOTE: Do not write to output or throw any exceptions! + * Hopefully the next page is going to display nice error or it recovers... + * + * @param string $sid + * @param string $session_data + * @return bool success + */ + public function handler_write($sid, $session_data) { + if ($this->failed) { + // Do not write anything back - we failed to start the session properly. + return false; + } + + $sessdata = base64_encode($session_data); // There might be some binary mess :-( + $hash = sha1($sessdata); + + if ($hash === $this->lasthash) { + return true; + } + + try { + if ($this->recordid) { + $this->database->set_field('sessions', 'sessdata', $sessdata, array('id'=>$this->recordid)); + } else { + // This happens in the first request when session record was just created in manager. + $this->database->set_field('sessions', 'sessdata', $sessdata, array('sid'=>$sid)); + } + } catch (\Exception $ex) { + // Do not rethrow exceptions here, this should not happen. + error_log('Unknown exception when writing database session data : '.$sid.' - '.$ex->getMessage()); + } + + return true; + } + + /** + * Destroy session handler. + * + * {@see http://php.net/manual/en/function.session-set-save-handler.php} + * + * @param string $sid + * @return bool success + */ + public function handler_destroy($sid) { + if (!$session = $this->database->get_record('sessions', array('sid'=>$sid), 'id, sid')) { + if ($sid == session_id()) { + $this->recordid = null; + $this->lasthash = null; + } + return true; + } + + if ($this->recordid and $session->id == $this->recordid) { + try { + $this->database->release_session_lock($this->recordid); + } catch (\Exception $ex) { + // Ignore problems. + } + $this->recordid = null; + $this->lasthash = null; + } + + $this->database->delete_records('sessions', array('id'=>$session->id)); + + return true; + } + + /** + * GC session handler. + * + * {@see http://php.net/manual/en/function.session-set-save-handler.php} + * + * @param int $ignored_maxlifetime moodle uses special timeout rules + * @return bool success + */ + public function handler_gc($ignored_maxlifetime) { + // This should do something only if cron is not running properly... + if (!$stalelifetime = ini_get('session.gc_maxlifetime')) { + return true; + } + $params = array('purgebefore' => (time() - $stalelifetime)); + $this->database->delete_records_select('sessions', 'userid = 0 AND timemodified < :purgebefore', $params); + return true; + } + + /** + * This makes sure the session is written to disk at the end of request. + */ + public function handler_shutdown() { + $this->database->dispose(); + } +} diff --git a/admin/tool/generator/cli/generate.php b/lib/classes/session/exception.php similarity index 64% rename from admin/tool/generator/cli/generate.php rename to lib/classes/session/exception.php index 353883e1d30..ea58b0f5e8f 100644 --- a/admin/tool/generator/cli/generate.php +++ b/lib/classes/session/exception.php @@ -15,23 +15,20 @@ // along with Moodle. If not, see")},s=function(e,t){M.atto_title.change_title(e,t,"")},o=function(e,t){M.atto_title.change_title(e,t,"")},u="
"+M.util.get_string("h3","atto_title")+"
",a=""+M.util.get_string("h4","atto_title")+"
",f=""+M.util.get_string("h5","atto_title")+"
",l=""+M.util.get_string("pre","atto_title")+"",c=" "+M.util.get_string("blockquote","atto_title")+"
",h=""+M.util.get_string("p","atto_title")+"
";M.editor_atto.add_toolbar_menu(e.elementid,"title",e.icon,[{text:u,handler:t},{text:a,handler:n},{text:f,handler:r},{text:l,handler:i},{text:c,handler:s},{text:h,handler:o}])},change_title:function(e,t,n){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("formatBlock",!1,n)}}},"@VERSION@",{requires:["node"]});
+YUI.add("moodle-atto_title-button",function(e,t){M.atto_title=M.atto_title||{init:function(e){var t=function(e,t){M.atto_title.change_title(e,t,"")},n=function(e,t){M.atto_title.change_title(e,t,"")},r=function(e,t){M.atto_title.change_title(e,t,"")},i=function(e,t){M.atto_title.change_title(e,t,"")},s=function(e,t){M.atto_title.change_title(e,t,"")},o=function(e,t){M.atto_title.change_title(e,t,"")},u="
"+M.util.get_string("h3","atto_title")+"
",a=""+M.util.get_string("h4","atto_title")+"
",f=""+M.util.get_string("h5","atto_title")+"
",l=""+M.util.get_string("pre","atto_title")+"",c=" "+M.util.get_string("blockquote","atto_title")+"
",h=""+M.util.get_string("p","atto_title")+"
";M.editor_atto.add_toolbar_menu(e.elementid,"title",e.icon,e.group,[{text:u,handler:t},{text:a,handler:n},{text:f,handler:r},{text:l,handler:i},{text:c,handler:s},{text:h,handler:o}])},change_title:function(e,t,n){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("formatBlock",!1,n)}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/title/yui/build/moodle-atto_title-button/moodle-atto_title-button.js b/lib/editor/atto/plugins/title/yui/build/moodle-atto_title-button/moodle-atto_title-button.js
index 641b0e5e92f..1908ff6bb6c 100644
--- a/lib/editor/atto/plugins/title/yui/build/moodle-atto_title-button/moodle-atto_title-button.js
+++ b/lib/editor/atto/plugins/title/yui/build/moodle-atto_title-button/moodle-atto_title-button.js
@@ -53,6 +53,7 @@ M.atto_title = M.atto_title || {
M.editor_atto.add_toolbar_menu(params.elementid,
'title',
params.icon,
+ params.group,
[
{'text' : h3, 'handler' : click_h3},
{'text' : h4, 'handler' : click_h4},
diff --git a/lib/editor/atto/plugins/title/yui/src/button/js/button.js b/lib/editor/atto/plugins/title/yui/src/button/js/button.js
index d6241e496b0..151311a591b 100644
--- a/lib/editor/atto/plugins/title/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/title/yui/src/button/js/button.js
@@ -51,6 +51,7 @@ M.atto_title = M.atto_title || {
M.editor_atto.add_toolbar_menu(params.elementid,
'title',
params.icon,
+ params.group,
[
{'text' : h3, 'handler' : click_h3},
{'text' : h4, 'handler' : click_h4},
diff --git a/lib/editor/atto/plugins/underline/lib.php b/lib/editor/atto/plugins/underline/lib.php
index bde1f90be8a..946145df476 100644
--- a/lib/editor/atto/plugins/underline/lib.php
+++ b/lib/editor/atto/plugins/underline/lib.php
@@ -31,21 +31,21 @@ defined('MOODLE_INTERNAL') || die();
function atto_underline_init_editor($elementid) {
global $PAGE, $OUTPUT;
- $icon = $OUTPUT->pix_icon('underline',
+ $icon = $OUTPUT->pix_icon('e/underline',
get_string('underline', 'atto_underline'),
- 'atto_underline',
+ 'moodle',
array('class'=>'icon'));
$PAGE->requires->yui_module('moodle-atto_underline-button',
'M.atto_underline.init',
- array(array('elementid'=>$elementid, 'icon'=>$icon)),
+ array(array('elementid'=>$elementid, 'icon'=>$icon, 'group'=>'style')),
true);
}
/**
* Return the order this plugin should be displayed in the toolbar
- * @return int
+ * @return int the absolute position within the toolbar
*/
function atto_underline_sort_order() {
return 2;
diff --git a/lib/editor/atto/plugins/underline/pix/underline.png b/lib/editor/atto/plugins/underline/pix/underline.png
deleted file mode 100644
index 329e4970db0..00000000000
Binary files a/lib/editor/atto/plugins/underline/pix/underline.png and /dev/null differ
diff --git a/lib/editor/atto/plugins/underline/pix/underline.svg b/lib/editor/atto/plugins/underline/pix/underline.svg
deleted file mode 100644
index 1d1feb4d523..00000000000
--- a/lib/editor/atto/plugins/underline/pix/underline.svg
+++ /dev/null
@@ -1,73 +0,0 @@
-
-
-
-
diff --git a/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-debug.js b/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-debug.js
index caa347e4017..218a6705384 100644
--- a/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-debug.js
+++ b/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-debug.js
@@ -32,7 +32,7 @@ M.atto_underline = M.atto_underline || {
document.execCommand('underline', false, null);
};
- M.editor_atto.add_toolbar_button(params.elementid, 'underline', params.icon, click);
+ M.editor_atto.add_toolbar_button(params.elementid, 'underline', params.icon, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-min.js b/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-min.js
index 6954ce22235..86d3a2f098e 100644
--- a/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-min.js
+++ b/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_underline-button",function(e,t){M.atto_underline=M.atto_underline||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("underline",!1,null)};M.editor_atto.add_toolbar_button(e.elementid,"underline",e.icon,t)}}},"@VERSION@",{requires:["node"]});
+YUI.add("moodle-atto_underline-button",function(e,t){M.atto_underline=M.atto_underline||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("underline",!1,null)};M.editor_atto.add_toolbar_button(e.elementid,"underline",e.icon,e.group,t)}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button.js b/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button.js
index caa347e4017..218a6705384 100644
--- a/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button.js
+++ b/lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button.js
@@ -32,7 +32,7 @@ M.atto_underline = M.atto_underline || {
document.execCommand('underline', false, null);
};
- M.editor_atto.add_toolbar_button(params.elementid, 'underline', params.icon, click);
+ M.editor_atto.add_toolbar_button(params.elementid, 'underline', params.icon, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/underline/yui/src/button/js/button.js b/lib/editor/atto/plugins/underline/yui/src/button/js/button.js
index f4ac98f8806..973461022af 100644
--- a/lib/editor/atto/plugins/underline/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/underline/yui/src/button/js/button.js
@@ -30,6 +30,6 @@ M.atto_underline = M.atto_underline || {
document.execCommand('underline', false, null);
};
- M.editor_atto.add_toolbar_button(params.elementid, 'underline', params.icon, click);
+ M.editor_atto.add_toolbar_button(params.elementid, 'underline', params.icon, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/unlink/lib.php b/lib/editor/atto/plugins/unlink/lib.php
index eef044961ac..b1023b046c5 100644
--- a/lib/editor/atto/plugins/unlink/lib.php
+++ b/lib/editor/atto/plugins/unlink/lib.php
@@ -29,21 +29,21 @@
function atto_unlink_init_editor($elementid) {
global $PAGE, $OUTPUT;
- $icon = $OUTPUT->pix_icon('unlink',
+ $icon = $OUTPUT->pix_icon('e/remove_link',
get_string('unlink', 'atto_unlink'),
- 'atto_unlink',
+ 'moodle',
array('class'=>'icon'));
$PAGE->requires->yui_module('moodle-atto_unlink-button',
'M.atto_unlink.init',
- array(array('elementid'=>$elementid, 'icon'=>$icon)),
+ array(array('elementid'=>$elementid, 'icon'=>$icon, 'group'=>'link')),
true);
}
/**
* Return the order this plugin should be displayed in the toolbar
- * @return int
+ * @return int the absolute position within the toolbar
*/
function atto_unlink_sort_order() {
return 12;
diff --git a/lib/editor/atto/plugins/unlink/pix/unlink.png b/lib/editor/atto/plugins/unlink/pix/unlink.png
deleted file mode 100644
index 1dbbb9ccd0c..00000000000
Binary files a/lib/editor/atto/plugins/unlink/pix/unlink.png and /dev/null differ
diff --git a/lib/editor/atto/plugins/unlink/pix/unlink.svg b/lib/editor/atto/plugins/unlink/pix/unlink.svg
deleted file mode 100644
index 79126bcda80..00000000000
--- a/lib/editor/atto/plugins/unlink/pix/unlink.svg
+++ /dev/null
@@ -1,92 +0,0 @@
-
-
-
-
diff --git a/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-debug.js b/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-debug.js
index 365a993c350..89959a891b3 100644
--- a/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-debug.js
+++ b/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-debug.js
@@ -32,7 +32,7 @@ M.atto_unlink = M.atto_unlink || {
document.execCommand('unlink', false, null);
};
- M.editor_atto.add_toolbar_button(params.elementid, 'unlink', params.icon, click);
+ M.editor_atto.add_toolbar_button(params.elementid, 'unlink', params.icon, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-min.js b/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-min.js
index 4f412d02606..c306b0a19ea 100644
--- a/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-min.js
+++ b/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_unlink-button",function(e,t){M.atto_unlink=M.atto_unlink||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("unlink",!1,null)};M.editor_atto.add_toolbar_button(e.elementid,"unlink",e.icon,t)}}},"@VERSION@",{requires:["node"]});
+YUI.add("moodle-atto_unlink-button",function(e,t){M.atto_unlink=M.atto_unlink||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("unlink",!1,null)};M.editor_atto.add_toolbar_button(e.elementid,"unlink",e.icon,e.group,t)}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button.js b/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button.js
index 365a993c350..89959a891b3 100644
--- a/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button.js
+++ b/lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button.js
@@ -32,7 +32,7 @@ M.atto_unlink = M.atto_unlink || {
document.execCommand('unlink', false, null);
};
- M.editor_atto.add_toolbar_button(params.elementid, 'unlink', params.icon, click);
+ M.editor_atto.add_toolbar_button(params.elementid, 'unlink', params.icon, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/unlink/yui/src/button/js/button.js b/lib/editor/atto/plugins/unlink/yui/src/button/js/button.js
index fd19d81ea2a..73923fba978 100644
--- a/lib/editor/atto/plugins/unlink/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/unlink/yui/src/button/js/button.js
@@ -30,6 +30,6 @@ M.atto_unlink = M.atto_unlink || {
document.execCommand('unlink', false, null);
};
- M.editor_atto.add_toolbar_button(params.elementid, 'unlink', params.icon, click);
+ M.editor_atto.add_toolbar_button(params.elementid, 'unlink', params.icon, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/unorderedlist/lib.php b/lib/editor/atto/plugins/unorderedlist/lib.php
index 6224f905bab..b70903eb262 100644
--- a/lib/editor/atto/plugins/unorderedlist/lib.php
+++ b/lib/editor/atto/plugins/unorderedlist/lib.php
@@ -29,21 +29,21 @@
function atto_unorderedlist_init_editor($elementid) {
global $PAGE, $OUTPUT;
- $icon = $OUTPUT->pix_icon('unorderedlist',
+ $icon = $OUTPUT->pix_icon('e/bullet_list',
get_string('unorderedlist', 'atto_unorderedlist'),
- 'atto_unorderedlist',
+ 'moodle',
array('class'=>'icon'));
$PAGE->requires->yui_module('moodle-atto_unorderedlist-button',
'M.atto_unorderedlist.init',
- array(array('elementid'=>$elementid, 'icon'=>$icon)),
+ array(array('elementid'=>$elementid, 'icon'=>$icon, 'group'=>'list')),
true);
}
/**
* Return the order this plugin should be displayed in the toolbar
- * @return int
+ * @return int the absolute position within the toolbar
*/
function atto_unorderedlist_sort_order() {
return 7;
diff --git a/lib/editor/atto/plugins/unorderedlist/pix/unorderedlist.png b/lib/editor/atto/plugins/unorderedlist/pix/unorderedlist.png
deleted file mode 100644
index 95b9d12d548..00000000000
Binary files a/lib/editor/atto/plugins/unorderedlist/pix/unorderedlist.png and /dev/null differ
diff --git a/lib/editor/atto/plugins/unorderedlist/pix/unorderedlist.svg b/lib/editor/atto/plugins/unorderedlist/pix/unorderedlist.svg
deleted file mode 100644
index 8cf123137e3..00000000000
--- a/lib/editor/atto/plugins/unorderedlist/pix/unorderedlist.svg
+++ /dev/null
@@ -1,111 +0,0 @@
-
-
-
-
diff --git a/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-debug.js b/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-debug.js
index 4915b2e40d7..e430b83d2fc 100644
--- a/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-debug.js
+++ b/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-debug.js
@@ -32,7 +32,7 @@ M.atto_unorderedlist = M.atto_unorderedlist || {
document.execCommand('insertUnorderedList', false, null);
};
- M.editor_atto.add_toolbar_button(params.elementid, 'unorderedlist', params.icon, click);
+ M.editor_atto.add_toolbar_button(params.elementid, 'unorderedlist', params.icon, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-min.js b/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-min.js
index 5694eae1bf4..dc606fc7f52 100644
--- a/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-min.js
+++ b/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-min.js
@@ -1 +1 @@
-YUI.add("moodle-atto_unorderedlist-button",function(e,t){M.atto_unorderedlist=M.atto_unorderedlist||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("insertUnorderedList",!1,null)};M.editor_atto.add_toolbar_button(e.elementid,"unorderedlist",e.icon,t)}}},"@VERSION@",{requires:["node"]});
+YUI.add("moodle-atto_unorderedlist-button",function(e,t){M.atto_unorderedlist=M.atto_unorderedlist||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("insertUnorderedList",!1,null)};M.editor_atto.add_toolbar_button(e.elementid,"unorderedlist",e.icon,e.group,t)}}},"@VERSION@",{requires:["node"]});
diff --git a/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button.js b/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button.js
index 4915b2e40d7..e430b83d2fc 100644
--- a/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button.js
+++ b/lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button.js
@@ -32,7 +32,7 @@ M.atto_unorderedlist = M.atto_unorderedlist || {
document.execCommand('insertUnorderedList', false, null);
};
- M.editor_atto.add_toolbar_button(params.elementid, 'unorderedlist', params.icon, click);
+ M.editor_atto.add_toolbar_button(params.elementid, 'unorderedlist', params.icon, params.group, click);
}
};
diff --git a/lib/editor/atto/plugins/unorderedlist/yui/src/button/js/button.js b/lib/editor/atto/plugins/unorderedlist/yui/src/button/js/button.js
index e86e3e9f84f..6e1d30cf283 100644
--- a/lib/editor/atto/plugins/unorderedlist/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/unorderedlist/yui/src/button/js/button.js
@@ -30,6 +30,6 @@ M.atto_unorderedlist = M.atto_unorderedlist || {
document.execCommand('insertUnorderedList', false, null);
};
- M.editor_atto.add_toolbar_button(params.elementid, 'unorderedlist', params.icon, click);
+ M.editor_atto.add_toolbar_button(params.elementid, 'unorderedlist', params.icon, params.group, click);
}
};
diff --git a/lib/editor/atto/styles.css b/lib/editor/atto/styles.css
index e794e853fe6..6511ee9f515 100644
--- a/lib/editor/atto/styles.css
+++ b/lib/editor/atto/styles.css
@@ -1,30 +1,40 @@
div.editor_atto {
background-color: white;
border: 1px solid #BBB;
+ width: 100%;
}
.editor_atto + textarea {
width: 100%;
padding: 0;
+ resize: vertical;
+ border-radius: 0;
+ border: 1px solid #BBB;
}
div.editor_atto_toolbar {
display: block;
background: #F2F2F2;
- min-height: 36px;
+ min-height: 42px;
border-top: 1px solid #BBB;
border-left: 1px solid #BBB;
border-right: 1px solid #BBB;
+ width: 100%;
+ padding: 0 0 9px 0;
}
div.editor_atto_toolbar button {
- margin-top: 5px;
- margin-bottom: 0px;
- padding: 3px;
+ padding: 5px 9px 4px 9px;
background: none;
- border: none;
+ border: 0px;
+ margin: 0;
+ border-radius: 0;
}
+div.editor_atto_toolbar button + button {
+ border-left: 1px solid #CCC;
+}
+
div.editor_atto_toolbar button[disabled] {
opacity: .45;
}
@@ -33,30 +43,20 @@ div.editor_atto_toolbar button img {
padding: 1px;
}
-div.editor_atto_toolbar button.atto_strike_button,
-div.editor_atto_toolbar button.atto_unorderedlist_button,
-div.editor_atto_toolbar button.atto_clear_button,
-div.editor_atto_toolbar button.atto_indent_button {
- margin-right: 1em;
-}
-body.dir-rtl div.editor_atto_toolbar button.atto_strike_button,
-body.dir-rtl div.editor_atto_toolbar button.atto_unorderedlist_button,
-body.dir-rtl div.editor_atto_toolbar button.atto_clear_button,
-body.dir-rtl div.editor_atto_toolbar button.atto_indent_button {
- margin-right: 0px;
- margin-left: 1em;
-}
-.atto_hasmenu .icon {
- width: 32px;
+div.editor_atto_toolbar div.atto_group {
+ display: inline-block;
+ border: 1px solid #CCC;
+ border-bottom: 1px solid #B3B3B3;
+ border-radius: 4px;
+ margin: 9px 0px 0px 9px;
+ background: #FFF;
}
.editor_atto img {
resize: both; overflow: auto;
}
-.atto_menuentry {
- clear: left;
-}
+
.atto_menuentry img {
width: 16px;
height: 16px;
@@ -74,6 +74,7 @@ body.dir-rtl div.editor_atto_toolbar button.atto_indent_button {
.atto_menuentry {
border-bottom: 1px solid #eee;
padding: 2px;
+ clear: left;
}
.atto_menuentry h1,
@@ -88,3 +89,16 @@ body.dir-rtl div.editor_atto_toolbar button.atto_indent_button {
margin: 5px;
}
+/* RTL Rules */
+body.dir-rtl div.editor_atto_toolbar button + button {
+ border-left: 0;
+ border-right: 1px solid #CCC;
+}
+
+body.dir-rtl div.editor_atto_toolbar img.icon {
+ padding: 0;
+}
+
+body.dir-rtl div.editor_atto_toolbar div.atto_group {
+ margin: 9px 9px 0px 0px;
+}
diff --git a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js
index 495df236b09..96ccd495ef5 100644
--- a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js
+++ b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js
@@ -154,17 +154,33 @@ M.editor_atto = M.editor_atto || {
* @param string elementid - the id of the textarea we created this editor from.
* @param string plugin - the plugin defining the button
* @param string icon - the html used for the content of the button
+ * @param string groupname - the group the button should be appended to.
* @handler function handler- A function to call when the button is clicked.
*/
- add_toolbar_menu : function(elementid, plugin, icon, entries) {
- var toolbar = Y.one('#' + elementid + '_toolbar');
- var button = Y.Node.create('