Merge branch '23178-29' of git://github.com/samhemelryk/moodle
authorEloy Lafuente (stronk7) <stronk7@moodle.org>
Tue, 2 Dec 2014 23:06:46 +0000 (00:06 +0100)
committerDan Poltawski <dan@moodle.com>
Thu, 4 Dec 2014 12:14:28 +0000 (12:14 +0000)
228 files changed:
admin/mnet/peer_forms.php
admin/mnet/peers.php
admin/mnet/testclient.php
admin/roles/module.js
admin/settings/appearance.php
admin/settings/security.php
admin/settings/server.php
admin/tool/assignmentupgrade/module.js
admin/tool/log/store/legacy/classes/log/store.php
admin/tool/log/store/standard/classes/log/store.php
admin/tool/monitor/tests/eventobservers_test.php
admin/tool/spamcleaner/module.js
admin/tool/xmldb/styles_bootstrapbase.css [new file with mode: 0644]
auth/email/auth.php
auth/ldap/auth.php
auth/upgrade.txt
availability/condition/completion/yui/build/moodle-availability_completion-form/moodle-availability_completion-form-debug.js
availability/condition/completion/yui/build/moodle-availability_completion-form/moodle-availability_completion-form-min.js
availability/condition/completion/yui/build/moodle-availability_completion-form/moodle-availability_completion-form.js
availability/condition/completion/yui/src/form/js/form.js
availability/condition/date/yui/build/moodle-availability_date-form/moodle-availability_date-form-debug.js
availability/condition/date/yui/build/moodle-availability_date-form/moodle-availability_date-form-min.js
availability/condition/date/yui/build/moodle-availability_date-form/moodle-availability_date-form.js
availability/condition/date/yui/src/form/js/form.js
availability/condition/grade/yui/build/moodle-availability_grade-form/moodle-availability_grade-form-debug.js
availability/condition/grade/yui/build/moodle-availability_grade-form/moodle-availability_grade-form-min.js
availability/condition/grade/yui/build/moodle-availability_grade-form/moodle-availability_grade-form.js
availability/condition/grade/yui/src/form/js/form.js
availability/condition/group/yui/build/moodle-availability_group-form/moodle-availability_group-form-debug.js
availability/condition/group/yui/build/moodle-availability_group-form/moodle-availability_group-form-min.js
availability/condition/group/yui/build/moodle-availability_group-form/moodle-availability_group-form.js
availability/condition/group/yui/src/form/js/form.js
availability/condition/grouping/yui/build/moodle-availability_grouping-form/moodle-availability_grouping-form-debug.js
availability/condition/grouping/yui/build/moodle-availability_grouping-form/moodle-availability_grouping-form-min.js
availability/condition/grouping/yui/build/moodle-availability_grouping-form/moodle-availability_grouping-form.js
availability/condition/grouping/yui/src/form/js/form.js
availability/condition/profile/yui/build/moodle-availability_profile-form/moodle-availability_profile-form-debug.js
availability/condition/profile/yui/build/moodle-availability_profile-form/moodle-availability_profile-form-min.js
availability/condition/profile/yui/build/moodle-availability_profile-form/moodle-availability_profile-form.js
availability/condition/profile/yui/src/form/js/form.js
availability/tests/behat/edit_availability.feature
availability/yui/build/moodle-core_availability-form/moodle-core_availability-form-debug.js
availability/yui/build/moodle-core_availability-form/moodle-core_availability-form-min.js
availability/yui/build/moodle-core_availability-form/moodle-core_availability-form.js
availability/yui/src/form/js/form.js
backup/moodle2/restore_stepslib.php
backup/util/helper/backup_cron_helper.class.php
backup/util/helper/backup_helper.class.php
blocks/comments/tests/behat/add_comment.feature
blocks/comments/tests/behat/behat_block_comments.php
blocks/completionstatus/block_completionstatus.php
blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-debug.js
blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-min.js
blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation.js
blocks/navigation/yui/src/navigation/js/navigation.js
blocks/tests/behat/configure_block_throughout_site.feature
cache/forms.php
cache/renderer.php
calendar/externallib.php
calendar/upgrade.txt
comment/lib.php
comment/locallib.php
completion/tests/behat/behat_completion.php
completion/tests/behat/teacher_manual_completion.feature [new file with mode: 0644]
course/completion.js
course/format/lib.php
course/format/topics/lib.php
course/format/weeks/lib.php
course/moodleform_mod.php
course/togglecompletion.php
enrol/manual/yui/quickenrolment/quickenrolment.js
enrol/meta/addinstance_form.php
enrol/meta/lang/en/enrol_meta.php
enrol/meta/settings.php
enrol/meta/version.php
enrol/yui/otherusersmanager/otherusersmanager.js
enrol/yui/rolemanager/rolemanager.js
grade/grading/form/guide/js/guideeditor.js
grade/grading/form/rubric/js/rubriceditor.js
grade/report/grader/module.js
group/index.php
lang/en/access.php
lang/en/admin.php
lang/en/auth.php
lang/en/availability.php
lang/en/backup.php
lang/en/badges.php
lang/en/block.php
lang/en/blog.php
lang/en/bulkusers.php
lang/en/cache.php
lang/en/calendar.php
lang/en/cohort.php
lang/en/completion.php
lang/en/countries.php
lang/en/currencies.php
lang/en/dbtransfer.php
lang/en/debug.php
lang/en/deprecated.txt
lang/en/editor.php
lang/en/edufields.php
lang/en/enrol.php
lang/en/error.php
lang/en/filters.php
lang/en/form.php
lang/en/grades.php
lang/en/grading.php
lang/en/group.php
lang/en/hub.php
lang/en/imscc.php
lang/en/install.php
lang/en/iso6392.php
lang/en/langconfig.php
lang/en/license.php
lang/en/mathslib.php
lang/en/message.php
lang/en/mimetypes.php
lang/en/mnet.php
lang/en/moodle.php
lang/en/my.php
lang/en/notes.php
lang/en/pagetype.php
lang/en/pix.php
lang/en/plagiarism.php
lang/en/portfolio.php
lang/en/question.php
lang/en/rating.php
lang/en/repository.php
lang/en/role.php
lang/en/search.php
lang/en/table.php
lang/en/tag.php
lang/en/timezones.php
lang/en/userkey.php
lang/en/webservice.php
lib/blocklib.php
lib/classes/task/file_temp_cleanup_task.php
lib/db/install.xml
lib/db/upgrade.php
lib/deprecatedlib.php
lib/editor/atto/upgrade.txt [new file with mode: 0644]
lib/editor/atto/yui/build/moodle-editor_atto-plugin/moodle-editor_atto-plugin-debug.js
lib/editor/atto/yui/build/moodle-editor_atto-plugin/moodle-editor_atto-plugin-min.js
lib/editor/atto/yui/build/moodle-editor_atto-plugin/moodle-editor_atto-plugin.js
lib/editor/atto/yui/src/editor/js/editor-plugin-buttons.js
lib/editor/tinymce/plugins/managefiles/module.js
lib/form/dndupload.js
lib/form/filemanager.js
lib/form/yui/build/moodle-form-showadvanced/moodle-form-showadvanced-debug.js
lib/form/yui/build/moodle-form-showadvanced/moodle-form-showadvanced-min.js
lib/form/yui/build/moodle-form-showadvanced/moodle-form-showadvanced.js
lib/form/yui/src/showadvanced/js/showadvanced.js
lib/moodlelib.php
lib/testing/generator/data_generator.php
lib/testing/tests/generator_test.php
lib/tests/behat/behat_general.php
lib/tests/blocklib_test.php
lib/thirdpartylibs.xml
lib/yui/build/moodle-core-dock/moodle-core-dock-debug.js
lib/yui/build/moodle-core-dock/moodle-core-dock-min.js
lib/yui/build/moodle-core-dock/moodle-core-dock.js
lib/yui/build/moodle-core-maintenancemodetimer/moodle-core-maintenancemodetimer-debug.js
lib/yui/build/moodle-core-maintenancemodetimer/moodle-core-maintenancemodetimer-min.js
lib/yui/build/moodle-core-maintenancemodetimer/moodle-core-maintenancemodetimer.js
lib/yui/src/dock/js/block.js
lib/yui/src/dock/js/dock.js
lib/yui/src/dock/js/dockeditem.js
lib/yui/src/maintenancemodetimer/js/maintenancemodetimer.js
login/change_password.php
login/change_password_form.php
login/lib.php
login/set_password_form.php
mnet/peer.php
mnet/xmlrpc/client.php
mod/chat/gui_ajax/module.js
mod/feedback/backup/moodle1/lib.php
mod/forum/lang/en/forum.php
mod/forum/lib.php
mod/forum/subscribe.php
mod/forum/tests/behat/discussion_subscriptions.feature
mod/forum/tests/mail_test.php
mod/imscp/backup/moodle1/lib.php
mod/imscp/backup/moodle2/backup_imscp_activity_task.class.php
mod/imscp/backup/moodle2/backup_imscp_stepslib.php
mod/imscp/backup/moodle2/restore_imscp_activity_task.class.php
mod/imscp/backup/moodle2/restore_imscp_stepslib.php
mod/imscp/db/install.php
mod/imscp/db/log.php
mod/imscp/db/upgrade.php
mod/imscp/index.php
mod/imscp/lang/en/imscp.php
mod/imscp/lib.php
mod/imscp/locallib.php
mod/imscp/mod_form.php
mod/imscp/module.js
mod/imscp/settings.php
mod/imscp/styles.css
mod/imscp/tests/generator_test.php
mod/imscp/version.php
mod/imscp/view.php
mod/lesson/format.php
mod/lti/mod_form.js
mod/quiz/lang/en/quiz.php
mod/quiz/module.js
mod/quiz/tests/behat/behat_mod_quiz.php
mod/quiz/yui/build/moodle-mod_quiz-dragdrop/moodle-mod_quiz-dragdrop-debug.js
mod/quiz/yui/build/moodle-mod_quiz-dragdrop/moodle-mod_quiz-dragdrop-min.js
mod/quiz/yui/build/moodle-mod_quiz-dragdrop/moodle-mod_quiz-dragdrop.js
mod/quiz/yui/src/dragdrop/js/resource.js
mod/scorm/module.js
mod/scorm/view.js
mod/survey/survey.js
question/format.php
question/format/gift/tests/behat/import_export.feature
question/format/xml/tests/behat/import_export.feature
report/completion/index.php
report/completion/lang/en/report_completion.php
report/log/classes/table_log.php
repository/filepicker.js
tag/tag.js
theme/base/style/user.css
theme/bootstrapbase/less/moodle/user.less
theme/bootstrapbase/style/moodle.css
user/lib.php
user/selector/module.js
user/tests/userlib_test.php
version.php
webservice/renderer.php

