Merge branch 'MDL-59048' of https://github.com/timhunt/moodle
authorDan Poltawski <dan@moodle.com>
Mon, 19 Jun 2017 14:43:28 +0000 (15:43 +0100)
committerDan Poltawski <dan@moodle.com>
Mon, 19 Jun 2017 14:43:28 +0000 (15:43 +0100)
354 files changed:
.eslintrc
Gruntfile.js
admin/registration/forms.php
admin/registration/lib.php
admin/registration/register.php
admin/settings/appearance.php
admin/settings/subsystems.php
admin/templates/setting_filetypes.mustache [new file with mode: 0644]
admin/tool/customlang/db/upgrade.php
admin/tool/log/backup/moodle2/restore_tool_log_logstore_subplugin.class.php
admin/tool/log/db/upgrade.php
admin/tool/log/store/database/db/upgrade.php
admin/tool/log/store/standard/db/upgrade.php
admin/tool/lp/amd/build/competencies.min.js
admin/tool/lp/amd/build/competencyactions.min.js
admin/tool/lp/amd/build/competencypicker.min.js
admin/tool/lp/amd/build/competencypicker_user_plans.min.js
admin/tool/lp/amd/build/competencyruleconfig.min.js
admin/tool/lp/amd/build/form-cohort-selector.min.js
admin/tool/lp/amd/build/form-user-selector.min.js
admin/tool/lp/amd/build/frameworks_datasource.min.js
admin/tool/lp/amd/build/planactions.min.js
admin/tool/lp/amd/build/user_competency_plan_popup.min.js
admin/tool/lp/amd/build/user_competency_workflow.min.js
admin/tool/lp/amd/build/user_evidence_actions.min.js
admin/tool/lp/amd/src/actionselector.js
admin/tool/lp/amd/src/competencies.js
admin/tool/lp/amd/src/competency_rule_points.js
admin/tool/lp/amd/src/competencyactions.js
admin/tool/lp/amd/src/competencypicker.js
admin/tool/lp/amd/src/competencypicker_user_plans.js
admin/tool/lp/amd/src/competencyruleconfig.js
admin/tool/lp/amd/src/evidence_delete.js
admin/tool/lp/amd/src/form-cohort-selector.js
admin/tool/lp/amd/src/form-user-selector.js
admin/tool/lp/amd/src/frameworks_datasource.js
admin/tool/lp/amd/src/parentcompetency_form.js
admin/tool/lp/amd/src/planactions.js
admin/tool/lp/amd/src/user_competency_plan_popup.js
admin/tool/lp/amd/src/user_competency_workflow.js
admin/tool/lp/amd/src/user_evidence_actions.js
admin/tool/monitor/db/upgrade.php
admin/tool/templatelibrary/amd/build/display.min.js
admin/tool/templatelibrary/amd/src/display.js
admin/tool/usertours/amd/src/usertours.js
admin/tool/usertours/db/upgrade.php
auth/cas/db/upgrade.php
auth/db/db/upgrade.php
auth/email/db/upgrade.php
auth/fc/db/upgrade.php
auth/imap/db/upgrade.php
auth/ldap/db/upgrade.php
auth/manual/db/upgrade.php
auth/mnet/db/upgrade.php
auth/nntp/db/upgrade.php
auth/none/db/upgrade.php
auth/oauth2/classes/api.php
auth/oauth2/classes/auth.php
auth/oauth2/db/upgrade.php
auth/pam/db/upgrade.php
auth/pop3/db/upgrade.php
auth/shibboleth/db/upgrade.php
availability/classes/info_section.php
availability/condition/date/classes/condition.php
backup/moodle2/backup_stepslib.php
backup/moodle2/restore_stepslib.php
blocks/badges/db/upgrade.php
blocks/calendar_month/db/upgrade.php
blocks/calendar_upcoming/db/upgrade.php
blocks/community/db/upgrade.php
blocks/completionstatus/db/upgrade.php
blocks/completionstatus/details.php
blocks/course_summary/db/upgrade.php
blocks/html/db/upgrade.php
blocks/myoverview/amd/build/event_list.min.js
blocks/myoverview/amd/build/tab_preferences.min.js [new file with mode: 0644]
blocks/myoverview/amd/src/event_list.js
blocks/myoverview/amd/src/tab_preferences.js [new file with mode: 0644]
blocks/myoverview/block_myoverview.php
blocks/myoverview/classes/output/courses_view.php
blocks/myoverview/classes/output/main.php
blocks/myoverview/lang/en/block_myoverview.php
blocks/myoverview/lib.php [new file with mode: 0644]
blocks/myoverview/settings.php [new file with mode: 0644]
blocks/myoverview/templates/main.mustache
blocks/myoverview/tests/behat/block_myoverview_progress.feature
blocks/myoverview/version.php
blocks/navigation/db/upgrade.php
blocks/quiz_results/db/upgrade.php
blocks/recent_activity/db/upgrade.php
blocks/rss_client/db/upgrade.php
blocks/section_links/db/upgrade.php
blocks/selfcompletion/db/upgrade.php
blocks/settings/db/upgrade.php
blocks/tags/lang/en/block_tags.php
blocks/tags/lang/en/deprecated.txt [deleted file]
cache/classes/loaders.php
cache/tests/cache_test.php
calendar/classes/local/event/data_access/event_vault.php
calendar/event_form.php
calendar/export_execute.php
calendar/lib.php
calendar/tests/rrule_manager_test.php
competency/classes/api.php
completion/completion_completion.php
composer.json
composer.lock
course/amd/build/actions.min.js
course/amd/src/actions.js
course/format/topics/db/upgrade.php
course/format/weeks/backup/moodle2/restore_format_weeks_plugin.class.php
course/format/weeks/db/upgrade.php
course/lib.php
course/tests/courselib_test.php
enrol/database/db/upgrade.php
enrol/flatfile/db/upgrade.php
enrol/guest/db/upgrade.php
enrol/imsenterprise/db/upgrade.php
enrol/lti/db/upgrade.php
enrol/manual/db/upgrade.php
enrol/mnet/db/upgrade.php
enrol/paypal/db/upgrade.php
enrol/self/db/upgrade.php
filter/mathjaxloader/db/upgrade.php
filter/mediaplugin/db/upgrade.php
filter/tex/db/upgrade.php
grade/edit/tree/item_form.php
grade/grading/form/guide/db/upgrade.php
grade/grading/form/rubric/db/upgrade.php
grade/report/grader/index.php
grade/report/grader/module.js
grade/report/user/db/upgrade.php
group/assign.php
group/classes/output/index_page.php [new file with mode: 0644]
group/classes/output/renderer.php [new file with mode: 0644]
group/index.php
group/members.php
group/templates/index.mustache [new file with mode: 0644]
install/lang/bi/langconfig.php [new file with mode: 0644]
install/lang/es/admin.php
install/lang/fa/error.php
install/lang/fa/install.php
install/lang/ja/install.php
install/lang/nl/install.php
install/lang/pt/install.php
install/lang/zh_tw/admin.php
lang/en/admin.php
lang/en/cache.php
lang/en/completion.php
lang/en/deprecated.txt
lang/en/form.php
lang/en/grades.php
lang/en/hub.php
lang/en/moodle.php
lang/en/notes.php
lang/en/role.php
lang/en/tag.php
lib/accesslib.php
lib/adminlib.php
lib/amd/build/ajax.min.js
lib/amd/build/fragment.min.js
lib/amd/build/templates.min.js
lib/amd/build/user_date.min.js
lib/amd/src/ajax.js
lib/amd/src/fragment.js
lib/amd/src/templates.js
lib/amd/src/user_date.js
lib/antivirus/clamav/db/upgrade.php
lib/classes/output/external.php
lib/classes/output/icon_system_fontawesome.php
lib/classes/plugininfo/repository.php
lib/completionlib.php
lib/cronlib.php
lib/db/caches.php
lib/db/install.xml
lib/db/services.php
lib/db/upgrade.php
lib/deprecatedlib.php
lib/editor/atto/db/upgrade.php
lib/editor/atto/plugins/equation/db/upgrade.php
lib/editor/tinymce/db/upgrade.php
lib/editor/tinymce/plugins/spellchecker/db/upgrade.php
lib/filelib.php
lib/filterlib.php
lib/form/amd/build/filetypes.min.js [new file with mode: 0644]
lib/form/amd/src/filetypes.js [new file with mode: 0644]
lib/form/classes/external.php [new file with mode: 0644]
lib/form/classes/filetypes_util.php [new file with mode: 0644]
lib/form/filetypes.php [new file with mode: 0644]
lib/form/templates/filetypes-browser.mustache [new file with mode: 0644]
lib/form/templates/filetypes-descriptions.mustache [new file with mode: 0644]
lib/form/templates/filetypes-trigger.mustache [new file with mode: 0644]
lib/form/tests/external_test.php [new file with mode: 0644]
lib/form/tests/filetypes_util_test.php [new file with mode: 0644]
lib/formslib.php
lib/navigationlib.php
lib/outputlib.php
lib/outputrenderers.php
lib/php-css-parser/Parser.php
lib/php-css-parser/moodle_readme.txt
lib/setuplib.php
lib/statslib.php
lib/tablelib.php
lib/templates/loginform.mustache [moved from lib/templates/login.mustache with 99% similarity]
lib/tests/accesslib_test.php
lib/tests/filelib_test.php
lib/tests/output_external_test.php [new file with mode: 0644]
lib/tests/useragent_test.php
lib/upgrade.txt
lib/upgradelib.php
message/amd/build/message_area_messages.min.js
message/amd/build/message_area_search.min.js
message/amd/build/message_repository.min.js
message/amd/src/message_area_messages.js
message/amd/src/message_area_search.js
message/amd/src/message_repository.js
message/output/email/db/upgrade.php
message/output/jabber/db/upgrade.php
message/output/popup/amd/build/message_popover_controller.min.js
message/output/popup/amd/build/notification_area_control_area.min.js
message/output/popup/amd/build/notification_popover_controller.min.js
message/output/popup/amd/build/notification_repository.min.js
message/output/popup/amd/src/message_popover_controller.js
message/output/popup/amd/src/notification_area_control_area.js
message/output/popup/amd/src/notification_popover_controller.js
message/output/popup/amd/src/notification_repository.js
message/output/popup/db/upgrade.php
mod/assign/amd/src/participant_selector.js
mod/assign/db/upgrade.php
mod/assign/feedback/comments/db/upgrade.php
mod/assign/feedback/comments/locallib.php
mod/assign/feedback/comments/tests/behat/feedback_comments.feature [new file with mode: 0644]
mod/assign/feedback/editpdf/db/upgrade.php
mod/assign/feedback/file/db/upgrade.php
mod/assign/gradingtable.php
mod/assign/locallib.php
mod/assign/override_form.php
mod/assign/overrideedit.php
mod/assign/submission/comments/db/upgrade.php
mod/assign/submission/file/db/upgrade.php
mod/assign/submission/onlinetext/db/upgrade.php
mod/assign/tests/markerallocation_test.php [new file with mode: 0644]
mod/assign/version.php
mod/assignment/db/upgrade.php
mod/book/db/upgrade.php
mod/chat/db/upgrade.php
mod/choice/db/upgrade.php
mod/data/db/upgrade.php
mod/data/tests/behat/completion_condition_entries.feature
mod/feedback/classes/completion.php
mod/feedback/classes/external.php
mod/feedback/db/upgrade.php
mod/feedback/lib.php
mod/folder/db/upgrade.php
mod/forum/db/upgrade.php
mod/forum/lang/en/deprecated.txt
mod/forum/lang/en/forum.php
mod/glossary/db/upgrade.php
mod/imscp/db/upgrade.php
mod/label/db/upgrade.php
mod/lesson/classes/external.php
mod/lesson/db/upgrade.php
mod/lesson/lang/en/deprecated.txt
mod/lesson/lang/en/lesson.php
mod/lesson/locallib.php
mod/lesson/tests/external_test.php
mod/lesson/upgrade.txt
mod/lti/amd/build/contentitem.min.js
mod/lti/amd/build/tool_card_controller.min.js
mod/lti/amd/src/contentitem.js
mod/lti/amd/src/tool_card_controller.js
mod/lti/db/services.php
mod/lti/db/upgrade.php
mod/page/db/upgrade.php
mod/quiz/comment.php
mod/quiz/db/upgrade.php
mod/quiz/mod_form.php
mod/quiz/module.js
mod/quiz/report/overview/db/upgrade.php
mod/quiz/report/statistics/db/upgrade.php
mod/quiz/tests/behat/editing_remove_multiple_questions.feature
mod/resource/db/upgrade.php
mod/scorm/db/upgrade.php
mod/scorm/lib.php
mod/scorm/mod_form.php
mod/scorm/player.php
mod/scorm/tests/lib_test.php
mod/survey/amd/build/validation.min.js
mod/survey/amd/src/validation.js
mod/survey/db/upgrade.php
mod/url/db/upgrade.php
mod/wiki/create.php
mod/wiki/db/upgrade.php
mod/wiki/locallib.php
mod/workshop/db/upgrade.php
mod/workshop/form/accumulative/db/upgrade.php
mod/workshop/form/comments/db/upgrade.php
mod/workshop/form/numerrors/db/upgrade.php
mod/workshop/form/rubric/db/upgrade.php
npm-shrinkwrap.json
package.json
portfolio/boxnet/db/upgrade.php
portfolio/googledocs/db/upgrade.php
portfolio/picasa/db/upgrade.php
question/behaviour/manualgraded/db/upgrade.php
question/type/calculated/db/upgrade.php
question/type/calculated/edit_calculated_form.php
question/type/calculatedmulti/edit_calculatedmulti_form.php
question/type/ddmarker/db/upgrade.php
question/type/essay/db/upgrade.php
question/type/match/db/upgrade.php
question/type/multianswer/db/upgrade.php
question/type/multichoice/classes/admin_setting_answernumbering.php [new file with mode: 0644]
question/type/multichoice/db/upgrade.php
question/type/multichoice/edit_multichoice_form.php
question/type/multichoice/lang/en/qtype_multichoice.php
question/type/multichoice/renderer.php
question/type/multichoice/settings.php [new file with mode: 0644]
question/type/numerical/db/upgrade.php
question/type/random/db/upgrade.php
question/type/randomsamatch/db/upgrade.php
question/type/shortanswer/db/upgrade.php
report/competency/amd/build/grading_popup.min.js
report/competency/amd/src/grading_popup.js
report/stats/locallib.php
report/stats/user.php
repository/boxnet/db/upgrade.php
repository/dropbox/db/upgrade.php
repository/googledocs/db/upgrade.php
repository/onedrive/db/upgrade.php
repository/picasa/db/upgrade.php
search/classes/engine.php
search/classes/manager.php
tag/tests/events_test.php
theme/boost/classes/output/core_renderer.php
theme/boost/cli/import-bootswatch.php
theme/boost/cli/readme_moodle.txt [deleted file]
theme/boost/scss/moodle/drawer.scss
theme/boost/scss/moodle/forms.scss
theme/boost/templates/core/loginform.mustache [moved from theme/boost/templates/core/login.mustache with 99% similarity]
theme/boost/templates/core_form/element-filetypes.mustache [new file with mode: 0644]
theme/boost/templates/header.mustache
theme/boost/tests/behat/behat_theme_boost_behat_course.php
theme/bootstrapbase/less/moodle/forms.less
theme/bootstrapbase/style/moodle.css
theme/clean/classes/core_renderer.php
theme/more/db/upgrade.php
theme/styles.php
theme/upgrade.txt
user/externallib.php
user/files.php
user/tests/externallib_test.php
user/tests/userlib_test.php
version.php

index 3514759..c50d2bb 100644 (file)
--- a/.eslintrc
+++ b/.eslintrc
@@ -1,4 +1,7 @@
 {
+  'plugins': [
+    'promise',
+  ],
   'env': {
     'browser': true,
     'amd': true
     'unicode-bom': 'error',
     'wrap-regex': 'off',
 
+    // === Promises ===
+    'promise/always-return': 'warn',
+    'promise/no-return-wrap': 'warn',
+    'promise/param-names': 'warn',
+    'promise/catch-or-return': ['warn', {terminationMethod: ['catch', 'fail']}],
+    'promise/no-native': 'warn',
+    'promise/no-promise-in-callback': 'warn',
+    'promise/no-callback-in-promise': 'warn',
+    'promise/avoid-new': 'warn',
+
     // === Deprecations ===
     "no-restricted-properties": ['warn', {
         'object': 'M',
         'property': 'str',
         'message': 'Use AMD module "core/str" or M.util.get_string()'
     }],
+
   }
 }
index 4cb9e9e..5f2302e 100644 (file)
@@ -338,7 +338,7 @@ module.exports = function(grunt) {
             opts: {stdio: 'inherit', env: process.env}
         }, function(error, result, code) {
             // Propagate the exit code.
-            done(code);
+            done(code === 0);
         });
     };
 
index ddf2db6..92b15e8 100644 (file)
@@ -249,6 +249,11 @@ class site_registration_form extends moodleform {
         $mediancoursesize = get_config('hub', 'site_mediancoursesize_' . $cleanhuburl);
         $participantnumberaveragecfg = get_config('hub', 'site_participantnumberaverage_' . $cleanhuburl);
         $modulenumberaveragecfg = get_config('hub', 'site_modulenumberaverage_' . $cleanhuburl);
+        // Mobile related information.
+        $mobileservicesenabled = get_config('hub', 'site_mobileservicesenabled_' . $cleanhuburl);
+        $mobilenotificacionsenabled = get_config('hub', 'site_mobilenotificacionsenabled_' . $cleanhuburl);
+        $registereduserdevices = get_config('hub', 'site_registereduserdevices_' . $cleanhuburl);
+        $registeredactiveuserdevices = get_config('hub', 'site_registeredactiveuserdevices_' . $cleanhuburl);
 
         //hidden parameters
         $mform->addElement('hidden', 'huburl', $huburl);
@@ -386,6 +391,21 @@ class site_registration_form extends moodleform {
         require_once($CFG->libdir . '/badgeslib.php');
         $badges = $DB->count_records_select('badge', 'status <> ' . BADGE_STATUS_ARCHIVED);
         $issuedbadges = $DB->count_records('badge_issued');
+        // Mobile related information.
+        $ismobileenabled = false;
+        $aremobilenotificationsenabled = false;
+        $registereduserdevicescount = 0;
+        $registeredactiveuserdevicescount = 0;
+        if (!empty($CFG->enablewebservices) && !empty($CFG->enablemobilewebservice)) {
+            $ismobileenabled = true;
+            $registereduserdevicescount = $DB->count_records('user_devices');
+            $airnotifierextpath = $CFG->dirroot . '/message/output/airnotifier/externallib.php';
+            if (file_exists($airnotifierextpath)) { // Maybe some one uninstalled the plugin.
+                require_once($airnotifierextpath);
+                $aremobilenotificationsenabled = (bool) message_airnotifier_external::is_system_configured();
+                $registeredactiveuserdevicescount = $DB->count_records('message_airnotifier_devices', array('enable' => 1));
+            }
+        }
 
         if (HUB_MOODLEORGHUBURL != $huburl) {
             $mform->addElement('checkbox', 'courses', get_string('sendfollowinginfo', 'hub'),
@@ -438,6 +458,28 @@ class site_registration_form extends moodleform {
                     " " . get_string('modulenumberaverage', 'hub', $modulenumberaverage));
             $mform->setDefault('modulenumberaverage', $modulenumberaveragecfg != -1);
             $mform->setType('modulenumberaverage', PARAM_FLOAT);
+
+            $mobileservicestatus = $ismobileenabled ? 'yes' : 'no';
+            $mform->addElement('checkbox', 'mobileservicesenabled', '',
+                    " " . get_string('mobileservicesenabled', 'hub', $mobileservicestatus));
+            $mform->setDefault('mobileservicesenabled', $mobileservicesenabled != -1);
+            $mform->setType('mobileservicesenabled', PARAM_INT);
+
+            $mobilenotificationsstatus = $aremobilenotificationsenabled ? 'yes' : 'no';
+            $mform->addElement('checkbox', 'mobilenotificacionsenabled', '',
+                    " " . get_string('mobilenotificacionsenabled', 'hub', $mobilenotificationsstatus));
+            $mform->setDefault('mobilenotificacionsenabled', $mobilenotificacionsenabled != -1);
+            $mform->setType('mobilenotificacionsenabled', PARAM_INT);
+
+            $mform->addElement('checkbox', 'registereduserdevices', '',
+                    " " . get_string('registereduserdevices', 'hub', $registereduserdevicescount));
+            $mform->setDefault('registereduserdevices', $registereduserdevices != -1);
+            $mform->setType('registereduserdevices', PARAM_INT);
+
+            $mform->addElement('checkbox', 'registeredactiveuserdevices', '',
+                    " " . get_string('registeredactiveuserdevices', 'hub', $registeredactiveuserdevicescount));
+            $mform->setDefault('registeredactiveuserdevices', $registeredactiveuserdevices != -1);
+            $mform->setType('registeredactiveuserdevices', PARAM_INT);
         } else {
             $mform->addElement('static', 'courseslabel', get_string('sendfollowinginfo', 'hub'),
                     " " . get_string('coursesnumber', 'hub', $coursecount));
@@ -489,6 +531,28 @@ class site_registration_form extends moodleform {
                     " " . get_string('modulenumberaverage', 'hub', $modulenumberaverage));
             $mform->addElement('hidden', 'modulenumberaverage', 1);
             $mform->setType('modulenumberaverage', PARAM_FLOAT);
+
+            $mobileservicestatus = $ismobileenabled ? 'yes' : 'no';
+            $mform->addElement('static', 'mobileservicesenabledlabel', '',
+                    " " . get_string('mobileservicesenabled', 'hub', $mobileservicestatus));
+            $mform->addElement('hidden', 'mobileservicesenabled', 1);
+            $mform->setType('mobileservicesenabled', PARAM_INT);
+
+            $mobilenotificationsstatus = $aremobilenotificationsenabled ? 'yes' : 'no';
+            $mform->addElement('static', 'mobilenotificacionsenabledlabel', '',
+                    " " . get_string('mobilenotificacionsenabled', 'hub', $mobilenotificationsstatus));
+            $mform->addElement('hidden', 'mobilenotificacionsenabled', 1);
+            $mform->setType('mobilenotificacionsenabled', PARAM_INT);
+
+            $mform->addElement('static', 'registereduserdeviceslabel', '',
+                    " " . get_string('registereduserdevices', 'hub', $registereduserdevicescount));
+            $mform->addElement('hidden', 'registereduserdevices', 1);
+            $mform->setType('registereduserdevices', PARAM_INT);
+
+            $mform->addElement('static', 'registeredactiveuserdeviceslabel', '',
+                    " " . get_string('registeredactiveuserdevices', 'hub', $registeredactiveuserdevicescount));
+            $mform->addElement('hidden', 'registeredactiveuserdevices', 1);
+            $mform->setType('registeredactiveuserdevices', PARAM_INT);
         }
 
         //check if it's a first registration or update
index 5a521e1..1e48bc8 100644 (file)
@@ -280,6 +280,21 @@ class registration_manager {
         $siteinfo['moodleversion'] = $CFG->version;
         $siteinfo['moodlerelease'] = $CFG->release;
         $siteinfo['url'] = $CFG->wwwroot;
+        // Mobile related information.
+        $siteinfo['mobileservicesenabled'] = 0;
+        $siteinfo['mobilenotificacionsenabled'] = 0;
+        $siteinfo['registereduserdevices'] = 0;
+        $siteinfo['registeredactiveuserdevices'] = 0;
+        if (!empty($CFG->enablewebservices) && !empty($CFG->enablemobilewebservice)) {
+            $siteinfo['mobileservicesenabled'] = 1;
+            $siteinfo['registereduserdevices'] = $DB->count_records('user_devices');
+            $airnotifierextpath = $CFG->dirroot . '/message/output/airnotifier/externallib.php';
+            if (file_exists($airnotifierextpath)) { // Maybe some one uninstalled the plugin.
+                require_once($airnotifierextpath);
+                $siteinfo['mobilenotificacionsenabled'] = message_airnotifier_external::is_system_configured();
+                $siteinfo['registeredactiveuserdevices'] = $DB->count_records('message_airnotifier_devices', array('enable' => 1));
+            }
+        }
 
         return $siteinfo;
     }
index 86ac102..895ae5c 100644 (file)
@@ -68,7 +68,8 @@ if (!empty($fromform) and confirm_sesskey()) {
     // Set to -1 all optional data marked as "don't send" by the admin.
     // The function get_site_info() will not calculate the optional data if config is set to -1.
     $inputnames = array('courses', 'users', 'roleassignments', 'posts', 'questions', 'resources',
-        'badges', 'issuedbadges', 'modulenumberaverage', 'participantnumberaverage');
+        'badges', 'issuedbadges', 'modulenumberaverage', 'participantnumberaverage',
+        'mobileservicesenabled', 'mobilenotificacionsenabled', 'registereduserdevices', 'registeredactiveuserdevices');
     foreach ($inputnames as $inputname) {
         if (empty($fromform->{$inputname})) {
             $fromform->{$inputname} = -1;
@@ -101,6 +102,10 @@ if (!empty($fromform) and confirm_sesskey()) {
     set_config('site_issuedbadges_' . $cleanhuburl, $fromform->issuedbadges, 'hub');
     set_config('site_modulenumberaverage_' . $cleanhuburl, $fromform->modulenumberaverage, 'hub');
     set_config('site_participantnumberaverage_' . $cleanhuburl, $fromform->participantnumberaverage, 'hub');
+    set_config('site_mobileservicesenabled_' . $cleanhuburl, $fromform->mobileservicesenabled, 'hub');
+    set_config('site_mobilenotificacionsenabled_' . $cleanhuburl, $fromform->mobilenotificacionsenabled, 'hub');
+    set_config('site_registereduserdevices_' . $cleanhuburl, $fromform->registereduserdevices, 'hub');
+    set_config('site_registeredactiveuserdevices_' . $cleanhuburl, $fromform->registeredactiveuserdevices, 'hub');
 }
 
 /////// UPDATE ACTION ////////
@@ -143,6 +148,10 @@ if (!empty($fromform) and empty($update) and confirm_sesskey()) {
         $fromform->modulenumberaverage = $siteinfo['modulenumberaverage'];
         $fromform->participantnumberaverage = $siteinfo['participantnumberaverage'];
         $fromform->street = $siteinfo['street'];
+        $fromform->mobileservicesenabled = $siteinfo['mobileservicesenabled'];
+        $fromform->mobilenotificacionsenabled = $siteinfo['mobilenotificacionsenabled'];
+        $fromform->registereduserdevices = $siteinfo['registereduserdevices'];
+        $fromform->registeredactiveuserdevices = $siteinfo['registeredactiveuserdevices'];
 
         $params = (array) $fromform; //we are using the form input as the redirection parameters (token, url and name)
 
index 5a20af8..1a63f4f 100644 (file)
@@ -179,7 +179,8 @@ preferences,moodle|/user/preferences.php|preferences',
         'idnumber' => new lang_string('sort_idnumber', 'admin'),
     );
     $temp->add(new admin_setting_configselect('navsortmycoursessort', new lang_string('navsortmycoursessort', 'admin'), new lang_string('navsortmycoursessort_help', 'admin'), 'sortorder', $sortoptions));
-    $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_configtext('navcourselimit', new lang_string('navcourselimit', 'admin'),
+        new lang_string('confignavcourselimit', 'admin'), 10, 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'), 1));
     $temp->add(new admin_setting_configcheckbox('linkcoursesections', new lang_string('linkcoursesections', 'admin'), new lang_string('linkcoursesections_help', 'admin'), 0));
index 6f5ded5..f2b50d5 100644 (file)
@@ -55,9 +55,6 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
     $optionalsubsystems->add(new admin_setting_configcheckbox('enableglobalsearch', new lang_string('enableglobalsearch', 'admin'),
         new lang_string('enableglobalsearch_desc', 'admin'), 0, 1, 0));
 
-    $choices = array();
-    $choices[0] = new lang_string('no');
-    $choices[1] = new lang_string('yes');
-    $optionalsubsystems->add(new admin_setting_configselect('allowstealth', new lang_string('allowstealthmodules'),
-        new lang_string('allowstealthmodules_help'), 0, $choices));
+    $optionalsubsystems->add(new admin_setting_configcheckbox('allowstealth', new lang_string('allowstealthmodules'),
+        new lang_string('allowstealthmodules_help'), 0, 1, 0));
 }
diff --git a/admin/templates/setting_filetypes.mustache b/admin/templates/setting_filetypes.mustache
new file mode 100644 (file)
index 0000000..7075ea2
--- /dev/null
@@ -0,0 +1,52 @@
+{{!
+    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/>.
+}}
+{{!
+    @template core_admin/setting_filetypes
+
+    Renders the admin_setting_filetypes setting element.
+
+    Context variables required for this template:
+    * id - element id
+    * name - form element name
+    * value - element value
+    * descriptions - data for the core_form/filetypes-descriptions template
+
+    Example context (json):
+    {
+        "id": "test0",
+        "name": "test",
+        "value": ".jpg,.gif",
+        "descriptions": {
+            "hasdescriptions": true,
+            "descriptions": [
+                {
+                    "description": "Image (JPEG)",
+                    "extensions": ".jpeg .jpe .jpg"
+                },
+                {
+                    "description": "Image (GIF)",
+                    "extensions": ".gif"
+                }
+            ]
+        }
+    }
+}}
+<div class="form-text defaultsnext">
+    <input type="text" name="{{name}}" value="{{value}}" size="30" id="{{id}}" class="text-ltr">
+    <span data-filetypesbrowser="{{id}}"></span>
+    <div data-filetypesdescriptions="{{id}}">{{#descriptions}}{{>core_form/filetypes-descriptions}}{{/descriptions}}</div>
+</div>
index 60bce5a..d6fa182 100644 (file)
@@ -44,5 +44,8 @@ function xmldb_tool_customlang_upgrade($oldversion) {
     // Automatically generated Moodle v3.2.0 release upgrade line.
     // Put any upgrade step following this.
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 0c00f00..c48c2c8 100644 (file)
@@ -84,8 +84,7 @@ abstract class restore_tool_log_logstore_subplugin extends restore_subplugin {
             }
         }
 
-        // Roll dates.
-        $data->timecreated = $this->apply_date_offset($data->timecreated);
+        // There is no need to roll dates. Logs are supposed to be immutable. See MDL-44961.
 
         // Revert other to its original php way.
         $data->other = unserialize(base64_decode($data->other));
index 8590d90..3d7f4f9 100644 (file)
@@ -48,5 +48,8 @@ function xmldb_tool_log_upgrade($oldversion) {
     // Automatically generated Moodle v3.2.0 release upgrade line.
     // Put any upgrade step following this.
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index ec98e95..789415e 100644 (file)
@@ -42,5 +42,8 @@ function xmldb_logstore_database_upgrade($oldversion) {
     // Automatically generated Moodle v3.2.0 release upgrade line.
     // Put any upgrade step following this.
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 3313749..2614e0a 100644 (file)
@@ -59,5 +59,8 @@ function xmldb_logstore_standard_upgrade($oldversion) {
     // Automatically generated Moodle v3.2.0 release upgrade line.
     // Put any upgrade step following this.
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 6177d34..1f5ddbe 100644 (file)
Binary files a/admin/tool/lp/amd/build/competencies.min.js and b/admin/tool/lp/amd/build/competencies.min.js differ
index 594bc75..143c935 100644 (file)
Binary files a/admin/tool/lp/amd/build/competencyactions.min.js and b/admin/tool/lp/amd/build/competencyactions.min.js differ
index e4312d2..e32b080 100644 (file)
Binary files a/admin/tool/lp/amd/build/competencypicker.min.js and b/admin/tool/lp/amd/build/competencypicker.min.js differ
index bf54916..fd3bf2d 100644 (file)
Binary files a/admin/tool/lp/amd/build/competencypicker_user_plans.min.js and b/admin/tool/lp/amd/build/competencypicker_user_plans.min.js differ
index f5f7f43..db877a5 100644 (file)
Binary files a/admin/tool/lp/amd/build/competencyruleconfig.min.js and b/admin/tool/lp/amd/build/competencyruleconfig.min.js differ
index b582a15..fd26ffb 100644 (file)
Binary files a/admin/tool/lp/amd/build/form-cohort-selector.min.js and b/admin/tool/lp/amd/build/form-cohort-selector.min.js differ
index d228806..febb8d4 100644 (file)
Binary files a/admin/tool/lp/amd/build/form-user-selector.min.js and b/admin/tool/lp/amd/build/form-user-selector.min.js differ
index cfc5c70..9f2ec48 100644 (file)
Binary files a/admin/tool/lp/amd/build/frameworks_datasource.min.js and b/admin/tool/lp/amd/build/frameworks_datasource.min.js differ
index aeb8b9e..f63fbbc 100644 (file)
Binary files a/admin/tool/lp/amd/build/planactions.min.js and b/admin/tool/lp/amd/build/planactions.min.js differ
index 610c935..755a664 100644 (file)
Binary files a/admin/tool/lp/amd/build/user_competency_plan_popup.min.js and b/admin/tool/lp/amd/build/user_competency_plan_popup.min.js differ
index 821222f..deab0e1 100644 (file)
Binary files a/admin/tool/lp/amd/build/user_competency_workflow.min.js and b/admin/tool/lp/amd/build/user_competency_workflow.min.js differ
index a69a9d8..f8b28d7 100644 (file)
Binary files a/admin/tool/lp/amd/build/user_evidence_actions.min.js and b/admin/tool/lp/amd/build/user_evidence_actions.min.js differ
index 582478b..380e05a 100644 (file)
@@ -131,6 +131,7 @@ define(['jquery',
                 html,
                 self._afterRender.bind(self)
             );
+            return;
         }).fail(Notification.exception);
     };
 
@@ -156,6 +157,7 @@ define(['jquery',
         return self._render().then(function(html) {
             self._find('[data-region="action-selector"]').replaceWith(html);
             self._afterRender();
+            return;
         });
     };
 
index 79e1aa9..1b82116 100644 (file)
@@ -181,13 +181,13 @@ define(['jquery',
                     pagerender = 'tool_lp/plan_page';
                     pageregion = 'plan-page';
                 }
-
                 ajax.call(requests)[requests.length - 1].then(function(context) {
-                    return templates.render(pagerender, context).done(function(html, js) {
-                        $('[data-region="' + pageregion + '"]').replaceWith(html);
-                        templates.runTemplateJS(js);
-                    });
-                }, notification.exception);
+                    return templates.render(pagerender, context);
+                }).then(function(html, js) {
+                    $('[data-region="' + pageregion + '"]').replaceWith(html);
+                    templates.runTemplateJS(js);
+                    return;
+                }).catch(notification.exception);
             });
         }
 
index 5c4ec17..542cf09 100644 (file)
@@ -166,6 +166,7 @@ define(['jquery',
             // We're done, let's trigger a change.
             self._templateLoaded = true;
             self._triggerChange();
+            return;
         });
     };
 
index 522e827..5c433f4 100644 (file)
@@ -430,12 +430,13 @@ define(['jquery',
                 var promises = ajax.call(calls);
 
                 promises[calls.length - 1].then(function(context) {
-                    return templates.render('tool_lp/related_competencies', context).done(function(html, js) {
-                        $('[data-region="relatedcompetencies"]').replaceWith(html);
-                        templates.runTemplateJS(js);
-                        updatedRelatedCompetencies();
-                    });
-                }, notification.exception);
+                    return templates.render('tool_lp/related_competencies', context);
+                }).then(function(html, js) {
+                    $('[data-region="relatedcompetencies"]').replaceWith(html);
+                    templates.runTemplateJS(js);
+                    updatedRelatedCompetencies();
+                    return;
+                }).catch(notification.exception);
             });
         }
 
@@ -472,7 +473,8 @@ define(['jquery',
                 relatedTarget.ruleconfig = config.ruleconfig;
                 renderCompetencySummary(relatedTarget);
             }
-        }, notification.exception);
+            return;
+        }).catch(notification.exception);
     };
 
     /**
@@ -692,28 +694,27 @@ define(['jquery',
                     type: strs[1]
                 };
             }
-        }).then(function() {
-            return templates.render('tool_lp/competency_summary', context).then(function(html) {
-                $('[data-region="competencyinfo"]').html(html);
-                $('[data-action="deleterelation"]').on('click', deleteRelatedHandler);
-            });
-        }).then(function() {
+            return context;
+        }).then(function(context) {
+            return templates.render('tool_lp/competency_summary', context);
+        }).then(function(html) {
+            $('[data-region="competencyinfo"]').html(html);
+            $('[data-action="deleterelation"]').on('click', deleteRelatedHandler);
             return templates.render('tool_lp/loading', {});
         }).then(function(html, js) {
             templates.replaceNodeContents('[data-region="relatedcompetencies"]', html, js);
-        }).done(function() {
-            ajax.call([{
+            return ajax.call([{
                 methodname: 'tool_lp_data_for_related_competencies_section',
-                args: {competencyid: competency.id},
-                done: function(context) {
-                    return templates.render('tool_lp/related_competencies', context).done(function(html, js) {
-                        $('[data-region="relatedcompetencies"]').replaceWith(html);
-                        templates.runTemplateJS(js);
-                        updatedRelatedCompetencies();
-                    });
-                }
-            }]);
-        }).fail(notification.exception);
+                args: {competencyid: competency.id}
+            }])[0];
+        }).then(function(context) {
+            return templates.render('tool_lp/related_competencies', context);
+        }).then(function(html, js) {
+            $('[data-region="relatedcompetencies"]').replaceWith(html);
+            templates.runTemplateJS(js);
+            updatedRelatedCompetencies();
+            return;
+        }).catch(notification.exception);
     };
 
     /**
@@ -776,16 +777,17 @@ define(['jquery',
             // Log Competency viewed event.
             triggerCompetencyViewedEvent(competency);
         }
-
         strSelectedTaxonomy(level).then(function(str) {
             selectedTitle.text(str);
-        });
+            return;
+        }).catch(notification.exception);
 
         strAddTaxonomy(sublevel).then(function(str) {
             btn.show()
                 .find('[data-region="term"]')
                 .text(str);
-        });
+            return;
+        }).catch(notification.exception);
 
         // We handled this event so consume it.
         evt.preventDefault();
index 1aebe05..23dce8a 100644 (file)
@@ -134,7 +134,7 @@ define(['jquery',
         if (!self._singleFramework) {
             self._find('[data-action="chooseframework"]').change(function(e) {
                 self._frameworkId = $(e.target).val();
-                self._loadCompetencies().then(self._refresh.bind(self));
+                self._loadCompetencies().then(self._refresh.bind(self)).catch(Notification.exception);
             });
         }
 
@@ -203,15 +203,15 @@ define(['jquery',
      */
     Picker.prototype.display = function() {
         var self = this;
-        return self._render().then(function(html) {
-            return Str.get_string('competencypicker', 'tool_lp').then(function(title) {
-                self._popup = new Dialogue(
-                    title,
-                    html,
-                    self._afterRender.bind(self)
-                );
-            });
-        }).fail(Notification.exception);
+        return $.when(Str.get_string('competencypicker', 'tool_lp'), self._render())
+        .then(function(title, render) {
+            self._popup = new Dialogue(
+                title,
+                render[0],
+                self._afterRender.bind(self)
+            );
+            return;
+        }).catch(Notification.exception);
     };
 
     /**
@@ -388,6 +388,7 @@ define(['jquery',
         return self._render().then(function(html) {
             self._find('[data-region="competencylinktree"]').replaceWith(html);
             self._afterRender();
+            return;
         });
     };
 
index 8d5b536..e621f32 100644 (file)
@@ -77,7 +77,8 @@ define(['jquery',
         if (!self._singlePlan) {
             self._find('[data-action="chooseplan"]').change(function(e) {
                 self._planId = $(e.target).val();
-                self._loadCompetencies().then(self._refresh.bind(self));
+                self._loadCompetencies().then(self._refresh.bind(self))
+                .catch(Notification.exception);
             });
         }
     };
index 3a956cb..b0e3db6 100644 (file)
@@ -165,14 +165,14 @@ define(['jquery',
         if (!self._competency) {
             return false;
         }
-        return self._render().then(function(html) {
-            return Str.get_string('competencyrule', 'tool_lp').then(function(title) {
-                self._popup = new Dialogue(
-                    title,
-                    html,
-                    self._afterRender.bind(self)
-                );
-            });
+        return $.when(Str.get_string('competencyrule', 'tool_lp'), self._render())
+        .then(function(title, render) {
+            self._popup = new Dialogue(
+                title,
+                render[0],
+                self._afterRender.bind(self)
+            );
+            return;
         }).fail(Notification.exception);
     };
 
@@ -312,9 +312,9 @@ define(['jquery',
      */
     RuleConfig.prototype._initOutcomes = function() {
         var self = this;
-
         return Outcomes.getAll().then(function(outcomes) {
             self._outcomesOption = outcomes;
+            return;
         });
     };
 