index ce1c1c0..a9339b0 100644 (file)
@@ -94,6 +94,15 @@ class mnet_review_host_form extends moodleform {
         $mform->setType('wwwroot', PARAM_URL);
         $mform->addRule('wwwroot', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
 
+        $options = array(
+            mnet_peer::SSL_NONE => get_string('none'),
+            mnet_peer::SSL_HOST => get_string('verifyhostonly', 'core_mnet'),
+            mnet_peer::SSL_HOST_AND_PEER => get_string('verifyhostandpeer', 'core_mnet')
+        );
+        $mform->addElement('select', 'sslverification', get_string('sslverification', 'core_mnet'), $options);
+        $mform->setDefault('sslverification', mnet_peer::SSL_HOST_AND_PEER);
+        $mform->addHelpButton('sslverification', 'sslverification', 'core_mnet');
+
         $themes = array('' => get_string('forceno'));
         foreach (array_keys(core_component::get_plugin_list('theme')) as $themename) {
             $themes[$themename] = get_string('pluginname', 'theme_'.$themename);
index 40759db..24ef9b6 100644 (file)
@@ -172,6 +172,7 @@ if ($formdata = $reviewform->get_data()) {
     $mnet_peer->public_key          = $formdata->public_key;
     $credentials                    = $mnet_peer->check_credentials($mnet_peer->public_key);
     $mnet_peer->public_key_expires  = $credentials['validTo_time_t'];
+    $mnet_peer->sslverification     = $formdata->sslverification;
 
     if ($mnet_peer->commit()) {
         redirect(new moodle_url('/admin/mnet/peers.php', array('hostid' => $mnet_peer->id)), get_string('changessaved'));
index 89734e3..f43225c 100644 (file)
@@ -66,12 +66,19 @@ if (!empty($hostid) && array_key_exists($hostid, $hosts)) {
 
     $mnet_request->set_method('system/listServices');
     $mnet_request->send($mnet_peer);
+
     $services = $mnet_request->response;
     $yesno = array('No', 'Yes');
     $servicenames = array();
 
     echo $OUTPUT->heading(get_string('servicesavailableonhost', 'mnet', $host->wwwroot));
 
+    if (!empty($mnet_request->error)) {
+        echo $OUTPUT->heading(get_string('error'), 3);
+        echo html_writer::alist($mnet_request->error);
+        $services = array();
+    }
+
     $table = new html_table();
     $table->head = array(
         get_string('serviceid', 'mnet'),
@@ -127,6 +134,7 @@ if (!empty($hostid) && array_key_exists($hostid, $hosts)) {
     echo html_writer::table($table);
 
 
+    $mnet_request = new mnet_xmlrpc_client();
     $mnet_request->set_method('system/listMethods');
     if (isset($servicename) && array_key_exists($servicename, $serviceinfo)) {
         echo $OUTPUT->heading(get_string('methodsavailableonhostinservice', 'mnet', (object)array('host' => $host->wwwroot, 'service' => $servicename)));
@@ -139,6 +147,11 @@ if (!empty($hostid) && array_key_exists($hostid, $hosts)) {
     $mnet_request->send($mnet_peer);
     $methods = $mnet_request->response;
 
+    if (!empty($mnet_request->error)) {
+        echo $OUTPUT->heading(get_string('error'), 3);
+        echo html_writer::alist($mnet_request->error);
+        $methods = array();
+    }
 
     $table = new html_table();
     $table->head = array(
@@ -171,6 +184,12 @@ if (!empty($hostid) && array_key_exists($hostid, $hosts)) {
 
         echo $OUTPUT->heading(get_string('methodsignature', 'mnet', $method));
 
+        if (!empty($mnet_request->error)) {
+            echo $OUTPUT->heading(get_string('error'), 3);
+            echo html_writer::alist($mnet_request->error);
+            $signature = array();
+        }
+
         $table = new html_table();
         $table->head = array(
             get_string('position', 'mnet'),
index 0cac01a..9c331f1 100644 (file)
@@ -53,9 +53,9 @@ M.core_role.init_cap_table_filter = function(Y, tableid, contextid) {
             // Create the capability search input.
             this.input = Y.Node.create('<input type="text" id="'+this.table.get('id')+'capabilitysearch" value="'+Y.Escape.html(filtervalue)+'" />');
             // Create a label for the search input.
-            this.label = Y.Node.create('<label for="'+this.input.get('id')+'">'+M.str.moodle.filter+' </label>');
+            this.label = Y.Node.create('<label for="'+this.input.get('id')+'">'+M.util.get_string('filter', 'moodle')+' </label>');
             // Create a clear button to clear the input.
-            this.button = Y.Node.create('<input type="button" value="'+M.str.moodle.clear+'" />').set('disabled', filtervalue=='');
+            this.button = Y.Node.create('<input type="button" value="'+M.util.get_string('clear', 'moodle')+'" />').set('disabled', filtervalue=='');
 
             // Tie it all together
             this.div.append(this.label).append(this.input).append(this.button);
index b904da2..062df28 100644 (file)
@@ -144,6 +144,7 @@ mybadges,badges|/badges/mybadges.php|award',
     $temp->add(new admin_setting_configtext('navcourselimit',new lang_string('navcourselimit','admin'),new lang_string('confignavcourselimit', 'admin'),20,PARAM_INT));
     $temp->add(new admin_setting_configcheckbox('usesitenameforsitepages', new lang_string('usesitenameforsitepages', 'admin'), new lang_string('configusesitenameforsitepages', 'admin'), 0));
     $temp->add(new admin_setting_configcheckbox('linkadmincategories', new lang_string('linkadmincategories', 'admin'), new lang_string('linkadmincategories_help', 'admin'), 0));
+    $temp->add(new admin_setting_configcheckbox('linkcoursesections', new lang_string('linkcoursesections', 'admin'), new lang_string('linkcoursesections_help', 'admin'), 0));
     $temp->add(new admin_setting_configcheckbox('navshowfrontpagemods', new lang_string('navshowfrontpagemods', 'admin'), new lang_string('navshowfrontpagemods_help', 'admin'), 1));
     $temp->add(new admin_setting_configcheckbox('navadduserpostslinks', new lang_string('navadduserpostslinks', 'admin'), new lang_string('navadduserpostslinks_help', 'admin'), 1));
 
index b455946..af1e6c8 100644 (file)
@@ -70,6 +70,11 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
     $temp->add(new admin_setting_configtext('minpasswordupper', new lang_string('minpasswordupper', 'admin'), new lang_string('configminpasswordupper', 'admin'), 1, PARAM_INT));
     $temp->add(new admin_setting_configtext('minpasswordnonalphanum', new lang_string('minpasswordnonalphanum', 'admin'), new lang_string('configminpasswordnonalphanum', 'admin'), 1, PARAM_INT));
     $temp->add(new admin_setting_configtext('maxconsecutiveidentchars', new lang_string('maxconsecutiveidentchars', 'admin'), new lang_string('configmaxconsecutiveidentchars', 'admin'), 0, PARAM_INT));
+
+    $temp->add(new admin_setting_configtext('passwordreuselimit',
+        new lang_string('passwordreuselimit', 'admin'),
+        new lang_string('passwordreuselimit_desc', 'admin'), 0, PARAM_INT));
+
     $pwresetoptions = array(
         300 => new lang_string('numminutes', '', 5),
         900 => new lang_string('numminutes', '', 15),
index 29a5966..4b3b11e 100644 (file)
@@ -153,6 +153,19 @@ $temp->add(new admin_setting_configselect('gradehistorylifetime', new lang_strin
                                                                                                      60 => new lang_string('numdays', '', 60),
                                                                                                      30 => new lang_string('numdays', '', 30))));
 
+$temp->add(new admin_setting_configselect('tempdatafoldercleanup', new lang_string('tempdatafoldercleanup', 'admin'),
+        new lang_string('configtempdatafoldercleanup', 'admin'), 168, array(
+            1 => new lang_string('numhours', '', 1),
+            3 => new lang_string('numhours', '', 3),
+            6 => new lang_string('numhours', '', 6),
+            9 => new lang_string('numhours', '', 9),
+            12 => new lang_string('numhours', '', 12),
+            18 => new lang_string('numhours', '', 18),
+            24 => new lang_string('numhours', '', 24),
+            48 => new lang_string('numdays', '', 2),
+            168 => new lang_string('numdays', '', 7),
+)));
+
 $ADMIN->add('server', $temp);
 
 
index 933408a..ab7f670 100644 (file)
@@ -55,7 +55,7 @@ M.tool_assignmentupgrade = {
             assignmentsinput = Y.one('input.selectedassignments');
             assignmentsinput.set('value', selectedassignments.join(','));
             if (selectedassignments.length == 0) {
-                alert(M.str.tool_assignmentupgrade.noassignmentsselected);
+                alert(M.util.get_string('noassignmentsselected', 'tool_assignmentupgrade'));
                 e.preventDefault();
             }
         });
index 48d561e..faabf71 100644 (file)
@@ -95,7 +95,7 @@ class store implements \tool_log\log\store, \core\log\sql_select_reader {
         $records = array();
 
         try {
-            $records = $DB->get_records_select('log', $selectwhere, $params, $sort, '*', $limitfrom, $limitnum);
+            $records = $DB->get_recordset_select('log', $selectwhere, $params, $sort, '*', $limitfrom, $limitnum);
         } catch (\moodle_exception $ex) {
             debugging("error converting legacy event data " . $ex->getMessage() . $ex->debuginfo, DEBUG_DEVELOPER);
         }
@@ -104,6 +104,8 @@ class store implements \tool_log\log\store, \core\log\sql_select_reader {
             $events[$data->id] = \logstore_legacy\event\legacy_logged::restore_legacy($data);
         }
 
+        $records->close();
+
         return $events;
     }
 
index 992d4cf..44c66de 100644 (file)
@@ -72,7 +72,7 @@ class store implements \tool_log\log\writer, \core\log\sql_internal_reader {
         $sort = self::tweak_sort_by_id($sort);
 
         $events = array();
-        $records = $DB->get_records_select('logstore_standard_log', $selectwhere, $params, $sort, '*', $limitfrom, $limitnum);
+        $records = $DB->get_recordset_select('logstore_standard_log', $selectwhere, $params, $sort, '*', $limitfrom, $limitnum);
 
         foreach ($records as $data) {
             $extra = array('origin' => $data->origin, 'ip' => $data->ip, 'realuserid' => $data->realuserid);
@@ -94,6 +94,8 @@ class store implements \tool_log\log\writer, \core\log\sql_internal_reader {
             }
         }
 
+        $records->close();
+
         return $events;
     }
 
index 73b684f..79356c6 100644 (file)
@@ -404,12 +404,11 @@ class tool_monitor_eventobservers_testcase extends advanced_testcase {
      * Run adhoc tasks.
      */
     protected function run_adhock_tasks() {
-        ob_start();
         while ($task = \core\task\manager::get_next_adhoc_task(time())) {
             $task->execute();
             \core\task\manager::adhoc_task_complete($task);
         }
-        ob_clean(); // Suppress mtrace debugging info.
+        $this->expectOutputRegex("/^Sending message to the user with id \d+ for the subscription with id \d+\.\.\..Sent./ms");
     }
 
     /**
index d7cd018..166d5cd 100644 (file)
@@ -6,7 +6,7 @@ M.tool_spamcleaner = {
     del_all: function() {
         var context = M.tool_spamcleaner;
 
-        var yes = confirm(M.str.tool_spamcleaner.spamdeleteallconfirm);
+        var yes = confirm(M.util.get_string('spamdeleteallconfirm', 'tool_spamcleaner'));
         if (yes) {
             var cfg = {
                 method: "POST",
@@ -15,7 +15,7 @@ M.tool_spamcleaner = {
                         try {
                             var resp = context.Y.JSON.parse(o.responseText);
                         } catch(e) {
-                            alert(M.str.tool_spamcleaner.spaminvalidresult);
+                            alert(M.util.get_string('spaminvalidresult', 'tool_spamcleaner'));
                             return;
                         }
                         if (resp == true) {
@@ -36,7 +36,7 @@ M.tool_spamcleaner = {
             return;
         }
 
-        var yes = confirm(M.str.tool_spamcleaner.spamdeleteconfirm);
+        var yes = confirm(M.util.get_string('spamdeleteconfirm', 'tool_spamcleaner'));
         if (yes) {
             context.row = obj;
             var cfg = {
@@ -46,7 +46,7 @@ M.tool_spamcleaner = {
                         try {
                             var resp = context.Y.JSON.parse(o.responseText);
                         } catch(e) {
-                            alert(M.str.tool_spamcleaner.spaminvalidresult);
+                            alert(M.util.get_string('spaminvalidresult', 'tool_spamcleaner'));
                             return;
                         }
                         if (context.row) {
@@ -57,7 +57,7 @@ M.tool_spamcleaner = {
                                 context.row.parentNode.removeChild(context.row);
                                 context.row = null;
                             } else {
-                                alert(M.str.tool_spamcleaner.spamcannotdelete);
+                                alert(M.util.get_string('spamcannotdelete', 'tool_spamcleaner'));
                             }
                         }
                     }
@@ -83,7 +83,7 @@ M.tool_spamcleaner = {
                     try {
                         var resp = context.Y.JSON.parse(o.responseText);
                     } catch(e) {
-                        alert(M.str.tool_spamcleaner.spaminvalidresult);
+                        alert(M.util.get_string('spaminvalidresult', 'tool_spamcleaner'));
                         return;
                     }
                     if (context.row) {
diff --git a/admin/tool/xmldb/styles_bootstrapbase.css b/admin/tool/xmldb/styles_bootstrapbase.css
new file mode 100644 (file)
index 0000000..ae530cb
--- /dev/null
@@ -0,0 +1,3 @@
+.path-admin-tool-xmldb a[name="lastused"] {
+    padding-top: 50px;
+}
index 8ab97ae..f244c45 100644 (file)
@@ -89,6 +89,7 @@ class auth_plugin_email extends auth_plugin_base {
         require_once($CFG->dirroot.'/user/profile/lib.php');
         require_once($CFG->dirroot.'/user/lib.php');
 
+        $plainpassword = $user->password;
         $user->password = hash_internal_user_password($user->password);
         if (empty($user->calendartype)) {
             $user->calendartype = $CFG->calendartype;
@@ -96,6 +97,8 @@ class auth_plugin_email extends auth_plugin_base {
 
         $user->id = user_create_user($user, false, false);
 
+        user_add_password_history($user->id, $plainpassword);
+
         // Save any custom profile field information.
         profile_save_data($user);
 
index f80ab19..03d483b 100644 (file)
@@ -539,6 +539,7 @@ class auth_plugin_ldap extends auth_plugin_base {
         global $CFG, $DB, $PAGE, $OUTPUT;
 
         require_once($CFG->dirroot.'/user/profile/lib.php');
+        require_once($CFG->dirroot.'/user/lib.php');
 
         if ($this->user_exists($user->username)) {
             print_error('auth_ldap_user_exists', 'auth_ldap');
@@ -553,6 +554,8 @@ class auth_plugin_ldap extends auth_plugin_base {
 
         $user->id = user_create_user($user, false, false);
 
+        user_add_password_history($user->id, $plainslashedpassword);
+
         // Save any custom profile field information
         profile_save_data($user);
 
index 0c6d453..a2acc9f 100644 (file)
@@ -5,6 +5,8 @@ information provided here is intended especially for developers.
 
 * Do not update user->firstaccess from any auth plugin, the complete_user_login() does it automatically.
 
+* Add user_add_password_history() to user_signup() method.
+
 === 2.8 ===
 
 * \core\session\manager::session_exists() now verifies the session is active
index 27350c4..803f565 100644 (file)
Binary files a/availability/condition/completion/yui/build/moodle-availability_completion-form/moodle-availability_completion-form-debug.js and b/availability/condition/completion/yui/build/moodle-availability_completion-form/moodle-availability_completion-form-debug.js differ
index eb98c96..17f1813 100644 (file)
Binary files a/availability/condition/completion/yui/build/moodle-availability_completion-form/moodle-availability_completion-form-min.js and b/availability/condition/completion/yui/build/moodle-availability_completion-form/moodle-availability_completion-form-min.js differ
index 27350c4..803f565 100644 (file)
Binary files a/availability/condition/completion/yui/build/moodle-availability_completion-form/moodle-availability_completion-form.js and b/availability/condition/completion/yui/build/moodle-availability_completion-form/moodle-availability_completion-form.js differ
index 926a9e4..1416260 100644 (file)
@@ -23,22 +23,21 @@ M.availability_completion.form.initInner = function(cms) {
 
 M.availability_completion.form.getNode = function(json) {
     // Create HTML structure.
-    var strings = M.str.availability_completion;
-    var html = strings.title + ' <span class="availability-group"><label>' +
-            '<span class="accesshide">' + strings.label_cm + ' </span>' +
-            '<select name="cm" title="' + strings.label_cm + '">' +
-            '<option value="0">' + M.str.moodle.choosedots + '</option>';
+    var html = M.util.get_string('title', 'availability_completion') + ' <span class="availability-group"><label>' +
+            '<span class="accesshide">' + M.util.get_string('label_cm', 'availability_completion') + ' </span>' +
+            '<select name="cm" title="' + M.util.get_string('label_cm', 'availability_completion') + '">' +
+            '<option value="0">' + M.util.get_string('choosedots', 'moodle') + '</option>';
     for (var i = 0; i < this.cms.length; i++) {
         var cm = this.cms[i];
         // String has already been escaped using format_string.
         html += '<option value="' + cm.id + '">' + cm.name + '</option>';
     }
-    html += '</select></label> <label><span class="accesshide">' + strings.label_completion +
-            ' </span><select name="e" title="' + strings.label_completion + '">' +
-            '<option value="1">' + strings.option_complete + '</option>' +
-            '<option value="0">' + strings.option_incomplete + '</option>' +
-            '<option value="2">' + strings.option_pass + '</option>' +
-            '<option value="3">' + strings.option_fail + '</option>' +
+    html += '</select></label> <label><span class="accesshide">' + M.util.get_string('label_completion', 'availability_completion') +
+            ' </span><select name="e" title="' + M.util.get_string('label_completion', 'availability_completion') + '">' +
+            '<option value="1">' + M.util.get_string('option_complete', 'availability_completion') + '</option>' +
+            '<option value="0">' + M.util.get_string('option_incomplete', 'availability_completion') + '</option>' +
+            '<option value="2">' + M.util.get_string('option_pass', 'availability_completion') + '</option>' +
+            '<option value="3">' + M.util.get_string('option_fail', 'availability_completion') + '</option>' +
             '</select></label></span>';
     var node = Y.Node.create('<span>' + html + '</span>');
 
index d84da8c..684d208 100644 (file)
Binary files a/availability/condition/date/yui/build/moodle-availability_date-form/moodle-availability_date-form-debug.js and b/availability/condition/date/yui/build/moodle-availability_date-form/moodle-availability_date-form-debug.js differ
index 7447c99..eeb12a3 100644 (file)
Binary files a/availability/condition/date/yui/build/moodle-availability_date-form/moodle-availability_date-form-min.js and b/availability/condition/date/yui/build/moodle-availability_date-form/moodle-availability_date-form-min.js differ
index d84da8c..684d208 100644 (file)
Binary files a/availability/condition/date/yui/build/moodle-availability_date-form/moodle-availability_date-form.js and b/availability/condition/date/yui/build/moodle-availability_date-form/moodle-availability_date-form.js differ
index 580c6eb..ca45ffc 100644 (file)
@@ -27,12 +27,11 @@ M.availability_date.form.initInner = function(html, defaultTime) {
 };
 
 M.availability_date.form.getNode = function(json) {
-    var strings = M.str.availability_date;
-    var html = strings.direction_before + ' <span class="availability-group">' +
-            '<label><span class="accesshide">' + strings.direction_label + ' </span>' +
+    var html = M.util.get_string('direction_before', 'availability_date') + ' <span class="availability-group">' +
+            '<label><span class="accesshide">' + M.util.get_string('direction_label', 'availability_date') + ' </span>' +
             '<select name="direction">' +
-            '<option value="&gt;=">' + strings.direction_from + '</option>' +
-            '<option value="&lt;">' + strings.direction_until + '</option>' +
+            '<option value="&gt;=">' + M.util.get_string('direction_from', 'availability_date') + '</option>' +
+            '<option value="&lt;">' + M.util.get_string('direction_until', 'availability_date') + '</option>' +
             '</select></label></span> ' + this.html;
     var node = Y.Node.create('<span>' + html + '</span>');
 
@@ -56,7 +55,7 @@ M.availability_date.form.getNode = function(json) {
                 }
             },
             failure : function() {
-                window.alert(M.str.availability_date.ajaxerror);
+                window.alert(M.util.get_string('ajaxerror', 'availability_date'));
             }
         }});
     } else {
@@ -130,7 +129,7 @@ M.availability_date.form.updateTime = function(node) {
             M.core_availability.form.update();
         },
         failure : function() {
-            window.alert(M.str.availability_date.ajaxerror);
+            window.alert(M.util.get_string('ajaxerror', 'availability_date'));
         }
     }});
 };
index 28f5f81..ad24176 100644 (file)
Binary files a/availability/condition/grade/yui/build/moodle-availability_grade-form/moodle-availability_grade-form-debug.js and b/availability/condition/grade/yui/build/moodle-availability_grade-form/moodle-availability_grade-form-debug.js differ
index 83c5e5a..57654e4 100644 (file)
Binary files a/availability/condition/grade/yui/build/moodle-availability_grade-form/moodle-availability_grade-form-min.js and b/availability/condition/grade/yui/build/moodle-availability_grade-form/moodle-availability_grade-form-min.js differ
index 28f5f81..ad24176 100644 (file)
Binary files a/availability/condition/grade/yui/build/moodle-availability_grade-form/moodle-availability_grade-form.js and b/availability/condition/grade/yui/build/moodle-availability_grade-form/moodle-availability_grade-form.js differ
index e0b0fe9..92108aa 100644 (file)
@@ -35,24 +35,23 @@ M.availability_grade.form.getNode = function(json) {
     this.nodesSoFar++;
 
     // Create HTML structure.
-    var strings = M.str.availability_grade;
-    var html = '<label>' + strings.title + ' <span class="availability-group">' +
-            '<select name="id"><option value="0">' + M.str.moodle.choosedots + '</option>';
+    var html = '<label>' + M.util.get_string('title', 'availability_grade') + ' <span class="availability-group">' +
+            '<select name="id"><option value="0">' + M.util.get_string('choosedots', 'moodle') + '</option>';
     for (var i = 0; i < this.grades.length; i++) {
         var grade = this.grades[i];
         // String has already been escaped using format_string.
         html += '<option value="' + grade.id + '">' + grade.name + '</option>';
     }
     html += '</select></span></label> <span class="availability-group">' +
-            '<label><input type="checkbox" name="min"/>' + strings.option_min +
-            '</label> <label><span class="accesshide">' + strings.label_min +
+            '<label><input type="checkbox" name="min"/>' + M.util.get_string('option_min', 'availability_grade') +
+            '</label> <label><span class="accesshide">' + M.util.get_string('label_min', 'availability_grade') +
             '</span><input type="text" name="minval" title="' +
-            strings.label_min + '"/></label>%</span>' +
+            M.util.get_string('label_min', 'availability_grade') + '"/></label>%</span>' +
             '<span class="availability-group">' +
-            '<label><input type="checkbox" name="max"/>' + strings.option_max +
-            '</label> <label><span class="accesshide">' + strings.label_max +
+            '<label><input type="checkbox" name="max"/>' + M.util.get_string('option_max', 'availability_grade') +
+            '</label> <label><span class="accesshide">' + M.util.get_string('label_max', 'availability_grade') +
             '</span><input type="text" name="maxval" title="' +
-            strings.label_max + '"/></label>%</span>';
+            M.util.get_string('label_max', 'availability_grade') + '"/></label>%</span>';
     var node = Y.Node.create('<span>' + html + '</span>');
 
     // Set initial values.
index a968d4e..a1b8a65 100644 (file)
Binary files a/availability/condition/group/yui/build/moodle-availability_group-form/moodle-availability_group-form-debug.js and b/availability/condition/group/yui/build/moodle-availability_group-form/moodle-availability_group-form-debug.js differ
index a5fe42f..3949118 100644 (file)
Binary files a/availability/condition/group/yui/build/moodle-availability_group-form/moodle-availability_group-form-min.js and b/availability/condition/group/yui/build/moodle-availability_group-form/moodle-availability_group-form-min.js differ
index a968d4e..a1b8a65 100644 (file)
Binary files a/availability/condition/group/yui/build/moodle-availability_group-form/moodle-availability_group-form.js and b/availability/condition/group/yui/build/moodle-availability_group-form/moodle-availability_group-form.js differ
index e68a703..f9ec44f 100644 (file)
@@ -31,11 +31,10 @@ M.availability_group.form.initInner = function(groups) {
 
 M.availability_group.form.getNode = function(json) {
     // Create HTML structure.
-    var strings = M.str.availability_group;
-    var html = '<label>' + strings.title + ' <span class="availability-group">' +
+    var html = '<label>' + M.util.get_string('title', 'availability_group') + ' <span class="availability-group">' +
             '<select name="id">' +
-            '<option value="choose">' + M.str.moodle.choosedots + '</option>' +
-            '<option value="any">' + strings.anygroup + '</option>';
+            '<option value="choose">' + M.util.get_string('choosedots', 'moodle') + '</option>' +
+            '<option value="any">' + M.util.get_string('anygroup', 'availability_group') + '</option>';
     for (var i = 0; i < this.groups.length; i++) {
         var group = this.groups[i];
         // String has already been escaped using format_string.
index 4c0a4ab..f3d616a 100644 (file)
Binary files a/availability/condition/grouping/yui/build/moodle-availability_grouping-form/moodle-availability_grouping-form-debug.js and b/availability/condition/grouping/yui/build/moodle-availability_grouping-form/moodle-availability_grouping-form-debug.js differ
index 305d8d5..34c9759 100644 (file)
Binary files a/availability/condition/grouping/yui/build/moodle-availability_grouping-form/moodle-availability_grouping-form-min.js and b/availability/condition/grouping/yui/build/moodle-availability_grouping-form/moodle-availability_grouping-form-min.js differ
index 4c0a4ab..f3d616a 100644 (file)
Binary files a/availability/condition/grouping/yui/build/moodle-availability_grouping-form/moodle-availability_grouping-form.js and b/availability/condition/grouping/yui/build/moodle-availability_grouping-form/moodle-availability_grouping-form.js differ
index dc09e83..b0fd7c9 100644 (file)
@@ -31,10 +31,9 @@ M.availability_grouping.form.initInner = function(groupings) {
 
 M.availability_grouping.form.getNode = function(json) {
     // Create HTML structure.
-    var strings = M.str.availability_grouping;
-    var html = '<label>' + strings.title + ' <span class="availability-group">' +
+    var html = '<label>' + M.util.get_string('title', 'availability_grouping') + ' <span class="availability-group">' +
             '<select name="id">' +
-            '<option value="choose">' + M.str.moodle.choosedots + '</option>';
+            '<option value="choose">' + M.util.get_string('choosedots', 'moodle') + '</option>';
     for (var i = 0; i < this.groupings.length; i++) {
         var grouping = this.groupings[i];
         // String has already been escaped using format_string.
index c4b906d..be46a7e 100644 (file)
Binary files a/availability/condition/profile/yui/build/moodle-availability_profile-form/moodle-availability_profile-form-debug.js and b/availability/condition/profile/yui/build/moodle-availability_profile-form/moodle-availability_profile-form-debug.js differ
index 6251004..63a87d3 100644 (file)
Binary files a/availability/condition/profile/yui/build/moodle-availability_profile-form/moodle-availability_profile-form-min.js and b/availability/condition/profile/yui/build/moodle-availability_profile-form/moodle-availability_profile-form-min.js differ
index c4b906d..be46a7e 100644 (file)
Binary files a/availability/condition/profile/yui/build/moodle-availability_profile-form/moodle-availability_profile-form.js and b/availability/condition/profile/yui/build/moodle-availability_profile-form/moodle-availability_profile-form.js differ
index aa01d6a..5336589 100644 (file)
@@ -33,10 +33,9 @@ M.availability_profile.form.initInner = function(standardFields, customFields) {
 
 M.availability_profile.form.getNode = function(json) {
     // Create HTML structure.
-    var strings = M.str.availability_profile;
-    var html = '<span class="availability-group"><label>' + strings.conditiontitle + ' ' +
+    var html = '<span class="availability-group"><label>' + M.util.get_string('conditiontitle', 'availability_profile') + ' ' +
             '<select name="field">' +
-            '<option value="choose">' + M.str.moodle.choosedots + '</option>';
+            '<option value="choose">' + M.util.get_string('choosedots', 'moodle') + '</option>';
     var fieldInfo;
     for (var i = 0; i < this.standardFields.length; i++) {
         fieldInfo = this.standardFields[i];
@@ -48,17 +47,17 @@ M.availability_profile.form.getNode = function(json) {
         // String has already been escaped using format_string.
         html += '<option value="cf_' + fieldInfo.field + '">' + fieldInfo.display + '</option>';
     }
-    html += '</select></label> <label><span class="accesshide">' + strings.label_operator +
-            ' </span><select name="op" title="' + strings.label_operator + '">';
+    html += '</select></label> <label><span class="accesshide">' + M.util.get_string('label_operator', 'availability_profile') +
+            ' </span><select name="op" title="' + M.util.get_string('label_operator', 'availability_profile') + '">';
     var operators = ['isequalto', 'contains', 'doesnotcontain', 'startswith', 'endswith',
             'isempty', 'isnotempty'];
     for (i = 0; i < operators.length; i++) {
         html += '<option value="' + operators[i] + '">' +
-                strings['op_' + operators[i]] + '</option>';
+                M.util.get_string('op_' + operators[i], 'availability_profile') + '</option>';
     }
-    html += '</select></label> <label><span class="accesshide">' + strings.label_value +
+    html += '</select></label> <label><span class="accesshide">' + M.util.get_string('label_value', 'availability_profile') +
             '</span><input name="value" type="text" style="width: 10em" title="' +
-            strings.label_value + '"/></label></span>';
+            M.util.get_string('label_value', 'availability_profile') + '"/></label></span>';
     var node = Y.Node.create('<span>' + html + '</span>');
 
     // Set initial values if specified.
index 3f2e3a2..82f3662 100644 (file)
@@ -172,3 +172,62 @@ Feature: edit_availability
     And I should not see "None" in the "Restrict access" "fieldset"
     And "Restriction type" "select" should be visible
     And I should see "Date" in the "Restrict access" "fieldset"
+
+  @javascript
+  Scenario: 'Add group/grouping access restriction' button unavailable
+    # Button does not exist when conditional access restrictions are turned off.
+    Given I log in as "admin"
+    And I follow "Course 1"
+    And I turn editing mode on
+    And I add a "Forum" to section "1"
+    When I expand all fieldsets
+    Then "Add group/grouping access restriction" "button" should not exist
+
+  @javascript
+  Scenario: Use the 'Add group/grouping access restriction' button
+    # Button should initially be disabled.
+    Given I log in as "admin"
+    And I set the following administration settings values:
+      | Enable conditional access | 1 |
+    And the following "groupings" exist:
+      | name | course | idnumber |
+      | GX1  | C1     | GXI1     |
+    And I am on homepage
+    And I follow "Course 1"
+    And I turn editing mode on
+    And I add a "Forum" to section "1"
+    And I set the following fields to these values:
+      | Forum name  | MyForum |
+      | Description | x       |
+    When I expand all fieldsets
+    Then the "Add group/grouping access restriction" "button" should be disabled
+
+    # Turn on separate groups.
+    And I set the field "Group mode" to "Separate groups"
+    And the "Add group/grouping access restriction" "button" should be enabled
+
+    # Press the button and check it adds a restriction and disables itself.
+    And I should see "None" in the "Restrict access" "fieldset"
+    And I press "Add group/grouping access restriction"
+    And I should see "Group" in the "Restrict access" "fieldset"
+    And the "Add group/grouping access restriction" "button" should be disabled
+
+    # Delete the restriction and check it is enabled again.
+    And I click on "Delete" "link" in the "Restrict access" "fieldset"
+    And the "Add group/grouping access restriction" "button" should be enabled
+
+    # Try a grouping instead.
+    And I set the field "Grouping" to "GX1"
+    And I press "Add group/grouping access restriction"
+    And I should see "Grouping" in the "Restrict access" "fieldset"
+
+    # Check the button still works after saving and editing.
+    And I press "Save and display"
+    And I navigate to "Edit settings" node in "Forum administration"
+    And I expand all fieldsets
+    And the "Add group/grouping access restriction" "button" should be disabled
+    And I should see "Grouping" in the "Restrict access" "fieldset"
+
+    # And check it's still active if I delete the condition.
+    And I click on "Delete" "link" in the "Restrict access" "fieldset"
+    And the "Add group/grouping access restriction" "button" should be enabled
index 185800c..61b4781 100644 (file)
Binary files a/availability/yui/build/moodle-core_availability-form/moodle-core_availability-form-debug.js and b/availability/yui/build/moodle-core_availability-form/moodle-core_availability-form-debug.js differ
index 9c578a0..92740e7 100644 (file)
Binary files a/availability/yui/build/moodle-core_availability-form/moodle-core_availability-form-min.js and b/availability/yui/build/moodle-core_availability-form/moodle-core_availability-form-min.js differ
index 185800c..61b4781 100644 (file)
Binary files a/availability/yui/build/moodle-core_availability-form/moodle-core_availability-form.js and b/availability/yui/build/moodle-core_availability-form/moodle-core_availability-form.js differ
index 099fe5e..c247441 100644 (file)
@@ -64,6 +64,14 @@ M.core_availability.form = {
      */
     idCounter : 0,
 
+    /**
+     * The 'Restrict by group' button if present.
+     *
+     * @property restrictByGroup
+     * @type Y.Node
+     */
+    restrictByGroup : null,
+
     /**
      * Called to initialise the system when the page loads. This method will
      * also call the init method for each plugin.
@@ -119,6 +127,22 @@ M.core_availability.form = {
         this.field.ancestor('form').on('submit', function() {
             this.mainDiv.all('input,textarea,select').set('disabled', true);
         }, this);
+
+        // If the form has group mode and/or grouping options, there is a
+        // 'add restriction' button there.
+        this.restrictByGroup = Y.one('#restrictbygroup');
+        if (this.restrictByGroup) {
+            this.restrictByGroup.on('click', this.addRestrictByGroup, this);
+            var groupmode = Y.one('#id_groupmode');
+            var groupingid = Y.one('#id_groupingid');
+            if (groupmode) {
+                groupmode.on('change', this.updateRestrictByGroup, this);
+            }
+            if (groupingid) {
+                groupingid.on('change', this.updateRestrictByGroup, this);
+            }
+            this.updateRestrictByGroup();
+        }
     },
 
     /**
@@ -141,6 +165,75 @@ M.core_availability.form = {
 
         // Set into hidden form field, JS-encoded.
         this.field.set('value', Y.JSON.stringify(jsValue));
+
+        // Also update the restrict by group button if present.
+        this.updateRestrictByGroup();
+    },
+
+    /**
+     * Updates the status of the 'restrict by group' button (enables or disables
+     * it) based on current availability restrictions and group/grouping settings.
+     */
+    updateRestrictByGroup : function() {
+        if (!this.restrictByGroup) {
+            return;
+        }
+
+        // If the root list is anything other than the default 'and' type, disable.
+        if (this.rootList.getValue().op !== '&') {
+            this.restrictByGroup.set('disabled', true);
+            return;
+        }
+
+        // If there's already a group restriction, disable it.
+        var alreadyGot = this.rootList.hasItemOfType('group') ||
+                this.rootList.hasItemOfType('grouping');
+        if (alreadyGot) {
+            this.restrictByGroup.set('disabled', true);
+            return;
+        }
+
+        // If the groupmode and grouping id aren't set, disable it.
+        var groupmode = Y.one('#id_groupmode');
+        var groupingid = Y.one('#id_groupingid');
+        if ((!groupmode || Number(groupmode.get('value')) === 0) &&
+                (!groupingid || Number(groupingid.get('value')) === 0)) {
+            this.restrictByGroup.set('disabled', true);
+            return;
+        }
+
+        this.restrictByGroup.set('disabled', false);
+    },
+
+    /**
+     * Called when the user clicks on the 'restrict by group' button. This is
+     * a special case that adds a group or grouping restriction.
+     *
+     * By default this restriction is not shown which makes it similar to the
+     *
+     * @param e Button click event
+     */
+    addRestrictByGroup : function(e) {
+        // If you don't prevent default, it submits the form for some reason.
+        e.preventDefault();
+
+        // Add the condition.
+        var groupingid = Y.one('#id_groupingid');
+        var newChild;
+        if (groupingid && Number(groupingid.get('value')) !== 0) {
+            // Add a grouping restriction if one is specified.
+            newChild = new M.core_availability.Item(
+                    {type : 'grouping', id : Number(groupingid.get('value'))}, true);
+        } else {
+            // Otherwise just add a group restriction.
+            newChild = new M.core_availability.Item({type : 'group'}, true);
+        }
+
+        // Refresh HTML.
+        this.rootList.addChild(newChild);
+        this.update();
+        this.rootList.renumber();
+        this.rootList.updateHtml();
     }
 };
 
@@ -258,24 +351,23 @@ M.core_availability.List = function(json, root, parentRoot) {
     if (root !== undefined) {
         this.root = root;
     }
-    var strings = M.str.availability;
     // Create DIV structure (without kids).
     this.node = Y.Node.create('<div class="availability-list"><h3 class="accesshide"></h3>' +
             '<div class="availability-inner">' +
-            '<div class="availability-header">' + strings.listheader_sign_before +
-            ' <label><span class="accesshide">' + strings.label_sign +
-            ' </span><select class="availability-neg" title="' + strings.label_sign + '">' +
-            '<option value="">' + strings.listheader_sign_pos + '</option>' +
-            '<option value="!">' + strings.listheader_sign_neg + '</option></select></label> ' +
-            '<span class="availability-single">' + strings.listheader_single + '</span>' +
-            '<span class="availability-multi">' + strings.listheader_multi_before +
-            ' <label><span class="accesshide">' + strings.label_multi + ' </span>' +
-            '<select class="availability-op" title="' + strings.label_multi + '"><option value="&">' +
-            strings.listheader_multi_and + '</option>' +
-            '<option value="|">' + strings.listheader_multi_or + '</option></select></label> ' +
-            strings.listheader_multi_after + '</span></div>' +
+            '<div class="availability-header">' + M.util.get_string('listheader_sign_before', 'availability') +
+            ' <label><span class="accesshide">' + M.util.get_string('label_sign', 'availability') +
+            ' </span><select class="availability-neg" title="' + M.util.get_string('label_sign', 'availability') + '">' +
+            '<option value="">' + M.util.get_string('listheader_sign_pos', 'availability') + '</option>' +
+            '<option value="!">' + M.util.get_string('listheader_sign_neg', 'availability') + '</option></select></label> ' +
+            '<span class="availability-single">' + M.util.get_string('listheader_single', 'availability') + '</span>' +
+            '<span class="availability-multi">' + M.util.get_string('listheader_multi_before', 'availability') +
+            ' <label><span class="accesshide">' + M.util.get_string('label_multi', 'availability') + ' </span>' +
+            '<select class="availability-op" title="' + M.util.get_string('label_multi', 'availability') + '"><option value="&">' +
+            M.util.get_string('listheader_multi_and', 'availability') + '</option>' +
+            '<option value="|">' + M.util.get_string('listheader_multi_or', 'availability') + '</option></select></label> ' +
+            M.util.get_string('listheader_multi_after', 'availability') + '</span></div>' +
             '<div class="availability-children"></div>' +
-            '<div class="availability-none">' + M.str.moodle.none + '</div>' +
+            '<div class="availability-none">' + M.util.get_string('none', 'moodle') + '</div>' +
             '<div class="availability-button"></div></div></div>');
     if (!root) {
         this.node.addClass('availability-childlist');
@@ -311,12 +403,12 @@ M.core_availability.List = function(json, root, parentRoot) {
 
         // Also if it's not the root, none is actually invalid, so add a label.
         noneNode.appendChild(Y.Node.create('<span class="label label-warning">' +
-                M.str.availability.invalid + '</span>'));
+                M.util.get_string('invalid', 'availability') + '</span>'));
     }
 
     // Create the button and add it.
     var button = Y.Node.create('<button type="button" class="btn btn-default">' +
-            M.str.availability.addrestriction + '</button>');
+            M.util.get_string('addrestriction', 'availability') + '</button>');
     button.on("click", function() { this.clickAdd(); }, this);
     this.node.one('div.availability-button').appendChild(button);
 
@@ -507,9 +599,9 @@ M.core_availability.List.prototype.updateHtml = function() {
     // Update connector text.
     var connectorText;
     if (this.inner.one('.availability-op').get('value') === '&') {
-        connectorText = M.str.availability.and;
+        connectorText = M.util.get_string('and', 'availability');
     } else {
-        connectorText = M.str.availability.or;
+        connectorText = M.util.get_string('or', 'availability');
     }
     this.inner.all('> .availability-children > .availability-connector span.label').each(function(span) {
         span.set('innerHTML', connectorText);
@@ -571,7 +663,7 @@ M.core_availability.List.prototype.clickAdd = function() {
     var content = Y.Node.create('<div>' +
             '<ul class="list-unstyled"></ul>' +
             '<div class="availability-buttons mdl-align">' +
-            '<button type="button" class="btn btn-default">' + M.str.moodle.cancel +
+            '<button type="button" class="btn btn-default">' + M.util.get_string('cancel', 'moodle') +
             '</button></div></div>');
     var cancel = content.one('button');
 
@@ -587,13 +679,12 @@ M.core_availability.List.prototype.clickAdd = function() {
         // Add entry for plugin.
         li = Y.Node.create('<li class="clearfix"></li>');
         id = 'availability_addrestriction_' + type;
-        var pluginStrings = M.str['availability_' + type];
         button = Y.Node.create('<button type="button" class="btn btn-default"' +
-                'id="' + id + '">' + pluginStrings.title + '</button>');
+                'id="' + id + '">' + M.util.get_string('title', 'availability_' + type) + '</button>');
         button.on('click', this.getAddHandler(type, dialogRef), this);
         li.appendChild(button);
         label = Y.Node.create('<label for="' + id + '">' +
-                pluginStrings.description + '</label>');
+                M.util.get_string('description', 'availability_' + type) + '</label>');
         li.appendChild(label);
         ul.appendChild(li);
     }
@@ -601,16 +692,16 @@ M.core_availability.List.prototype.clickAdd = function() {
     li = Y.Node.create('<li class="clearfix"></li>');
     id = 'availability_addrestriction_list_';
     button = Y.Node.create('<button type="button" class="btn btn-default"' +
-            'id="' + id + '">' + M.str.availability.condition_group + '</button>');
+            'id="' + id + '">' + M.util.get_string('condition_group', 'availability') + '</button>');
     button.on('click', this.getAddHandler(null, dialogRef), this);
     li.appendChild(button);
     label = Y.Node.create('<label for="' + id + '">' +
-            M.str.availability.condition_group_info + '</label>');
+            M.util.get_string('condition_group_info', 'availability') + '</label>');
     li.appendChild(label);
     ul.appendChild(li);
 
     var config = {
-        headerContent : M.str.availability.addrestriction,
+        headerContent : M.util.get_string('addrestriction', 'availability'),
         bodyContent : content,
         additionalBaseClass : 'availability-dialogue',
         draggable : true,
@@ -709,6 +800,31 @@ M.core_availability.List.prototype.fillErrors = function(errors) {
     }
 };
 
+/**
+ * Checks whether the list contains any items of the given type name.
+ *
+ * @method hasItemOfType
+ * @param {String} pluginType Required plugin type (name)
+ * @return {Boolean} True if there is one
+ */
+M.core_availability.List.prototype.hasItemOfType = function(pluginType) {
+    // Check each item.
+    for (var i = 0; i < this.children.length; i++) {
+        var child = this.children[i];
+        if (child instanceof M.core_availability.List) {
+            // Recursive call.
+            if (child.hasItemOfType(pluginType)) {
+                return true;
+            }
+        } else {
+            if (child.pluginType === pluginType) {
+                return true;
+            }
+        }
+    }
+    return false;
+};
+
 /**
  * Eye icon for this list (null if none).
  *
@@ -764,7 +880,7 @@ M.core_availability.Item = function(json, root) {
         // Handle undefined plugins.
         this.plugin = null;
         this.pluginNode = Y.Node.create('<div class="availability-warning">' +
-                M.str.availability.missingplugin + '</div>');
+                M.util.get_string('missingplugin', 'availability') + '</div>');
     } else {
         // Plugin is known.
         this.plugin = M.core_availability.form.plugins[json.type];
@@ -835,7 +951,7 @@ M.core_availability.Item.prototype.fillErrors = function(errors) {
     // If any errors were added, add the marker to this item.
     var errorLabel = this.node.one('> .label-warning');
     if (errors.length !== before && !errorLabel.get('firstChild')) {
-        errorLabel.appendChild(document.createTextNode(M.str.availability.invalid));
+        errorLabel.appendChild(document.createTextNode(M.util.get_string('invalid', 'availability')));
     } else if (errors.length === before && errorLabel.get('firstChild')) {
         errorLabel.get('firstChild').remove();
     }
@@ -851,7 +967,7 @@ M.core_availability.Item.prototype.renumber = function(number) {
     // Update heading for item.
     var headingParams = { number: number };
     if (this.plugin) {
-        headingParams.type = M.str['availability_' + this.pluginType].title;
+        headingParams.type = M.util.get_string('title', 'availability_' + this.pluginType);
     } else {
         headingParams.type = '[' + this.pluginType + ']';
     }
@@ -930,19 +1046,21 @@ M.core_availability.EyeIcon = function(individual, shown) {
     this.span.appendChild(icon);
 
     // Set up button text and icon.
-    var suffix = individual ? '_individual' : '_all';
-    var setHidden = function() {
-        icon.set('src', M.util.image_url('i/show', 'core'));
-        icon.set('alt', M.str.availability['hidden' + suffix]);
-        this.span.set('title', M.str.availability['hidden' + suffix] + ' \u2022 ' +
-                M.str.availability.show_verb);
-    };
-    var setShown = function() {
-        icon.set('src', M.util.image_url('i/hide', 'core'));
-        icon.set('alt', M.str.availability['shown' + suffix]);
-        this.span.set('title', M.str.availability['shown' + suffix] + ' \u2022 ' +
-                M.str.availability.hide_verb);
-    };
+    var suffix = individual ? '_individual' : '_all',
+        setHidden = function() {
+            var hiddenStr = M.util.get_string('hidden' + suffix, 'availability');
+            icon.set('src', M.util.image_url('i/show', 'core'));
+            icon.set('alt', hiddenStr);
+            this.span.set('title', hiddenStr + ' \u2022 ' +
+                    M.util.get_string('show_verb', 'availability'));
+        },
+        setShown = function() {
+            var shownStr = M.util.get_string('shown' + suffix, 'availability');
+            icon.set('src', M.util.image_url('i/hide', 'core'));
+            icon.set('alt', shownStr);
+            this.span.set('title', shownStr + ' \u2022 ' +
+                    M.util.get_string('hide_verb', 'availability'));
+        };
     if(shown) {
         setShown.call(this);
     } else {
@@ -987,8 +1105,8 @@ M.core_availability.EyeIcon.prototype.span = null;
  * @return {Boolean} True if this icon is set to 'hidden'
  */
 M.core_availability.EyeIcon.prototype.isHidden = function() {
-    var suffix = this.individual ? '_individual' : '_all';
-    var compare = M.str.availability['hidden' + suffix];
+    var suffix = this.individual ? '_individual' : '_all',
+        compare = M.util.get_string('hidden' + suffix, 'availability');
     return this.span.one('img').get('alt') === compare;
 };
 
@@ -1002,9 +1120,9 @@ M.core_availability.EyeIcon.prototype.isHidden = function() {
  */
 M.core_availability.DeleteIcon = function(toDelete) {
     this.span = Y.Node.create('<a class="availability-delete" href="#" title="' +
-            M.str.moodle['delete'] + '" role="button">');
+            M.util.get_string('delete', 'moodle') + '" role="button">');
     var img = Y.Node.create('<img src="' + M.util.image_url('t/delete', 'core') +
-            '" alt="' + M.str.moodle['delete'] + '" />');
+            '" alt="' + M.util.get_string('delete', 'moodle') + '" />');
     this.span.appendChild(img);
     var click = function(e) {
         e.preventDefault();
index ee4fa9d..7df7a2e 100644 (file)
@@ -3187,11 +3187,52 @@ class restore_block_instance_structure_step extends restore_structure_step {
         }
 
         if (!$bi->instance_allow_multiple()) {
-            if ($DB->record_exists_sql("SELECT bi.id
-                                          FROM {block_instances} bi
-                                          JOIN {block} b ON b.name = bi.blockname
-                                         WHERE bi.parentcontextid = ?
-                                           AND bi.blockname = ?", array($data->parentcontextid, $data->blockname))) {
+            // The block cannot be added twice, so we will check if the same block is already being
+            // displayed on the same page. For this, rather than mocking a page and using the block_manager
+            // we use a similar query to the one in block_manager::load_blocks(), this will give us
+            // a very good idea of the blocks already displayed in the context.
+            $params =  array(
+                'blockname' => $data->blockname
+            );
+
+            // Context matching test.
+            $context = context::instance_by_id($data->parentcontextid);
+            $contextsql = 'bi.parentcontextid = :contextid';
+            $params['contextid'] = $context->id;
+
+            $parentcontextids = $context->get_parent_context_ids();
+            if ($parentcontextids) {
+                list($parentcontextsql, $parentcontextparams) =
+                        $DB->get_in_or_equal($parentcontextids, SQL_PARAMS_NAMED);
+                $contextsql = "($contextsql OR (bi.showinsubcontexts = 1 AND bi.parentcontextid $parentcontextsql))";
+                $params = array_merge($params, $parentcontextparams);
+            }
+
+            // Page type pattern test.
+            $pagetypepatterns = matching_page_type_patterns_from_pattern($data->pagetypepattern);
+            list($pagetypepatternsql, $pagetypepatternparams) =
+                $DB->get_in_or_equal($pagetypepatterns, SQL_PARAMS_NAMED);
+            $params = array_merge($params, $pagetypepatternparams);
+
+            // Sub page pattern test.
+            $subpagepatternsql = 'bi.subpagepattern IS NULL';
+            if ($data->subpagepattern !== null) {
+                $subpagepatternsql = "($subpagepatternsql OR bi.subpagepattern = :subpagepattern)";
+                $params['subpagepattern'] = $data->subpagepattern;
+            }
+
+            $exists = $DB->record_exists_sql("SELECT bi.id
+                                                FROM {block_instances} bi
+                                                JOIN {block} b ON b.name = bi.blockname
+                                               WHERE bi.blockname = :blockname
+                                                 AND $contextsql
+                                                 AND bi.pagetypepattern $pagetypepatternsql
+                                                 AND $subpagepatternsql", $params);
+            if ($exists) {
+                // There is at least one very similar block visible on the page where we
+                // are trying to restore the block. In these circumstances the block API
+                // would not allow the user to add another instance of the block, so we
+                // apply the same rule here.
                 return false;
             }
         }
index 7f76cff..5adb973 100644 (file)
@@ -396,18 +396,36 @@ abstract class backup_cron_automated_helper {
             $results = $bc->get_results();
             $outcome = self::outcome_from_results($results);
             $file = $results['backup_destination']; // May be empty if file already moved to target location.
-            if (!file_exists($dir) || !is_dir($dir) || !is_writable($dir)) {
+
+            if (empty($dir) && $storage !== 0) {
+                // This is intentionally left as a warning instead of an error because of the current behaviour of backup settings.
+                // See MDL-48266 for details.
+                $bc->log('No directory specified for automated backups',
+                        backup::LOG_WARNING);
+                $outcome = self::BACKUP_STATUS_WARNING;
+            } else if (!file_exists($dir) || !is_dir($dir) || !is_writable($dir) && $storage !== 0) {
+                // If we need to copy the backup file to an external dir and it is not writable, change status to error.
+                $bc->log('Specified backup directory is not writable - ',
+                        backup::LOG_ERROR, $dir);
                 $dir = null;
+                $outcome = self::BACKUP_STATUS_ERROR;
             }
+
             // Copy file only if there was no error.
             if ($file && !empty($dir) && $storage !== 0 && $outcome != self::BACKUP_STATUS_ERROR) {
                 $filename = backup_plan_dbops::get_default_backup_filename($format, $type, $course->id, $users, $anonymised,
                         !$config->backup_shortname);
                 if (!$file->copy_content_to($dir.'/'.$filename)) {
+                    $bc->log('Attempt to copy backup file to the specified directory failed - ',
+                            backup::LOG_ERROR, $dir);
                     $outcome = self::BACKUP_STATUS_ERROR;
                 }
                 if ($outcome != self::BACKUP_STATUS_ERROR && $storage === 1) {
-                    $file->delete();
+                    if (!$file->delete()) {
+                        $outcome = self::BACKUP_STATUS_WARNING;
+                        $bc->log('Attempt to delete the backup file from course automated backup area failed - ',
+                                backup::LOG_WARNING, $file->get_filename());
+                    }
                 }
             }
 
index 963e471..4096371 100644 (file)
@@ -298,6 +298,10 @@ abstract class backup_helper {
                     @chmod($filedest, $CFG->filepermissions); // may fail because the permissions may not make sense outside of dataroot
                     unlink($filepath);
                     return null;
+                } else {
+                    $bc = backup_controller::load_controller($backupid);
+                    $bc->log('Attempt to copy backup file to the specified directory using filesystem failed - ',
+                            backup::LOG_WARNING, $dir);
                 }
                 // bad luck, try to deal with the file the old way - keep backup in file area if we can not copy to ext system
             }
index 1fd16c6..3619914 100644 (file)
@@ -33,3 +33,67 @@ Feature: Add a comment to the comments block
     When I follow "Show comments"
     And I add "I'm a comment from student1" comment to comments block
     Then I should see "I'm a comment from student1"
+
+  @javascript
+  Scenario: Test comment block pagination
+    When I add "Super test comment 01" comment to comments block
+    And I add "Super test comment 02" comment to comments block
+    And I add "Super test comment 03" comment to comments block
+    And I add "Super test comment 04" comment to comments block
+    And I add "Super test comment 05" comment to comments block
+    And I add "Super test comment 06" comment to comments block
+    And I add "Super test comment 07" comment to comments block
+    And I add "Super test comment 08" comment to comments block
+    And I add "Super test comment 09" comment to comments block
+    And I add "Super test comment 10" comment to comments block
+    And I add "Super test comment 11" comment to comments block
+    And I add "Super test comment 12" comment to comments block
+    And I add "Super test comment 13" comment to comments block
+    And I add "Super test comment 14" comment to comments block
+    And I add "Super test comment 15" comment to comments block
+    And I add "Super test comment 16" comment to comments block
+    And I add "Super test comment 17" comment to comments block
+    And I add "Super test comment 18" comment to comments block
+    And I add "Super test comment 19" comment to comments block
+    And I add "Super test comment 20" comment to comments block
+    And I add "Super test comment 21" comment to comments block
+    And I add "Super test comment 22" comment to comments block
+    And I add "Super test comment 23" comment to comments block
+    And I add "Super test comment 24" comment to comments block
+    And I add "Super test comment 25" comment to comments block
+    And I add "Super test comment 26" comment to comments block
+    And I add "Super test comment 27" comment to comments block
+    And I add "Super test comment 28" comment to comments block
+    And I add "Super test comment 29" comment to comments block
+    And I add "Super test comment 30" comment to comments block
+    And I add "Super test comment 31" comment to comments block
+    Then I should see "Super test comment 01"
+    And I should see "Super test comment 31"
+    And I follow "Course 1"
+    And I should not see "Super test comment 01"
+    And I should not see "Super test comment 02"
+    And I should not see "Super test comment 16"
+    And I should see "Super test comment 17"
+    And I should see "Super test comment 31"
+    And I should see "1" in the ".block_comments .comment-paging" "css_element"
+    And I should see "2" in the ".block_comments .comment-paging" "css_element"
+    And I should see "3" in the ".block_comments .comment-paging" "css_element"
+    And I should not see "4" in the ".block_comments .comment-paging" "css_element"
+    And I click on "2" "link" in the ".block_comments .comment-paging" "css_element"
+    And I should not see "Super test comment 01"
+    And I should see "Super test comment 02"
+    And I should see "Super test comment 16"
+    And I should not see "Super test comment 17"
+    And I should not see "Super test comment 31"
+    And I click on "3" "link" in the ".block_comments .comment-paging" "css_element"
+    And I should see "Super test comment 01"
+    And I should not see "Super test comment 02"
+    And I should not see "Super test comment 16"
+    And I should not see "Super test comment 17"
+    And I should not see "Super test comment 31"
+    And I click on "1" "link" in the ".block_comments .comment-paging" "css_element"
+    And I should not see "Super test comment 01"
+    And I should not see "Super test comment 02"
+    And I should not see "Super test comment 16"
+    And I should see "Super test comment 17"
+    And I should see "Super test comment 31"
index 094c668..491c342 100644 (file)
@@ -64,6 +64,9 @@ class behat_block_comments extends behat_base {
             $commentstextarea->setValue($comment);
 
             $this->find_link(get_string('savecomment'))->click();
+            // Delay after clicking so that additional comments will have unique time stamps.
+            // We delay 1 second which is all we need.
+            $this->getSession()->wait(1000, false);
 
         } else {
 
index c27067b..041bfe8 100644 (file)
@@ -57,6 +57,8 @@ class block_completionstatus extends block_base {
 
         // Create empty content.
         $this->content = new stdClass();
+        $this->content->text = '';
+        $this->content->footer = '';
 
         // Can edit settings?
         $can_edit = has_capability('moodle/course:update', $context);
@@ -67,13 +69,13 @@ class block_completionstatus extends block_base {
         // Don't display if completion isn't enabled!
         if (!completion_info::is_enabled_for_site()) {
             if ($can_edit) {
-                $this->content->text = get_string('completionnotenabledforsite', 'completion');
+                $this->content->text .= get_string('completionnotenabledforsite', 'completion');
             }
             return $this->content;
 
         } else if (!$info->is_enabled()) {
             if ($can_edit) {
-                $this->content->text = get_string('completionnotenabledforcourse', 'completion');
+                $this->content->text .= get_string('completionnotenabledforcourse', 'completion');
             }
             return $this->content;
         }
@@ -84,7 +86,7 @@ class block_completionstatus extends block_base {
         // Check if this course has any criteria.
         if (empty($completions)) {
             if ($can_edit) {
-                $this->content->text = get_string('nocriteriaset', 'completion');
+                $this->content->text .= get_string('nocriteriaset', 'completion');
             }
             return $this->content;
         }
@@ -230,11 +232,11 @@ class block_completionstatus extends block_base {
             $rows = array_merge($rows, $srows);
 
             $table->data = $rows;
-            $this->content->text = html_writer::table($table);
+            $this->content->text .= html_writer::table($table);
 
             // Display link to detailed view.
             $details = new moodle_url('/blocks/completionstatus/details.php', array('course' => $course->id));
-            $this->content->footer = html_writer::link($details, get_string('moredetails', 'completion'));
+            $this->content->footer .= html_writer::link($details, get_string('moredetails', 'completion'));
         } else {
             // If user is not enrolled, show error.
             $this->content->text = get_string('nottracked', 'completion');
index e02b874..b483ee2 100644 (file)
Binary files a/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-debug.js and b/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-debug.js differ
index 9431ffc..463c78c 100644 (file)
Binary files a/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-min.js and b/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-min.js differ
index 18bccb6..37b4dd9 100644 (file)
Binary files a/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation.js and b/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation.js differ
index 52f2f08..9544816 100644 (file)
@@ -748,8 +748,8 @@ BRANCH.prototype = {
             url = M.cfg.wwwroot+'/course/index.php?categoryid=' + branch.get('key');
         }
         branch.addChild({
-            name : M.str.moodle.viewallcourses,
-            title : M.str.moodle.viewallcourses,
+            name : M.util.get_string('viewallcourses', 'moodle'),
+            title : M.util.get_string('viewallcourses', 'moodle'),
             link : url,
             haschildren : false,
             icon : {'pix':"i/navigationitem",'component':'moodle'}
index b51143a..b71303c 100644 (file)
@@ -32,3 +32,27 @@ Feature: Add and configure blocks throughout the site
     And I follow "Course 1"
     # The first block matching the pattern should be top-left block
     And I should see "Comments" in the "//*[@id='region-pre' or @id='block-region-side-pre']/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' block ')]" "xpath_element"
+
+  Scenario: Blocks on the my home page cannot have roles assigned to them
+    Given the following "users" exist:
+      | username | firstname | lastname | email |
+      | manager1 | Manager | 1 | manager1@asd.com |
+    And I log in as "manager1"
+    And I click on "My home" "link" in the "Navigation" "block"
+    When I press "Customise this page"
+    Then I should not see "Assign roles in Navigation block"
+
+  Scenario: Blocks on courses can have roles assigned to them
+    Given the following "courses" exist:
+      | fullname | shortname | category |
+      | Course 1 | C1 | 0 |
+    And the following "users" exist:
+      | username | firstname | lastname | email               |
+      | teacher1 | teacher   | 1        | teacher@example.com |
+    And the following "course enrolments" exist:
+      | user     | course | role           |
+      | teacher1 | C1     | editingteacher |
+    And I log in as "teacher1"
+    And I follow "Course 1"
+    And I follow "Turn editing on"
+    Then I should see "Assign roles in Search forums block"
index 2abfabc..5a2ef3b 100644 (file)
@@ -65,13 +65,13 @@ class cachestore_addinstance_form extends moodleform {
         }
 
         if (is_array($locks)) {
-            $form->addElement('select', 'lock', get_string('lockmethod', 'cache'), $locks);
-            $form->addHelpButton('lock', 'lockmethod', 'cache');
+            $form->addElement('select', 'lock', get_string('locking', 'cache'), $locks);
+            $form->addHelpButton('lock', 'locking', 'cache');
             $form->setType('lock', PARAM_ALPHANUMEXT);
         } else {
             $form->addElement('hidden', 'lock', '');
             $form->setType('lock', PARAM_ALPHANUMEXT);
-            $form->addElement('static', 'lock-value', get_string('lockmethod', 'cache'),
+            $form->addElement('static', 'lock-value', get_string('locking', 'cache'),
                     '<em>'.get_string('nativelocking', 'cache').'</em>');
         }
 
index 1ccb4bb..f6ceb1d 100644 (file)
@@ -53,7 +53,7 @@ class core_cache_renderer extends plugin_renderer_base {
             get_string('mappings', 'cache'),
             get_string('modes', 'cache'),
             get_string('supports', 'cache'),
-            get_string('lockingmeans', 'cache'),
+            get_string('locking', 'cache') . ' ' . $this->output->help_icon('locking', 'cache'),
             get_string('actions', 'cache'),
         );
         $table->colclasses = array(
index ac5917e..902693d 100644 (file)
@@ -142,8 +142,8 @@ class core_calendar_external extends external_api {
                                              "Time from which events should be returned",
                                              VALUE_DEFAULT, 0, NULL_ALLOWED),
                                     'timeend' => new external_value(PARAM_INT,
-                                             "Time to which the events should be returned",
-                                             VALUE_DEFAULT, time(), NULL_ALLOWED),
+                                             "Time to which the events should be returned. We treat 0 and null as no end",
+                                             VALUE_DEFAULT, 0, NULL_ALLOWED),
                                     'ignorehidden' => new external_value(PARAM_BOOL,
                                              "Ignore hidden events or not",
                                              VALUE_DEFAULT, true, NULL_ALLOWED),
@@ -215,6 +215,11 @@ class core_calendar_external extends external_api {
             $funcparam['courses'][] = $SITE->id;
         }
 
+        // We treat 0 and null as no end.
+        if (empty($params['options']['timeend'])) {
+            $params['options']['timeend'] = PHP_INT_MAX;
+        }
+
         $eventlist = calendar_get_events($params['options']['timestart'], $params['options']['timeend'], $funcparam['users'], $funcparam['groups'],
                 $funcparam['courses'], true, $params['options']['ignorehidden']);
         // WS expects arrays.
index 72a666a..a5d31af 100644 (file)
@@ -1,6 +1,11 @@
 This files describes API changes in /calendar/* ,
 information provided here is intended especially for developers.
 
+=== 2.9 ===
+default values changes in code:
+* core_calendar_external::get_calendar_events_parameters() 'timeend' default option changed; now, by default,
+  all events are returned, not only the past ones.
+
 === 2.5 ===
 required changes in code:
 * calendar_add_icalendar_event() now requires a valid subscriptionid
index cea8c66..17e9736 100644 (file)
@@ -48,7 +48,14 @@ class comment {
     private $courseid;
     /** @var stdClass course module object, only be used to help find pluginname automatically */
     private $cm;
-    /** @var string The component that this comment is for. It is STRONGLY recommended to set this. */
+    /**
+     * The component that this comment is for.
+     *
+     * It is STRONGLY recommended to set this.
+     * Added as a database field in 2.9, old comments will have a null component.
+     *
+     * @var string
+     */
     private $component;
     /** @var string This is calculated by normalising the component */
     private $pluginname;
@@ -241,10 +248,11 @@ class comment {
         }
         // setup variables for non-js interface
         self::$nonjs = optional_param('nonjscomment', '', PARAM_ALPHANUM);
-        self::$comment_itemid  = optional_param('comment_itemid',  '', PARAM_INT);
+        self::$comment_itemid = optional_param('comment_itemid',  '', PARAM_INT);
+        self::$comment_component = optional_param('comment_component', '', PARAM_COMPONENT);
         self::$comment_context = optional_param('comment_context', '', PARAM_INT);
-        self::$comment_page    = optional_param('comment_page',    '', PARAM_INT);
-        self::$comment_area    = optional_param('comment_area',    '', PARAM_AREA);
+        self::$comment_page = optional_param('comment_page',    '', PARAM_INT);
+        self::$comment_area = optional_param('comment_area',    '', PARAM_AREA);
 
         $page->requires->strings_for_js(array(
                 'addcomment',
@@ -264,6 +272,7 @@ class comment {
      * invalidates permission checks.
      * A coding_error is now thrown if code attempts to change the component.
      *
+     * @throws coding_exception if you try to change the component after it has been set.
      * @param string $component
      */
     public function set_component($component) {
@@ -325,6 +334,7 @@ class comment {
             'nonjscomment'    => true,
             'comment_itemid'  => $this->itemid,
             'comment_context' => $this->context->id,
+            'comment_component' => $this->get_component(),
             'comment_area'    => $this->commentarea,
         ));
         $link->remove_params(array('comment_page'));
@@ -530,10 +540,19 @@ class comment {
         $perpage = (!empty($CFG->commentsperpage))?$CFG->commentsperpage:15;
         $start = $page * $perpage;
         $ufields = user_picture::fields('u');
+
+        list($componentwhere, $component) = $this->get_component_select_sql('c');
+        if ($component) {
+            $params['component'] = $component;
+        }
+
         $sql = "SELECT $ufields, c.id AS cid, c.content AS ccontent, c.format AS cformat, c.timecreated AS ctimecreated
                   FROM {comments} c
                   JOIN {user} u ON u.id = c.userid
-                 WHERE c.contextid = :contextid AND c.commentarea = :commentarea AND c.itemid = :itemid
+                 WHERE c.contextid = :contextid AND
+                       c.commentarea = :commentarea AND
+                       c.itemid = :itemid AND
+                       $componentwhere
               ORDER BY c.timecreated DESC";
         $params['contextid'] = $this->contextid;
         $params['commentarea'] = $this->commentarea;
@@ -573,6 +592,25 @@ class comment {
         return $comments;
     }
 
+    /**
+     * Returns an SQL fragment and param for selecting on component.
+     * @param string $alias
+     * @return array
+     */
+    protected function get_component_select_sql($alias = '') {
+        $component = $this->get_component();
+        if ($alias) {
+            $alias = $alias.'.';
+        }
+        if (empty($component)) {
+            $componentwhere = "{$alias}component IS NULL";
+            $component = null;
+        } else {
+            $componentwhere = "({$alias}component IS NULL OR {$alias}component = :component)";
+        }
+        return array($componentwhere, $component);
+    }
+
     /**
      * Returns the number of comments associated with the details of this object
      *
@@ -582,7 +620,18 @@ class comment {
     public function count() {
         global $DB;
         if ($this->totalcommentcount === null) {
-            $this->totalcommentcount = $DB->count_records('comments', array('itemid' => $this->itemid, 'commentarea' => $this->commentarea, 'contextid' => $this->context->id));
+            list($where, $component) = $this->get_component_select_sql();
+            $where .= ' AND itemid = :itemid AND commentarea = :commentarea AND contextid = :contextid';
+            $params = array(
+                'itemid' => $this->itemid,
+                'commentarea' => $this->commentarea,
+                'contextid' => $this->context->id,
+            );
+            if ($component) {
+                $params['component'] = $component;
+            }
+
+            $this->totalcommentcount = $DB->count_records_select('comments', $where, $params);
         }
         return $this->totalcommentcount;
     }
@@ -641,6 +690,7 @@ class comment {
         $newcmt->contextid    = $this->contextid;
         $newcmt->commentarea  = $this->commentarea;
         $newcmt->itemid       = $this->itemid;
+        $newcmt->component    = !empty($this->component) ? $this->component : null;
         $newcmt->content      = $content;
         $newcmt->format       = $format;
         $newcmt->userid       = $USER->id;
@@ -785,10 +835,11 @@ class comment {
             return '';
         }
 
-        $html = '';
         if (!(self::$comment_itemid == $this->itemid &&
             self::$comment_context == $this->context->id &&
-            self::$comment_area == $this->commentarea)) {
+            self::$comment_area == $this->commentarea &&
+            self::$comment_component == $this->component
+        )) {
             $page = 0;
         }
         $comments = $this->get_comments($page);
@@ -918,13 +969,24 @@ class comment {
     }
 
     /**
-     * Returns the component associated with the comment
+     * Returns the component associated with the comment.
+     *
      * @return string
      */
-    public function get_compontent() {
+    public function get_component() {
         return $this->component;
     }
 
+    /**
+     * Do not call! I am a deprecated method because of the typo in my name.
+     * @deprecated since 2.9
+     * @see comment::get_component()
+     * @return string
+     */
+    public function get_compontent() {
+        return $this->get_component();
+    }
+
     /**
      * Returns the context associated with the comment
      * @return stdClass
index 606d335..14a4965 100644 (file)
@@ -62,7 +62,7 @@ class comment_manager {
         $comments = array();
 
         $usernamefields = get_all_user_name_fields(true, 'u');
-        $sql = "SELECT c.id, c.contextid, c.itemid, c.commentarea, c.userid, c.content, $usernamefields, c.timecreated
+        $sql = "SELECT c.id, c.contextid, c.itemid, c.component, c.commentarea, c.userid, c.content, $usernamefields, c.timecreated
                   FROM {comments} c
                   JOIN {user} u
                        ON u.id=c.userid
index 89b6344..807c7fe 100644 (file)
@@ -105,4 +105,20 @@ class behat_completion extends behat_base {
         return $steps;
     }
 
+    /**
+     * Toggles completion tracking for course being in the course page.
+     *
+     * @When /^completion tracking is "(?P<completion_status_string>Enabled|Disabled)" in current course$/
+     * @param string $completionstatus The status, enabled or disabled.
+     */
+    public function completion_is_toggled_in_course($completionstatus) {
+
+        $toggle = strtolower($completionstatus) == 'enabled' ? get_string('yes') : get_string('no');
+
+        return array(
+            new Given('I follow "'.get_string('editsettings').'"'),
+            new Given('I set the field "'.get_string('enablecompletion', 'completion').'" to "'.$toggle.'"'),
+            new Given('I press "'.get_string('savechanges').'"')
+        );
+    }
 }
diff --git a/completion/tests/behat/teacher_manual_completion.feature b/completion/tests/behat/teacher_manual_completion.feature
new file mode 100644 (file)
index 0000000..d15691a
--- /dev/null
@@ -0,0 +1,45 @@
+@core @core_completion
+Feature: Allow teachers to manually mark users as complete when configured
+  In order for teachers to mark students as complete
+  As a teacher
+  I need to be able to use the completion report mark complete functionality
+
+  Scenario: Mark a student as complete using the completion report
+    Given the following "courses" exist:
+      | fullname          | shortname | category |
+      | Completion course | CC1       | 0        |
+    And the following "users" exist:
+      | username | firstname | lastname | email                |
+      | student1 | Student   | First    | student1@example.com |
+      | teacher1 | Teacher   | First    | teacher1@example.com |
+    And the following "course enrolments" exist:
+      | user     | course | role           |
+      | student1 | CC1    | student        |
+      | teacher1 | CC1    | editingteacher |
+    And I log in as "admin"
+    And I set the following administration settings values:
+      | Enable completion tracking | 1 |
+    And I am on homepage
+    And I follow "Completion course"
+    And completion tracking is "Enabled" in current course
+    And I follow "Course completion"
+    And I set the field "Teacher" to "1"
+    And I press "Save changes"
+    And I turn editing mode on
+    And I add the "Course completion status" block
+    And I log out
+    And I log in as "student1"
+    And I follow "Completion course"
+    And I should see "Status: Not yet started"
+    And I log out
+    When I log in as "teacher1"
+    And I follow "Completion course"
+    And I follow "View course report"
+    And I should see "Student First"
+    And I follow "Click to mark user complete"
+    And I trigger cron
+    And I am on homepage
+    And I log out
+    Then I log in as "student1"
+    And I follow "Completion course"
+    And I should see "Status: Complete"
index 4d9542a..eb548f3 100644 (file)
@@ -18,17 +18,19 @@ M.core_completion.init = function(Y) {
 
         } else {
             var current = args.state.get('value');
-            var modulename = args.modulename.get('value');
+            var modulename = args.modulename.get('value'),
+                altstr,
+                titlestr;
             if (current == 1) {
-                var altstr = M.str.completion['completion-alt-manual-y'].replace('{$a}', modulename);
-                var titlestr = M.str.completion['completion-title-manual-y'].replace('{$a}', modulename);
+                altstr = M.util.get_string('completion-alt-manual-y', 'completion', modulename);
+                titlestr = M.util.get_string('completion-title-manual-y', 'completion', modulename);
                 args.state.set('value', 0);
                 args.image.set('src', M.util.image_url('i/completion-manual-y', 'moodle'));
                 args.image.set('alt', altstr);
                 args.image.set('title', titlestr);
             } else {
-                var altstr = M.str.completion['completion-alt-manual-n'].replace('{$a}', modulename);
-                var titlestr = M.str.completion['completion-title-manual-n'].replace('{$a}', modulename);
+                altstr = M.util.get_string('completion-alt-manual-n', 'completion', modulename);
+                titlestr = M.util.get_string('completion-title-manual-n', 'completion', modulename);
                 args.state.set('value', 1);
                 args.image.set('src', M.util.image_url('i/completion-manual-n', 'moodle'));
                 args.image.set('alt', altstr);
index 33b9f55..eaeaff7 100644 (file)
@@ -395,6 +395,7 @@ abstract class format_base {
      * @return null|moodle_url
      */
     public function get_view_url($section, $options = array()) {
+        global $CFG;
         $course = $this->get_course();
         $url = new moodle_url('/course/view.php', array('id' => $course->id));
 
@@ -405,7 +406,7 @@ abstract class format_base {
         } else {
             $sectionno = $section;
         }
-        if (!empty($options['navigation']) && $sectionno !== null) {
+        if (empty($CFG->linkcoursesections) && !empty($options['navigation']) && $sectionno !== null) {
             // by default assume that sections are never displayed on separate pages
             return null;
         }
index f7e91bb..cb0a7ed 100644 (file)
@@ -75,6 +75,7 @@ class format_topics extends format_base {
      * @return null|moodle_url
      */
     public function get_view_url($section, $options = array()) {
+        global $CFG;
         $course = $this->get_course();
         $url = new moodle_url('/course/view.php', array('id' => $course->id));
 
@@ -101,7 +102,7 @@ class format_topics extends format_base {
             if ($sectionno != 0 && $usercoursedisplay == COURSE_DISPLAY_MULTIPAGE) {
                 $url->param('section', $sectionno);
             } else {
-                if (!empty($options['navigation'])) {
+                if (empty($CFG->linkcoursesections) && !empty($options['navigation'])) {
                     return null;
                 }
                 $url->set_anchor('section-'.$sectionno);
index 73d97c2..66152d2 100644 (file)
@@ -82,6 +82,7 @@ class format_weeks extends format_base {
      * @return null|moodle_url
      */
     public function get_view_url($section, $options = array()) {
+        global $CFG;
         $course = $this->get_course();
         $url = new moodle_url('/course/view.php', array('id' => $course->id));
 
@@ -108,7 +109,7 @@ class format_weeks extends format_base {
             if ($sectionno != 0 && $usercoursedisplay == COURSE_DISPLAY_MULTIPAGE) {
                 $url->param('section', $sectionno);
             } else {
-                if (!empty($options['navigation'])) {
+                if (empty($CFG->linkcoursesections) && !empty($options['navigation'])) {
                     return null;
                 }
                 $url->set_anchor('section-'.$sectionno);
index 33014d6..6a3a845 100644 (file)
@@ -436,6 +436,14 @@ abstract class moodleform_mod extends moodleform {
         }
 
         if (!empty($CFG->enableavailability)) {
+            // Add special button to end of previous section if groups/groupings
+            // are enabled.
+            if ($this->_features->groups || $this->_features->groupings) {
+                $mform->addElement('static', 'restrictgroupbutton', '',
+                        html_writer::tag('button', get_string('restrictbygroup', 'availability'),
+                        array('id' => 'restrictbygroup', 'disabled' => 'disabled')));
+            }
+
             // Availability field. This is just a textarea; the user interface
             // interaction is all implemented in JavaScript.
             $mform->addElement('header', 'availabilityconditionsheader',
index 53992db..234b8c8 100644 (file)
@@ -19,6 +19,9 @@
  * Toggles the manual completion flag for a particular activity or course completion
  * and the current user.
  *
+ * If by student params: course=2
+ * If by manager params: course=2&user=4&rolec=3&sesskey=ghfgsdf
+ *
  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  * @package course
  */
@@ -31,6 +34,10 @@ $cmid = optional_param('id', 0, PARAM_INT);
 $courseid = optional_param('course', 0, PARAM_INT);
 $confirm = optional_param('confirm', 0, PARAM_BOOL);
 
+// Check if we are marking a user complete via the completion report.
+$user = optional_param('user', 0, PARAM_INT);
+$rolec = optional_param('rolec', 0, PARAM_INT);
+
 if (!$cmid && !$courseid) {
     print_error('invalidarguments');
 }
@@ -45,16 +52,14 @@ if ($courseid) {
     require_login($course);
 
     $completion = new completion_info($course);
+    $trackeduser = ($user ? $user : $USER->id);
+
     if (!$completion->is_enabled()) {
         throw new moodle_exception('completionnotenabled', 'completion');
-    } elseif (!$completion->is_tracked_user($USER->id)) {
+    } else if (!$completion->is_tracked_user($trackeduser)) {
         throw new moodle_exception('nottracked', 'completion');
     }
 
-    // Check if we are marking a user complete via the completion report
-    $user = optional_param('user', 0, PARAM_INT);
-    $rolec = optional_param('rolec', 0, PARAM_INT);
-
     if ($user && $rolec) {
         require_sesskey();
 
index 46e6e1f..f4d1bee 100644 (file)
@@ -89,7 +89,7 @@ YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
             var recovergrades = null;
             if (this.get(UEP.DISABLEGRADEHISTORY) != true) {
                 recovergrades = create('<div class="'+CSS.ENROLMENTOPTION+' '+CSS.RECOVERGRADES+'"></div>')
-                    .append(create('<label class="'+CSS.RECOVERGRADESTITLE+'" for="'+CSS.RECOVERGRADES+'">'+M.str.enrol.recovergrades+'</label>'))
+                    .append(create('<label class="'+CSS.RECOVERGRADESTITLE+'" for="'+CSS.RECOVERGRADES+'">'+M.util.get_string('recovergrades', 'enrol')+'</label>'))
                     .append(create('<input type="checkbox" id="'+CSS.RECOVERGRADES+'" name="'+CSS.RECOVERGRADES+'"'+ this.get(UEP.RECOVERGRADESDEFAULT) +' />'))
             }
 
@@ -97,21 +97,21 @@ YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
                 .append(create('<div class="'+CSS.WRAP+'"></div>')
                     .append(create('<div class="'+CSS.HEADER+' header"></div>')
                         .append(create('<div class="'+CSS.CLOSE+'"></div>'))
-                        .append(create('<h2>'+M.str.enrol.enrolusers+'</h2>')))
+                        .append(create('<h2>'+M.util.get_string('enrolusers', 'enrol')+'</h2>')))
                     .append(create('<div class="'+CSS.CONTENT+'"></div>')
                         .append(create('<div class="'+CSS.SEARCHCONTROLS+'"></div>')
-                            .append(create('<div class="'+CSS.ENROLMENTOPTION+' '+CSS.ROLE+'"><label for="id_enrol_manual_assignable_roles">'+M.str.role.assignroles+'</label></div>')
-                                    .append(create('<select id="id_enrol_manual_assignable_roles"><option value="">'+M.str.enrol.none+'</option></select>'))
+                            .append(create('<div class="'+CSS.ENROLMENTOPTION+' '+CSS.ROLE+'"><label for="id_enrol_manual_assignable_roles">'+M.util.get_string('assignroles', 'role')+'</label></div>')
+                                    .append(create('<select id="id_enrol_manual_assignable_roles"><option value="">'+M.util.get_string('none', 'enrol')+'</option></select>'))
                             )
                             .append(create('<div class="'+CSS.ENTITYSELECTOR+'"></div>'))
                             .append(create('<div class="'+CSS.SEARCHOPTIONS+'"></div>')
-                                .append(create('<div class="'+CSS.COLLAPSIBLEHEADING+'"><img alt="" />'+M.str.enrol.enrolmentoptions+'</div>'))
+                                .append(create('<div class="'+CSS.COLLAPSIBLEHEADING+'"><img alt="" />'+M.util.get_string('enrolmentoptions', 'enrol')+'</div>'))
                                 .append(create('<div class="'+CSS.COLLAPSIBLEAREA+' '+CSS.HIDDEN+'"></div>')
                                     .append(recovergrades)
-                                    .append(create('<div class="'+CSS.ENROLMENTOPTION+' '+CSS.STARTDATE+'">'+M.str.moodle.startingfrom+'</div>')
+                                    .append(create('<div class="'+CSS.ENROLMENTOPTION+' '+CSS.STARTDATE+'">'+M.util.get_string('startingfrom', 'moodle')+'</div>')
                                         .append(create('<select></select>')))
-                                    .append(create('<div class="'+CSS.ENROLMENTOPTION+' '+CSS.DURATION+'">'+M.str.enrol.enrolperiod+'</div>')
-                                        .append(create('<select><option value="0" selected="selected">'+M.str.enrol.unlimitedduration+'</option></select>')))
+                                    .append(create('<div class="'+CSS.ENROLMENTOPTION+' '+CSS.DURATION+'">'+M.util.get_string('enrolperiod', 'enrol')+'</div>')
+                                        .append(create('<select><option value="0" selected="selected">'+M.util.get_string('unlimitedduration', 'enrol')+'</option></select>')))
                                 )
                             )
                         )
@@ -121,12 +121,12 @@ YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
                                 .setAttribute('src', M.util.image_url('i/loading', 'moodle')))
                             .setStyle('opacity', 0.5)))
                     .append(create('<div class="'+CSS.FOOTER+'"></div>')
-                        .append(create('<div class="'+CSS.SEARCH+'"><label for="enrolusersearch" class="accesshide">'+M.str.enrol.usersearch+'</label></div>')
+                        .append(create('<div class="'+CSS.SEARCH+'"><label for="enrolusersearch" class="accesshide">'+M.util.get_string('usersearch', 'enrol')+'</label></div>')
                             .append(create('<input type="text" id="enrolusersearch" value="" />'))
-                                .append(create('<input type="button" id="searchbtn" class="'+CSS.SEARCHBTN+'" value="'+M.str.enrol.usersearch+'" />'))
+                                .append(create('<input type="button" id="searchbtn" class="'+CSS.SEARCHBTN+'" value="'+M.util.get_string('usersearch', 'enrol')+'" />'))
                         )
                         .append(create('<div class="'+CSS.CLOSEBTN+'"></div>')
-                            .append(create('<input type="button" value="'+M.str.enrol.finishenrollingusers+'" />'))
+                            .append(create('<input type="button" value="'+M.util.get_string('finishenrollingusers', 'enrol')+'" />'))
                         )
                     )
                 )
@@ -152,9 +152,9 @@ YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
             if (this.get(UEP.COHORTSAVAILABLE)) {
                 this.get(UEP.BASE).one('.'+CSS.ENTITYSELECTOR)
                     .append(create('<input type="radio" id="id_enrol_manual_entity_users" name="enrol_manual_entity" value="users" checked="checked"/>'))
-                    .append(create('<label for="id_enrol_manual_entity_users">'+ M.str.enrol_manual.browseusers+'</label>'))
+                    .append(create('<label for="id_enrol_manual_entity_users">'+ M.util.get_string('browseusers', 'enrol_manual')+'</label>'))
                     .append(create('<input type="radio" id="id_enrol_manual_entity_cohorts" name="enrol_manual_entity" value="cohorts"/>'))
-                    .append(create('<label for="id_enrol_manual_entity_cohorts">'+M.str.enrol_manual.browsecohorts+'</label>'));
+                    .append(create('<label for="id_enrol_manual_entity_cohorts">'+M.util.get_string('browsecohorts', 'enrol_manual')+'</label>'));
                 this.get(UEP.BASE).one('#id_enrol_manual_entity_cohorts').on('change', this.search, this);
                 this.get(UEP.BASE).one('#id_enrol_manual_entity_users').on('change', this.search, this);
             } else {
@@ -380,7 +380,7 @@ YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
                 new M.core.exception(e);
             }
             if (!result.success) {
-                this.setContent = M.str.enrol.errajaxsearch;
+                this.setContent = M.util.get_string('errajaxsearch', 'enrol');
             }
             var users;
             if (!args.append) {
@@ -401,17 +401,17 @@ YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
                         .append(create('<div class="'+CSS.FULLNAME+'">'+user.fullname+'</div>'))
                         .append(create('<div class="'+CSS.EXTRAFIELDS+'">'+user.extrafields+'</div>')))
                     .append(create('<div class="'+CSS.OPTIONS+'"></div>')
-                        .append(create('<input type="button" class="'+CSS.ENROL+'" value="'+M.str.enrol.enrol+'" />')))
+                        .append(create('<input type="button" class="'+CSS.ENROL+'" value="'+M.util.get_string('enrol', 'enrol')+'" />')))
                 );
             }
             this.set(UEP.USERCOUNT, count);
             if (!args.append) {
-                var usersstr = (result.response.totalusers == '1')?M.str.enrol.ajaxoneuserfound:M.util.get_string('ajaxxusersfound','enrol', result.response.totalusers);
+                var usersstr = (result.response.totalusers == '1')?M.util.get_string('ajaxoneuserfound', 'enrol'):M.util.get_string('ajaxxusersfound','enrol', result.response.totalusers);
                 var content = create('<div class="'+CSS.SEARCHRESULTS+'"></div>')
                     .append(create('<div class="'+CSS.TOTALUSERS+'">'+usersstr+'</div>'))
                     .append(users);
                 if (result.response.totalusers > (this.get(UEP.PAGE)+1)*this.get(UEP.PERPAGE)) {
-                    var fetchmore = create('<div class="'+CSS.MORERESULTS+'"><a href="#">'+M.str.enrol.ajaxnext25+'</a></div>');
+                    var fetchmore = create('<div class="'+CSS.MORERESULTS+'"><a href="#">'+M.util.get_string('ajaxnext25', 'enrol')+'</a></div>');
                     fetchmore.on('click', this.search, this, true);
                     content.append(fetchmore)
                 }
@@ -433,7 +433,7 @@ YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
                 new M.core.exception(e);
             }
             if (!result.success) {
-                this.setContent = M.str.enrol.errajaxsearch;
+                this.setContent = M.util.get_string('errajaxsearch', 'enrol');
             }
             var cohorts;
             if (!args.append) {
@@ -456,13 +456,13 @@ YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
             }
             this.set(UEP.COHORTCOUNT, count);
             if (!args.append) {
-                //var usersstr = (result.response.totalusers == '1')?M.str.enrol.ajaxoneuserfound:M.util.get_string('ajaxxusersfound','enrol', result.response.totalusers);
+                //var usersstr = (result.response.totalusers == '1')?M.util.get_string('ajaxoneuserfound', 'enrol'):M.util.get_string('ajaxxusersfound','enrol', result.response.totalusers);
                 var cohortsstr = 'Found '+result.response.totalcohorts+' cohorts'; // TODO
                 var content = create('<div class="'+CSS.SEARCHRESULTS+'"></div>')
                     .append(create('<div class="'+CSS.TOTALCOHORTS+'">'+cohortsstr+'</div>'))
                     .append(cohorts);
                 if (result.response.totalcohorts > (this.get(UEP.PAGE)+1)*this.get(UEP.PERPAGE)) {
-                    var fetchmore = create('<div class="'+CSS.MORERESULTS+'"><a href="#">'+M.str.enrol.ajaxnext25+'</a></div>');
+                    var fetchmore = create('<div class="'+CSS.MORERESULTS+'"><a href="#">'+M.util.get_string('ajaxnext25', 'enrol')+'</a></div>');
                     fetchmore.on('click', this.search, this, true);
                     content.append(fetchmore)
                 }
index c463082..ecc3f54 100644 (file)
@@ -42,7 +42,11 @@ class enrol_meta_addinstance_form extends moodleform {
         $courses = array('' => get_string('choosedots'));
         $select = ', ' . context_helper::get_preload_record_columns_sql('ctx');
         $join = "LEFT JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :contextlevel)";
-        $sql = "SELECT c.id, c.fullname, c.shortname, c.visible $select FROM {course} c $join ORDER BY c.sortorder ASC";
+
+        $plugin = enrol_get_plugin('meta');
+        $sortorder = 'c.' . $plugin->get_config('coursesort', 'sortorder') . ' ASC';
+
+        $sql = "SELECT c.id, c.fullname, c.shortname, c.visible $select FROM {course} c $join ORDER BY " . $sortorder;
         $rs = $DB->get_recordset_sql($sql, array('contextlevel' => CONTEXT_COURSE));
         foreach ($rs as $c) {
             if ($c->id == SITEID or $c->id == $course->id or isset($existing[$c->id])) {
index e554ddd..b47f526 100644 (file)
@@ -22,6 +22,8 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+$string['coursesort'] = 'Sort course list';
+$string['coursesort_help'] = 'This determines whether the list of courses that can be linked are sorted by sort order (i.e. the order set in Site administration > Courses > Manage courses and categories) or alphabetically by course setting.';
 $string['linkedcourse'] = 'Link course';
 $string['meta:config'] = 'Configure meta enrol instances';
 $string['meta:selectaslinked'] = 'Select course as meta linked';
index 3cbc4e1..8fb9a7d 100644 (file)
@@ -41,5 +41,19 @@ if ($ADMIN->fulltree) {
             ENROL_EXT_REMOVED_SUSPENDNOROLES => get_string('extremovedsuspendnoroles', 'core_enrol'),
         );
         $settings->add(new admin_setting_configselect('enrol_meta/unenrolaction', get_string('extremovedaction', 'enrol'), get_string('extremovedaction_help', 'enrol'), ENROL_EXT_REMOVED_SUSPENDNOROLES, $options));
+
+        $sortoptions = array(
+            'sortorder' => new lang_string('sort_sortorder', 'admin'),
+            'fullname' => new lang_string('sort_fullname', 'admin'),
+            'shortname' => new lang_string('sort_shortname', 'admin'),
+            'idnumber' => new lang_string('sort_idnumber', 'admin'),
+        );
+        $settings->add(new admin_setting_configselect(
+            'enrol_meta/coursesort',
+            new lang_string('coursesort', 'enrol_meta'),
+            new lang_string('coursesort_help', 'enrol_meta'),
+            'sortorder',
+            $sortoptions
+        ));
     }
 }
index df71ec9..216b532 100644 (file)
@@ -24,7 +24,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2014111000;        // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version   = 2014112400;        // The current plugin version (Date: YYYYMMDDXX)
 $plugin->requires  = 2014110400;        // Requires this Moodle version
 $plugin->component = 'enrol_meta';      // Full name of the plugin (used for diagnostics)
 $plugin->cron      = 60*60;             // run cron every hour by default, it is not out-of-sync often
index 1df94ce..89390ee 100644 (file)
@@ -58,7 +58,7 @@ YUI.add('moodle-enrol-otherusersmanager', function(Y) {
                 .append(Y.Node.create('<div class="'+CSS.WRAP+'"></div>')
                     .append(Y.Node.create('<div class="'+CSS.HEADER+' header"></div>')
                         .append(Y.Node.create('<div class="'+CSS.CLOSE+'"></div>'))
-                        .append(Y.Node.create('<h2>'+M.str.enrol.usersearch+'</h2>')))
+                        .append(Y.Node.create('<h2>'+M.util.get_string('usersearch', 'enrol')+'</h2>')))
                     .append(Y.Node.create('<div class="'+CSS.CONTENT+'"></div>')
                         .append(Y.Node.create('<div class="'+CSS.AJAXCONTENT+'"></div>'))
                         .append(Y.Node.create('<div class="'+CSS.LIGHTBOX+' '+CSS.HIDDEN+'"></div>')
@@ -66,7 +66,7 @@ YUI.add('moodle-enrol-otherusersmanager', function(Y) {
                                 .setAttribute('src', M.util.image_url('i/loading', 'moodle')))
                             .setStyle('opacity', 0.5)))
                     .append(Y.Node.create('<div class="'+CSS.FOOTER+'"></div>')
-                        .append(Y.Node.create('<div class="'+CSS.SEARCH+'"><label>'+M.str.enrol.usersearch+'</label></div>')
+                        .append(Y.Node.create('<div class="'+CSS.SEARCH+'"><label>'+M.util.get_string('usersearch', 'enrol')+'</label></div>')
                             .append(Y.Node.create('<input type="text" id="oump-usersearch" value="" />'))
                         )
                     )
@@ -169,7 +169,7 @@ YUI.add('moodle-enrol-otherusersmanager', function(Y) {
                 new M.core.exception(e);
             }
             if (!result.success) {
-                this.setContent = M.str.enrol.errajaxsearch;
+                this.setContent = M.util.get_string('errajaxsearch', 'enrol');
             }
             var usersnode, users = [], i=0, count=0, user;
             if (!args.append) {
@@ -186,12 +186,12 @@ YUI.add('moodle-enrol-otherusersmanager', function(Y) {
             }
             this.set(USERCOUNT, count);
             if (!args.append) {
-                var usersstr = (result.response.totalusers == '1')?M.str.enrol.ajaxoneuserfound:M.util.get_string('ajaxxusersfound','enrol', result.response.totalusers);
+                var usersstr = (result.response.totalusers == '1')?M.util.get_string('ajaxoneuserfound', 'enrol'):M.util.get_string('ajaxxusersfound','enrol', result.response.totalusers);
                 var content = Y.Node.create('<div class="'+CSS.SEARCHRESULTS+'"></div>')
                     .append(Y.Node.create('<div class="'+CSS.TOTALUSERS+'">'+usersstr+'</div>'))
                     .append(usersnode);
                 if (result.response.totalusers > (this.get(PAGE)+1)*25) {
-                    var fetchmore = Y.Node.create('<div class="'+CSS.MORERESULTS+'"><a href="#">'+M.str.enrol.ajaxnext25+'</a></div>');
+                    var fetchmore = Y.Node.create('<div class="'+CSS.MORERESULTS+'"><a href="#">'+M.util.get_string('ajaxnext25', 'enrol')+'</a></div>');
                     fetchmore.on('click', this.getUsers, this, true);
                     content.append(fetchmore)
                 }
@@ -312,7 +312,7 @@ YUI.add('moodle-enrol-otherusersmanager', function(Y) {
                         .append(Y.Node.create('<div class="'+CSS.FULLNAME+'">'+this.get(FULLNAME)+'</div>'))
                         .append(Y.Node.create('<div class="'+CSS.EXTRAFIELDS+'">'+this.get(EXTRAFIELDS)+'</div>'))
                     )
-                    .append(Y.Node.create('<div class="'+CSS.OPTIONS+'"><span class="label">'+M.str.role.assignrole+': </span></div>'))
+                    .append(Y.Node.create('<div class="'+CSS.OPTIONS+'"><span class="label">'+M.util.get_string('assignrole', 'role')+': </span></div>'))
                 );
             var id = 0, roles = this._manager.get(ASSIGNABLEROLES);
             for (id in roles) {
index 7e35cbd..6e6dfa2 100644 (file)
@@ -112,14 +112,14 @@ YUI.add('moodle-enrol-rolemanager', function(Y) {
             e.halt();
             var event = this.on('assignablerolesloaded', function(){
                 event.detach();
-                var s = M.str.role, confirmation = {
+                var confirmation = {
                     modal:  true,
                     visible  :  true,
                     centered :  true,
-                    title    :  s.confirmunassigntitle,
-                    question :  s.confirmunassign,
-                    yesLabel :  s.confirmunassignyes,
-                    noLabel  :  s.confirmunassignno
+                    title    :  M.util.get_string('confirmunassigntitle', 'role'),
+                    question :  M.util.get_string('confirmunassign', 'role'),
+                    yesLabel :  M.util.get_string('confirmunassignyes', 'role'),
+                    noLabel  :  M.util.get_string('confirmunassignno', 'role')
                 };
                 new M.core.confirm(confirmation).on('complete-yes', this.removeRoleCallback, this, user.get(USERID), roleid);
             }, this);
@@ -347,7 +347,7 @@ YUI.add('moodle-enrol-rolemanager', function(Y) {
         submitevent : null,
         initializer : function() {
             var i, m = this.get(MANIPULATOR);
-            var element = Y.Node.create('<div class="enrolpanel roleassign"><div class="container"><div class="header"><h2>'+M.str.role.assignroles+'</h2><div class="close"></div></div><div class="content"></div></div></div>');
+            var element = Y.Node.create('<div class="enrolpanel roleassign"><div class="container"><div class="header"><h2>'+M.util.get_string('assignroles', 'role')+'</h2><div class="close"></div></div><div class="content"></div></div></div>');
             var content = element.one('.content');
             var roles = m.get(ASSIGNABLEROLES);
             for (i in roles) {
index 94c1e15..bd7229d 100644 (file)
@@ -105,10 +105,10 @@ M.gradingform_guideeditor.editmode = function(el, editmode) {
         if (value.length) {
             taplain.removeClass('empty')
         } else if (ta.get('name').indexOf('[shortname]') > 1){
-            value = M.str.gradingform_guide.clicktoeditname
+            value = M.util.get_string('clicktoeditname', 'gradingform_guide')
             taplain.addClass('editname')
         } else {
-            value = M.str.gradingform_guide.clicktoedit
+            value = M.util.get_string('clicktoedit', 'gradingform_guide')
             taplain.addClass('empty')
         }
         taplain.one('.textvalue').set('innerHTML', Y.Escape.html(value))
@@ -219,7 +219,7 @@ M.gradingform_guideeditor.buttonclick = function(e, confirmed) {
             Y.one('#'+name+'-'+section+'-'+chunks[2]).remove()
             M.gradingform_guideeditor.assignclasses(elements_str)
         } else {
-            dialog_options['message'] = M.str.gradingform_guide.confirmdeletecriterion
+            dialog_options['message'] = M.util.get_string('confirmdeletecriterion', 'gradingform_guide')
             M.util.show_confirm_dialog(e, dialog_options);
         }
     } else {
index f0dd3ae..6b9137a 100644 (file)
@@ -90,7 +90,7 @@ M.gradingform_rubriceditor.editmode = function(el, editmode, focustb) {
         var value = ta.get('value')
         if (value.length) taplain.removeClass('empty')
         else {
-            value = (el.hasClass('level')) ? M.str.gradingform_rubric.levelempty : M.str.gradingform_rubric.criterionempty
+            value = (el.hasClass('level')) ? M.util.get_string('levelempty', 'gradingform_rubric') : M.util.get_string('criterionempty', 'gradingform_rubric')
             taplain.addClass('empty')
         }
         taplain.one('.textvalue').set('innerHTML', Y.Escape.html(value));
@@ -201,7 +201,7 @@ M.gradingform_rubriceditor.buttonclick = function(e, confirmed) {
             Y.one('#'+name+'-criteria-'+chunks[2]).remove()
             M.gradingform_rubriceditor.assignclasses(elements_str)
         } else {
-            dialog_options['message'] = M.str.gradingform_rubric.confirmdeletecriterion
+            dialog_options['message'] = M.util.get_string('confirmdeletecriterion', 'gradingform_rubric')
             M.util.show_confirm_dialog(e, dialog_options);
         }
     } else if (chunks.length == 6 && action == 'delete') {
@@ -210,7 +210,7 @@ M.gradingform_rubriceditor.buttonclick = function(e, confirmed) {
             Y.one('#'+name+'-criteria-'+chunks[2]+'-'+chunks[3]+'-'+chunks[4]).remove()
             M.gradingform_rubriceditor.assignclasses(elements_str)
         } else {
-            dialog_options['message'] = M.str.gradingform_rubric.confirmdeletelevel
+            dialog_options['message'] = M.util.get_string('confirmdeletelevel', 'gradingform_rubric')
             M.util.show_confirm_dialog(e, dialog_options);
         }
     } else {
index 7fa1600..706571e 100644 (file)
@@ -446,7 +446,7 @@ M.gradereport_grader.classes.ajax.prototype.submission_outcome = function(tid, o
     try {
         outcome = this.report.Y.JSON.parse(outcome.responseText);
     } catch(e) {
-        var message = M.str.gradereport_grader.ajaxfailedupdate;
+        var message = M.util.get_string('ajaxfailedupdate', 'gradereport_grader');
         message = message.replace(/\[1\]/, args.type);
         message = message.replace(/\[2\]/, this.report.users[args.properties.userid]);
 
@@ -506,7 +506,7 @@ M.gradereport_grader.classes.ajax.prototype.submission_outcome = function(tid, o
                         // If we are here the grade value of the cell currently being edited has changed !!!!!!!!!
                         // If the user has not actually changed the old value yet we will automatically correct it
                         // otherwise we will prompt the user to choose to use their value or the new value!
-                        if (!this.current.has_changed() || confirm(M.str.gradereport_grader.ajaxfieldchanged)) {
+                        if (!this.current.has_changed() || confirm(M.util.get_string('ajaxfieldchanged', 'gradereport_grader'))) {
                             this.current.set_grade(finalgrade);
                             this.current.grade.set('value', finalgrade);
                         }
@@ -554,7 +554,7 @@ M.gradereport_grader.classes.ajax.prototype.submission_outcome = function(tid, o
  */
 M.gradereport_grader.classes.ajax.prototype.display_submission_error = function(message, cell) {
     var erroroverlay = new this.report.Y.Overlay({
-        headerContent : '<div><strong class="error">'+M.str.gradereport_grader.ajaxerror+'</strong>  <em>'+M.str.gradereport_grader.ajaxclicktoclose+'</em></div>',
+        headerContent : '<div><strong class="error">'+M.util.get_string('ajaxerror', 'gradereport_grader')+'</strong>  <em>'+M.util.get_string('ajaxclicktoclose', 'gradereport_grader')+'</em></div>',
         bodyContent : message,
         visible : false,
         zIndex : 3
@@ -1009,7 +1009,7 @@ M.gradereport_grader.classes.scalefield = function(report, node) {
     this.gradespan = node.one('.gradevalue');
     this.inputdiv = this.report.Y.Node.create('<div></div>');
     this.editfeedback = this.report.ajax.showquickfeedback;
-    this.grade = this.report.Y.Node.create('<select type="text" class="text" /><option value="-1">'+M.str.gradereport_grader.ajaxchoosescale+'</option></select>');
+    this.grade = this.report.Y.Node.create('<select type="text" class="text" /><option value="-1">'+M.util.get_string('ajaxchoosescale', 'gradereport_grader')+'</option></select>');
     this.gradetype = 'scale';
     this.inputdiv.append(this.grade);
     if (this.editfeedback) {
index f3e7f37..cbbb143 100644 (file)
@@ -163,11 +163,9 @@ echo '<form id="groupeditform" action="index.php" method="post">'."\n";
 echo '<div>'."\n";
 echo '<input type="hidden" name="id" value="' . $courseid . '" />'."\n";
 
-echo '<table cellpadding="6" class="generaltable generalbox groupmanagementtable boxaligncenter" summary="">'."\n";
-echo '<tr>'."\n";
+echo html_writer::start_tag('div', array('class' => 'groupmanagementtable boxaligncenter'));
+echo html_writer::start_tag('div', array('class' => 'groups'));
 
-
-echo "<td>\n";
 echo '<p><label for="groups"><span id="groupslabel">'.get_string('groups').':</span><span id="thegrouping">&nbsp;</span></label></p>'."\n";
 
 $onchange = 'M.core_group.membersCombo.refreshMembers();';
@@ -220,8 +218,8 @@ echo '<p><input type="submit" name="act_showautocreategroupsform" id="showautocr
 echo '<p><input type="submit" name="act_showimportgroups" id="showimportgroups" value="'
         . get_string('importgroups', 'core_group') . '" /></p>'."\n";
 
-echo '</td>'."\n";
-echo '<td>'."\n";
+echo html_writer::end_tag('div');
+echo html_writer::start_tag('div', array('class' => 'members'));
 
 echo '<p><label for="members"><span id="memberslabel">'.
     get_string('membersofselectedgroup', 'group').
@@ -255,9 +253,8 @@ echo '</select>'."\n";
 
 echo '<p><input type="submit" ' . $showaddmembersform_disabled . ' name="act_showaddmembersform" '
         . 'id="showaddmembersform" value="' . get_string('adduserstogroup', 'group'). '" /></p>'."\n";
-echo '</td>'."\n";
-echo '</tr>'."\n";
-echo '</table>'."\n";
+echo html_writer::end_tag('div');
+echo html_writer::end_tag('div');
 
 //<input type="hidden" name="rand" value="om" />
 echo '</div>'."\n";
index ee81afc..6e8789e 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'access', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   access
+ * @package   core_access
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index 63e1d81..7cfb317 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'admin', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   admin
+ * @package   core
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -342,6 +341,7 @@ $string['configstripalltitletags'] = 'Uncheck this setting to allow HTML tags in
 $string['configsupportemail'] = 'This email address will be published to users of this site as the one to email when they need general help (for example, when new users create their own accounts).  If this email is left blank then no such helpful email address is supplied.';
 $string['configsupportname'] = 'This is the name of a person or other entity offering general help via the support email or web address.';
 $string['configsupportpage'] = 'This web address will be published to users of this site as the one to go to when they need general help (for example, when new users create their own accounts).  If this address is left blank then no link will be supplied.';
+$string['configtempdatafoldercleanup'] = 'Remove temporary data files from the data folder that are older than the selected time.';
 $string['configthemedesignermode'] = 'Normally all theme images and style sheets are cached in browsers and on the server for a very long time, for performance. If you are designing themes or developing code then you probably want to turn this mode on so that you are not served cached versions.  Warning: this will make your site slower for all users!  Alternatively, you can also reset the theme caches manually from the Theme selection page.';
 $string['configthemelist'] = 'Leave this blank to allow any valid theme to be used.  If you want to shorten the theme menu, you can specify a comma-separated list of names here (Don\'t use spaces!).
 For example:  standard,orangewhite.';
@@ -617,6 +617,8 @@ $string['legacyfilesinnewcourses_help'] = 'By default, legacy course files areas
 $string['licensesettings'] = 'Licence settings';
 $string['linkadmincategories'] = 'Link admin categories';
 $string['linkadmincategories_help'] = 'If enabled admin setting categories will be displayed as links in the navigation and will lead to the admin category pages.';
+$string['linkcoursesections'] = 'Always link course sections';
+$string['linkcoursesections_help'] = 'Always try to provide a link for course sections. Course sections are usually only shown as links if the course format displays a single section per page. If this setting is enabled a link will always be provided.';
 $string['loading'] = 'Loading';
 $string['localetext'] = 'Sitewide locale';
 $string['localstringcustomization'] = 'Local string customization';
@@ -777,6 +779,8 @@ $string['passwordchangelogout'] = 'Log out after password change';
 $string['passwordchangelogout_desc'] = 'If enabled, when a password is changed, all browser sessions are terminated, apart from the one in which the new password is specified. (This setting does not affect password changes via bulk user upload.)';
 $string['passwordpolicy'] = 'Password policy';
 $string['passwordresettime'] = 'Maximum time to validate password reset request';
+$string['passwordreuselimit'] = 'Password rotation limit';
+$string['passwordreuselimit_desc'] = 'Number of times a user must change their password before they are allowed to reuse a password. Hashes of previously used passwords are stored in local database table. This feature might not be compatible with some external authentication plugins.';
 $string['pathtoclam'] = 'clam AV path';
 $string['pathtodot'] = 'Path to dot';
 $string['pathtodot_help'] = 'Path to dot. Probably something like /usr/bin/dot. To be able to generate graphics from DOT files, you must have installed the dot executable and point to it here. Note that, for now, this only used by the profiling features (Development->Profiling) built into Moodle.';
@@ -1037,6 +1041,7 @@ $string['tasksessioncleanup'] = 'Cleanup old sessions';
 $string['taskstatscron'] = 'Background processing for statistics';
 $string['tasktagcron'] = 'Background processing for tags';
 $string['tasktempfilecleanup'] = 'Delete stale temp files';
+$string['tempdatafoldercleanup'] = 'Clean up temporary data files older than';
 $string['themedesignermode'] = 'Theme designer mode';
 $string['themelist'] = 'Theme list';
 $string['themenoselected'] = 'No theme selected';
index 0bfe51b..6c892f9 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'auth', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   auth
+ * @package   core_auth
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -79,6 +78,7 @@ $string['errorminpassworddigits'] = 'Passwords must have at least {$a} digit(s).
 $string['errorminpasswordlength'] = 'Passwords must be at least {$a} characters long.';
 $string['errorminpasswordlower'] = 'Passwords must have at least {$a} lower case letter(s).';
 $string['errorminpasswordnonalphanum'] = 'Passwords must have at least {$a} non-alphanumeric character(s).';
+$string['errorpasswordreused'] = 'This password has been used before, and is not permitted to be reused';
 $string['errorminpasswordupper'] = 'Passwords must have at least {$a} upper case letter(s).';
 $string['errorpasswordupdate'] = 'Error updating password, password not changed';
 $string['eventuserloggedin'] = 'User has logged in';
@@ -103,6 +103,7 @@ $string['informminpassworddigits'] = 'at least {$a} digit(s)';
 $string['informminpasswordlength'] = 'at least {$a} characters';
 $string['informminpasswordlower'] = 'at least {$a} lower case letter(s)';
 $string['informminpasswordnonalphanum'] = 'at least {$a} non-alphanumeric character(s)';
+$string['informminpasswordreuselimit'] = 'Passwords can be reused after {$a} changes';
 $string['informminpasswordupper'] = 'at least {$a} upper case letter(s)';
 $string['informpasswordpolicy'] = 'The password must have {$a}';
 $string['instructions'] = 'Instructions';
index 9951d3f..0ec9ace 100644 (file)
@@ -62,5 +62,6 @@ $string['manageplugins'] = 'Manage restrictions';
 $string['missingplugin'] = 'Missing restriction plugin';
 $string['or'] = 'or';
 $string['restrictaccess'] = 'Restrict access';
+$string['restrictbygroup'] = 'Add group/grouping access restriction';
 $string['setheading'] = '{$a->number} Set of {$a->count} restriction(s)';
 $string['unknowncondition'] = 'Unknown condition (deleted condition plugin)';
index e1de7b8..6e68819 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * This file contains the strings used by backup
  *
- * @package   moodlecore
+ * @package   core
  * @copyright 2010 Eloy
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index ddb6691..21cf94e 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Language file for 'badges' component
  *
- * @package    core
+ * @package    core_badges
  * @subpackage badges
  * @copyright  2012 onwards Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
index e38b5a4..7b31859 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'block', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   block
+ * @package   core_block
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index 0904968..10193b2 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,8 +17,7 @@
 /**
  * Strings for core subsystem 'blog'
  *
- * @package    core
- * @subpackage blog
+ * @package    core_blog
  * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index 28571d2..deb03d1 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
index 6feeadf..5fac49e 100644 (file)
@@ -20,7 +20,7 @@
  * This file is part of Moodle's cache API, affectionately called MUC.
  * It contains the components that are requried in order to use caching.
  *
- * @package    core
+ * @package    core_cache
  * @category   cache
  * @copyright  2012 Sam Hemelryk
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
@@ -99,9 +99,8 @@ $string['invalidlock'] = 'Invalid lock';
 $string['invalidplugin'] = 'Invalid plugin';
 $string['invalidstore'] = 'Invalid cache store provided';
 $string['lockdefault'] = 'Default';
-$string['lockingmeans'] = 'Locking mechanism';
-$string['lockmethod'] = 'Lock method';
-$string['lockmethod_help'] = 'This is the method used for locking when required of this store.';
+$string['locking'] = 'Locking';
+$string['locking_help'] = 'Locking is a mechanism that restricts access to cached data to one process at a time to prevent the data from being overwritten. The locking method determines how the lock is acquired and checked.';
 $string['lockname'] = 'Name';
 $string['locknamedesc'] = 'The name must be unique and can only consist of the characters: a-zA-Z_';
 $string['locknamenotunique'] = 'The name you have selected is not unique. Please select a unique name.';
@@ -170,3 +169,8 @@ $string['unsupportedmode'] = 'Unsupported mode';
 $string['untestable'] = 'Untestable';
 $string['userinputsharingkey'] = 'Custom key for sharing';
 $string['userinputsharingkey_help'] = 'Enter your own private key here. When you set up other stores on other sites you wish to share data with make sure you set the exact same key there.';
+
+// Deprecated since 2.9.
+$string['lockingmeans'] = 'Locking mechanism';
+$string['lockmethod'] = 'Lock method';
+$string['lockmethod_help'] = 'This is the method used for locking when required of this store.';
\ No newline at end of file
index 475e340..7215442 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'calendar', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   calendar
+ * @package   core_calendar
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index 868194e..7b1dc38 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'cohort', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package    moodlecore
+ * @package    core_cohort
  * @subpackage cohort
  * @copyright  2010 Petr Skoda (info@skodak.org)
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
index 2573482..4dca573 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
index bab797f..dc2046e 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'countries', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   countries
+ * @package   core
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index eb032e8..c5c3459 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'currencies', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   currencies
+ * @package   core
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index 0bd37d5..eae0e68 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,8 @@
 /**
  * Strings for component 'dbtransfer', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   dbtransfer
+ * @package   core
+ * @subpackage dbtransfer
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index a18c1bc..bfd0c89 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'debug', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   debug
+ * @package   core
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index a17e036..134a8f0 100644 (file)
@@ -7,3 +7,6 @@ hidden,core_role
 simpleview,core_grades
 fullview,core_grades
 categoriesedit,core_grades
+lockingmeans,core_cache
+lockmethod,core_cache
+lockmethod_help,core_cache
\ No newline at end of file
index d2922d6..123bdad 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'editor', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   editor
+ * @package   core_editor
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index ed6da26..d35a18d 100644 (file)
@@ -1,30 +1,24 @@
 <?php
-
-///////////////////////////////////////////////////////////////////////////
-//                                                                       //
-// This file is part of Moodle - http://moodle.org/                      //
-// Moodle - Modular Object-Oriented Dynamic Learning Environment         //
-//                                                                       //
-// 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/>.       //
-//                                                                       //
-///////////////////////////////////////////////////////////////////////////
+// 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/>.
 
 /**
  *
  * Strings describing subject classification (Codes are from ASCED 2001)
  *
- * @package   moodle
+ * @package   core
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index 91fbf4e..23da526 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'core_enrol', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package    core
+ * @package    core_enrol
  * @subpackage enrol
  * @copyright  2010 Petr Skoda {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
index 82b3a01..963e0d4 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'error', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   error
+ * @package   core
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index 53b10b3..6cc2acf 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'filters', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   filters
+ * @package   core_filters
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index feb6833..ecd7bd7 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
 /**
  * Strings for component 'form', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   form
- * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package    core
+ * @subpackage form
+ * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
 $string['addfields'] = 'Add {$a} field(s) to form';
index 1d9513d..0e9438c 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'grades', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   grades
+ * @package   core_grades
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index 589efa5..c0849e1 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for the advanced grading methods subsystem
  *
- * @package    core
+ * @package    core_grading
  * @subpackage grading
  * @copyright  2011 David Mudrak <david@moodle.com>
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
index 63f3bb6..14060bb 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'group', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   group
+ * @package   core
  * @copyright 2006 The Open University
  * @author    J.White AT open.ac.uk
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
index 6a91a6b..753ebe4 100644 (file)
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
-
 /**
  *
  * Hub related strings
  *
- * @package   hub
+ * @package   core_hub
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index e2f0964..20ef87f 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
 /**
  * Strings for component 'imscc', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   imscc
- * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package    core
+ * @subpackage imscc
+ * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
 $string['cc_import_req_dom'] = 'ERROR: The Common Cartridge import requires DOM extension.';
index 7542f00..ee6fb04 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'install', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   install
+ * @package   core
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index b80f28c..51ceee9 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
  *  - array indexes are the ISO 639-2 "T" values (terminology)
  *  - some language names are shortened
  *
- * @package   iso6392
- * @copyright 2010 onwards Martin Dougiamas  {@link http://moodle.com}
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package    core
+ * @subpackage iso6392
+ * @copyright  2010 onwards Martin Dougiamas  {@link http://moodle.com}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
 $string['aar'] = 'Afar';
index 7905f66..f44d7f9 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'langconfig', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   langconfig
+ * @package   core
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index 47d4912..1c40f78 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'license', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   license
+ * @package   core
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index 488cbf8..907f2dc 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'mathslib', language 'en', branch 'MOODLE_19_STABLE'
  *
- * @package   mathslib
+ * @package   core
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index 9dfcc56..2e93263 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'message', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   message
+ * @package   core_message
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index 64624fb..f391d1e 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -31,7 +30,7 @@
  *
  * @see       get_mimetypes_array()
  * @see       get_mimetype_description()
- * @package   mimetypes
+ * @package   core
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index d2469c8..a7051e2 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'mnet', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   mnet
+ * @package   core_mnet
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -216,6 +215,12 @@ $string['showlocal'] = 'Show local users';
 $string['showremote'] = 'Show remote users';
 $string['ssl_acl_allow'] = 'SSO ACL: Allow user \'{$a->user}\' from \'{$a->host}\'';
 $string['ssl_acl_deny'] = 'SSO ACL: Deny user \'{$a->user}\' from \'{$a->host}\'';
+$string['sslverification'] = 'SSL verification';
+$string['sslverification_help'] = 'This option allows you to configure the level of security when connecting to a peer using HTTPS.
+
+* None: no level of security
+* Verify host only: validates the domain of the SSL certificate
+* Verify host and peer (recommended): validates the domain and issuer of the SSL certificate';
 $string['ssoaccesscontrol'] = 'SSO access control';
 $string['ssoacldescr'] = 'Use this page to grant/deny access to specific users from remote MNet hosts. This is functional when you are offering SSO services to remote users. To control your <em>local</em> users\' ability to roam to other MNet hosts, use the roles system to grant them the <em>mnetlogintoremote</em> capability.';
 $string['ssoaclneeds'] = 'For this functionality to work, you must have Networking on, plus the MNet authentication plugin enabled.';
@@ -244,6 +249,8 @@ $string['userchangepasswordlink'] = '<br /> You may be able to change your passw
 $string['usernotfullysetup'] = 'Your user account is incomplete.  You need to go <a href="{$a}">back to your provider</a> and ensure your profile is completed there.  You may need to log out and in again for this to take effect.';
 $string['usersareonline'] = 'Warning: {$a} users from that server are currently logged on to your site.';
 $string['validated_by'] = 'It is validated by the network: <code>{$a}</code>';
+$string['verifyhostandpeer'] = 'Verify host and peer';
+$string['verifyhostonly'] = 'Verify host only';
 $string['verifysignature-error'] = 'The signature verification failed. An error has occurred.';
 $string['verifysignature-invalid'] = 'The signature verification failed. It appears that this payload was not signed by you.';
 $string['version'] = 'Version';
index 1974d59..4e45ae2 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'moodle', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   moodle
+ * @package   core
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index 56d9d5f..ce961e3 100644 (file)
@@ -1,6 +1,26 @@
-<?php // $Id$
-      // my.php - created with Moodle 1.7 beta + (2006101003)
+<?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/>.
 
+/**
+ * Strings used by My Moodle pages.
+ *
+ * @package   core
+ * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
 
 $string['mymoodle'] = 'My home';
 $string['nocourses'] = 'No course information to show.';
index 48aab57..95f909b 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
 /**
  * Strings for component 'notes', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   notes
- * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package    core_notes
+ * @subpackage notes
+ * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
 $string['addnewnote'] = 'Add a new note';
index a969884..0237fe0 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'pagetype', language 'en'
  *
- * @package   pagetype
+ * @package   core
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index 310edda..a608b01 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
 /**
  * Strings for component 'pix', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   pix
- * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package    core_pix
+ * @subpackage pix
+ * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
 $string['angry'] = 'angry';
index bceee51..213eaea 100644 (file)
@@ -1,4 +1,27 @@
 <?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/>.
+
+/**
+ * Strings used by the plagiarism component
+ *
+ * @package    core_plagiarism
+ * @copyright  2010 Dan Marsden <dan@danmarsden.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
 $string['availableplugins'] = 'Available plugins';
 $string['configplagiarismplugins'] = 'Please choose the plagiarism plugin you would like to configure';
 $string['enableplagiarism'] ='Enable plagiarism plugins';
index edd7cc4..b9f013f 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'portfolio', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   portfolio
+ * @package   core_portfolio
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index e19ac9f..0e7927c 100644 (file)
@@ -17,7 +17,7 @@
 /**
  * Strings for component 'question', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   question
+ * @package   core_question
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index 1282b05..864c428 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'rating', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   rating
+ * @package   core_rating
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index 0452ef0..082fa47 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'repository', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   repository
+ * @package   core_repository
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index 32a1000..3bc7777 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'role', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   role
+ * @package   core_role
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index 4e2f25a..355c392 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'search', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   search
+ * @package   core
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index 02763e5..69a0a8e 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'table', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   table
+ * @package   core
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index 247b7cc..4b27fc6 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'tag', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   tag
+ * @package   core_tag
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index 80e58c1..01a1fea 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
 /**
  * Strings for component 'timezones', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   timezones
- * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package    core
+ * @subpackage timezones
+ * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
 $string['africa/abidjan'] = 'Africa/Abidjan';
index 99b8b34..35d772c 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'userkey', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   userkey
+ * @package   core
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index 8c8a47b..7617216 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,7 +17,7 @@
 /**
  * Strings for component 'webservice', language 'en', branch 'MOODLE_20_STABLE'
  *
- * @package   webservice
+ * @package   core_webservice
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index f8d12bf..f75455a 100644 (file)
@@ -1082,7 +1082,7 @@ class block_manager {
         }
 
         // Assign roles icon.
-        if (has_capability('moodle/role:assign', $block->context)) {
+        if ($this->page->pagetype != 'my-index' && has_capability('moodle/role:assign', $block->context)) {
             //TODO: please note it is sloppy to pass urls through page parameters!!
             //      it is shortened because some web servers (e.g. IIS by default) give
             //      a 'security' error if you try to pass a full URL as a GET parameter in another URL.
@@ -1721,6 +1721,30 @@ function matching_page_type_patterns($pagetype) {
     return $patterns;
 }
 
+/**
+ * Give an specific pattern, return all the page type patterns that would also match it.
+ *
+ * @param  string $pattern the pattern, e.g. 'mod-forum-*' or 'mod-quiz-view'.
+ * @return array of all the page type patterns matching.
+ */
+function matching_page_type_patterns_from_pattern($pattern) {
+    $patterns = array($pattern);
+    if ($pattern === '*') {
+        return $patterns;
+    }
+
+    // Only keep the part before the star because we will append -* to all the bits.
+    $star = strpos($pattern, '-*');
+    if ($star !== false) {
+        $pattern = substr($pattern, 0, $star);
+    }
+
+    $patterns = array_merge($patterns, matching_page_type_patterns($pattern));
+    $patterns = array_unique($patterns);
+
+    return $patterns;
+}
+
 /**
  * Given a specific page type, parent context and currect context, return all the page type patterns
  * that might be used by this block.
index b5aeea8..516e3c5 100644 (file)
@@ -46,7 +46,7 @@ class file_temp_cleanup_task extends scheduled_task {
 
         $tmpdir = $CFG->tempdir;
         // Default to last weeks time.
-        $time = strtotime('-1 week');
+        $time = time() - ($CFG->tempdatafoldercleanup * 3600);
 
         $dir = new \RecursiveDirectoryIterator($tmpdir);
         // Show all child nodes prior to their parent.
index 6a6c9e4..ee5f49d 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<XMLDB PATH="lib/db" VERSION="20141017" COMMENT="XMLDB file for core Moodle tables"
+<XMLDB PATH="lib/db" VERSION="20141202" COMMENT="XMLDB file for core Moodle tables"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
 >
         <INDEX NAME="courseid" UNIQUE="false" FIELDS="courseid"/>
       </INDEXES>
     </TABLE>
+    <TABLE NAME="user_password_history" COMMENT="A rotating log of hashes of previously used passwords for each user.">
+      <FIELDS>
+        <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
+        <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
+        <FIELD NAME="hash" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
+        <FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
+      </FIELDS>
+      <KEYS>
+        <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+        <KEY NAME="userid" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id"/>
+      </KEYS>
+    </TABLE>
     <TABLE NAME="scale" COMMENT="Defines grading scales">
       <FIELDS>
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
         <FIELD NAME="force_theme" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
         <FIELD NAME="theme" TYPE="char" LENGTH="100" NOTNULL="false" SEQUENCE="false"/>
         <FIELD NAME="applicationid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="1" SEQUENCE="false"/>
+        <FIELD NAME="sslverification" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
       </FIELDS>
       <KEYS>
         <KEY NAME="primary" TYPE="primary" FIELDS="id" COMMENT="primary key of the mnet_host table"/>
       <FIELDS>
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
         <FIELD NAME="contextid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
+        <FIELD NAME="component" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="The plugin this comment belongs to."/>
         <FIELD NAME="commentarea" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
         <FIELD NAME="itemid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
         <FIELD NAME="content" TYPE="text" NOTNULL="true" SEQUENCE="false"/>
index c9728de..df4e681 100644 (file)
@@ -4058,5 +4058,60 @@ function xmldb_main_upgrade($oldversion) {
     // Moodle v2.8.0 release upgrade line.
     // Put any upgrade step following this.
 
+
+    if ($oldversion < 2014120100.00) {
+
+        // Define field sslverification to be added to mnet_host.
+        $table = new xmldb_table('mnet_host');
+        $field = new xmldb_field('sslverification', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0', 'applicationid');
+
+        // Conditionally launch add field sslverification.
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        // Main savepoint reached.
+        upgrade_main_savepoint(true, 2014120100.00);
+    }
+
+    if ($oldversion < 2014120101.00) {
+
+        // Define field component to be added to comments.
+        $table = new xmldb_table('comments');
+        $field = new xmldb_field('component', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'contextid');
+
+        // Conditionally launch add field component.
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        // Main savepoint reached.
+        upgrade_main_savepoint(true, 2014120101.00);
+    }
+
+    if ($oldversion < 2014120102.00) {
+
+        // Define table user_password_history to be created.
+        $table = new xmldb_table('user_password_history');
+
+        // Adding fields to table user_password_history.
+        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+        $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
+        $table->add_field('hash', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
+        $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
+
+        // Adding keys to table user_password_history.
+        $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+        $table->add_key('userid', XMLDB_KEY_FOREIGN, array('userid'), 'user', array('id'));
+
+        // Conditionally launch create table for user_password_history.
+        if (!$dbman->table_exists($table)) {
+            $dbman->create_table($table);
+        }
+
+        // Main savepoint reached.
+        upgrade_main_savepoint(true, 2014120102.00);
+    }
+
     return true;
 }
index 91d58f6..987e5c2 100644 (file)
@@ -537,45 +537,18 @@ function update_log_display_entry($module, $action, $mtable, $field) {
 }
 
 /**
- * Given some text in HTML format, this function will pass it
- * through any filters that have been configured for this context.
- *
  * @deprecated use the text formatting in a standard way instead (http://docs.moodle.org/dev/Output_functions)
  *             this was abused mostly for embedding of attachments
- * @todo final deprecation of this function in MDL-40607
- * @param string $text The text to be passed through format filters
- * @param int $courseid The current course.
- * @return string the filtered string.
  */
 function filter_text($text, $courseid = NULL) {
-    global $CFG, $COURSE;
-
-    debugging('filter_text() is deprecated, use format_text(), format_string() etc instead.', DEBUG_DEVELOPER);
-
-    if (!$courseid) {
-        $courseid = $COURSE->id;
-    }
-
-    if (!$context = context_course::instance($courseid, IGNORE_MISSING)) {
-        return $text;
-    }
-
-    return filter_manager::instance()->filter_text($text, $context);
+    throw new coding_exception('filter_text() can not be used anymore, use format_text(), format_string() etc instead.');
 }
 
 /**
- * This function indicates that current page requires the https
- * when $CFG->loginhttps enabled.
- *
- * By using this function properly, we can ensure 100% https-ized pages
- * at our entire discretion (login, forgot_password, change_password)
  * @deprecated use $PAGE->https_required() instead
- * @todo final deprecation of this function in MDL-40607
  */
 function httpsrequired() {
-    global $PAGE;
-    debugging('httpsrequired() is deprecated use $PAGE->https_required() instead.', DEBUG_DEVELOPER);
-    $PAGE->https_required();
+    throw new coding_exception('httpsrequired() can not be used any more use $PAGE->https_required() instead.');
 }
 
 /**
@@ -638,30 +611,17 @@ function get_file_url($path, $options=null, $type='coursefile') {
 }
 
 /**
- * Return all course participant for a given course
- *
  * @deprecated use get_enrolled_users($context) instead.
- * @todo final deprecation of this function in MDL-40607
- * @param integer $courseid
- * @return array of user
  */
 function get_course_participants($courseid) {
-    debugging('get_course_participants() is deprecated, use get_enrolled_users() instead.', DEBUG_DEVELOPER);
-    return get_enrolled_users(context_course::instance($courseid));
+    throw new coding_exception('get_course_participants() can not be used any more, use get_enrolled_users() instead.');
 }
 
 /**
- * Return true if the user is a participant for a given course
- *
  * @deprecated use is_enrolled($context, $userid) instead.
- * @todo final deprecation of this function in MDL-40607
- * @param integer $userid
- * @param integer $courseid
- * @return boolean
  */
 function is_course_participant($userid, $courseid) {
-    debugging('is_course_participant() is deprecated, use is_enrolled() instead.', DEBUG_DEVELOPER);
-    return is_enrolled(context_course::instance($courseid), $userid);
+    throw new coding_exception('is_course_participant() can not be used any more, use is_enrolled() instead.');
 }
 
 /**
@@ -696,25 +656,9 @@ function get_recent_enrolments($courseid, $timestart) {
 
 /**
  * @deprecated use clean_param($string, PARAM_FILE) instead.
- * @todo final deprecation of this function in MDL-40607
- *
- * @param string $string ?
- * @param int $allowdots ?
- * @return bool
  */
 function detect_munged_arguments($string, $allowdots=1) {
-    debugging('detect_munged_arguments() is deprecated, please use clean_param(,PARAM_FILE) instead.', DEBUG_DEVELOPER);
-    if (substr_count($string, '..') > $allowdots) {   // Sometimes we allow dots in references
-        return true;
-    }
-    if (preg_match('/[\|\`]/', $string)) {  // check for other bad characters
-        return true;
-    }
-    if (empty($string) or $string == '/') {
-        return true;
-    }
-
-    return false;
+    throw new coding_exception('detect_munged_arguments() can not be used any more, please use clean_param(,PARAM_FILE) instead.');
 }
 
 
@@ -860,25 +804,10 @@ function zip_files ($originalfiles, $destination) {
 }
 
 /**
- * Get the IDs for the user's groups in the given course.
- *
- * @global object
- * @param int $courseid The course being examined - the 'course' table id field.
- * @return array|bool An _array_ of groupids, or false
- * (Was return $groupids[0] - consequences!)
  * @deprecated use groups_get_all_groups() instead.
- * @todo final deprecation of this function in MDL-40607
  */
 function mygroupid($courseid) {
-    global $USER;
-
-    debugging('mygroupid() is deprecated, please use groups_get_all_groups() instead.', DEBUG_DEVELOPER);
-
-    if ($groups = groups_get_all_groups($courseid, $USER->id)) {
-        return array_keys($groups);
-    } else {
-        return false;
-    }
+    throw new coding_exception('mygroupid() can not be used any more, please use groups_get_all_groups() instead.');
 }
 
 
@@ -1022,14 +951,9 @@ function error($message, $link='') {
 
 /**
  * @deprecated use $PAGE->theme->name instead.
- * @todo final deprecation of this function in MDL-40607
- * @return string the name of the current theme.
  */
 function current_theme() {
-    global $PAGE;
-
-    debugging('current_theme() is deprecated, please use $PAGE->theme->name instead', DEBUG_DEVELOPER);
-    return $PAGE->theme->name;
+    throw new coding_exception('current_theme() can not be used any more, please use $PAGE->theme->name instead');
 }
 
 /**
@@ -1045,93 +969,31 @@ function formerr($error) {
 }
 
 /**
- * Return the markup for the destination of the 'Skip to main content' links.
- * Accessibility improvement for keyboard-only users.
- *
- * Used in course formats, /index.php and /course/index.php
- *
  * @deprecated use $OUTPUT->skip_link_target() in instead.
- * @todo final deprecation of this function in MDL-40607
- * @return string HTML element.
  */
 function skip_main_destination() {
-    global $OUTPUT;
-
-    debugging('skip_main_destination() is deprecated, please use $OUTPUT->skip_link_target() instead.', DEBUG_DEVELOPER);
-    return $OUTPUT->skip_link_target();
+    throw new coding_exception('skip_main_destination() can not be used any more, please use $OUTPUT->skip_link_target() instead.');
 }
 
 /**
- * Print a message in a standard themed container.
- *
  * @deprecated use $OUTPUT->container() instead.
- * @todo final deprecation of this function in MDL-40607
- * @param string $message, the content of the container
- * @param boolean $clearfix clear both sides
- * @param string $classes, space-separated class names.
- * @param string $idbase
- * @param boolean $return, return as string or just print it
- * @return string|void Depending on value of $return
  */
 function print_container($message, $clearfix=false, $classes='', $idbase='', $return=false) {
-    global $OUTPUT;
-
-    debugging('print_container() is deprecated. Please use $OUTPUT->container() instead.', DEBUG_DEVELOPER);
-    if ($clearfix) {
-        $classes .= ' clearfix';
-    }
-    $output = $OUTPUT->container($message, $classes, $idbase);
-    if ($return) {
-        return $output;
-    } else {
-        echo $output;
-    }
+    throw new coding_exception('print_container() can not be used any more. Please use $OUTPUT->container() instead.');
 }
 
 /**
- * Starts a container using divs
- *
  * @deprecated use $OUTPUT->container_start() instead.
- * @todo final deprecation of this function in MDL-40607
- * @param boolean $clearfix clear both sides
- * @param string $classes, space-separated class names.
- * @param string $idbase
- * @param boolean $return, return as string or just print it
- * @return string|void Based on value of $return
  */
 function print_container_start($clearfix=false, $classes='', $idbase='', $return=false) {
-    global $OUTPUT;
-
-    debugging('print_container_start() is deprecated. Please use $OUTPUT->container_start() instead.', DEBUG_DEVELOPER);
-
-    if ($clearfix) {
-        $classes .= ' clearfix';
-    }
-    $output = $OUTPUT->container_start($classes, $idbase);
-    if ($return) {
-        return $output;
-    } else {
-        echo $output;
-    }
+    throw new coding_exception('print_container_start() can not be used any more. Please use $OUTPUT->container_start() instead.');
 }
 
 /**
- * Simple function to end a container (see above)
- *
  * @deprecated use $OUTPUT->container_end() instead.
- * @todo final deprecation of this function in MDL-40607
- * @param boolean $return, return as string or just print it
- * @return string|void Based on $return
  */
 function print_container_end($return=false) {
-    global $OUTPUT;
-    debugging('print_container_end() is deprecated. Please use $OUTPUT->container_end() instead.', DEBUG_DEVELOPER);
-    $output = $OUTPUT->container_end();
-    if ($return) {
-        return $output;
-    } else {
-        echo $output;
-    }
+    throw new coding_exception('print_container_end() can not be used any more. Please use $OUTPUT->container_end() instead.');
 }
 
 /**
@@ -1161,191 +1023,36 @@ function notify($message, $classes = 'notifyproblem', $align = 'center', $return
 }
 
 /**
- * Print a continue button that goes to a particular URL.
- *
  * @deprecated use $OUTPUT->continue_button() instead.
- * @todo final deprecation of this function in MDL-40607
- *
- * @param string $link The url to create a link to.
- * @param bool $return If set to true output is returned rather than echoed, default false
- * @return string|void HTML String if return=true nothing otherwise
  */
 function print_continue($link, $return = false) {
-    global $CFG, $OUTPUT;
-
-    debugging('print_continue() is deprecated. Please use $OUTPUT->continue_button() instead.', DEBUG_DEVELOPER);
-
-    if ($link == '') {
-        if (!empty($_SERVER['HTTP_REFERER'])) {
-            $link = $_SERVER['HTTP_REFERER'];
-            $link = str_replace('&', '&amp;', $link); // make it valid XHTML
-        } else {
-            $link = $CFG->wwwroot .'/';
-        }
-    }
-
-    $output = $OUTPUT->continue_button($link);
-    if ($return) {
-        return $output;
-    } else {
-        echo $output;
-    }
+    throw new coding_exception('print_continue() can not be used any more. Please use $OUTPUT->continue_button() instead.');
 }
 
 /**
- * Print a standard header
- *
  * @deprecated use $PAGE methods instead.
- * @todo final deprecation of this function in MDL-40607
- * @param string  $title Appears at the top of the window
- * @param string  $heading Appears at the top of the page
- * @param string  $navigation Array of $navlinks arrays (keys: name, link, type) for use as breadcrumbs links
- * @param string  $focus Indicates form element to get cursor focus on load eg  inputform.password
- * @param string  $meta Meta tags to be added to the header
- * @param boolean $cache Should this page be cacheable?
- * @param string  $button HTML code for a button (usually for module editing)
- * @param string  $menu HTML code for a popup menu
- * @param boolean $usexml use XML for this page
- * @param string  $bodytags This text will be included verbatim in the <body> tag (useful for onload() etc)
- * @param bool    $return If true, return the visible elements of the header instead of echoing them.
- * @return string|void If return=true then string else void
  */
 function print_header($title='', $heading='', $navigation='', $focus='',
                       $meta='', $cache=true, $button='&nbsp;', $menu=null,
                       $usexml=false, $bodytags='', $return=false) {
-    global $PAGE, $OUTPUT;
-
-    debugging('print_header() is deprecated. Please use $PAGE methods instead.', DEBUG_DEVELOPER);
-
-    $PAGE->set_title($title);
-    $PAGE->set_heading($heading);
-    $PAGE->set_cacheable($cache);
-    if ($button == '') {
-        $button = '&nbsp;';
-    }
-    $PAGE->set_button($button);
-    $PAGE->set_headingmenu($menu);
-
-    // TODO $menu
-
-    if ($meta) {
-        throw new coding_exception('The $meta parameter to print_header is no longer supported. '.
-                'You should be able to do everything you want with $PAGE->requires and other such mechanisms.');
-    }
-    if ($usexml) {
-        throw new coding_exception('The $usexml parameter to print_header is no longer supported.');
-    }
-    if ($bodytags) {
-        throw new coding_exception('The $bodytags parameter to print_header is no longer supported.');
-    }
 
-    $output = $OUTPUT->header();
-
-    if ($return) {
-        return $output;
-    } else {
-        echo $output;
-    }
+    throw new coding_exception('print_header() can not be used any more. Please use $PAGE methods instead.');
 }
 
 /**
- * This version of print_header is simpler because the course name does not have to be
- * provided explicitly in the strings. It can be used on the site page as in courses
- * Eventually all print_header could be replaced by print_header_simple
- *
  * @deprecated use $PAGE methods instead.
- * @todo final deprecation of this function in MDL-40607
- * @param string $title Appears at the top of the window
- * @param string $heading Appears at the top of the page
- * @param string $navigation Premade navigation string (for use as breadcrumbs links)
- * @param string $focus Indicates form element to get cursor focus on load eg  inputform.password
- * @param string $meta Meta tags to be added to the header
- * @param boolean $cache Should this page be cacheable?
- * @param string $button HTML code for a button (usually for module editing)
- * @param string $menu HTML code for a popup menu
- * @param boolean $usexml use XML for this page
- * @param string $bodytags This text will be included verbatim in the <body> tag (useful for onload() etc)
- * @param bool   $return If true, return the visible elements of the header instead of echoing them.
- * @return string|void If $return=true the return string else nothing
  */
 function print_header_simple($title='', $heading='', $navigation='', $focus='', $meta='',
                        $cache=true, $button='&nbsp;', $menu='', $usexml=false, $bodytags='', $return=false) {
 
-    global $COURSE, $CFG, $PAGE, $OUTPUT;
-
-    debugging('print_header_simple() is deprecated. Please use $PAGE methods instead.', DEBUG_DEVELOPER);
-
-    if ($meta) {
-        throw new coding_exception('The $meta parameter to print_header is no longer supported. '.
-                'You should be able to do everything you want with $PAGE->requires and other such mechanisms.');
-    }
-    if ($usexml) {
-        throw new coding_exception('The $usexml parameter to print_header is no longer supported.');
-    }
-    if ($bodytags) {
-        throw new coding_exception('The $bodytags parameter to print_header is no longer supported.');
-    }
-
-    $PAGE->set_title($title);
-    $PAGE->set_heading($heading);
-    $PAGE->set_cacheable(true);
-    $PAGE->set_button($button);
-
-    $output = $OUTPUT->header();
-
-    if ($return) {
-        return $output;
-    } else {
-        echo $output;
-    }
+    throw new coding_exception('print_header_simple() can not be used any more. Please use $PAGE methods instead.');
 }
 
 /**
- * Prints a nice side block with an optional header.  The content can either
- * be a block of HTML or a list of text with optional icons.
- *
- * @static int $block_id Increments for each call to the function
- * @param string $heading HTML for the heading. Can include full HTML or just
- *   plain text - plain text will automatically be enclosed in the appropriate
- *   heading tags.
- * @param string $content HTML for the content
- * @param array $list an alternative to $content, it you want a list of things with optional icons.
- * @param array $icons optional icons for the things in $list.
- * @param string $footer Extra HTML content that gets output at the end, inside a &lt;div class="footer">
- * @param array $attributes an array of attribute => value pairs that are put on the
- * outer div of this block. If there is a class attribute ' block' gets appended to it. If there isn't
- * already a class, class='block' is used.
- * @param string $title Plain text title, as embedded in the $heading.
  * @deprecated use $OUTPUT->block() instead.
- * @todo final deprecation of this function in MDL-40607
  */
 function print_side_block($heading='', $content='', $list=NULL, $icons=NULL, $footer='', $attributes = array(), $title='') {
-    global $OUTPUT;
-
-    debugging('print_side_block() is deprecated, please use $OUTPUT->block() instead.', DEBUG_DEVELOPER);
-    // We don't use $heading, becuse it often contains HTML that we don't want.
-    // However, sometimes $title is not set, but $heading is.
-    if (empty($title)) {
-        $title = strip_tags($heading);
-    }
-
-    // Render list contents to HTML if required.
-    if (empty($content) && $list) {
-        $content = $OUTPUT->list_block_contents($icons, $list);
-    }
-
-    $bc = new block_contents();
-    $bc->content = $content;
-    $bc->footer = $footer;
-    $bc->title = $title;
-
-    if (isset($attributes['id'])) {
-        $bc->id = $attributes['id'];
-        unset($attributes['id']);
-    }
-    $bc->attributes = $attributes;
-
-    echo $OUTPUT->block($bc, BLOCK_POS_LEFT); // POS LEFT may be wrong, but no way to get a better guess here.
+    throw new coding_exception('print_side_block() can not be used any more, please use $OUTPUT->block() instead.');
 }
 
 /**
@@ -1445,7 +1152,7 @@ function editorshortcutshelpbutton() {
  * provide this function with the language strings for sortasc and sortdesc.
  *
  * @deprecated use $OUTPUT->arrow() instead.
- * @todo final deprecation of this function in MDL-40607
+ * @todo final deprecation of this function once MDL-45448 is resolved
  *
  * If no sort string is associated with the direction, an arrow with no alt text will be printed/returned.
  *
@@ -1554,66 +1261,18 @@ function choose_from_menu ($options, $name, $selected='', $nothing='choose', $sc
 }
 
 /**
- * Prints a help button about a scale
- *
  * @deprecated use $OUTPUT->help_icon_scale($courseid, $scale) instead.
- * @todo final deprecation of this function in MDL-40607
- *
- * @global object
- * @param id $courseid
- * @param object $scale
- * @param boolean $return If set to true returns rather than echo's
- * @return string|bool Depending on value of $return
  */
 function print_scale_menu_helpbutton($courseid, $scale, $return=false) {
-    global $OUTPUT;
-
-    debugging('print_scale_menu_helpbutton() is deprecated. Please use $OUTPUT->help_icon_scale($courseid, $scale) instead.', DEBUG_DEVELOPER);
-
-    $output = $OUTPUT->help_icon_scale($courseid, $scale);
-
-    if ($return) {
-        return $output;
-    } else {
-        echo $output;
-    }
+    throw new coding_exception('print_scale_menu_helpbutton() can not be used any more. '.
+        'Please use $OUTPUT->help_icon_scale($courseid, $scale) instead.');
 }
 
 /**
- * Display an standard html checkbox with an optional label
- *
  * @deprecated use html_writer::checkbox() instead.
- * @todo final deprecation of this function in MDL-40607
- *
- * @staticvar int $idcounter
- * @param string $name    The name of the checkbox
- * @param string $value   The valus that the checkbox will pass when checked
- * @param bool $checked The flag to tell the checkbox initial state
- * @param string $label   The label to be showed near the checkbox
- * @param string $alt     The info to be inserted in the alt tag
- * @param string $script If not '', then this is added to the checkbox element
- *                       as an onchange handler.
- * @param bool $return Whether this function should return a string or output
- *                     it (defaults to false)
- * @return string|void If $return=true returns string, else echo's and returns void
  */
 function print_checkbox($name, $value, $checked = true, $label = '', $alt = '', $script='', $return=false) {
-    global $OUTPUT;
-
-    debugging('print_checkbox() is deprecated. Please use html_writer::checkbox() instead.', DEBUG_DEVELOPER);
-
-    if (!empty($script)) {
-        debugging('The use of the $script param in print_checkbox has not been migrated into html_writer::checkbox().', DEBUG_DEVELOPER);
-    }
-
-    $output = html_writer::checkbox($name, $value, $checked, $label);
-
-    if (empty($return)) {
-        echo $output;
-    } else {
-        return $output;
-    }
-
+    throw new coding_exception('print_checkbox() can not be used any more. Please use html_writer::checkbox() instead.');
 }
 
 /**
@@ -1644,148 +1303,34 @@ function update_module_button($cmid, $ignored, $string) {
 }
 
 /**
- * Prints breadcrumb trail of links, called in theme/-/header.html
- *
- * This function has now been deprecated please use output's navbar method instead
- * as shown below
- *
- * <code php>
- * echo $OUTPUT->navbar();
- * </code>
- *
  * @deprecated use $OUTPUT->navbar() instead
- * @todo final deprecation of this function in MDL-40607
- * @param mixed $navigation deprecated
- * @param string $separator OBSOLETE, and now deprecated
- * @param boolean $return False to echo the breadcrumb string (default), true to return it.
- * @return string|void String or null, depending on $return.
  */
 function print_navigation ($navigation, $separator=0, $return=false) {
-    global $OUTPUT,$PAGE;
-
-    debugging('print_navigation() is deprecated, please update use $OUTPUT->navbar() instead.', DEBUG_DEVELOPER);
-
-    $output = $OUTPUT->navbar();
-
-    if ($return) {
-        return $output;
-    } else {
-        echo $output;
-    }
+    throw new coding_exception('print_navigation() can not be used any more, please update use $OUTPUT->navbar() instead.');
 }
 
 /**
- * This function will build the navigation string to be used by print_header
- * and others.
- *
- * It automatically generates the site and course level (if appropriate) links.
- *
- * If you pass in a $cm object, the method will also generate the activity (e.g. 'Forums')
- * and activityinstances (e.g. 'General Developer Forum') navigation levels.
- *
- * If you want to add any further navigation links after the ones this function generates,
- * the pass an array of extra link arrays like this:
- * array(
- *     array('name' => $linktext1, 'link' => $url1, 'type' => $linktype1),
- *     array('name' => $linktext2, 'link' => $url2, 'type' => $linktype2)
- * )
- * The normal case is to just add one further link, for example 'Editing forum' after
- * 'General Developer Forum', with no link.
- * To do that, you need to pass
- * array(array('name' => $linktext, 'link' => '', 'type' => 'title'))
- * However, becuase this is a very common case, you can use a shortcut syntax, and just
- * pass the string 'Editing forum', instead of an array as $extranavlinks.
- *
- * At the moment, the link types only have limited significance. Type 'activity' is
- * recognised in order to implement the $CFG->hideactivitytypenavlink feature. Types
- * that are known to appear are 'home', 'course', 'activity', 'activityinstance' and 'title'.
- * This really needs to be documented better. In the mean time, try to be consistent, it will
- * enable people to customise the navigation more in future.
- *
- * When passing a $cm object, the fields used are $cm->modname, $cm->name and $cm->course.
- * If you get the $cm object using the function get_coursemodule_from_instance or
- * get_coursemodule_from_id (as recommended) then this will be done for you automatically.
- * If you don't have $cm->modname or $cm->name, this fuction will attempt to find them using
- * the $cm->module and $cm->instance fields, but this takes extra database queries, so a
- * warning is printed in developer debug mode.
- *
  * @deprecated Please use $PAGE->navabar methods instead.
- * @todo final deprecation of this function in MDL-40607
- * @param mixed $extranavlinks - Normally an array of arrays, keys: name, link, type. If you
- *      only want one extra item with no link, you can pass a string instead. If you don't want
- *      any extra links, pass an empty string.
- * @param mixed $cm deprecated
- * @return array Navigation array
  */
 function build_navigation($extranavlinks, $cm = null) {
-    global $CFG, $COURSE, $DB, $SITE, $PAGE;
-
-    debugging('build_navigation() is deprecated, please use $PAGE->navbar methods instead.', DEBUG_DEVELOPER);
-    if (is_array($extranavlinks) && count($extranavlinks)>0) {
-        foreach ($extranavlinks as $nav) {
-            if (array_key_exists('name', $nav)) {
-                if (array_key_exists('link', $nav) && !empty($nav['link'])) {
-                    $link = $nav['link'];
-                } else {
-                    $link = null;
-                }
-                $PAGE->navbar->add($nav['name'],$link);
-            }
-        }
-    }
-
-    return(array('newnav' => true, 'navlinks' => array()));
+    throw new coding_exception('build_navigation() can not be used any more, please use $PAGE->navbar methods instead.');
 }
 
 /**
  * @deprecated not relevant with global navigation in Moodle 2.x+
- * @todo remove completely in MDL-40607
  */
 function navmenu($course, $cm=NULL, $targetwindow='self') {
-    // This function has been deprecated with the creation of the global nav in
-    // moodle 2.0
-    debugging('navmenu() is deprecated, it is no longer relevant with global navigation.', DEBUG_DEVELOPER);
-
-    return '';
+    throw new coding_exception('navmenu() can not be used any more, it is no longer relevant with global navigation.');
 }
 
 /// CALENDAR MANAGEMENT  ////////////////////////////////////////////////////////////////
 
 
 /**
- * Call this function to add an event to the calendar table and to call any calendar plugins
- *
- * @param object $event An object representing an event from the calendar table.
- * The event will be identified by the id field. The object event should include the following:
- *  <ul>
- *    <li><b>$event->name</b> - Name for the event
- *    <li><b>$event->description</b> - Description of the event (defaults to '')
- *    <li><b>$event->format</b> - Format for the description (using formatting types defined at the top of weblib.php)
- *    <li><b>$event->courseid</b> - The id of the course this event belongs to (0 = all courses)
- *    <li><b>$event->groupid</b> - The id of the group this event belongs to (0 = no group)
- *    <li><b>$event->userid</b> - The id of the user this event belongs to (0 = no user)
- *    <li><b>$event->modulename</b> - Name of the module that creates this event
- *    <li><b>$event->instance</b> - Instance of the module that owns this event
- *    <li><b>$event->eventtype</b> - The type info together with the module info could
- *             be used by calendar plugins to decide how to display event
- *    <li><b>$event->timestart</b>- Timestamp for start of event
- *    <li><b>$event->timeduration</b> - Duration (defaults to zero)
- *    <li><b>$event->visible</b> - 0 if the event should be hidden (e.g. because the activity that created it is hidden)
- *  </ul>
- * @return int|false The id number of the resulting record or false if failed
  * @deprecated please use calendar_event::create() instead.
- * @todo final deprecation of this function in MDL-40607
  */
  function add_event($event) {
-    global $CFG;
-    require_once($CFG->dirroot.'/calendar/lib.php');
-
-    debugging('add_event() is deprecated, please use calendar_event::create() instead.', DEBUG_DEVELOPER);
-    $event = calendar_event::create($event);
-    if ($event !== false) {
-        return $event->id;
-    }
-    return false;
+    throw new coding_exception('add_event() can not be used any more, please use calendar_event::create() instead.');
 }
 
 /**
@@ -1807,59 +1352,27 @@ function update_event($event) {
 }
 
 /**
- * Call this function to delete the event with id $id from calendar table.
- *
- * @param int $id The id of an event from the 'event' table.
- * @return bool
  * @deprecated please use calendar_event->delete() instead.
- * @todo final deprecation of this function in MDL-40607
  */
 function delete_event($id) {
-    global $CFG;
-    require_once($CFG->dirroot.'/calendar/lib.php');
-
-    debugging('delete_event() is deprecated, please use calendar_event->delete() instead.', DEBUG_DEVELOPER);
-
-    $event = calendar_event::load($id);
-    return $event->delete();
+    throw new coding_exception('delete_event() can not be used any more, please use '.
+        'calendar_event->delete() instead.');
 }
 
 /**
- * Call this function to hide an event in the calendar table
- * the event will be identified by the id field of the $event object.
- *
- * @param object $event An object representing an event from the calendar table. The event will be identified by the id field.
- * @return true
  * @deprecated please use calendar_event->toggle_visibility(false) instead.
- * @todo final deprecation of this function in MDL-40607
  */
 function hide_event($event) {
-    global $CFG;
-    require_once($CFG->dirroot.'/calendar/lib.php');
-
-    debugging('hide_event() is deprecated, please use calendar_event->toggle_visibility(false) instead.', DEBUG_DEVELOPER);
-
-    $event = new calendar_event($event);
-    return $event->toggle_visibility(false);
+    throw new coding_exception('hide_event() can not be used any more, please use '.
+        'calendar_event->toggle_visibility(false) instead.');
 }
 
 /**
- * Call this function to unhide an event in the calendar table
- * the event will be identified by the id field of the $event object.
- *
- * @param object $event An object representing an event from the calendar table. The event will be identified by the id field.
- * @return true
  * @deprecated please use calendar_event->toggle_visibility(true) instead.
- * @todo final deprecation of this function in MDL-40607
  */
 function show_event($event) {
-    global $CFG;
-    require_once($CFG->dirroot.'/calendar/lib.php');
-
-    debugging('show_event() is deprecated, please use calendar_event->toggle_visibility(true) instead.', DEBUG_DEVELOPER);
-
-    $event = new calendar_event($event);
-    return $event->toggle_visibility(true);
+    throw new coding_exception('show_event() can not be used any more, please use '.
+        'calendar_event->toggle_visibility(true) instead.');
 }
 
 /**
diff --git a/lib/editor/atto/upgrade.txt b/lib/editor/atto/upgrade.txt
new file mode 100644 (file)
index 0000000..d5267f6
--- /dev/null
@@ -0,0 +1,9 @@
+This files describes API changes in the editor_atto code.
+
+=== 2.9 ===
+
+* When adding a shortcut to the button of a plugin, atto will add a layer of validation
+to ensure that only the required keys are pressed. However, if you are using a custom
+keyConfig object you must validate the shortcut yourself. This is particularly important
+for non-English keyboard users. For more information read the documentation of
+EditorPluginButtons::_addKeyboardListener() and MDL-47133.
index cde46db..a378090 100644 (file)
Binary files a/lib/editor/atto/yui/build/moodle-editor_atto-plugin/moodle-editor_atto-plugin-debug.js and b/lib/editor/atto/yui/build/moodle-editor_atto-plugin/moodle-editor_atto-plugin-debug.js differ
index ecebe59..72e69db 100644 (file)
Binary files a/lib/editor/atto/yui/build/moodle-editor_atto-plugin/moodle-editor_atto-plugin-min.js and b/lib/editor/atto/yui/build/moodle-editor_atto-plugin/moodle-editor_atto-plugin-min.js differ
index 3654b27..eaf01d0 100644 (file)
Binary files a/lib/editor/atto/yui/build/moodle-editor_atto-plugin/moodle-editor_atto-plugin.js and b/lib/editor/atto/yui/build/moodle-editor_atto-plugin/moodle-editor_atto-plugin.js differ
index 8aad568..13cdd8f 100644 (file)
@@ -634,7 +634,11 @@ EditorPluginButtons.prototype = {
      * The keyConfig will take either an array of keyConfigurations, in
      * which case _addKeyboardListener is called multiple times; an object
      * containing an optional eventtype, optional container, and a set of
-     * keyCodes, or just a string containing the keyCodes.
+     * keyCodes, or just a string containing the keyCodes. When keyConfig is
+     * not an object, it is wrapped around a function that ensures that
+     * only the expected key modifiers were used. For instance, it checks
+     * that space+ctrl is not triggered when the user presses ctrl+shift+space.
+     * When using an object, the developer should check that manually.
      *
      * @method _addKeyboardListener
      * @param {function} callback
@@ -648,7 +652,9 @@ EditorPluginButtons.prototype = {
     _addKeyboardListener: function(callback, keyConfig, buttonName) {
         var eventtype = 'key',
             container = CSS.EDITORWRAPPER,
-            keys;
+            keys,
+            handler,
+            modifier;
 
         if (Y.Lang.isArray(keyConfig)) {
             // If an Array was specified, call the add function for each element.
@@ -669,19 +675,27 @@ EditorPluginButtons.prototype = {
 
             // Must be specified.
             keys = keyConfig.keyCodes;
+            handler = callback;
 
         } else {
-            keys = this._getKeyEvent() + keyConfig + this._getDefaultMetaKey();
+            modifier = this._getDefaultMetaKey();
+            keys = this._getKeyEvent() + keyConfig + '+' + modifier;
             if (typeof this._primaryKeyboardShortcut[buttonName] === 'undefined') {
                 this._primaryKeyboardShortcut[buttonName] = this._getDefaultMetaKeyDescription(keyConfig);
             }
 
+            // Wrap the callback into a handler to check if it uses the specified modifiers, not more.
+            handler = Y.bind(function(modifiers, e) {
+                if (this._eventUsesExactKeyModifiers(modifiers, e)) {
+                    callback.apply(this, [e]);
+                }
+            }, this, [modifier]);
         }
 
         this._buttonHandlers.push(
             this.editor.delegate(
                 eventtype,
-                callback,
+                handler,
                 keys,
                 container,
                 this
@@ -692,6 +706,34 @@ EditorPluginButtons.prototype = {
                 'debug', LOGNAME);
     },
 
+    /**
+     * Checks if a key event was strictly defined for the modifiers passed.
+     *
+     * @method _eventUsesExactKeyModifiers
+     * @param  {Array} modifiers List of key modifiers to check for (alt, ctrl, meta or shift).
+     * @param  {EventFacade} e The event facade.
+     * @return {Boolean} True if the event was stricly using the modifiers specified.
+     */
+    _eventUsesExactKeyModifiers: function(modifiers, e) {
+        var exactMatch = true,
+            hasKey;
+
+        if (e.type !== 'key') {
+            return false;
+        }
+
+        hasKey = Y.Array.indexOf(modifiers, 'alt') > -1;
+        exactMatch = exactMatch && ((e.altKey && hasKey) || (!e.altKey && !hasKey));
+        hasKey = Y.Array.indexOf(modifiers, 'ctrl') > -1;
+        exactMatch = exactMatch && ((e.ctrlKey && hasKey) || (!e.ctrlKey && !hasKey));
+        hasKey = Y.Array.indexOf(modifiers, 'meta') > -1;
+        exactMatch = exactMatch && ((e.metaKey && hasKey) || (!e.metaKey && !hasKey));
+        hasKey = Y.Array.indexOf(modifiers, 'shift') > -1;
+        exactMatch = exactMatch && ((e.shiftKey && hasKey) || (!e.shiftKey && !hasKey));
+
+        return exactMatch;
+    },
+
     /**
      * Determine if this plugin is enabled, based upon the state of it's buttons.
      *
@@ -829,9 +871,9 @@ EditorPluginButtons.prototype = {
      */
     _getDefaultMetaKey: function() {
         if (Y.UA.os === 'macintosh') {
-            return '+meta';
+            return 'meta';
         } else {
-            return '+ctrl';
+            return 'ctrl';
         }
     },
 
index 4d90789..a1d4743 100644 (file)
@@ -14,7 +14,7 @@ M.tinymce_managefiles.analysefiles = function(Y) {
     }
     if (missingfiles !== '') {
         form.addClass('hasmissingfiles')
-        form.one('.managefilesstatus').setContent(M.str.tinymce_managefiles.hasmissingfiles + ' <ul>' + missingfiles + '</ul>').addClass('error');
+        form.one('.managefilesstatus').setContent(M.util.get_string('hasmissingfiles', 'tinymce_managefiles') + ' <ul>' + missingfiles + '</ul>').addClass('error');
     }
     delfilesfieldset.all('.felement.fcheckbox').each(function(el) {
         var chb = el.one('input[type=checkbox]'),
@@ -25,6 +25,6 @@ M.tinymce_managefiles.analysefiles = function(Y) {
         }
     });
     if (missingfiles === '' && !form.hasClass('hasunusedfiles')) {
-        form.one('.managefilesstatus').setContent(M.str.tinymce_managefiles.allfilesok);
+        form.one('.managefilesstatus').setContent(M.util.get_string('allfilesok', 'tinymce_managefiles'));
     }
 }
index b53446d..e29d3c0 100644 (file)
@@ -543,10 +543,10 @@ M.form_dndupload.init = function(Y, options) {
          * @param string type - 'error' or 'info'
<