@@ -328,11 +328,11 @@ define(['jquery',
     RuleConfig.prototype._initRules = function() {
         var self = this,
             promises = [];
-
         $.each(self._rules, function(index, rule) {
             var promise = rule.init().then(function() {
                 rule.setTargetCompetency(self._competency);
                 rule.on('change', self._afterRuleConfigChange.bind(self));
+                return;
             }, function() {
                 // Upon failure remove the rule, and resolve the promise.
                 self._rules.splice(index, 1);
@@ -518,13 +518,13 @@ define(['jquery',
             self._afterChange();
             return;
         }
-
         rule.injectTemplate(container).then(function() {
             container.show();
-        }, function() {
-            container.empty().hide();
+            return;
         }).always(function() {
             self._afterChange();
+        }).catch(function() {
+            container.empty().hide();
         });
     };
 
index ac59ad9..e695e12 100644 (file)
@@ -76,6 +76,7 @@ define(['jquery',
                         }]);
                         promise[0].then(function() {
                             parent.remove();
+                            return;
                         }).fail(Notification.exception);
                     }
                 );
index 486c25c..281c492 100644 (file)
@@ -51,7 +51,6 @@ define(['jquery', 'core/ajax', 'core/templates'], function($, Ajax, Templates) {
                     includes: includes
                 }
             }]);
-
             promise[0].then(function(results) {
                 var promises = [],
                     i = 0;
@@ -69,9 +68,10 @@ define(['jquery', 'core/ajax', 'core/templates'], function($, Ajax, Templates) {
                         i++;
                     });
                     success(results.cohorts);
+                    return;
                 });
 
-            }failure);
+            }).catch(failure);
         }
 
     };
index 8713d06..3d1e613 100644 (file)
@@ -79,9 +79,10 @@ define(['jquery', 'core/ajax', 'core/templates'], function($, Ajax, Templates) {
                         i++;
                     });
                     success(results.users);
+                    return;
                 });
 
-            }failure);
+            }).catch(failure);
         }
 
     };
index 3638830..c46d005 100644 (file)
@@ -35,20 +35,17 @@ define(['jquery', 'core/ajax', 'core/notification'], function($, Ajax, Notificat
          * @return {Promise}
          */
         list: function(contextId, options) {
-            var promise,
-                args = {
+            var args = {
                     context: {
                         contextid: contextId
                     }
                 };
 
             $.extend(args, typeof options === 'undefined' ? {} : options);
-            promise = Ajax.call([{
+            return Ajax.call([{
                 methodname: 'core_competency_list_competency_frameworks',
                 args: args
             }])[0];
-
-            return promise.fail(Notification.exception);
         },
 
         /**
@@ -76,6 +73,7 @@ define(['jquery', 'core/ajax', 'core/notification'], function($, Ajax, Notificat
          * @param {String} query The query string.
          * @param {Function} callback A callback function receiving an array of results.
          */
+        /* eslint-disable promise/no-callback-in-promise */
         transport: function(selector, query, callback) {
             var el = $(selector),
                 contextId = el.data('contextid'),
@@ -84,11 +82,10 @@ define(['jquery', 'core/ajax', 'core/notification'], function($, Ajax, Notificat
             if (!contextId) {
                 throw new Error('The attribute data-contextid is required on ' + selector);
             }
-
             this.list(contextId, {
                 query: query,
                 onlyvisible: onlyVisible,
-            }).then(callback);
+            }).then(callback).catch(Notification.exception);
         }
     };
 
index 705ab29..c42aece 100644 (file)
@@ -81,6 +81,7 @@ define(['jquery', 'core/ajax', 'core/str', 'tool_lp/competencypicker', 'core/tem
             Str.get_string('competencyframeworkroot', 'tool_lp').then(function(rootframework) {
                 $(self.staticElementSelector).html(rootframework);
                 $(self.inputHiddenSelector).val(data.competencyId);
+                return;
             }).fail(Notification.exception);
         }
     };
index 932318f..be39396 100644 (file)
@@ -110,15 +110,16 @@ define(['jquery',
      * Callback to render the region template.
      *
      * @param {Object} context The context for the template.
+     * @return {Promise}
      */
     PlanActions.prototype._renderView = function(context) {
         var self = this;
-        templates.render(self._template, context)
-            .done(function(newhtml, newjs) {
+        return templates.render(self._template, context)
+            .then(function(newhtml, newjs) {
                 $(self._region).replaceWith(newhtml);
                 templates.runTemplateJS(newjs);
-            })
-            .fail(notification.exception);
+                return;
+            });
     };
 
     /**
@@ -130,16 +131,15 @@ define(['jquery',
      */
     PlanActions.prototype._callAndRefresh = function(calls, planData) {
         var self = this;
-
         calls.push({
             methodname: self._contextMethod,
             args: self._getContextArgs(planData)
         });
 
         // Apply all the promises, and refresh when the last one is resolved.
-        return $.when.apply($.when, ajax.call(calls))
+        return $.when.apply($, ajax.call(calls))
             .then(function() {
-                self._renderView(arguments[arguments.length - 1]);
+                return self._renderView(arguments[arguments.length - 1]);
             })
             .fail(notification.exception);
     };
index 6600b1f..e7aff46 100644 (file)
@@ -58,7 +58,6 @@ define(['jquery', 'core/notification', 'core/str', 'core/ajax', 'core/templates'
             done: this._contextLoaded.bind(this),
             fail: notification.exception
         }]);
-
         // Log the user competency viewed in plan event.
         requests[0].then(function(result) {
             var eventMethodName = 'core_competency_user_competency_viewed_in_plan';
@@ -66,12 +65,11 @@ define(['jquery', 'core/notification', 'core/str', 'core/ajax', 'core/templates'
             if (result.plan.iscompleted) {
                 eventMethodName = 'core_competency_user_competency_plan_viewed';
             }
-            ajax.call([{
+            return ajax.call([{
                 methodname: eventMethodName,
-                args: {competencyid: competencyId, userid: userId, planid: planId},
-                fail: notification.exception
-            }]);
-        });
+                args: {competencyid: competencyId, userid: userId, planid: planId}
+            }])[0];
+        }).catch(notification.exception);
     };
 
     /**
index f61c0f9..d6d6b71 100644 (file)
@@ -61,7 +61,7 @@ define(['jquery',
         Ajax.call([call])[0].then(function() {
             this._trigger('review-request-cancelled', data);
             this._trigger('status-changed', data);
-        }.bind(this)function() {
+        }.bind(this)).catch(function() {
             this._trigger('error-occured', data);
         }.bind(this));
     };
@@ -106,7 +106,7 @@ define(['jquery',
         Ajax.call([call])[0].then(function() {
             this._trigger('review-requested', data);
             this._trigger('status-changed', data);
-        }.bind(this)function() {
+        }.bind(this)).catch(function() {
             this._trigger('error-occured', data);
         }.bind(this));
     };
@@ -147,11 +147,10 @@ define(['jquery',
                 competencyid: data.competencyid
             }
         };
-
         Ajax.call([call])[0].then(function() {
             this._trigger('review-started', data);
             this._trigger('status-changed', data);
-        }.bind(this)function() {
+        }.bind(this)).catch(function() {
             this._trigger('error-occured', data);
         }.bind(this));
     };
@@ -196,7 +195,7 @@ define(['jquery',
         Ajax.call([call])[0].then(function() {
             this._trigger('review-stopped', data);
             this._trigger('status-changed', data);
-        }.bind(this)function() {
+        }.bind(this)).catch(function() {
             this._trigger('error-occured', data);
         }.bind(this));
     };
index b9ef686..1c37728 100644 (file)
@@ -98,14 +98,15 @@ define(['jquery',
      * Callback to render the region template.
      *
      * @param {Object} context The context for the template.
+     * @return {Promise}
      */
     UserEvidenceActions.prototype._renderView = function(context) {
         var self = this;
-        templates.render(self._template, context)
-            .done(function(newhtml, newjs) {
+        return templates.render(self._template, context)
+            .then(function(newhtml, newjs) {
                 templates.replaceNode($(self._region), newhtml, newjs);
-            })
-            .fail(notification.exception);
+                return;
+            });
     };
 
     /**
@@ -117,7 +118,6 @@ define(['jquery',
      */
     UserEvidenceActions.prototype._callAndRefresh = function(calls, evidenceData) {
         var self = this;
-
         calls.push({
             methodname: self._contextMethod,
             args: self._getContextArgs(evidenceData)
@@ -126,7 +126,7 @@ define(['jquery',
         // Apply all the promises, and refresh when the last one is resolved.
         return $.when.apply($.when, ajax.call(calls))
             .then(function() {
-                self._renderView(arguments[arguments.length - 1]);
+                return self._renderView(arguments[arguments.length - 1]);
             })
             .fail(notification.exception);
     };
index b9982d5..1a47f3a 100644 (file)
@@ -97,5 +97,8 @@ function xmldb_tool_monitor_upgrade($oldversion) {
         upgrade_plugin_savepoint(true, 2017021300, 'tool', 'monitor');
     }
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 4b5103c..0c75544 100644 (file)
Binary files a/admin/tool/templatelibrary/amd/build/display.min.js and b/admin/tool/templatelibrary/amd/build/display.min.js differ
index 1c2e4de..3979366 100644 (file)
@@ -126,7 +126,8 @@ define(['jquery', 'core/ajax', 'core/log', 'core/notification', 'core/templates'
             args: {
                     component: component,
                     template: name,
-                    themename: config.theme
+                    themename: config.theme,
+                    includecomments: true
             }
         }, {
             methodname: 'tool_templatelibrary_load_canonical_template',
index e0d877d..4541f23 100644 (file)
@@ -68,6 +68,7 @@ function(ajax, BootstrapTour, $, templates, str, log, notification) {
                 templates.render('tool_usertours/tourstep', {})
             ).then(function(response, template) {
                 usertours.startBootstrapTour(tourId, template[0], response.tourconfig);
+                return;
             }).fail(notification.exception);
         },
 
@@ -213,6 +214,7 @@ function(ajax, BootstrapTour, $, templates, str, log, notification) {
                 if (response.startTour) {
                     usertours.fetchTour(response.startTour);
                 }
+                return;
             }).fail(notification.exception);
         }
     };
index aa235b1..acd503d 100644 (file)
@@ -45,5 +45,8 @@ function xmldb_tool_usertours_upgrade($oldversion) {
     // Automatically generated Moodle v3.2.0 release upgrade line.
     // Put any upgrade step following this.
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 7c7c1c0..a17ef91 100644 (file)
@@ -66,5 +66,8 @@ function xmldb_auth_cas_upgrade($oldversion) {
         upgrade_plugin_savepoint(true, 2017020700, 'auth', 'cas');
     }
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 00e18b9..536eee8 100644 (file)
@@ -42,5 +42,8 @@ function xmldb_auth_db_upgrade($oldversion) {
         upgrade_plugin_savepoint(true, 2017032800, 'auth', 'db');
     }
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index cd0ed73..2fa9a44 100644 (file)
@@ -42,5 +42,8 @@ function xmldb_auth_email_upgrade($oldversion) {
         upgrade_plugin_savepoint(true, 2017020700, 'auth', 'email');
     }
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 61a9bab..e4f3b98 100644 (file)
@@ -42,5 +42,8 @@ function xmldb_auth_fc_upgrade($oldversion) {
         upgrade_plugin_savepoint(true, 2017020700, 'auth', 'fc');
     }
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index d26dac2..d33e70a 100644 (file)
@@ -42,5 +42,8 @@ function xmldb_auth_imap_upgrade($oldversion) {
         upgrade_plugin_savepoint(true, 2017020700, 'auth', 'imap');
     }
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 355b719..a2fe48e 100644 (file)
@@ -66,5 +66,8 @@ function xmldb_auth_ldap_upgrade($oldversion) {
         upgrade_plugin_savepoint(true, 2017020700, 'auth', 'ldap');
     }
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index ca483c0..6a947d8 100644 (file)
@@ -54,5 +54,8 @@ function xmldb_auth_manual_upgrade($oldversion) {
         upgrade_plugin_savepoint(true, 2017020700, 'auth', 'manual');
     }
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 46786fe..c66ead0 100644 (file)
@@ -53,5 +53,8 @@ function xmldb_auth_mnet_upgrade($oldversion) {
         upgrade_plugin_savepoint(true, 2017020700, 'auth', 'mnet');
     }
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 8708487..bd869bc 100644 (file)
@@ -42,5 +42,8 @@ function xmldb_auth_nntp_upgrade($oldversion) {
         upgrade_plugin_savepoint(true, 2017020700, 'auth', 'nntp');
     }
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 56bf606..62516a6 100644 (file)
@@ -42,5 +42,8 @@ function xmldb_auth_none_upgrade($oldversion) {
         upgrade_plugin_savepoint(true, 2017020700, 'auth', 'none');
     }
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 689ad1c..9d678a8 100644 (file)
@@ -192,10 +192,10 @@ class api {
         ];
         $confirmationurl = new moodle_url('/auth/oauth2/confirm-linkedlogin.php', $params);
 
-        // Remove data parameter just in case it was included in the confirmation so we can add it manually later.
-        $data->link = $confirmationurl->out();
+        $data->link = $confirmationurl->out(false);
+        $message = get_string('confirmlinkedloginemail', 'auth_oauth2', $data);
 
-        $message     = get_string('confirmlinkedloginemail', 'auth_oauth2', $data);
+        $data->link = $confirmationurl->out();
         $messagehtml = text_to_html(get_string('confirmlinkedloginemail', 'auth_oauth2', $data), false, false, true);
 
         $user->mailformat = 1;  // Always send HTML version as well.
@@ -303,9 +303,10 @@ class api {
         ];
         $confirmationurl = new moodle_url('/auth/oauth2/confirm-account.php', $params);
 
-        $data->link = $confirmationurl->out();
+        $data->link = $confirmationurl->out(false);
+        $message = get_string('confirmaccountemail', 'auth_oauth2', $data);
 
-        $message     = get_string('confirmaccountemail', 'auth_oauth2', $data);
+        $data->link = $confirmationurl->out();
         $messagehtml = text_to_html(get_string('confirmaccountemail', 'auth_oauth2', $data), false, false, true);
 
         $user->mailformat = 1;  // Always send HTML version as well.
index fc230aa..812cf98 100644 (file)
@@ -247,6 +247,10 @@ class auth extends \auth_plugin_base {
         if (!empty($user->picture)) {
             return false;
         }
+        if (!empty($CFG->enablegravatar)) {
+            return false;
+        }
+
         $picture = $this->get_static_user_picture();
         if (empty($picture)) {
             return false;
index c64c633..358006a 100644 (file)
@@ -38,5 +38,8 @@ function xmldb_auth_oauth2_upgrade($oldversion) {
     // Automatically generated Moodle v3.2.0 release upgrade line.
     // Put any upgrade step following this.
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index e69eb57..8fe55c2 100644 (file)
@@ -42,5 +42,8 @@ function xmldb_auth_pam_upgrade($oldversion) {
         upgrade_plugin_savepoint(true, 2017020700, 'auth', 'pam');
     }
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index e540c77..39bc594 100644 (file)
@@ -42,5 +42,8 @@ function xmldb_auth_pop3_upgrade($oldversion) {
         upgrade_plugin_savepoint(true, 2017020700, 'auth', 'pop3');
     }
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 38cec6c..ae1566b 100644 (file)
@@ -42,5 +42,8 @@ function xmldb_auth_shibboleth_upgrade($oldversion) {
         upgrade_plugin_savepoint(true, 2017020700, 'auth', 'shibboleth');
     }
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index d65729a..6ef4fc9 100644 (file)
@@ -62,8 +62,12 @@ class info_section extends info {
 
     protected function set_in_database($availability) {
         global $DB;
-        $DB->set_field('course_sections', 'availability', $availability,
-                array('id' => $this->section->id));
+
+        $section = new \stdClass();
+        $section->id = $this->section->id;
+        $section->availability = $availability;
+        $section->timemodified = time();
+        $DB->update_record('course_sections', $section);
     }
 
     /**
index 722b024..63b61b4 100644 (file)
@@ -287,8 +287,12 @@ class condition extends \core_availability\condition {
 
             // Save the updated course module.
             if ($changed) {
-                $DB->set_field('course_sections', 'availability', json_encode($tree->save()),
-                        array('id' => $section->id));
+                $updatesection = new \stdClass();
+                $updatesection->id = $section->id;
+                $updatesection->availability = json_encode($tree->save());
+                $updatesection->timemodified = time();
+                $DB->update_record('course_sections', $updatesection);
+
                 $anychanged = true;
             }
         }
index acdf34b..2ea5248 100644 (file)
@@ -325,7 +325,7 @@ class backup_section_structure_step extends backup_structure_step {
 
         $section = new backup_nested_element('section', array('id'), array(
                 'number', 'name', 'summary', 'summaryformat', 'sequence', 'visible',
-                'availabilityjson'));
+                'availabilityjson', 'timemodified'));
 
         // attach format plugin structure to $section element, only one allowed
         $this->add_plugin_structure('format', $section, false);
index bfd741b..f886496 100644 (file)
@@ -795,7 +795,8 @@ class restore_rebuild_course_cache extends restore_execution_step {
             if (!$DB->record_exists('course_sections', array('course' => $this->get_courseid(), 'section' => $i))) {
                 $sectionrec = array(
                     'course' => $this->get_courseid(),
-                    'section' => $i);
+                    'section' => $i,
+                    'timemodified' => time());
                 $DB->insert_record('course_sections', $sectionrec); // missing section created
             }
         }
@@ -1575,8 +1576,9 @@ class restore_section_structure_step extends restore_structure_step {
         $section = new stdclass();
         $section->course  = $this->get_courseid();
         $section->section = $data->number;
+        $section->timemodified = isset($data->timemodified) ? $this->apply_date_offset($data->timemodified) : 0;
         // Section doesn't exist, create it with all the info from backup
-        if (!$secrec = $DB->get_record('course_sections', (array)$section)) {
+        if (!$secrec = $DB->get_record('course_sections', ['course' => $this->get_courseid(), 'section' => $data->number])) {
             $section->name = $data->name;
             $section->summary = $data->summary;
             $section->summaryformat = $data->summaryformat;
@@ -1721,8 +1723,12 @@ class restore_section_structure_step extends restore_structure_step {
                     array('id' => $availfield->coursesectionid), MUST_EXIST);
             $newvalue = \core_availability\info::add_legacy_availability_field_condition(
                     $currentvalue, $availfield, $show);
-            $DB->set_field('course_sections', 'availability', $newvalue,
-                    array('id' => $availfield->coursesectionid));
+
+            $section = new stdClass();
+            $section->id = $availfield->coursesectionid;
+            $section->availability = $newvalue;
+            $section->timemodified = time();
+            $DB->update_record('course_sections', $section);
         }
     }
 
@@ -2658,8 +2664,9 @@ class restore_calendarevents_structure_step extends restore_structure_step {
         $isuseroverride = !$data->courseid && $data->modulename && $data->instance;
 
         // If we don't want to include user data and this record is a user override event
-        // for an activity then we should not create it.
-        if (!$this->task->get_setting_value('userinfo') && $isuseroverride) {
+        // for an activity then we should not create it. (Only activity events can be user override events - which must have this
+        // setting).
+        if ($isuseroverride && $this->task->setting_exists('userinfo') && !$this->task->get_setting_value('userinfo')) {
             return;
         }
 
@@ -3082,7 +3089,8 @@ class restore_course_logs_structure_step extends restore_structure_step {
 
         $data = (object)($data);
 
-        $data->time = $this->apply_date_offset($data->time);
+        // There is no need to roll dates. Logs are supposed to be immutable. See MDL-44961.
+
         $data->userid = $this->get_mappingid('user', $data->userid);
         $data->course = $this->get_courseid();
         $data->cmid = 0;
@@ -3129,7 +3137,8 @@ class restore_activity_logs_structure_step extends restore_course_logs_structure
 
         $data = (object)($data);
 
-        $data->time = $this->apply_date_offset($data->time);
+        // There is no need to roll dates. Logs are supposed to be immutable. See MDL-44961.
+
         $data->userid = $this->get_mappingid('user', $data->userid);
         $data->course = $this->get_courseid();
         $data->cmid = $this->task->get_moduleid();
@@ -4031,11 +4040,13 @@ class restore_module_structure_step extends restore_structure_step {
         if (!$data->section) { // no sections in course, create section 0 and 1 and assign module to 1
             $sectionrec = array(
                 'course' => $this->get_courseid(),
-                'section' => 0);
+                'section' => 0,
+                'timemodified' => time());
             $DB->insert_record('course_sections', $sectionrec); // section 0
             $sectionrec = array(
                 'course' => $this->get_courseid(),
-                'section' => 1);
+                'section' => 1,
+                'timemodified' => time());
             $data->section = $DB->insert_record('course_sections', $sectionrec); // section 1
         }
         $data->groupingid= $this->get_mappingid('grouping', $data->groupingid);      // grouping
@@ -4089,7 +4100,12 @@ class restore_module_structure_step extends restore_structure_step {
         } else {
             $sequence = $newitemid;
         }
-        $DB->set_field('course_sections', 'sequence', $sequence, array('id' => $data->section));
+
+        $updatesection = new \stdClass();
+        $updatesection->id = $data->section;
+        $updatesection->sequence = $sequence;
+        $updatesection->timemodified = time();
+        $DB->update_record('course_sections', $updatesection);
 
         // If there is the legacy showavailability data, store this for later use.
         // (This data is not present when restoring 'new' backups.)
index 09ca077..7d529ad 100644 (file)
@@ -88,5 +88,8 @@ function xmldb_block_badges_upgrade($oldversion, $block) {
     // Automatically generated Moodle v3.2.0 release upgrade line.
     // Put any upgrade step following this.
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index f1c1a52..09f73a6 100644 (file)
@@ -88,5 +88,8 @@ function xmldb_block_calendar_month_upgrade($oldversion, $block) {
     // Automatically generated Moodle v3.2.0 release upgrade line.
     // Put any upgrade step following this.
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 7a20754..d867d08 100644 (file)
@@ -88,5 +88,8 @@ function xmldb_block_calendar_upcoming_upgrade($oldversion, $block) {
     // Automatically generated Moodle v3.2.0 release upgrade line.
     // Put any upgrade step following this.
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index cbdb471..12ae568 100644 (file)
@@ -61,5 +61,8 @@ function xmldb_block_community_upgrade($oldversion) {
     // Automatically generated Moodle v3.2.0 release upgrade line.
     // Put any upgrade step following this.
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index ec79d8a..a04db38 100644 (file)
@@ -63,5 +63,8 @@ function xmldb_block_completionstatus_upgrade($oldversion, $block) {
     // Automatically generated Moodle v3.2.0 release upgrade line.
     // Put any upgrade step following this.
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index abb479b..33bbe77 100644 (file)
@@ -90,7 +90,7 @@ echo html_writer::start_tag('tbody');
 if ($USER->id != $user->id) {
     echo html_writer::start_tag('tr');
     echo html_writer::start_tag('td', array('colspan' => '2'));
-    echo html_writer::tag('b', get_string('showinguser', 'completion'));
+    echo html_writer::tag('b', get_string('showinguser', 'completion') . ' ');
     $url = new moodle_url('/user/view.php', array('id' => $user->id, 'course' => $course->id));
     echo html_writer::link($url, fullname($user));
     echo html_writer::end_tag('td');
@@ -99,7 +99,7 @@ if ($USER->id != $user->id) {
 
 echo html_writer::start_tag('tr');
 echo html_writer::start_tag('td', array('colspan' => '2'));
-echo html_writer::tag('b', get_string('status'));
+echo html_writer::tag('b', get_string('status') . ' ');
 
 // Is course complete?
 $coursecomplete = $info->is_course_complete($user->id);
@@ -141,7 +141,7 @@ if (empty($completions)) {
 } else {
     echo html_writer::start_tag('tr');
     echo html_writer::start_tag('td', array('colspan' => '2'));
-    echo html_writer::tag('b', get_string('required'));
+    echo html_writer::tag('b', get_string('required') . ' ');
 
     // Get overall aggregation method.
     $overall = $info->get_aggregation_method();
@@ -214,7 +214,7 @@ if (empty($completions)) {
                     echo core_text::strtolower(get_string('any', 'completion'));
                 }
 
-                echo html_writer::end_tag('i') .core_text::strtolower(get_string('required')).')';
+                echo ' ' . html_writer::end_tag('i') .core_text::strtolower(get_string('required')).')';
                 $agg_type = false;
             }
         }
index 3613226..3d972ce 100644 (file)
@@ -63,5 +63,8 @@ function xmldb_block_course_summary_upgrade($oldversion, $block) {
     // Automatically generated Moodle v3.2.0 release upgrade line.
     // Put any upgrade step following this.
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 3e57de1..2899efe 100644 (file)
@@ -48,5 +48,8 @@ function xmldb_block_html_upgrade($oldversion) {
     // Automatically generated Moodle v3.2.0 release upgrade line.
     // Put any upgrade step following this.
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index cdc315e..0d04b59 100644 (file)
Binary files a/blocks/myoverview/amd/build/event_list.min.js and b/blocks/myoverview/amd/build/event_list.min.js differ
diff --git a/blocks/myoverview/amd/build/tab_preferences.min.js b/blocks/myoverview/amd/build/tab_preferences.min.js
new file mode 100644 (file)
index 0000000..da5bd97
Binary files /dev/null and b/blocks/myoverview/amd/build/tab_preferences.min.js differ
index 17e888f..5d1d42d 100644 (file)
@@ -350,34 +350,37 @@ define(['jquery', 'core/notification', 'core/templates',
 
         // Request data from the server.
         return promise.then(function(result) {
-            return result.events;
-        }).then(function(calendarEvents) {
-            if (!calendarEvents.length || (calendarEvents.length < limit)) {
-                // We have no more events so mark the list as done.
+            if (!result.events.length) {
+                // No events, nothing to do.
                 setLoadedAll(root);
+                return 0;
             }
 
-            if (calendarEvents.length) {
-                // Remember the last id we've seen.
-                root.attr('data-last-id', calendarEvents[calendarEvents.length - 1].id);
-
-                // Render the events.
-                return render(root, calendarEvents).then(function(renderCount) {
-                    updateContentVisibility(root, calendarEvents.length);
-
-                    if (renderCount < calendarEvents.length) {
-                        // if the number of events that was rendered is less than
-                        // the number we sent for rendering we can assume that there
-                        // are no groups to add them in. Since the ordering of the
-                        // events is guaranteed it means that any future requests will
-                        // also yield events that can't be rendered, so let's not bother
-                        // sending any more requests.
-                        setLoadedAll(root);
-                    }
-                });
-            } else {
-                updateContentVisibility(root, calendarEvents.length);
+            var calendarEvents = result.events;
+
+            // Remember the last id we've seen.
+            root.attr('data-last-id', calendarEvents[calendarEvents.length - 1].id);
+
+            if (calendarEvents.length < limit) {
+                // No more events to load, disable loading button.
+                setLoadedAll(root);
             }
+
+            // Render the events.
+            return render(root, calendarEvents).then(function(renderCount) {
+                if (renderCount < calendarEvents.length) {
+                    // if the number of events that was rendered is less than
+                    // the number we sent for rendering we can assume that there
+                    // are no groups to add them in. Since the ordering of the
+                    // events is guaranteed it means that any future requests will
+                    // also yield events that can't be rendered, so let's not bother
+                    // sending any more requests.
+                    setLoadedAll(root);
+                }
+                return calendarEvents.length;
+            });
+        }).then(function(eventCount) {
+            return updateContentVisibility(root, eventCount);
         }).fail(
             Notification.exception
         ).always(function() {
diff --git a/blocks/myoverview/amd/src/tab_preferences.js b/blocks/myoverview/amd/src/tab_preferences.js
new file mode 100644 (file)
index 0000000..25ac2ee
--- /dev/null
@@ -0,0 +1,61 @@
+// 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/>.
+
+/**
+ * Javascript used to save the user's tab preference.
+ *
+ * @package    block_myoverview
+ * @copyright  2017 Mark Nelson <markn@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+define(['jquery', 'core/ajax', 'core/custom_interaction_events',
+    'core/notification'], function($, Ajax, CustomEvents, Notification) {
+
+    /**
+     * Registers an event that saves the user's tab preference when switching between them.
+     *
+     * @param {object} root The container element
+     */
+    var registerEventListeners = function(root) {
+        CustomEvents.define(root, [CustomEvents.events.activate]);
+        root.on(CustomEvents.events.activate, "[data-toggle='tab']", function(e) {
+            var tabname = $(e.currentTarget).data('tabname');
+            // Bootstrap does not change the URL when using BS tabs, so need to do this here.
+            // Also check to make sure the browser supports the history API.
+            if (typeof window.history.pushState === "function") {
+                window.history.pushState(null, null, '?myoverviewtab=' + tabname);
+            }
+            var request = {
+                methodname: 'core_user_update_user_preferences',
+                args: {
+                    preferences: [
+                        {
+                            type: 'block_myoverview_last_tab',
+                            value: tabname
+                        }
+                    ]
+                }
+            };
+
+            Ajax.call([request])[0]
+                .fail(Notification.exception);
+        });
+    };
+
+    return {
+        registerEventListeners: registerEventListeners
+    };
+});
index f22ce15..8afd4a1 100644 (file)
@@ -50,7 +50,16 @@ class block_myoverview extends block_base {
             return $this->content;
         }
 
-        $renderable = new \block_myoverview\output\main();
+        // Check if the tab to select wasn't passed in the URL, if so see if the user has any preference.
+        if (!$tab = optional_param('myoverviewtab', null, PARAM_ALPHA)) {
+            // Check if the user has no preference, if so get the site setting.
+            if (!$tab = get_user_preferences('block_myoverview_last_tab')) {
+                $config = get_config('block_myoverview');
+                $tab = $config->defaulttab;
+            }
+        }
+
+        $renderable = new \block_myoverview\output\main($tab);
         $renderer = $this->page->get_renderer('block_myoverview');
 
         $this->content = new stdClass();
@@ -68,4 +77,13 @@ class block_myoverview extends block_base {
     public function applicable_formats() {
         return array('my' => true);
     }
+
+    /**
+     * This block does contain a configuration settings.
+     *
+     * @return boolean
+     */
+    public function has_config() {
+        return true;
+    }
 }
index 31dc8c6..798eb7b 100644 (file)
@@ -63,8 +63,6 @@ class courses_view implements renderable, templatable {
      * @return array
      */
     public function export_for_template(renderer_base $output) {
-        $today = time();
-
         // Build courses view data structure.
         $coursesview = [
             'hascourses' => !empty($this->courses)
@@ -73,8 +71,6 @@ class courses_view implements renderable, templatable {
         // How many courses we have per status?
         $coursesbystatus = ['past' => 0, 'inprogress' => 0, 'future' => 0];
         foreach ($this->courses as $course) {
-            $startdate = $course->startdate;
-            $enddate = $course->enddate;
             $courseid = $course->id;
             $context = \context_course::instance($courseid);
             $exporter = new course_summary_exporter($course, [
@@ -84,14 +80,17 @@ class courses_view implements renderable, templatable {
             // Convert summary to plain text.
             $exportedcourse->summary = content_to_text($exportedcourse->summary, $exportedcourse->summaryformat);
 
+            $courseprogress = null;
+
+            $classified = course_classify_for_timeline($course);
+
             if (isset($this->coursesprogress[$courseid])) {
-                $coursecompleted = $this->coursesprogress[$courseid]['completed'];
                 $courseprogress = $this->coursesprogress[$courseid]['progress'];
                 $exportedcourse->hasprogress = !is_null($courseprogress);
                 $exportedcourse->progress = $courseprogress;
             }
 
-            if ((isset($coursecompleted) && $coursecompleted) || (!empty($enddate) && $enddate < $today)) {
+            if ($classified == COURSE_TIMELINE_PAST) {
                 // Courses that have already ended.
                 $pastpages = floor($coursesbystatus['past'] / $this::COURSES_PER_PAGE);
 
@@ -100,7 +99,7 @@ class courses_view implements renderable, templatable {
                 $coursesview['past']['pages'][$pastpages]['page'] = $pastpages + 1;
                 $coursesview['past']['haspages'] = true;
                 $coursesbystatus['past']++;
-            } else if ($startdate > $today) {
+            } else if ($classified == COURSE_TIMELINE_FUTURE) {
                 // Courses that have not started yet.
                 $futurepages = floor($coursesbystatus['future'] / $this::COURSES_PER_PAGE);
 
index 6215a5a..2435f54 100644 (file)
@@ -29,6 +29,7 @@ use renderer_base;
 use templatable;
 use core_completion\progress;
 
+require_once($CFG->dirroot . '/blocks/myoverview/lib.php');
 require_once($CFG->libdir . '/completionlib.php');
 
 /**
@@ -39,6 +40,20 @@ require_once($CFG->libdir . '/completionlib.php');
  */
 class main implements renderable, templatable {
 
+    /**
+     * @var string The tab to display.
+     */
+    public $tab;
+
+    /**
+     * Constructor.
+     *
+     * @param string $tab The tab to display.
+     */
+    public function __construct($tab) {
+        $this->tab = $tab;
+    }
+
     /**
      * Export this data so it can be used as the context for a mustache template.
      *
@@ -73,13 +88,24 @@ class main implements renderable, templatable {
         $nocoursesurl = $output->image_url('courses', 'block_myoverview')->out();
         $noeventsurl = $output->image_url('activities', 'block_myoverview')->out();
 
+        // Now, set the tab we are going to be viewing.
+        $viewingtimeline = false;
+        $viewingcourses = false;
+        if ($this->tab == BLOCK_MYOVERVIEW_TIMELINE_VIEW) {
+            $viewingtimeline = true;
+        } else {
+            $viewingcourses = true;
+        }
+
         return [
             'midnight' => usergetmidnight(time()),
             'coursesview' => $coursesview->export_for_template($output),
             'urls' => [
                 'nocourses' => $nocoursesurl,
                 'noevents' => $noeventsurl
-            ]
+            ],
+            'viewingtimeline' => $viewingtimeline,
+            'viewingcourses' => $viewingcourses
         ];
     }
 }
index 4c464f5..99fb83f 100644 (file)
@@ -22,6 +22,8 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+$string['defaulttab'] = 'Default tab';
+$string['defaulttab_desc'] = 'This is the default tab that will be shown to a user.';
 $string['future'] = 'Future';
 $string['inprogress'] = 'In progress';
 $string['morecourses'] = 'More courses';
diff --git a/blocks/myoverview/lib.php b/blocks/myoverview/lib.php
new file mode 100644 (file)
index 0000000..a73db25
--- /dev/null
@@ -0,0 +1,52 @@
+<?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/>.
+
+/**
+ * Contains functions called by core.
+ *
+ * @package    block_myoverview
+ * @copyright  2017 Mark Nelson <markn@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * The timeline view.
+ */
+define('BLOCK_MYOVERVIEW_TIMELINE_VIEW', 'timeline');
+
+/**
+ * The courses view.
+ */
+define('BLOCK_MYOVERVIEW_COURSES_VIEW', 'courses');
+
+/**
+ * Returns the name of the user preferences as well as the details this plugin uses.
+ *
+ * @return array
+ */
+function block_myoverview_user_preferences() {
+    $preferences = array();
+    $preferences['block_myoverview_last_tab'] = array(
+        'type' => PARAM_ALPHA,
+        'null' => NULL_NOT_ALLOWED,
+        'default' => BLOCK_MYOVERVIEW_TIMELINE_VIEW,
+        'choices' => array(BLOCK_MYOVERVIEW_TIMELINE_VIEW, BLOCK_MYOVERVIEW_COURSES_VIEW)
+    );
+
+    return $preferences;
+}
diff --git a/blocks/myoverview/settings.php b/blocks/myoverview/settings.php
new file mode 100644 (file)
index 0000000..10f084d
--- /dev/null
@@ -0,0 +1,39 @@
+<?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/>.
+
+/**
+ * Settings for the overview block.
+ *
+ * @package    block_myoverview
+ * @copyright  2017 Mark Nelson <markn@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+require_once($CFG->dirroot . '/blocks/myoverview/lib.php');
+
+if ($ADMIN->fulltree) {
+
+    $options = [
+        BLOCK_MYOVERVIEW_TIMELINE_VIEW => get_string('timeline', 'block_myoverview'),
+        BLOCK_MYOVERVIEW_COURSES_VIEW => get_string('courses')
+    ];
+
+    $settings->add(new admin_setting_configselect('block_myoverview/defaulttab',
+        get_string('defaulttab', 'block_myoverview'),
+        get_string('defaulttab_desc', 'block_myoverview'), 'timeline', $options));
+}
index 3a1a942..e9b21bd 100644 (file)
 }}
 
 <div id="block-myoverview-{{uniqid}}" class="block-myoverview" data-region="myoverview">
-    <ul class="nav nav-tabs" role="tablist">
+    <ul id="block-myoverview-view-choices-{{uniqid}}" class="nav nav-tabs" role="tablist">
         <li class="nav-item">
-            <a class="nav-link active" href="#myoverview_timeline_view" role="tab" data-toggle="tab">
+            <a class="nav-link {{#viewingtimeline}}active{{/viewingtimeline}}" href="#myoverview_timeline_view" role="tab" data-toggle="tab" data-tabname="timeline">
                 {{#str}} timeline, block_myoverview {{/str}}
             </a>
         </li>
         <li class="nav-item">
-            <a class="nav-link" href="#myoverview_courses_view" role="tab" data-toggle="tab">
+            <a class="nav-link {{#viewingcourses}}active{{/viewingcourses}}" href="#myoverview_courses_view" role="tab" data-toggle="tab" data-tabname="courses">
                 {{#str}} courses {{/str}}
             </a>
         </li>
     </ul>
     <div class="tab-content content-centred">
-        <div role="tabpanel" class="tab-pane fade in active" id="myoverview_timeline_view">
+        <div role="tabpanel" class="tab-pane fade {{#viewingtimeline}}in active{{/viewingtimeline}}" id="myoverview_timeline_view">
             {{> block_myoverview/timeline-view }}
         </div>
-        <div role="tabpanel" class="tab-pane fade" id="myoverview_courses_view">
+        <div role="tabpanel" class="tab-pane fade {{#viewingcourses}}in active{{/viewingcourses}}" id="myoverview_courses_view">
             {{#coursesview}}
                 {{> block_myoverview/courses-view }}
             {{/coursesview}}
         </div>
     </div>
 </div>
+{{#js}}
+require(['jquery', 'block_myoverview/tab_preferences'], function($, TabPreferences) {
+    var root = $('#block-myoverview-view-choices-{{uniqid}}');
+    TabPreferences.registerEventListeners(root);
+});
+{{/js}}
index 8bd3afe..e8d8692 100644 (file)
@@ -53,6 +53,7 @@ Feature: Course overview block show users their progress on courses
     And I am on "Course 1" course homepage
     And I follow "Test choice 1"
     And I follow "Dashboard" in the user menu
+    And I click on "Timeline" "link" in the "Course overview" "block"
     And I click on "Sort by courses" "link" in the "Course overview" "block"
     And I should see "100%" in the "Course overview" "block"
     And I click on "Courses" "link" in the "Course overview" "block"
index fd45f77..a637f48 100644 (file)
@@ -24,6 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2017051500;         // The current plugin version (Date: YYYYMMDDXX).
+$plugin->version   = 2017051502;         // The current plugin version (Date: YYYYMMDDXX).
 $plugin->requires  = 2017050500;         // Requires this Moodle version.
 $plugin->component = 'block_myoverview'; // Full name of the plugin (used for diagnostics).
index 3de8a6b..538c829 100644 (file)
@@ -70,5 +70,8 @@ function xmldb_block_navigation_upgrade($oldversion, $block) {
     // Automatically generated Moodle v3.2.0 release upgrade line.
     // Put any upgrade step following this.
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 32ae28c..68dfa30 100644 (file)
@@ -113,5 +113,8 @@ function xmldb_block_quiz_results_upgrade($oldversion, $block) {
     // Automatically generated Moodle v3.2.0 release upgrade line.
     // Put any upgrade step following this.
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
\ No newline at end of file
index 45b9d65..186c752 100644 (file)
@@ -62,5 +62,8 @@ function xmldb_block_recent_activity_upgrade($oldversion, $block) {
     // Automatically generated Moodle v3.2.0 release upgrade line.
     // Put any upgrade step following this.
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 845a93c..78e0c0a 100644 (file)
@@ -59,5 +59,8 @@ function xmldb_block_rss_client_upgrade($oldversion) {
     // Automatically generated Moodle v3.2.0 release upgrade line.
     // Put any upgrade step following this.
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 6363ae0..397768a 100644 (file)
@@ -64,5 +64,8 @@ function xmldb_block_section_links_upgrade($oldversion, $block) {
     // Automatically generated Moodle v3.2.0 release upgrade line.
     // Put any upgrade step following this.
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 0f23a91..cc7971d 100644 (file)
@@ -63,5 +63,8 @@ function xmldb_block_selfcompletion_upgrade($oldversion, $block) {
     // Automatically generated Moodle v3.2.0 release upgrade line.
     // Put any upgrade step following this.
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 98bbed2..b48a648 100644 (file)
@@ -70,5 +70,8 @@ function xmldb_block_settings_upgrade($oldversion, $block) {
     // Automatically generated Moodle v3.2.0 release upgrade line.
     // Put any upgrade step following this.
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 642e147..5fce06e 100644 (file)
@@ -39,52 +39,3 @@ $string['taggeditemscontext_help'] = 'You can limit the tag cloud to the tags th
 $string['tags:addinstance'] = 'Add a new tags block';
 $string['tags:myaddinstance'] = 'Add a new tags block to Dashboard';
 
-// Deprecated since 3.0.
-
-$string['add'] = 'Add';
-$string['alltags'] = 'All tags:';
-$string['arrowtitle'] = 'Click here to enter the suggested text (grey letters).';
-$string['coursetags'] = 'Course tags:';
-$string['edit'] = 'edit...';
-$string['editdeletemytag'] = 'Delete tag from this course:';
-$string['editmytags'] = 'My tags - shortcuts to all your tagged courses.';
-$string['editmytagsfor'] = 'Edit my tags for {$a}';
-$string['editnopersonaltags'] = 'No personal tags have been created yet.';
-$string['edittags'] = 'Edit my tags...';
-$string['edittagthisunit'] = 'Add tag to this course:';
-$string['editthiscoursetags'] = 'My tags for this course are {$a}';
-$string['edittitle'] = 'My tags';
-$string['entries'] = 'entries';
-$string['entry'] = 'entry';
-$string['jserror1'] = 'Tags must have between one and 50 characters. Please adjust your tag.';
-$string['jserror2'] = 'Tags cannot contain these special characters - backslash, less than (<) or greater than (>). Please adjust your tag.';
-$string['login'] = 'log in';
-$string['more'] = 'more...';
-$string['moreorder'] = 'Order:';
-$string['moreorderalpha'] = 'Alphabetical';
-$string['moreorderdate'] = 'Date created';
-$string['moreorderpop'] = 'Popularity';
-$string['moreshow'] = 'Show:';
-$string['moreshowalltags'] = 'All tags';
-$string['moreshowcommtags'] = 'Non-official tags';
-$string['moreshowcoursetags'] = 'Tags for \'{$a}\'';
-$string['moreshowmytags'] = 'My tags';
-$string['moreshowofficialtags'] = 'Official tags';
-$string['moretags'] = 'Show and filter more tags';
-$string['moretitle'] = 'More tags';
-$string['morewelcome'] = 'Welcome to the \'More tags\' tag cloud.
-Tags are user created links to things. Tags allow you to categorise and link things
-like your favourite courses, your blogs or your profile with your own words.
-Different groups of tags may be displayed with the links on the \'Show:\' line,
-and may be reordered with the links on the \'Order:\' line.';
-$string['mycoursetags'] = 'My course tags:';
-$string['mytags'] = 'My tags:';
-$string['notagsyet'] = 'No tags yet';
-$string['please'] = 'Please';
-$string['select'] = 'Select...';
-$string['showcoursetags'] = 'Show course tags';
-$string['showcoursetagsdef'] = 'Display the course tagging features in the tags block, allowing students to tag courses.';
-$string['suggestedtagthisunit'] = 'Suggested tag to this course:';
-$string['tags'] = 'tags';
-$string['tagthisunit'] = 'Tag this course:';
-$string['tagunits'] = 'to tag your favourite courses';
diff --git a/blocks/tags/lang/en/deprecated.txt b/blocks/tags/lang/en/deprecated.txt
deleted file mode 100644 (file)
index 7c83a9d..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-add,block_tags
-alltags,block_tags
-arrowtitle,block_tags
-coursetags,block_tags
-edit,block_tags
-editdeletemytag,block_tags
-editmytags,block_tags
-editmytagsfor,block_tags
-editnopersonaltags,block_tags
-edittags,block_tags
-edittagthisunit,block_tags
-editthiscoursetags,block_tags
-edittitle,block_tags
-entries,block_tags
-entry,block_tags
-jserror1,block_tags
-jserror2,block_tags
-login,block_tags
-more,block_tags
-moreorder,block_tags
-moreorderalpha,block_tags
-moreorderdate,block_tags
-moreorderpop,block_tags
-moreshow,block_tags
-moreshowalltags,block_tags
-moreshowcommtags,block_tags
-moreshowcoursetags,block_tags
-moreshowmytags,block_tags
-moreshowofficialtags,block_tags
-moretags,block_tags
-moretitle,block_tags
-morewelcome,block_tags
-mytags,block_tags
-notagsyet,block_tags
-please,block_tags
-select,block_tags
-showcoursetags,block_tags
-showcoursetagsdef,block_tags
-suggestedtagthisunit,block_tags
-tags,block_tags
-tagthisunit,block_tags
-tagunits,block_tags
index 9ea6df3..168124d 100644 (file)
@@ -1077,7 +1077,7 @@ class cache implements cache_loader {
                 $result = $data;
             }
         }
-        if ($result) {
+        if ($result !== false) {
             if ($this->perfdebug) {
                 cache_helper::record_cache_hit('** static acceleration **', $this->definition);
             }
@@ -2162,4 +2162,4 @@ class cache_session extends cache {
  */
 class cache_request extends cache {
     // This comment appeases code pre-checker ;) !
-}
\ No newline at end of file
+}
index ef47096..0a47e4f 100644 (file)
@@ -2177,6 +2177,39 @@ class core_cache_testcase extends advanced_testcase {
             $startstats[$requestid]['stores']['cachestore_static']['sets']);
     }
 
+    public function test_static_cache() {
+        global $CFG;
+        $this->resetAfterTest(true);
+        $CFG->perfdebug = 15;
+
+        // Create cache store with static acceleration.
+        $instance = cache_config_testing::instance();
+        $applicationid = 'phpunit/applicationperf';
+        $instance->phpunit_add_definition($applicationid, array(
+            'mode' => cache_store::MODE_APPLICATION,
+            'component' => 'phpunit',
+            'area' => 'applicationperf',
+            'simplekeys' => true,
+            'staticacceleration' => true,
+            'staticaccelerationsize' => 3
+        ));
+
+        $application = cache::make('phpunit', 'applicationperf');
+
+        // Check that stores register sets.
+        $this->assertTrue($application->set('setMe1', 1));
+        $this->assertTrue($application->set('setMe2', 0));
+        $this->assertTrue($application->set('setMe3', array()));
+        $this->assertTrue($application->get('setMe1') !== false);
+        $this->assertTrue($application->get('setMe2') !== false);
+        $this->assertTrue($application->get('setMe3') !== false);
+
+        // Check that the static acceleration worked, even on empty arrays and the number 0.
+        $endstats = cache_helper::get_stats();
+        $this->assertEquals(0, $endstats[$applicationid]['stores']['** static acceleration **']['misses']);
+        $this->assertEquals(3, $endstats[$applicationid]['stores']['** static acceleration **']['hits']);
+    }
+
     public function test_performance_debug_off() {
         global $CFG;
         $this->resetAfterTest(true);
index c24c5f8..c6c7c68 100644 (file)
@@ -164,7 +164,7 @@ class event_vault implements event_vault_interface {
             $coursesfilter,
             $where,
             $params,
-            "e.timesort ASC, e.id ASC",
+            "COALESCE(e.timesort, e.timestart) ASC, e.id ASC",
             $offset,
             $limitnum,
             $ignorehidden
index 6a1bdf9..cfff16f 100644 (file)
@@ -72,7 +72,8 @@ class event_form extends moodleform {
             if (!empty($eventtypes->groups) && is_array($eventtypes->groups)) {
                 $groupoptions = array();
                 foreach ($eventtypes->groups as $group) {
-                    $groupoptions[$group->id] = $group->name;
+                    $groupoptions[$group->id] = format_string($group->name, true,
+                        array('context' => context_course::instance($group->courseid)));
                 }
                 $mform->addElement('select', 'groupid', get_string('typegroup', 'calendar'), $groupoptions);
                 $mform->disabledIf('groupid', 'eventtype', 'noteq', 'group');
index a711b76..9952dee 100644 (file)
@@ -184,6 +184,7 @@ $events = calendar_get_legacy_events($timestart, $timeend, $users, $groups, arra
 
 $ical = new iCalendar;
 $ical->add_property('method', 'PUBLISH');
+$ical->add_property('prodid', '-//Moodle Pty Ltd//NONSGML Moodle Version ' . $CFG->version . '//EN');
 foreach($events as $event) {
     if (!empty($event->modulename)) {
         $instances = get_fast_modinfo($event->courseid, $userid)->get_instances_of($event->modulename);
@@ -194,10 +195,20 @@ foreach($events as $event) {
     $hostaddress = str_replace('http://', '', $CFG->wwwroot);
     $hostaddress = str_replace('https://', '', $hostaddress);
 
-    $ev = new iCalendar_event;
+    $me = new calendar_event($event); // To use moodle calendar event services.
+    $ev = new iCalendar_event; // To export in ical format.
     $ev->add_property('uid', $event->id.'@'.$hostaddress);
-    $ev->add_property('summary', $event->name);
-    $ev->add_property('description', clean_param($event->description, PARAM_NOTAGS));
+
+    // Set iCal event summary from event name.
+    $ev->add_property('summary', format_string($event->name, true, ['context' => $me->context]));
+
+    // Format the description text.
+    $description = format_text($me->description, $me->format, ['context' => $me->context]);
+    // Then convert it to plain text, since it's the only format allowed for the event description property.
+    // We use html_to_text in order to convert <br> and <p> tags to new line characters for descriptions in HTML format.
+    $description = html_to_text($description, 0);
+    $ev->add_property('description', $description);
+
     $ev->add_property('class', 'PUBLIC'); // PUBLIC / PRIVATE / CONFIDENTIAL
     $ev->add_property('last-modified', Bennu::timestamp_to_datetime($event->timemodified));
     $ev->add_property('dtstamp', Bennu::timestamp_to_datetime()); // now
index 1d5c19b..55ad67f 100644 (file)
@@ -1481,7 +1481,7 @@ function calendar_get_mini($courses, $groups, $users, $calmonth = false, $calyea
                 $class .= ' duration_finish';
             }
             $data = array(
-                'url' => $dayhref,
+                'url' => $dayhref->out(false),
                 'day' => $day,
                 'content' => $popupdata['data-core_calendar-popupcontent'],
                 'title' => $popupdata['data-core_calendar-title']
index 2c050f4..ca28508 100644 (file)
@@ -705,7 +705,9 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
         $rrule = "FREQ=MONTHLY;INTERVAL=12;BYMONTHDAY=2";
 
         $mang = new rrule_manager($rrule);
-        $until = time() + (YEARSECS * $mang::TIME_UNLIMITED_YEARS);
+        $untildate = new DateTime();
+        $untildate->add(new DateInterval('P' . $mang::TIME_UNLIMITED_YEARS . 'Y'));
+        $until = $untildate->getTimestamp();
 
         $mang->parse_rrule();
         $mang->create_events($this->event);
@@ -860,7 +862,9 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
         $rrule = "FREQ=MONTHLY;INTERVAL=12;BYDAY=1MO";
 
         $mang = new rrule_manager($rrule);
-        $until = time() + (YEARSECS * $mang::TIME_UNLIMITED_YEARS);
+        $untildate = new DateTime();
+        $untildate->add(new DateInterval('P' . $mang::TIME_UNLIMITED_YEARS . 'Y'));
+        $until = $untildate->getTimestamp();
 
         $mang->parse_rrule();
         $mang->create_events($this->event);
@@ -1024,8 +1028,8 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
     public function test_yearly_september_every_two_years_forever() {
         global $DB;
 
-        // Change the start date for forever events to 9am on the month of September of the current year using the current day.
-        $this->change_event_startdate(date('Y09d\T090000'));
+        // Change the start date for forever events to 9am on the 2nd day of September of the current year.
+        $this->change_event_startdate(date('Y0902\T090000'));
 
         $rrule = "FREQ=YEARLY;BYMONTH=9;INTERVAL=2"; // Forever event.
         $mang = new rrule_manager($rrule);
@@ -1038,7 +1042,7 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
         $records = $DB->get_records('event', ['repeatid' => $this->event->id], 'timestart ASC', 'id, repeatid, timestart');
 
         $interval = new DateInterval('P2Y');
-        $expecteddate = new DateTime(date('Y09d\T090000'));
+        $expecteddate = new DateTime(date('Y0902\T090000'));
         foreach ($records as $record) {
             $this->assertLessThanOrEqual($untiltimestamp, $record->timestart);
             $this->assertEquals($expecteddate->format('Y-m-d H:i:s'), date('Y-m-d H:i:s', $record->timestart));
index 60adab4..0604f3e 100644 (file)
@@ -5154,9 +5154,12 @@ class api {
         $syscontext = context_system::instance();
         $hassystem = has_capability($capability, $syscontext, $userid);
 
-        $access = get_user_access_sitewide($userid);
+        $access = get_user_roles_sitewide_accessdata($userid);
         // Build up a list of level 2 contexts (candidates to be user context).
         $filtercontexts = array();
+        // Build list of roles to check overrides.
+        $roles = array();
+
         foreach ($access['ra'] as $path => $role) {
             $parts = explode('/', $path);
             if (count($parts) == 3) {
@@ -5165,24 +5168,23 @@ class api {
                 // We know this is not a user context because there is another path with more than 2 levels.
                 unset($filtercontexts[$parts[2]]);
             }
+            $roles = array_merge($roles, $role);
         }
 
         // Add all contexts in which a role may be overidden.
-        foreach ($access['rdef'] as $pathandroleid => $def) {
-            $matches = array();
-            if (!isset($def[$capability])) {
-                // The capability is not mentioned, we can ignore.
-                continue;
-            }
-
-            list($contextpath, $roleid) = explode(':', $pathandroleid, 2);
-            $parts = explode('/', $contextpath);
-            if (count($parts) != 3) {
-                // Only get potential user contexts, they only ever have 2 slashes /parentId/Id.
-                continue;
+        $rdefs = get_role_definitions($roles);
+        foreach ($rdefs as $roledef) {
+            foreach ($roledef as $path => $caps) {
+                if (!isset($caps[$capability])) {
+                    // The capability is not mentioned, we can ignore.
+                    continue;
+                }
+                $parts = explode('/', $path);
+                if (count($parts) === 3) {
+                    // Only get potential user contexts, they only ever have 2 slashes /parentId/Id.
+                    $filtercontexts[$parts[2]] = $parts[2];
+                }
             }
-
-            $filtercontexts[$parts[2]] = $parts[2];
         }
 
         // No interesting contexts - return all or no results.
index 4e0a33c..04b05c4 100644 (file)
@@ -74,7 +74,16 @@ class completion_completion extends data_object {
      * @return data_object instance of data_object or false if none found.
      */
     public static function fetch($params) {
-        return self::fetch_helper('course_completions', __CLASS__, $params);
+        $cache = cache::make('core', 'coursecompletion');
+
+        $key = $params['userid'] . '_' . $params['course'];
+        if ($hit = $cache->get($key)) {
+            return $hit['value'];
+        }
+
+        $tocache = self::fetch_helper('course_completions', __CLASS__, $params);
+        $cache->set($key, ['value' => $tocache]);
+        return $tocache;
     }
 
     /**
@@ -179,9 +188,10 @@ class completion_completion extends data_object {
             $this->timeenrolled = 0;
         }
 
+        $result = false;
         // Save record
         if ($this->id) {
-            return $this->update();
+            $result = $this->update();
         } else {
             // Make sure reaggregate field is not null
             if (!$this->reaggregate) {
@@ -193,7 +203,17 @@ class completion_completion extends data_object {
                 $this->timestarted = 0;
             }
 
-            return $this->insert();
+            $result = $this->insert();
+        }
+
+        if ($result) {
+            // Update the cached record.
+            $cache = cache::make('core', 'coursecompletion');
+            $data = $this->get_record_data();
+            $key = $data->userid . '_' . $data->course;
+            $cache->set($key, ['value' => $data]);
         }
+
+        return $result;
     }
 }
index 92dfb41..dcb07cf 100644 (file)
@@ -7,7 +7,7 @@
     "require-dev": {
         "phpunit/phpunit": "5.5.*",
         "phpunit/dbUnit": "1.4.*",
-        "moodlehq/behat-extension": "3.33.1",
+        "moodlehq/behat-extension": "3.34.0",
         "mikey179/vfsStream": "^1.6"
     }
 }
index 1fca2b9..cb64acd 100644 (file)
@@ -4,26 +4,27 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
         "This file is @generated automatically"
     ],
-    "content-hash": "751fc6623b264b33856167019b391053",
+    "hash": "87cf286828dd74f76aa6021b4cf7ecd5",
+    "content-hash": "ce905d6cf20a164ed747648b85732e8d",
     "packages": [],
     "packages-dev": [
         {
             "name": "behat/behat",
-            "version": "v3.3.0",
+            "version": "v3.3.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/Behat/Behat.git",
-                "reference": "15a3a1857457eaa29cdf41564a5e421effb09526"
+                "reference": "44a58c1480d6144b2dc2c2bf02b9cef73c83840d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/Behat/Behat/zipball/15a3a1857457eaa29cdf41564a5e421effb09526",
-                "reference": "15a3a1857457eaa29cdf41564a5e421effb09526",
+                "url": "https://api.github.com/repos/Behat/Behat/zipball/44a58c1480d6144b2dc2c2bf02b9cef73c83840d",
+                "reference": "44a58c1480d6144b2dc2c2bf02b9cef73c83840d",
                 "shasum": ""
             },
             "require": {
                 "behat/gherkin": "^4.4.4",
-                "behat/transliterator": "~1.0",
+                "behat/transliterator": "^1.2",
                 "container-interop/container-interop": "^1.1",
                 "ext-mbstring": "*",
                 "php": ">=5.3.3",
@@ -87,7 +88,7 @@
                 "symfony",
                 "testing"
             ],
-            "time": "2016-12-25T13:43:52+00:00"
+            "time": "2017-05-15 16:49:16"
         },
         {
             "name": "behat/gherkin",
                 "gherkin",
                 "parser"
             ],
-            "time": "2016-10-30T11:50:56+00:00"
+            "time": "2016-10-30 11:50:56"
         },
         {
             "name": "behat/mink",
                 "testing",
                 "web"
             ],
-            "time": "2016-03-05T08:26:18+00:00"
+            "time": "2016-03-05 08:26:18"
         },
         {
             "name": "behat/mink-browserkit-driver",
                 "browser",
                 "testing"
             ],
-            "time": "2016-03-05T08:59:47+00:00"
+            "time": "2016-03-05 08:59:47"
         },
         {
             "name": "behat/mink-extension",
                 "test",
                 "web"
             ],
-            "time": "2016-02-15T07:55:18+00:00"
+            "time": "2016-02-15 07:55:18"
         },
         {
             "name": "behat/mink-goutte-driver",
                 "headless",
                 "testing"
             ],
-            "time": "2016-03-05T09:04:22+00:00"
+            "time": "2016-03-05 09:04:22"
         },
         {
             "name": "behat/mink-selenium2-driver",
                 "testing",
                 "webdriver"
             ],
-            "time": "2016-03-05T09:10:18+00:00"
+            "time": "2016-03-05 09:10:18"
         },
         {
             "name": "behat/transliterator",
-            "version": "v1.1.0",
+            "version": "v1.2.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/Behat/Transliterator.git",
-                "reference": "868e05be3a9f25ba6424c2dd4849567f50715003"
+                "reference": "826ce7e9c2a6664c0d1f381cbb38b1fb80a7ee2c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/Behat/Transliterator/zipball/868e05be3a9f25ba6424c2dd4849567f50715003",
-                "reference": "868e05be3a9f25ba6424c2dd4849567f50715003",
+                "url": "https://api.github.com/repos/Behat/Transliterator/zipball/826ce7e9c2a6664c0d1f381cbb38b1fb80a7ee2c",
+                "reference": "826ce7e9c2a6664c0d1f381cbb38b1fb80a7ee2c",
                 "shasum": ""
             },
             "require": {
                 "php": ">=5.3.3"
             },
+            "require-dev": {
+                "chuyskywalker/rolling-curl": "^3.1",
+                "php-yaoi/php-yaoi": "^1.0"
+            },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.1-dev"
+                    "dev-master": "1.2-dev"
                 }
             },
             "autoload": {
                 "slug",
                 "transliterator"
             ],
-            "time": "2015-09-28T16:26:35+00:00"
+            "time": "2017-04-04 11:38:05"
         },
         {
             "name": "container-interop/container-interop",
-            "version": "1.1.0",
+            "version": "1.2.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/container-interop/container-interop.git",
-                "reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e"
+                "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/container-interop/container-interop/zipball/fc08354828f8fd3245f77a66b9e23a6bca48297e",
-                "reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e",
+                "url": "https://api.github.com/repos/container-interop/container-interop/zipball/79cbf1341c22ec75643d841642dd5d6acd83bdb8",
+                "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8",
                 "shasum": ""
             },
+            "require": {
+                "psr/container": "^1.0"
+            },
             "type": "library",
             "autoload": {
                 "psr-4": {
                 "MIT"
             ],
             "description": "Promoting the interoperability of container objects (DIC, SL, etc.)",
-            "time": "2014-12-30T15:22:37+00:00"
+            "homepage": "https://github.com/container-interop/container-interop",
+            "time": "2017-02-14 19:40:03"
         },
         {
             "name": "doctrine/instantiator",
                 "constructor",
                 "instantiate"
             ],
-            "time": "2015-06-14T21:17:01+00:00"
+            "time": "2015-06-14 21:17:01"
         },
         {
             "name": "fabpot/goutte",
             "keywords": [
                 "scraper"
             ],
-            "time": "2017-01-03T13:21:43+00:00"
+            "time": "2017-01-03 13:21:43"
         },
         {
             "name": "guzzlehttp/guzzle",
-            "version": "6.2.2",
+            "version": "6.2.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/guzzle.git",
-                "reference": "ebf29dee597f02f09f4d5bbecc68230ea9b08f60"
+                "reference": "8d6c6cc55186db87b7dc5009827429ba4e9dc006"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/ebf29dee597f02f09f4d5bbecc68230ea9b08f60",
-                "reference": "ebf29dee597f02f09f4d5bbecc68230ea9b08f60",
+                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/8d6c6cc55186db87b7dc5009827429ba4e9dc006",
+                "reference": "8d6c6cc55186db87b7dc5009827429ba4e9dc006",
                 "shasum": ""
             },
             "require": {
                 "guzzlehttp/promises": "^1.0",
-                "guzzlehttp/psr7": "^1.3.1",
+                "guzzlehttp/psr7": "^1.4",
                 "php": ">=5.5"
             },
             "require-dev": {
                 "rest",
                 "web service"
             ],
-            "time": "2016-10-08T15:01:37+00:00"
+            "time": "2017-02-28 22:50:30"
         },
         {
             "name": "guzzlehttp/promises",
             "keywords": [
                 "promise"
             ],
-            "time": "2016-12-20T10:07:11+00:00"
+            "time": "2016-12-20 10:07:11"
         },
         {
             "name": "guzzlehttp/psr7",
-            "version": "1.3.1",
+            "version": "1.4.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/psr7.git",
-                "reference": "5c6447c9df362e8f8093bda8f5d8873fe5c7f65b"
+                "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/psr7/zipball/5c6447c9df362e8f8093bda8f5d8873fe5c7f65b",
-                "reference": "5c6447c9df362e8f8093bda8f5d8873fe5c7f65b",
+                "url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c",
+                "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c",
                 "shasum": ""
             },
             "require": {
                     "name": "Michael Dowling",
                     "email": "mtdowling@gmail.com",
                     "homepage": "https://github.com/mtdowling"
+                },
+                {
+                    "name": "Tobias Schultze",
+                    "homepage": "https://github.com/Tobion"
                 }
             ],
-            "description": "PSR-7 message implementation",
+            "description": "PSR-7 message implementation that also provides common utility methods",
             "keywords": [
                 "http",
                 "message",
+                "request",
+                "response",
                 "stream",
-                "uri"
+                "uri",
+                "url"
             ],
-            "time": "2016-06-24T23:00:38+00:00"
+            "time": "2017-03-20 17:10:46"
         },
         {
             "name": "instaclick/php-webdriver",
                 {
                     "name": "Anthon Pang",
                     "email": "apang@softwaredevelopment.ca",
-                    "role": "Fork Maintainer"
+                    "role": "Fork maintainer"
                 }
             ],
             "description": "PHP WebDriver for Selenium 2",
                 "webdriver",
                 "webtest"
             ],
-            "time": "2015-06-15T20:19:33+00:00"
+            "time": "2015-06-15 20:19:33"
         },
         {
             "name": "mikey179/vfsStream",
             ],
             "description": "Virtual file system to mock the real file system in unit tests.",
             "homepage": "http://vfs.bovigo.org/",
-            "time": "2016-07-18T14:02:57+00:00"
+            "time": "2016-07-18 14:02:57"
         },
         {
             "name": "moodlehq/behat-extension",
-            "version": "v3.33.1",
+            "version": "v3.34.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/moodlehq/moodle-behat-extension.git",
                 "Behat",
                 "moodle"
             ],
-            "time": "2017-01-20T02:48:22+00:00"
+            "time": "2017-01-20 02:48:22"
         },
         {
             "name": "myclabs/deep-copy",
-            "version": "1.6.0",
+            "version": "1.6.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/myclabs/DeepCopy.git",
-                "reference": "5a5a9fc8025a08d8919be87d6884d5a92520cefe"
+                "reference": "8e6e04167378abf1ddb4d3522d8755c5fd90d102"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/5a5a9fc8025a08d8919be87d6884d5a92520cefe",
-                "reference": "5a5a9fc8025a08d8919be87d6884d5a92520cefe",
+                "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/8e6e04167378abf1ddb4d3522d8755c5fd90d102",
+                "reference": "8e6e04167378abf1ddb4d3522d8755c5fd90d102",
                 "shasum": ""
             },
             "require": {
                 "object",
                 "object graph"
             ],
-            "time": "2017-01-26T22:05:40+00:00"
+            "time": "2017-04-12 18:52:22"
         },
         {
             "name": "phpdocumentor/reflection-common",
                 "reflection",
                 "static analysis"
             ],
-            "time": "2015-12-27T11:43:31+00:00"
+            "time": "2015-12-27 11:43:31"
         },
         {
             "name": "phpdocumentor/reflection-docblock",
                 }
             ],
             "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
-            "time": "2016-09-30T07:12:33+00:00"
+            "time": "2016-09-30 07:12:33"
         },
         {
             "name": "phpdocumentor/type-resolver",
                     "email": "me@mikevanriel.com"
                 }
             ],
-            "time": "2016-11-25T06:54:22+00:00"
+            "time": "2016-11-25 06:54:22"
         },
         {
             "name": "phpspec/prophecy",
-            "version": "v1.6.2",
+            "version": "v1.7.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phpspec/prophecy.git",
-                "reference": "6c52c2722f8460122f96f86346600e1077ce22cb"
+                "reference": "93d39f1f7f9326d746203c7c056f300f7f126073"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpspec/prophecy/zipball/6c52c2722f8460122f96f86346600e1077ce22cb",
-                "reference": "6c52c2722f8460122f96f86346600e1077ce22cb",
+                "url": "https://api.github.com/repos/phpspec/prophecy/zipball/93d39f1f7f9326d746203c7c056f300f7f126073",
+                "reference": "93d39f1f7f9326d746203c7c056f300f7f126073",
                 "shasum": ""
             },
             "require": {
                 "doctrine/instantiator": "^1.0.2",
                 "php": "^5.3|^7.0",
                 "phpdocumentor/reflection-docblock": "^2.0|^3.0.2",
-                "sebastian/comparator": "^1.1",
-                "sebastian/recursion-context": "^1.0|^2.0"
+                "sebastian/comparator": "^1.1|^2.0",
+                "sebastian/recursion-context": "^1.0|^2.0|^3.0"
             },
             "require-dev": {
-                "phpspec/phpspec": "^2.0",
+                "phpspec/phpspec": "^2.5|^3.2",
                 "phpunit/phpunit": "^4.8 || ^5.6.5"
             },
             "type": "library",
                 "spy",
                 "stub"
             ],
-            "time": "2016-11-21T14:58:47+00:00"
+            "time": "2017-03-02 20:05:34"
         },
         {
             "name": "phpunit/dbunit",
                 "testing",
                 "xunit"
             ],
-            "time": "2015-08-07T04:57:38+00:00"
+            "time": "2015-08-07 04:57:38"
         },
         {
             "name": "phpunit/php-code-coverage",
-            "version": "4.0.5",
+            "version": "4.0.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
-                "reference": "c19cfc7cbb0e9338d8c469c7eedecc2a428b0971"
+                "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c19cfc7cbb0e9338d8c469c7eedecc2a428b0971",
-                "reference": "c19cfc7cbb0e9338d8c469c7eedecc2a428b0971",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ef7b2f56815df854e66ceaee8ebe9393ae36a40d",
+                "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d",
                 "shasum": ""
             },
             "require": {
+                "ext-dom": "*",
+                "ext-xmlwriter": "*",
                 "php": "^5.6 || ^7.0",
-                "phpunit/php-file-iterator": "~1.3",
-                "phpunit/php-text-template": "~1.2",
-                "phpunit/php-token-stream": "^1.4.2",
-                "sebastian/code-unit-reverse-lookup": "~1.0",
+                "phpunit/php-file-iterator": "^1.3",
+                "phpunit/php-text-template": "^1.2",
+                "phpunit/php-token-stream": "^1.4.2 || ^2.0",
+                "sebastian/code-unit-reverse-lookup": "^1.0",
                 "sebastian/environment": "^1.3.2 || ^2.0",
-                "sebastian/version": "~1.0|~2.0"
+                "sebastian/version": "^1.0 || ^2.0"
             },
             "require-dev": {
-                "ext-xdebug": ">=2.1.4",
-                "phpunit/phpunit": "^5.4"
+                "ext-xdebug": "^2.1.4",
+                "phpunit/phpunit": "^5.7"
             },
             "suggest": {
-                "ext-dom": "*",
-                "ext-xdebug": ">=2.4.0",
-                "ext-xmlwriter": "*"
+                "ext-xdebug": "^2.5.1"
             },
             "type": "library",
             "extra": {
                 "testing",
                 "xunit"
             ],
-            "time": "2017-01-20T15:06:43+00:00"
+            "time": "2017-04-02 07:44:40"
         },
         {
             "name": "phpunit/php-file-iterator",
                 "filesystem",
                 "iterator"
             ],
-            "time": "2016-10-03T07:40:28+00:00"
+            "time": "2016-10-03 07:40:28"
         },
         {
             "name": "phpunit/php-text-template",
             "keywords": [
                 "template"
             ],
-            "time": "2015-06-21T13:50:34+00:00"
+            "time": "2015-06-21 13:50:34"
         },
         {
             "name": "phpunit/php-timer",
-            "version": "1.0.8",
+            "version": "1.0.9",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/php-timer.git",
-                "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260"
+                "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/38e9124049cf1a164f1e4537caf19c99bf1eb260",
-                "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f",
+                "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.3"
+                "php": "^5.3.3 || ^7.0"
             },
             "require-dev": {
-                "phpunit/phpunit": "~4|~5"
+                "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0"
             },
             "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0-dev"
+                }
+            },
             "autoload": {
                 "classmap": [
                     "src/"
             "keywords": [
                 "timer"
             ],
-            "time": "2016-05-12T18:03:57+00:00"
+            "time": "2017-02-26 11:10:40"
         },
         {
             "name": "phpunit/php-token-stream",
-            "version": "1.4.9",
+            "version": "1.4.11",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/php-token-stream.git",
-                "reference": "3b402f65a4cc90abf6e1104e388b896ce209631b"
+                "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3b402f65a4cc90abf6e1104e388b896ce209631b",
-                "reference": "3b402f65a4cc90abf6e1104e388b896ce209631b",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e03f8f67534427a787e21a385a67ec3ca6978ea7",
+                "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7",
                 "shasum": ""
             },
             "require": {
             "keywords": [
                 "tokenizer"
             ],
-            "time": "2016-11-15T14:06:22+00:00"
+            "time": "2017-02-27 10:12:30"
         },
         {
             "name": "phpunit/phpunit",
                 "testing",
                 "xunit"
             ],
-            "time": "2016-10-03T13:04:15+00:00"
+            "time": "2016-10-03 13:04:15"
         },
         {
             "name": "phpunit/phpunit-mock-objects",
                 "mock",
                 "xunit"
             ],
-            "time": "2016-12-08T20:27:08+00:00"
+            "time": "2016-12-08 20:27:08"
+        },
+        {
+            "name": "psr/container",
+            "version": "1.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/container.git",
+                "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
+                "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Container\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common Container Interface (PHP FIG PSR-11)",
+            "homepage": "https://github.com/php-fig/container",
+            "keywords": [
+                "PSR-11",
+                "container",
+                "container-interface",
+                "container-interop",
+                "psr"
+            ],
+            "time": "2017-02-14 16:28:37"
         },
         {
             "name": "psr/http-message",
                 "request",
                 "response"
             ],
-            "time": "2016-08-06T14:39:51+00:00"
+            "time": "2016-08-06 14:39:51"
         },
         {
             "name": "psr/log",
                 "psr",
                 "psr-3"
             ],
-            "time": "2016-10-10T12:19:37+00:00"
+            "time": "2016-10-10 12:19:37"
         },
         {
             "name": "sebastian/code-unit-reverse-lookup",
-            "version": "1.0.0",
+            "version": "1.0.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
-                "reference": "c36f5e7cfce482fde5bf8d10d41a53591e0198fe"
+                "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/c36f5e7cfce482fde5bf8d10d41a53591e0198fe",
-                "reference": "c36f5e7cfce482fde5bf8d10d41a53591e0198fe",
+                "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
+                "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.6"
+                "php": "^5.6 || ^7.0"
             },
             "require-dev": {
-                "phpunit/phpunit": "~5"
+                "phpunit/phpunit": "^5.7 || ^6.0"
             },
             "type": "library",
             "extra": {
             ],
             "description": "Looks up which function or method a line of code belongs to",
             "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
-            "time": "2016-02-13T06:45:14+00:00"
+            "time": "2017-03-04 06:30:41"
         },
         {
             "name": "sebastian/comparator",
                 "compare",
                 "equality"
             ],
-            "time": "2017-01-29T09:50:25+00:00"
+            "time": "2017-01-29 09:50:25"
         },
         {
             "name": "sebastian/diff",
-            "version": "1.4.1",
+            "version": "1.4.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/diff.git",
-                "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e"
+                "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e",
-                "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e",
+                "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4",
+                "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.3"
+                "php": "^5.3.3 || ^7.0"
             },
             "require-dev": {
-                "phpunit/phpunit": "~4.8"
+                "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0"
             },
             "type": "library",
             "extra": {
             "keywords": [
                 "diff"
             ],
-            "time": "2015-12-08T07:14:41+00:00"
+            "time": "2017-05-22 07:24:03"
         },
         {
             "name": "sebastian/environment",
                 "environment",
                 "hhvm"
             ],
-            "time": "2016-11-26T07:53:53+00:00"
+            "time": "2016-11-26 07:53:53"
         },
         {
             "name": "sebastian/exporter",
                 "export",
                 "exporter"
             ],
-            "time": "2016-06-17T09:04:28+00:00"
+            "time": "2016-06-17 09:04:28"
         },
         {
             "name": "sebastian/global-state",
             "keywords": [
                 "global state"
             ],
-            "time": "2015-10-12T03:26:01+00:00"
+            "time": "2015-10-12 03:26:01"
         },
         {
             "name": "sebastian/object-enumerator",
             ],
             "description": "Traverses array structures and object graphs to enumerate all referenced objects",
             "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
-            "time": "2016-01-28T13:25:10+00:00"
+            "time": "2016-01-28 13:25:10"
         },
         {
             "name": "sebastian/recursion-context",
-            "version": "1.0.2",
+            "version": "1.0.5",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/recursion-context.git",
-                "reference": "913401df809e99e4f47b27cdd781f4a258d58791"
+                "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/913401df809e99e4f47b27cdd781f4a258d58791",
-                "reference": "913401df809e99e4f47b27cdd781f4a258d58791",
+                "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/b19cc3298482a335a95f3016d2f8a6950f0fbcd7",
+                "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7",
                 "shasum": ""
             },
             "require": {
             ],
             "description": "Provides functionality to recursively process PHP variables",
             "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
-            "time": "2015-11-11T19:50:13+00:00"
+            "time": "2016-10-03 07:41:43"
         },
         {
             "name": "sebastian/resource-operations",
             ],
             "description": "Provides a list of PHP built-in functions that operate on resources",
             "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
-            "time": "2015-07-28T20:34:47+00:00"
+            "time": "2015-07-28 20:34:47"
         },
         {
             "name": "sebastian/version",
             ],
             "description": "Library that helps with managing the version number of Git-hosted PHP projects",
             "homepage": "https://github.com/sebastianbergmann/version",
-            "time": "2016-10-03T07:35:21+00:00"
+            "time": "2016-10-03 07:35:21"
         },
         {
             "name": "symfony/browser-kit",
-            "version": "v3.2.2",
+            "version": "v3.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/browser-kit.git",
-                "reference": "548f8230bad9f77463b20b15993a008f03e96db5"
+                "reference": "c2c8ceb1aa9dab9eae54e9150e6a588ce3e53be1"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/browser-kit/zipball/548f8230bad9f77463b20b15993a008f03e96db5",
-                "reference": "548f8230bad9f77463b20b15993a008f03e96db5",
+                "url": "https://api.github.com/repos/symfony/browser-kit/zipball/c2c8ceb1aa9dab9eae54e9150e6a588ce3e53be1",
+                "reference": "c2c8ceb1aa9dab9eae54e9150e6a588ce3e53be1",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "3.2-dev"
+                    "dev-master": "3.3-dev"
                 }
             },
             "autoload": {
             ],
             "description": "Symfony BrowserKit Component",
             "homepage": "https://symfony.com",
-            "time": "2017-01-02T20:32:22+00:00"
+            "time": "2017-04-12 14:14:56"
         },
         {
             "name": "symfony/class-loader",
-            "version": "v3.2.2",
+            "version": "v3.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/class-loader.git",
-                "reference": "0152f7a47acd564ca62c652975c2b32ac6d613a6"
+                "reference": "b0aff75bf18e4bbf37209235227e6e50a5aec8f5"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/class-loader/zipball/0152f7a47acd564ca62c652975c2b32ac6d613a6",
-                "reference": "0152f7a47acd564ca62c652975c2b32ac6d613a6",
+                "url": "https://api.github.com/repos/symfony/class-loader/zipball/b0aff75bf18e4bbf37209235227e6e50a5aec8f5",
+                "reference": "b0aff75bf18e4bbf37209235227e6e50a5aec8f5",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "3.2-dev"
+                    "dev-master": "3.3-dev"
                 }
             },
             "autoload": {
             ],
             "description": "Symfony ClassLoader Component",
             "homepage": "https://symfony.com",
-            "time": "2017-01-10T14:14:38+00:00"
+            "time": "2017-04-12 14:14:56"
         },
         {
             "name": "symfony/config",
-            "version": "v3.2.2",
+            "version": "v3.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/config.git",
-                "reference": "c5ea878b5a7f6a01b9a2f182f905831711b9ff3f"
+                "reference": "79f86253ba482ca7f17718e886e6d164e5ba6d45"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/config/zipball/c5ea878b5a7f6a01b9a2f182f905831711b9ff3f",
-                "reference": "c5ea878b5a7f6a01b9a2f182f905831711b9ff3f",
+                "url": "https://api.github.com/repos/symfony/config/zipball/79f86253ba482ca7f17718e886e6d164e5ba6d45",
+                "reference": "79f86253ba482ca7f17718e886e6d164e5ba6d45",
                 "shasum": ""
             },
             "require": {
                 "php": ">=5.5.9",
                 "symfony/filesystem": "~2.8|~3.0"
             },
+            "conflict": {
+                "symfony/dependency-injection": "<3.3"
+            },
             "require-dev": {
+                "symfony/dependency-injection": "~3.3",
                 "symfony/yaml": "~3.0"
             },
             "suggest": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "3.2-dev"
+                    "dev-master": "3.3-dev"
                 }
             },
             "autoload": {
             ],
             "description": "Symfony Config Component",
             "homepage": "https://symfony.com",
-            "time": "2017-01-02T20:32:22+00:00"
+            "time": "2017-05-29 18:41:32"
         },
         {
             "name": "symfony/console",
-            "version": "v3.2.2",
+            "version": "v3.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/console.git",
-                "reference": "4f9e449e76996adf310498a8ca955c6deebe29dd"
+                "reference": "c80e63f3f5e3a331bfc25e6e9332b10422eb9b05"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/console/zipball/4f9e449e76996adf310498a8ca955c6deebe29dd",
-                "reference": "4f9e449e76996adf310498a8ca955c6deebe29dd",
+                "url": "https://api.github.com/repos/symfony/console/zipball/c80e63f3f5e3a331bfc25e6e9332b10422eb9b05",
+                "reference": "c80e63f3f5e3a331bfc25e6e9332b10422eb9b05",
                 "shasum": ""
             },
             "require": {
                 "symfony/debug": "~2.8|~3.0",
                 "symfony/polyfill-mbstring": "~1.0"
             },
+            "conflict": {
+                "symfony/dependency-injection": "<3.3"
+            },
             "require-dev": {
                 "psr/log": "~1.0",
+                "symfony/dependency-injection": "~3.3",
                 "symfony/event-dispatcher": "~2.8|~3.0",
                 "symfony/filesystem": "~2.8|~3.0",
+                "symfony/http-kernel": "~2.8|~3.0",
                 "symfony/process": "~2.8|~3.0"
             },
             "suggest": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "3.2-dev"
+                    "dev-master": "3.3-dev"
                 }
             },
             "autoload": {
             ],
             "description": "Symfony Console Component",
             "homepage": "https://symfony.com",
-            "time": "2017-01-08T20:47:33+00:00"
+            "time": "2017-05-28 14:08:56"
         },
         {
             "name": "symfony/css-selector",
-            "version": "v3.2.2",
+            "version": "v3.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/css-selector.git",
-                "reference": "f0e628f04fc055c934b3211cfabdb1c59eefbfaa"
+                "reference": "4d882dced7b995d5274293039370148e291808f2"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/css-selector/zipball/f0e628f04fc055c934b3211cfabdb1c59eefbfaa",
-                "reference": "f0e628f04fc055c934b3211cfabdb1c59eefbfaa",
+                "url": "https://api.github.com/repos/symfony/css-selector/zipball/4d882dced7b995d5274293039370148e291808f2",
+                "reference": "4d882dced7b995d5274293039370148e291808f2",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "3.2-dev"
+                    "dev-master": "3.3-dev"
                 }
             },
             "autoload": {
             ],
             "description": "Symfony CssSelector Component",
             "homepage": "https://symfony.com",
-            "time": "2017-01-02T20:32:22+00:00"
+            "time": "2017-05-01 15:01:29"
         },
         {
             "name": "symfony/debug",
-            "version": "v3.2.2",
+            "version": "v3.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/debug.git",
-                "reference": "810ba5c1c5352a4ddb15d4719e8936751dff0b05"
+                "reference": "ef5f19a7a68075a0bd05969a329ead3b0776fb7a"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/debug/zipball/810ba5c1c5352a4ddb15d4719e8936751dff0b05",
-                "reference": "810ba5c1c5352a4ddb15d4719e8936751dff0b05",
+                "url": "https://api.github.com/repos/symfony/debug/zipball/ef5f19a7a68075a0bd05969a329ead3b0776fb7a",
+                "reference": "ef5f19a7a68075a0bd05969a329ead3b0776fb7a",
                 "shasum": ""
             },
             "require": {
                 "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2"
             },
             "require-dev": {
-                "symfony/class-loader": "~2.8|~3.0",
                 "symfony/http-kernel": "~2.8|~3.0"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "3.2-dev"
+                    "dev-master": "3.3-dev"
                 }
             },
             "autoload": {
             ],
             "description": "Symfony Debug Component",
             "homepage": "https://symfony.com",
-            "time": "2017-01-02T20:32:22+00:00"
+            "time": "2017-05-27 16:02:27"
         },
         {
             "name": "symfony/dependency-injection",
-            "version": "v3.2.2",
+            "version": "v3.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/dependency-injection.git",
-                "reference": "22b2c97cffc6a612db82084f9e7823b095958751"
+                "reference": "988c7bd6ec880690792ccf2a1e5ca05401c2a63d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/22b2c97cffc6a612db82084f9e7823b095958751",
-                "reference": "22b2c97cffc6a612db82084f9e7823b095958751",
+                "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/988c7bd6ec880690792ccf2a1e5ca05401c2a63d",
+                "reference": "988c7bd6ec880690792ccf2a1e5ca05401c2a63d",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.5.9"
+                "php": ">=5.5.9",
+                "psr/container": "^1.0"
             },
             "conflict": {
-                "symfony/yaml": "<3.2"
+                "symfony/config": "<=3.3-beta1",
+                "symfony/finder": "<3.3",
+                "symfony/yaml": "<3.3"
+            },
+            "provide": {
+                "psr/container-implementation": "1.0"
             },
             "require-dev": {
-                "symfony/config": "~2.8|~3.0",
+                "symfony/config": "~3.3",
                 "symfony/expression-language": "~2.8|~3.0",
-                "symfony/yaml": "~3.2"
+                "symfony/yaml": "~3.3"
             },
             "suggest": {
                 "symfony/config": "",
                 "symfony/expression-language": "For using expressions in service container configuration",
+                "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required",
                 "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them",
                 "symfony/yaml": ""
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "3.2-dev"
+                    "dev-master": "3.3-dev"
                 }
             },
             "autoload": {
             ],
             "description": "Symfony DependencyInjection Component",
             "homepage": "https://symfony.com",
-            "time": "2017-01-10T14:21:25+00:00"
+            "time": "2017-05-25 23:10:31"
         },
         {
             "name": "symfony/dom-crawler",
-            "version": "v3.2.2",
+            "version": "v3.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/dom-crawler.git",
-                "reference": "27d9790840a4efd3b7bb8f5f4f9efc27b36b7024"
+                "reference": "fc2c588ce376e9fe04a7b8c79e3ec62fe32d95b1"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/27d9790840a4efd3b7bb8f5f4f9efc27b36b7024",
-                "reference": "27d9790840a4efd3b7bb8f5f4f9efc27b36b7024",
+                "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/fc2c588ce376e9fe04a7b8c79e3ec62fe32d95b1",
+                "reference": "fc2c588ce376e9fe04a7b8c79e3ec62fe32d95b1",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "3.2-dev"
+                    "dev-master": "3.3-dev"
                 }
             },
             "autoload": {
             ],
             "description": "Symfony DomCrawler Component",
             "homepage": "https://symfony.com",
-            "time": "2017-01-02T20:32:22+00:00"
+            "time": "2017-05-25 23:10:31"
         },
         {
             "name": "symfony/event-dispatcher",
-            "version": "v3.2.2",
+            "version": "v3.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/event-dispatcher.git",
-                "reference": "9137eb3a3328e413212826d63eeeb0217836e2b6"
+                "reference": "a9f8b02b0ef07302eca92cd4bba73200b7980e9c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9137eb3a3328e413212826d63eeeb0217836e2b6",
-                "reference": "9137eb3a3328e413212826d63eeeb0217836e2b6",
+                "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/a9f8b02b0ef07302eca92cd4bba73200b7980e9c",
+                "reference": "a9f8b02b0ef07302eca92cd4bba73200b7980e9c",
                 "shasum": ""
             },
             "require": {
                 "php": ">=5.5.9"
             },
+            "conflict": {
+                "symfony/dependency-injection": "<3.3"
+            },
             "require-dev": {
                 "psr/log": "~1.0",
                 "symfony/config": "~2.8|~3.0",
-                "symfony/dependency-injection": "~2.8|~3.0",
+                "symfony/dependency-injection": "~3.3",
                 "symfony/expression-language": "~2.8|~3.0",
                 "symfony/stopwatch": "~2.8|~3.0"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "3.2-dev"
+                    "dev-master": "3.3-dev"
                 }
             },
             "autoload": {
             ],
             "description": "Symfony EventDispatcher Component",
             "homepage": "https://symfony.com",
-            "time": "2017-01-02T20:32:22+00:00"
+            "time": "2017-05-04 12:23:07"
         },
         {
             "name": "symfony/filesystem",
-            "version": "v3.2.2",
+            "version": "v3.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/filesystem.git",
-                "reference": "a0c6ef2dc78d33b58d91d3a49f49797a184d06f4"
+                "reference": "c709670bf64721202ddbe4162846f250735842c0"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/filesystem/zipball/a0c6ef2dc78d33b58d91d3a49f49797a184d06f4",
-                "reference": "a0c6ef2dc78d33b58d91d3a49f49797a184d06f4",
+                "url": "https://api.github.com/repos/symfony/filesystem/zipball/c709670bf64721202ddbe4162846f250735842c0",
+                "reference": "c709670bf64721202ddbe4162846f250735842c0",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "3.2-dev"
+                    "dev-master": "3.3-dev"
                 }
             },
             "autoload": {
             ],
             "description": "Symfony Filesystem Component",
             "homepage": "https://symfony.com",
-            "time": "2017-01-08T20:47:33+00:00"
+            "time": "2017-05-28 14:08:56"
         },
         {
             "name": "symfony/polyfill-mbstring",
                 "portable",
                 "shim"
             ],
-            "time": "2016-11-14T01:06:16+00:00"
+            "time": "2016-11-14 01:06:16"
         },
         {
             "name": "symfony/process",
-            "version": "v2.8.16",
+            "version": "v2.8.21",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/process.git",
-                "reference": "ebb3c2abe0940a703f08e0cbe373f62d97d40231"
+                "reference": "d54232f5682fda2f8bbebff7c81b864646867ab9"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/process/zipball/ebb3c2abe0940a703f08e0cbe373f62d97d40231",
-                "reference": "ebb3c2abe0940a703f08e0cbe373f62d97d40231",
+                "url": "https://api.github.com/repos/symfony/process/zipball/d54232f5682fda2f8bbebff7c81b864646867ab9",
+                "reference": "d54232f5682fda2f8bbebff7c81b864646867ab9",
                 "shasum": ""
             },
             "require": {
             ],
             "description": "Symfony Process Component",
             "homepage": "https://symfony.com",
-            "time": "2017-01-02T20:30:24+00:00"
+            "time": "2017-05-08 01:19:21"
         },
         {
             "name": "symfony/translation",
-            "version": "v3.2.2",
+            "version": "v3.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/translation.git",
-                "reference": "6520f3d4cce604d9dd1e86cac7af954984dd9bda"
+                "reference": "dc3b2a0c6cfff60327ba1c043a82092735397543"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/translation/zipball/6520f3d4cce604d9dd1e86cac7af954984dd9bda",
-                "reference": "6520f3d4cce604d9dd1e86cac7af954984dd9bda",
+                "url": "https://api.github.com/repos/symfony/translation/zipball/dc3b2a0c6cfff60327ba1c043a82092735397543",
+                "reference": "dc3b2a0c6cfff60327ba1c043a82092735397543",
                 "shasum": ""
             },
             "require": {
                 "symfony/polyfill-mbstring": "~1.0"
             },
             "conflict": {
-                "symfony/config": "<2.8"
+                "symfony/config": "<2.8",
+                "symfony/yaml": "<3.3"
             },
             "require-dev": {
                 "psr/log": "~1.0",
                 "symfony/config": "~2.8|~3.0",
-                "symfony/intl": "~2.8|~3.0",
-                "symfony/yaml": "~2.8|~3.0"
+                "symfony/intl": "^2.8.18|^3.2.5",
+                "symfony/yaml": "~3.3"
             },
             "suggest": {
                 "psr/log": "To use logging capability in translator",
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "3.2-dev"
+                    "dev-master": "3.3-dev"
                 }
             },
             "autoload": {
             ],
             "description": "Symfony Translation Component",
             "homepage": "https://symfony.com",
-            "time": "2017-01-02T20:32:22+00:00"
+            "time": "2017-05-22 07:42:36"
         },
         {
             "name": "symfony/yaml",
-            "version": "v3.2.2",
+            "version": "v3.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/yaml.git",
-                "reference": "50eadbd7926e31842893c957eca362b21592a97d"
+                "reference": "885db865f6b2b918404a1fae28f9ac640f71f994"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/yaml/zipball/50eadbd7926e31842893c957eca362b21592a97d",
-                "reference": "50eadbd7926e31842893c957eca362b21592a97d",
+                "url": "https://api.github.com/repos/symfony/yaml/zipball/885db865f6b2b918404a1fae28f9ac640f71f994",
+                "reference": "885db865f6b2b918404a1fae28f9ac640f71f994",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "3.2-dev"
+                    "dev-master": "3.3-dev"
                 }
             },
             "autoload": {
             ],
             "description": "Symfony Yaml Component",
             "homepage": "https://symfony.com",
-            "time": "2017-01-03T13:51:32+00:00"
+            "time": "2017-05-28 10:56:20"
         },
         {
             "name": "webmozart/assert",
                 "check",
                 "validate"
             ],
-            "time": "2016-11-23T20:04:58+00:00"
+            "time": "2016-11-23 20:04:58"
         }
     ],
     "aliases": [],
index ce8c49c..904a587 100644 (file)
Binary files a/course/amd/build/actions.min.js and b/course/amd/build/actions.min.js differ
index a804b45..d7e7b84 100644 (file)
@@ -354,27 +354,32 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/str'
          * @param {String} titlestr string for "title" attribute (if different from stringname)
          * @param {String} titlecomponent
          * @param {String} newaction new value for data-action attribute of the link
+         * @return {Promise} promise which is resolved when the replacement has completed
          */
         var replaceActionItem = function(actionitem, image, stringname,
                                            stringcomponent, titlestr, titlecomponent, newaction) {
 
-            str.get_string(stringname, stringcomponent).done(function(newstring) {
-                actionitem.find('span.menu-action-text').html(newstring);
-                actionitem.attr('title', newstring);
-            });
+
+            var stringRequests = [{key: stringname, component: stringcomponent}];
             if (titlestr) {
-                str.get_string(titlestr, titlecomponent).then(function(newtitle) {
-                    templates.renderPix(image, 'core', newtitle).then(function(html) {
-                        actionitem.find('.icon').replaceWith(html);
-                    });
-                    actionitem.attr('title', newtitle);
-                });
-            } else {
-                templates.renderPix(image, 'core', '').then(function(html) {
-                    actionitem.find('.icon').replaceWith(html);
-                });
+                stringRequests.push({key: titlestr, component: titlecomponent});
             }
-            actionitem.attr('data-action', newaction);
+
+            return str.get_strings(stringRequests).then(function(strings) {
+                actionitem.find('span.menu-action-text').html(strings[0]);
+                actionitem.attr('title', strings[0]);
+
+                var title = '';
+                if (titlestr) {
+                    title = strings[1];
+                    actionitem.attr('title', title);
+                }
+                return templates.renderPix(image, 'core', title);
+            }).then(function(pixhtml) {
+                actionitem.find('.icon').replaceWith(pixhtml);
+                actionitem.attr('data-action', newaction);
+                return;
+            }).catch(notification.exception);
         };
 
         /**
index 69bc85e..d71b254 100644 (file)
@@ -43,5 +43,8 @@ function xmldb_format_topics_upgrade($oldversion) {
         upgrade_plugin_savepoint(true, 2017020200, 'format', 'topics');
     }
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index fb70b62..e0ec9c8 100644 (file)
@@ -162,8 +162,11 @@ class restore_format_weeks_plugin extends restore_format_plugin {
 
         $data = $this->connectionpoint->get_data();
 
+        // Backup may not include the end date, so set it to 0.
+        $enddate = isset($data['tags']['enddate']) ? $data['tags']['enddate'] : 0;
+
         // Set the automatic end date setting and the course end date (if applicable).
-        $this->update_automatic_end_date($data['tags']['enddate']);
+        $this->update_automatic_end_date($enddate);
 
         if (isset($data['tags']['numsections'])) {
             // Update course sections visibility.
index 4458412..9d4e0e9 100644 (file)
@@ -79,5 +79,8 @@ function xmldb_format_weeks_upgrade($oldversion) {
         upgrade_plugin_savepoint(true, 2017050300, 'format', 'weeks');
     }
 
+    // Automatically generated Moodle v3.3.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index e60bd2c..f079a74 100644 (file)
@@ -55,6 +55,10 @@ define('FIRSTUSEDEXCELROW', 3);
 define('MOD_CLASS_ACTIVITY', 0);
 define('MOD_CLASS_RESOURCE', 1);
 
+define('COURSE_TIMELINE_PAST', 'past');
+define('COURSE_TIMELINE_INPROGRESS', 'inprogress');
+define('COURSE_TIMELINE_FUTURE', 'future');
+
 function make_log_url($module, $url) {
     switch ($module) {
         case 'course':
@@ -863,6 +867,7 @@ function course_create_section($courseorid, $position = 0, $skipcheck = false) {
     $cw->name = null;
     $cw->visible = 1;
     $cw->availability = null;
+    $cw->timemodified = time();
     $cw->id = $DB->insert_record("course_sections", $cw);
 
     // Now move it to the specified position.
@@ -1611,6 +1616,7 @@ function course_update_section($course, $section, $data) {
 
     // Update record in the DB and course format options.
     $data['id'] = $section->id;
+    $data['timemodified'] = time();
     $DB->update_record('course_sections', $data);
     rebuild_course_cache($courseid, true);
     course_get_format($courseid)->update_section_format_options($data);
@@ -4001,6 +4007,46 @@ function course_check_updates($course, $tocheck, $filter = array()) {
     return array($instances, $warnings);
 }
 
+/**
+ * This function classifies a course as past, in progress or future.
+ *
+ * This function may incur a DB hit to calculate course completion.
+ * @param stdClass $course Course record
+ * @param stdClass $user User record (optional - defaults to $USER).
+ * @param completion_info $completioninfo Completion record for the user (optional - will be fetched if required).
+ * @return string (one of COURSE_TIMELINE_FUTURE, COURSE_TIMELINE_INPROGRESS or COURSE_TIMELINE_PAST)
+ */
+function course_classify_for_timeline($course, $user = null, $completioninfo = null) {
+    global $USER;
+
+    if ($user == null) {
+        $user = $USER;
+    }
+
+    $today = time();
+    // End date past.
+    if (!empty($course->enddate) && $course->enddate < $today) {
+        return COURSE_TIMELINE_PAST;
+    }
+
+    if ($completioninfo == null) {
+        $completioninfo = new completion_info($course);
+    }
+
+    // Course was completed.
+    if ($completioninfo->is_enabled() && $completioninfo->is_course_complete($user->id)) {
+        return COURSE_TIMELINE_PAST;
+    }
+
+    // Start date not reached.
+    if (!empty($course->startdate) && $course->startdate > $today) {
+        return COURSE_TIMELINE_FUTURE;
+    }
+
+    // Everything else is in progress.
+    return COURSE_TIMELINE_INPROGRESS;
+}
+
 /**
  * Check module updates since a given time.
  * This function checks for updates in the module config, file areas, completion, grades, comments and ratings.
index 61169d5..5ce1ffa 100644 (file)
@@ -678,6 +678,29 @@ class core_course_courselib_testcase extends advanced_testcase {
         }
     }
 
+    public function test_update_course_section_time_modified() {
+        global $DB;
+
+        $this->resetAfterTest();
+
+        // Create the course with sections.
+        $course = $this->getDataGenerator()->create_course(array('numsections' => 10), array('createsections' => true));
+        $sections = $DB->get_records('course_sections', array('course' => $course->id));
+
+        // Get the last section's time modified value.
+        $section = array_pop($sections);
+        $oldtimemodified = $section->timemodified;
+