// indirectly calls the protected init() method is good here.
core_component::get_core_subsystems();
+if (is_major_upgrade_required() && isloggedin()) {
+ // A major upgrade is required.
+ // Terminate the session and redirect back here before anything DB-related happens.
+ redirect_if_major_upgrade_required();
+}
+
require_once($CFG->libdir.'/adminlib.php'); // various admin-only functions
require_once($CFG->libdir.'/upgradelib.php'); // general upgrade/install related functions
// Look at all the templates dirs for subsystems.
$subsystems = core_component::get_core_subsystems();
foreach ($subsystems as $subsystem => $dir) {
+ if (empty($dir)) {
+ continue;
+ }
$dir .= '/templates';
if (is_dir($dir)) {
$dirs = mustache_template_finder::get_template_directories_for_component('core_' . $subsystem, $themename);
$data->error = $this->error;
$data->forgotpasswordurl = $this->forgotpasswordurl->out(false);
$data->hasidentityproviders = !empty($this->identityproviders);
- $data->hasinstructions = !empty($this->instructions);
+ $data->hasinstructions = !empty($this->instructions) || $this->cansignup;
$data->identityproviders = $identityproviders;
list($data->instructions, $data->instructionsformat) = external_format_text($this->instructions, FORMAT_MOODLE,
context_system::instance()->id);
$userid = $USER->id;
}
+ if (linked_login::has_existing_issuer_match($issuer, $userinfo['username'])) {
+ throw new moodle_exception('alreadylinked', 'auth_oauth2');
+ }
+
if (\core\session\manager::is_loggedinas()) {
throw new moodle_exception('notwhileloggedinas', 'auth_oauth2');
}
$record->issuerid = $issuer->get('id');
$record->username = $userinfo['username'];
$record->userid = $userid;
- $existing = linked_login::get_record((array)$record);
- if ($existing) {
- return false;
+ if (linked_login::has_existing_issuer_match($issuer, $userinfo['username'])) {
+ throw new moodle_exception('alreadylinked', 'auth_oauth2');
}
$record->email = $userinfo['email'];
$record->confirmtoken = random_string(32);
require_once($CFG->dirroot.'/user/profile/lib.php');
require_once($CFG->dirroot.'/user/lib.php');
+ if (linked_login::has_existing_issuer_match($issuer, $userinfo['username'])) {
+ throw new moodle_exception('alreadylinked', 'auth_oauth2');
+ }
+
$user = new stdClass();
$user->username = $userinfo['username'];
$user->email = $userinfo['email'];
$login->delete();
}
+
+ /**
+ * Delete linked logins for a user.
+ *
+ * @param \core\event\user_deleted $event
+ * @return boolean
+ */
+ public static function user_deleted(\core\event\user_deleted $event) {
+ global $DB;
+
+ $userid = $event->objectid;
+
+ return $DB->delete_records(linked_login::TABLE, ['userid' => $userid]);
+ }
}
);
}
+ /**
+ * Check whether there are any valid linked accounts for this issuer
+ * and username combination.
+ *
+ * @param \core\oauth2\issuer $issuer The issuer
+ * @param string $username The username to check
+ */
+ public static function has_existing_issuer_match(\core\oauth2\issuer $issuer, $username) {
+ global $DB;
+
+ $where = "issuerid = :issuerid
+ AND username = :username
+ AND (confirmtokenexpires = 0 OR confirmtokenexpires > :maxexpiry)";
+
+ $count = $DB->count_records_select(static::TABLE, $where, [
+ 'issuerid' => $issuer->get('id'),
+ 'username' => $username,
+ 'maxexpiry' => (new \DateTime('NOW'))->getTimestamp(),
+ ]);
+
+ return $count > 0;
+ }
+
/**
* Remove all linked logins that are using issuers that have been deleted.
*
echo $OUTPUT->footer();
exit;
} else {
- print_error('invalidconfirmdata');
+ \core\notification::error(get_string('confirmationinvalid', 'auth_oauth2'));
}
redirect("$CFG->wwwroot/");
echo $OUTPUT->footer();
exit;
} else {
- print_error('invalidconfirmdata');
+ \core\notification::error(get_string('confirmationinvalid', 'auth_oauth2'));
}
redirect("$CFG->wwwroot/");
--- /dev/null
+<?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/>.
+
+/**
+ * This file definies observers needed by the plugin.
+ *
+ * @package auth_oauth2
+ * @copyright 2017 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+// List of observers.
+$observers = [
+ [
+ 'eventname' => '\core\event\user_deleted',
+ 'callback' => '\auth_oauth2\api::user_deleted',
+ ],
+];
If you need help, please contact the site administrator,
{$a->admin}';
$string['confirmaccountemailsubject'] = '{$a}: account confirmation';
+$string['confirmationinvalid'] = 'The confirmation link is either invalid, or has expired. Please start the login process again to generate a new confirmation email.';
$string['confirmationpending'] = 'This account is pending email confirmation.';
$string['confirmlinkedloginemail'] = 'Hi {$a->fullname},
$string['loginerror_nouserinfo'] = 'No user information was returned. The OAuth 2 service may be configured incorrectly.';
$string['loginerror_invaliddomain'] = 'The email address is not allowed at this site.';
$string['loginerror_authenticationfailed'] = 'The authentication process failed.';
-$string['loginerror_cannotcreateaccounts'] = 'The account does not exist and this site does not allow self-registration.';
+$string['loginerror_cannotcreateaccounts'] = 'An account with your email address could not be found.';
$string['notloggedindebug'] = 'The login attempt failed. Reason: {$a}';
$string['notwhileloggedinas'] = 'Linked logins cannot be managed while logged in as another user.';
$string['oauth2:managelinkedlogins'] = 'Manage own linked login accounts';
$string['plugindescription'] = 'This authentication plugin displays a list of the configured identity providers on the login page. Selecting an identity provider allows users to login with their credentials from an OAuth 2 provider.';
$string['pluginname'] = 'OAuth 2';
+$string['alreadylinked'] = 'This external account is already linked to an account on this site';
$userinfo = $client->get_userinfo();
if (!empty($userinfo)) {
- \auth_oauth2\api::link_login($userinfo, $issuer);
- redirect($PAGE->url, get_string('changessaved'), null, \core\output\notification::NOTIFY_SUCCESS);
+ try {
+ \auth_oauth2\api::link_login($userinfo, $issuer);
+ redirect($PAGE->url, get_string('changessaved'), null, \core\output\notification::NOTIFY_SUCCESS);
+ } catch (Exception $e) {
+ redirect($PAGE->url, $e->getMessage(), null, \core\output\notification::NOTIFY_ERROR);
+ }
} else {
redirect($PAGE->url, get_string('notloggedin', 'auth_oauth2'), null, \core\output\notification::NOTIFY_ERROR);
}
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2017051500; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->version = 2017051501; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2017050500; // Requires this Moodle version.
$plugin->component = 'auth_oauth2'; // Full name of the plugin (used for diagnostics).
'userid' => $data->userid,
'repeatid' => $this->get_mappingid('event', $data->repeatid),
'modulename' => $data->modulename,
- 'type' => $data->type,
+ 'type' => isset($data->type) ? $data->type : 0,
'eventtype' => $data->eventtype,
'timestart' => $this->apply_date_offset($data->timestart),
'timeduration' => $data->timeduration,
- 'timesort' => $this->apply_date_offset($data->timesort),
+ 'timesort' => isset($data->timesort) ? $this->apply_date_offset($data->timesort) : null,
'visible' => $data->visible,
'uuid' => $data->uuid,
'sequence' => $data->sequence,
protected function get_other_values(renderer_base $output) {
$event = $this->related['event'];
+ if (!$event->get_course_module()) {
+ // TODO MDL-58866 Only activity modules currently support this callback.
+ return ['showitemcount' => false];
+ }
$modulename = $event->get_course_module()->get('modname');
$component = 'mod_' . $modulename;
$showitemcountcallback = 'core_calendar_event_action_shows_item_count';
$values = [];
$event = $this->event;
$context = $this->related['context'];
- $modulename = $event->get_course_module()->get('modname');
- $moduleid = $event->get_course_module()->get('id');
+ if ($moduleproxy = $event->get_course_module()) {
+ $modulename = $moduleproxy->get('modname');
+ $moduleid = $moduleproxy->get('id');
+ $url = new \moodle_url(sprintf('/mod/%s/view.php', $modulename), ['id' => $moduleid]);
+ } else {
+ // TODO MDL-58866 We do not have any way to find urls for events outside of course modules.
+ global $CFG;
+ require_once($CFG->dirroot.'/course/lib.php');
+ $url = \course_get_url($this->related['course'] ?: SITEID);
+ }
$timesort = $event->get_times()->get_sort_time()->getTimestamp();
- $url = new \moodle_url(sprintf('/mod/%s/view.php', $modulename), ['id' => $moduleid]);
$iconexporter = new event_icon_exporter($event, ['context' => $context]);
$values['url'] = $url->out(false);
$userid = $user ? $user->get('id') : null;
$isactivityevent = !empty($coursemodule);
$isglobalevent = ($course && $courseid == SITEID);
- $iscourseevent = ($course && !empty($courseid) && $courseid != SITEID && $group && empty($groupid));
+ $iscourseevent = ($course && !empty($courseid) && $courseid != SITEID && empty($groupid));
$isgroupevent = ($group && !empty($groupid));
$isuserevent = ($user && !empty($userid));
$getcallback('action'),
$getcallback('visibility'),
function ($dbrow) {
- // At present we only handle callbacks in course modules.
+ // At present we only have a bail-out check for events in course modules.
if (empty($dbrow->modulename)) {
return false;
}
// Callbacks will get supplied a "legacy" version
// of the event class.
$mapper = self::$eventmapper;
- $action = component_callback(
- 'mod_' . $event->get_course_module()->get('modname'),
- 'core_calendar_provide_event_action',
- [
- $mapper->from_event_to_legacy_event($event),
- self::$actionfactory
- ]
- );
+ $action = null;
+ if ($event->get_course_module()) {
+ // TODO MDL-58866 Only activity modules currently support this callback.
+ // Any other event will not be displayed on the dashboard.
+ $action = component_callback(
+ 'mod_' . $event->get_course_module()->get('modname'),
+ 'core_calendar_provide_event_action',
+ [
+ $mapper->from_event_to_legacy_event($event),
+ self::$actionfactory
+ ]
+ );
+ }
// If we get an action back, return an action event, otherwise
// continue piping through the original event.
// This is enforced by the event_factory.
'visibility' => function (event_interface $event) {
$mapper = self::$eventmapper;
- $eventvisible = component_callback(
- 'mod_' . $event->get_course_module()->get('modname'),
- 'core_calendar_is_event_visible',
- [
- $mapper->from_event_to_legacy_event($event)
- ]
- );
+ $eventvisible = null;
+ if ($event->get_course_module()) {
+ // TODO MDL-58866 Only activity modules currently support this callback.
+ $eventvisible = component_callback(
+ 'mod_' . $event->get_course_module()->get('modname'),
+ 'core_calendar_is_event_visible',
+ [
+ $mapper->from_event_to_legacy_event($event)
+ ]
+ );
+ }
// Do not display the event if there is nothing to action.
if ($event instanceof action_event_interface && $event->get_action()->get_item_count() === 0) {
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 3.3
*/
-define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/str', 'core/url', 'core/yui'],
- function($, ajax, templates, notification, str, url, Y) {
+define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/str', 'core/url', 'core/yui',
+ 'core/modal_factory', 'core/modal_events', 'core/key_codes'],
+ function($, ajax, templates, notification, str, url, Y, ModalFactory, ModalEvents, KeyCodes) {
var CSS = {
EDITINPROGRESS: 'editinprogress',
SECTIONDRAGGABLE: 'sectiondraggable',
MENU: '.moodle-actionmenu[data-enhance=moodle-core-actionmenu]',
TOGGLE: '.toggle-display,.dropdown-toggle',
SECTIONLI: 'li.section',
- SECTIONACTIONMENU: '.section_action_menu'
+ SECTIONACTIONMENU: '.section_action_menu',
+ ADDSECTIONS: '#changenumsections [data-add-sections]'
};
Y.use('moodle-course-coursebase', function() {
editSection(sectionElement, sectionId, actionItem, courseformat);
}
});
+
+ // Add a handler for "Add sections" link to ask for a number of sections to add.
+ str.get_string('numberweeks').done(function(strNumberSections) {
+ var trigger = $(SELECTOR.ADDSECTIONS),
+ modalTitle = trigger.attr('data-add-sections');
+ var modalBody = $('<div><label for="add_section_numsections"></label> ' +
+ '<input id="add_section_numsections" type="number" min="1" value="1"></div>');
+ modalBody.find('label').html(strNumberSections);
+ ModalFactory.create({
+ title: modalTitle,
+ type: ModalFactory.types.SAVE_CANCEL,
+ body: modalBody.html()
+ }, trigger)
+ .done(function(modal) {
+ var numSections = $(modal.getBody()).find('#add_section_numsections'),
+ addSections = function() {
+ // Check if value of the "Number of sections" is a valid positive integer and redirect
+ // to adding a section script.
+ if ('' + parseInt(numSections.val()) === numSections.val() && parseInt(numSections.val()) >= 1) {
+ document.location = trigger.attr('href') + '&numsections=' + parseInt(numSections.val());
+ }
+ };
+ modal.setSaveButtonText(modalTitle);
+ modal.getRoot().on(ModalEvents.shown, function() {
+ // When modal is shown focus and select the input and add a listener to keypress of "Enter".
+ numSections.focus().select().on('keydown', function(e) {
+ if (e.keyCode === KeyCodes.enter) {
+ addSections();
+ }
+ });
+ });
+ modal.getRoot().on(ModalEvents.save, function(e) {
+ // When modal "Add" button is pressed.
+ e.preventDefault();
+ addSections();
+ });
+ });
+ });
},
/**
$courseid = required_param('courseid', PARAM_INT);
$increase = optional_param('increase', null, PARAM_BOOL);
$insertsection = optional_param('insertsection', null, PARAM_INT); // Insert section at position; 0 means at the end.
+$numsections = optional_param('numsections', 1, PARAM_INT); // Number of sections to insert.
$returnurl = optional_param('returnurl', null, PARAM_LOCALURL); // Where to return to after the action.
$sectionreturn = optional_param('sectionreturn', null, PARAM_INT); // Section to return to, ignored if $returnurl is specified.
// Inserting sections at any position except in the very end requires capability to move sections.
require_capability('moodle/course:movesections', context_course::instance($course->id));
}
- $section = course_create_section($course, $insertsection);
+ $sections = [];
+ for ($i = 0; $i < max($numsections, 1); $i ++) {
+ $sections[] = course_create_section($course, $insertsection);
+ }
if (!$returnurl) {
- $returnurl = course_get_url($course, $section->section,
+ $returnurl = course_get_url($course, $sections[0]->section,
($sectionreturn !== null) ? ['sr' => $sectionreturn] : []);
}
}
$cancelurl = course_get_url($course, $sectioninfo, array('sr' => $sectionreturn));
if (course_can_delete_section($course, $sectioninfo)) {
$confirm = optional_param('confirm', false, PARAM_BOOL) && confirm_sesskey();
+ if (!$confirm && optional_param('sesskey', null, PARAM_RAW) !== null &&
+ empty($sectioninfo->summary) && empty($sectioninfo->sequence) && confirm_sesskey()) {
+ // Do not ask for confirmation if section is empty and sesskey is already provided.
+ $confirm = true;
+ }
if ($confirm) {
course_delete_section($course, $sectioninfo, true, true);
- $courseurl = course_get_url($course, 0, array('sr' => $sectionreturn));
+ $courseurl = course_get_url($course, $sectioninfo->section - 1, array('sr' => $sectionreturn));
redirect($courseurl);
} else {
if (get_string_manager()->string_exists('deletesection', 'format_' . $course->format)) {
$url = new moodle_url('/course/editsection.php', array(
'id' => $section->id,
'sr' => $sectionreturn,
- 'delete' => 1));
+ 'delete' => 1,
+ 'sesskey' => sesskey()));
$controls['delete'] = array(
'url' => $url,
'icon' => 'i/delete',
// capabilities 'moodle/course:update' and 'moodle/course:movesections'.
echo html_writer::start_tag('div', array('id' => 'changenumsections', 'class' => 'mdl-right'));
- if (get_string_manager()->string_exists('addsection', 'format_'.$course->format)) {
- $straddsection = get_string('addsection', 'format_'.$course->format);
+ if (get_string_manager()->string_exists('addsections', 'format_'.$course->format)) {
+ $straddsections = get_string('addsections', 'format_'.$course->format);
} else {
- $straddsection = get_string('addsection');
+ $straddsections = get_string('addsections');
}
$url = new moodle_url('/course/changenumsections.php',
['courseid' => $course->id, 'insertsection' => 0, 'sesskey' => sesskey()]);
if ($sectionreturn !== null) {
$url->param('sectionreturn', $sectionreturn);
}
- $icon = $this->output->pix_icon('t/add', $straddsection);
- echo html_writer::link($url, $icon . $straddsection, array('class' => 'add-section'));
+ $icon = $this->output->pix_icon('t/add', $straddsections);
+ echo html_writer::link($url, $icon . $straddsections,
+ array('class' => 'add-sections', 'data-add-sections' => $straddsections));
echo html_writer::end_tag('div');
}
}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-$string['addsection'] = 'Add topic';
+$string['addsections'] = 'Add topics';
$string['currentsection'] = 'This topic';
$string['editsection'] = 'Edit topic';
$string['editsectionname'] = 'Edit topic name';
And I should not see "Test chat name"
And I should see "Test choice name" in the "li#section-4" "css_element"
And I should see "Topic 4"
+
+ @javascript
+ Scenario: Adding sections in topics format
+ When I follow "Add topics"
+ Then the field "Number of sections" matches value "1"
+ And I press "Add topics"
+ And I should see "Topic 6" in the "li#section-6" "css_element"
+ And "li#section-7" "css_element" should not exist
+ And I follow "Add topics"
+ And I set the field "Number of sections" to "3"
+ And I press "Add topics"
+ And I should see "Topic 7" in the "li#section-7" "css_element"
+ And I should see "Topic 8" in the "li#section-8" "css_element"
+ And I should see "Topic 9" in the "li#section-9" "css_element"
+ And "li#section-10" "css_element" should not exist
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-$string['addsection'] = 'Add week';
+$string['addsections'] = 'Add weeks';
$string['currentsection'] = 'This week';
$string['editsection'] = 'Edit week';
$string['editsectionname'] = 'Edit week name';
And I should not see "Test chat name"
And I should see "Test choice name" in the "li#section-4" "css_element"
And I should see "22 May - 28 May"
+
+ @javascript
+ Scenario: Adding sections in weeks format
+ When I follow "Add weeks"
+ Then the field "Number of sections" matches value "1"
+ And I press "Add weeks"
+ And I should see "5 June - 11 June" in the "li#section-6" "css_element"
+ And "li#section-7" "css_element" should not exist
+ And I follow "Add weeks"
+ And I set the field "Number of sections" to "3"
+ And I press "Add weeks"
+ And I should see "12 June - 18 June" in the "li#section-7" "css_element"
+ And I should see "19 June - 25 June" in the "li#section-8" "css_element"
+ And I should see "26 June - 2 July" in the "li#section-9" "css_element"
+ And "li#section-10" "css_element" should not exist
$string['choosefilefromactivitybackup_help'] = 'Activity backups made using default settings are stored here.';
$string['choosefilefromautomatedbackup'] = 'Automated backups';
$string['choosefilefromautomatedbackup_help'] = 'Contains automatically generated backups.';
-$string['config_keep_groups_and_groupings'] = 'By default keep current roles and enrolments';
-$string['config_keep_roles_and_enrolments'] = 'By default keep current groups and groupings';
+$string['config_keep_groups_and_groupings'] = 'By default keep current groups and groupings.';
+$string['config_keep_roles_and_enrolments'] = 'By default keep current roles and enrolments.';
$string['config_overwrite_conf'] = 'Allows user to overwrite the current course configuration';
$string['config_overwrite_course_fullname'] = 'By default overwrite course full name with the one from the backup file. This requires "Overwrite course configuration" to be checked and current user to have the capability to change course full name (moodle/course:changefullname)';
$string['config_overwrite_course_shortname'] = 'By default overwrite course short name with the one from the backup file. This requires "Overwrite course configuration" to be checked and current user to have the capability to change course short name (moodle/course:changeshortname)';
$string['hiddenrules'] = 'Some settings specific to <b>{$a}</b> have been hidden. To view unselect other activities';
$string['editcoursecompletionsettings'] = 'Edit course completion settings';
$string['enablecompletion'] = 'Enable completion tracking';
-$string['enablecompletion_help'] = 'If enabled, activity completion conditions may be set in the activity settings and/or course completion conditions may be set. It is recommended to have this enabled in order for the course progress dashboard to display meaningful data.';
+$string['enablecompletion_help'] = 'If enabled, activity completion conditions may be set in the activity settings and/or course completion conditions may be set. It is recommended to have this enabled so that meaningful data is displayed in the course overview on the Dashboard.';
$string['enrolmentduration'] = 'Enrolment duration';
$string['enrolmentdurationlength'] = 'User must remain enrolled for';
$string['err_noactivities'] = 'Completion information is not enabled for any activity, so none can be displayed. You can enable completion information by editing the settings for an activity.';
$string['addresourceoractivity'] = 'Add an activity or resource';
$string['addresourcetosection'] = 'Add a resource to section \'{$a}\'';
$string['address'] = 'Address';
-$string['addsection'] = 'Add section';
+$string['addsections'] = 'Add sections';
$string['addstudent'] = 'Add student';
$string['addsubcategory'] = 'Add a subcategory';
$string['addteacher'] = 'Add teacher';
}.bind(this));
};
+ /**
+ * Allows to overwrite the text of "Save changes" button.
+ *
+ * @param {String} text
+ */
+ ModalSaveCancel.prototype.setSaveButtonText = function(text) {
+ this.getFooter().find(SELECTORS.SAVE_BUTTON).text(text);
+ };
+
return ModalSaveCancel;
});
'core:i/grade_correct' => 'fa-check text-success',
'core:i/grade_incorrect' => 'fa-remove text-danger',
'core:i/grade_partiallycorrect' => 'fa-check-square',
- 'core:i/grades' => 'fa-graduation-cap',
+ 'core:i/grades' => 'fa-table',
'core:i/groupevent' => 'fa-group',
'core:i/groupn' => 'fa-user',
'core:i/group' => 'fa-users',
'core:t/enrolusers' => 'fa-user-plus',
'core:t/expanded' => 'fa-caret-down',
'core:t/go' => 'fa-play',
- 'core:t/grades' => 'fa-graduation-cap',
+ 'core:t/grades' => 'fa-table',
'core:t/groupn' => 'fa-user',
'core:t/groups' => 'fa-user-circle',
'core:t/groupv' => 'fa-user-circle-o',
continue;
}
// Check if the plugin implements *_refresh_events() and call it when it does.
- $refresheventsfunction = $plugin->name . '_refresh_events';
- if (function_exists($refresheventsfunction)) {
- mtrace('Calling ' . $refresheventsfunction);
- call_user_func($refresheventsfunction);
+ if (component_callback_exists('mod_' . $plugin->name, 'refresh_events')) {
+ mtrace('Refreshing events for ' . $plugin->name);
+ component_callback('mod_' . $plugin->name, 'refresh_events');
}
}
}
$dbman->add_field($table, $field);
}
- // Create adhoc task for upgrading of existing calendar events.
- $record = new \stdClass();
- $record->classname = "\\core\\task\\refresh_mod_calendar_events_task";
- $record->component = 'core';
- // Next run time based from nextruntime computation in \core\task\manager::queue_adhoc_task().
- $nextruntime = time() - 1;
- $record->nextruntime = $nextruntime;
- $DB->insert_record('task_adhoc', $record);
-
- // This same task is queued again in a later step, but if we already queue it here
- // then there is no need to queue it again. We use this flag in the second step.
- $refresheventsadhocadded = true;
-
// Main savepoint reached.
upgrade_main_savepoint(true, 2017030700.00);
}
upgrade_main_savepoint(true, 2017040402.00);
}
- if ($oldversion < 2017040403.00) {
- // Create adhoc task for upgrading of existing calendar events.
- $record = new \stdClass();
- $record->classname = "\\core\\task\\refresh_mod_calendar_events_task";
- $record->component = 'core';
-
- // Next run time based from nextruntime computation in \core\task\manager::queue_adhoc_task().
- $nextruntime = time() - 1;
- $record->nextruntime = $nextruntime;
- $DB->insert_record('task_adhoc', $record);
-
- // Main savepoint reached.
- upgrade_main_savepoint(true, 2017040403.00);
- }
-
if ($oldversion < 2017040700.01) {
// Define table oauth2_issuer to be created.
upgrade_main_savepoint(true, 2017041801.00);
}
- if ($oldversion < 2017042600.01) {
- // If the previous step didn't execute and queue the task.
- if (!isset($refresheventsadhocadded)) {
- // Create adhoc task for upgrading of existing calendar events.
- $record = new \stdClass();
- $record->classname = "\\core\\task\\refresh_mod_calendar_events_task";
- $record->component = 'core';
-
- // Next run time based from nextruntime computation in \core\task\manager::queue_adhoc_task().
- $nextruntime = time() - 1;
- $record->nextruntime = $nextruntime;
- $DB->insert_record('task_adhoc', $record);
-
- }
-
- // Main savepoint reached.
- upgrade_main_savepoint(true, 2017042600.01);
- }
-
if ($oldversion < 2017050500.01) {
// Get the list of parent event IDs.
$sql = "SELECT DISTINCT repeatid
upgrade_main_savepoint(true, 2017050500.02);
}
+ if ($oldversion < 2017050900.01) {
+ // Create adhoc task for upgrading of existing calendar events.
+ $record = new \stdClass();
+ $record->classname = '\core\task\refresh_mod_calendar_events_task';
+ $record->component = 'core';
+
+ // Next run time based from nextruntime computation in \core\task\manager::queue_adhoc_task().
+ $nextruntime = time() - 1;
+ $record->nextruntime = $nextruntime;
+ $DB->insert_record('task_adhoc', $record);
+
+ // Main savepoint reached.
+ upgrade_main_savepoint(true, 2017050900.01);
+ }
+
return true;
}
}
/**
- * Check whether a major upgrade is needed. That is defined as an upgrade that
- * changes something really fundamental in the database, so nothing can possibly
- * work until the database has been updated, and that is defined by the hard-coded
- * version number in this function.
+ * Check whether a major upgrade is needed.
+ *
+ * That is defined as an upgrade that changes something really fundamental
+ * in the database, so nothing can possibly work until the database has
+ * been updated, and that is defined by the hard-coded version number in
+ * this function.
+ *
+ * @return bool
*/
-function redirect_if_major_upgrade_required() {
+function is_major_upgrade_required() {
global $CFG;
$lastmajordbchanges = 2017040403.00;
- if (empty($CFG->version) or (float)$CFG->version < $lastmajordbchanges or
- during_initial_install() or !empty($CFG->adminsetuppending)) {
+
+ $required = empty($CFG->version);
+ $required = $required || (float)$CFG->version < $lastmajordbchanges;
+ $required = $required || during_initial_install();
+ $required = $required || !empty($CFG->adminsetuppending);
+
+ return $required;
+}
+
+/**
+ * Redirect to the Notifications page if a major upgrade is required, and
+ * terminate the current user session.
+ */
+function redirect_if_major_upgrade_required() {
+ global $CFG;
+ if (is_major_upgrade_required()) {
try {
@\core\session\manager::terminate_current();
} catch (Exception $e) {
Example context (json):
{
- "col1content": "<div class='alert alert-error'>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam pellentesque id urna sit amet tempor.</div>",
- "col2content": "<div class='alert alert-success'>Donec lacus nisl, molestie eget sodales non, sodales et nibh. Praesent dignissim placerat sodales.</div>",
- "col3content": "<div class='alert alert-info'>Praesent sit amet ante odio. In mollis nisl at mi bibendum venenatis.</div>"
+ "col1content": "<div class='alert alert-error'>1. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam pellentesque id urna sit amet tempor.</div>",
+ "col2content": "<div class='alert alert-success'>2. Donec lacus nisl, molestie eget sodales non, sodales et nibh. Praesent dignissim placerat sodales.</div>",
+ "col3content": "<div class='alert alert-info'>3. Praesent sit amet ante odio. In mollis nisl at mi bibendum venenatis.</div>"
}
}}
Example context (json):
{
- "col1content": "<div class='alert alert-info'>Lorem ipsum dolor sit amet, consectetur adipiscing elit. In porttitor vulputate turpis, quis tempor arcu.</div>",
- "col2content": "<div class='alert alert-success'>Vivamus ac orci in velit fringilla aliquam a a nisl. Cras luctus quam laoreet magna pulvinar aliquet.</div>"
+ "col1content": "<div class='alert alert-info'>1. Lorem ipsum dolor sit amet, consectetur adipiscing elit. In porttitor vulputate turpis, quis tempor arcu.</div>",
+ "col2content": "<div class='alert alert-success'>2. Vivamus ac orci in velit fringilla aliquam a a nisl. Cras luctus quam laoreet magna pulvinar aliquet.</div>"
}
}}
Example context (json):
{
- "col1content": "<div class='alert alert-info'>Lorem ipsum dolor sit amet, consectetur adipiscing elit. In porttitor vulputate turpis, quis tempor arcu.</div>",
- "col2content": "<div class='alert alert-success'>Vivamus ac orci in velit fringilla aliquam a a nisl. Cras luctus quam laoreet magna pulvinar aliquet.</div>"
+ "col1content": "<div class='alert alert-info'>1. Lorem ipsum dolor sit amet, consectetur adipiscing elit. In porttitor vulputate turpis, quis tempor arcu.</div>",
+ "col2content": "<div class='alert alert-success'>2. Vivamus ac orci in velit fringilla aliquam a a nisl. Cras luctus quam laoreet magna pulvinar aliquet.</div>"
}
}}
<div class="row-fluid rtl-compatible">
echo $OUTPUT->header();
-echo $OUTPUT->render($mform_signup);
+if ($mform_signup instanceof renderable) {
+ // Try and use the renderer from the auth plugin if it exists.
+ try {
+ $renderer = $PAGE->get_renderer('auth_' . $authplugin->authtype);
+ } catch (coding_exception $ce) {
+ // Fall back on the general renderer.
+ $renderer = $OUTPUT;
+ }
+ echo $renderer->render($mform_signup);
+} else {
+ // Fall back for auth plugins not using renderables.
+ $mform_signup->display();
+}
echo $OUTPUT->footer();
upgrade_mod_savepoint(true, 2017021500, 'assign');
}
- if ($oldversion < 2017031000) {
- // Set priority of assign user overrides.
- $params = [
- 'modulename' => 'assign',
- 'courseid' => 0,
- 'groupid' => 0,
- 'repeatid' => 0
- ];
- // CALENDAR_EVENT_USER_OVERRIDE_PRIORITY has a value of 9999999.
- $DB->set_field('event', 'priority', 9999999, $params);
-
- // Set priority for group overrides for existing assign events.
- $where = 'groupid IS NOT NULL';
- $assignoverridesrs = $DB->get_recordset_select('assign_overrides', $where, null, '', 'id, assignid, groupid, sortorder');
- foreach ($assignoverridesrs as $record) {
- $params = [
- 'modulename' => 'assign',
- 'instance' => $record->assignid,
- 'groupid' => $record->groupid,
- 'repeatid' => 0
- ];
- $DB->set_field('event', 'priority', $record->sortorder, $params);
- }
- $assignoverridesrs->close();
-
- // Assign savepoint reached.
- upgrade_mod_savepoint(true, 2017031000, 'assign');
- }
-
if ($oldversion < 2017031300) {
// Add a 'gradingduedate' field to the 'assign' table.
$table = new xmldb_table('assign');
// Execute DB update for assign instances.
$DB->execute($sql, $params);
- // Create adhoc task for upgrading of existing mod_assign calendar events.
- $task = new \stdClass();
- $task->classname = "\\core\\task\\refresh_mod_calendar_events_task";
- $task->component = 'core';
-
- // Next run time based from nextruntime computation in \core\task\manager::queue_adhoc_task().
- $nextruntime = time() - 1;
- $task->nextruntime = $nextruntime;
- // Indicate to the adhoc task that only the assignment module will be refreshed.
- $task->customdata = json_encode(['plugins' => ['assign']]);
- $DB->insert_record('task_adhoc', $task);
-
// Assign savepoint reached.
upgrade_mod_savepoint(true, 2017042800, 'assign');
}
JOIN {groups_members} gm ON gm.groupid = g.id
WHERE go.assignid = :assignmentid6
)
- ) AS merged
+ ) merged
GROUP BY merged.userid
) priority ON priority.userid = u.id
JOIN (
(SELECT 9999999 AS priority,
u.id AS userid,
-
a.allowsubmissionsfromdate,
a.duedate,
a.cutoffdate
UNION
(SELECT 0 AS priority,
uo.userid,
-
uo.allowsubmissionsfromdate,
uo.duedate,
uo.cutoffdate
UNION
(SELECT go.sortorder AS priority,
gm.userid,
-
go.allowsubmissionsfromdate,
go.duedate,
go.cutoffdate
WHERE go.assignid = :assignmentid9
)
- ) AS effective ON effective.priority = priority.priority AND effective.userid = priority.userid ';
+ ) effective ON effective.priority = priority.priority AND effective.userid = priority.userid ';
}
if (!empty($this->assignment->get_instance()->blindmarking)) {
$conds['groupid'] = $override->groupid;
}
}
- $oldevents = $DB->get_records('event', $conds);
+ $oldevents = $DB->get_records('event', $conds, 'id ASC');
// Now make a to-do list of all that needs to be updated.
if (empty($override)) {
- // We are updating the primary settings for the assign, so we need to add all the overrides.
- $overrides = $DB->get_records('assign_overrides', array('assignid' => $assigninstance->id));
- // As well as the original assign (empty override).
- $overrides[] = new stdClass();
+ // We are updating the primary settings for the assignment, so we need to add all the overrides.
+ $overrides = $DB->get_records('assign_overrides', array('assignid' => $assigninstance->id), 'id ASC');
+ // It is necessary to add an empty stdClass to the beginning of the array as the $oldevents
+ // list contains the original (non-override) event for the module. If this is not included
+ // the logic below will end up updating the wrong row when we try to reconcile this $overrides
+ // list against the $oldevents list.
+ array_unshift($overrides, new stdClass());
} else {
// Just do the one override.
$overrides = array($override);
$event->timesort = $event->timestart + $event->timeduration;
$event->visible = instance_is_visible('assign', $assigninstance);
$event->eventtype = ASSIGN_EVENT_TYPE_DUE;
+ $event->priority = null;
// Determine the event name and priority.
if ($groupid) {
$conds['groupid'] = $override->groupid;
}
}
- $oldevents = $DB->get_records('event', $conds);
+ $oldevents = $DB->get_records('event', $conds, 'id ASC');
// Now make a to-do list of all that needs to be updated.
if (empty($override)) {
// We are updating the primary settings for the lesson, so we need to add all the overrides.
- $overrides = $DB->get_records('lesson_overrides', array('lessonid' => $lesson->id));
- // As well as the original lesson (empty override).
- $overrides[] = new stdClass();
+ $overrides = $DB->get_records('lesson_overrides', array('lessonid' => $lesson->id), 'id ASC');
+ // It is necessary to add an empty stdClass to the beginning of the array as the $oldevents
+ // list contains the original (non-override) event for the module. If this is not included
+ // the logic below will end up updating the wrong row when we try to reconcile this $overrides
+ // list against the $oldevents list.
+ array_unshift($overrides, new stdClass());
} else {
// Just do the one override.
$overrides = array($override);
$event->timesort = $available;
$event->visible = instance_is_visible('lesson', $lesson);
$event->eventtype = LESSON_EVENT_TYPE_OPEN;
+ $event->priority = null;
// Determine the event name and priority.
if ($groupid) {
/**
* Echo an exception message encapsulated in XML.
*
- * @param \Exception $exception The exception that was thrown
+ * @param \Exception|\Throwable $exception The exception that was thrown
*/
- public function handle(\Exception $exception) {
+ public function handle($exception) {
$message = $exception->getMessage();
// Add the exception backtrace for developers.
$conds['groupid'] = $override->groupid;
}
}
- $oldevents = $DB->get_records('event', $conds);
+ $oldevents = $DB->get_records('event', $conds, 'id ASC');
// Now make a to-do list of all that needs to be updated.
if (empty($override)) {
- // We are updating the primary settings for the lesson, so we need to add all the overrides.
- $overrides = $DB->get_records('quiz_overrides', array('quiz' => $quiz->id));
- // As well as the original quiz (empty override).
- $overrides[] = new stdClass();
+ // We are updating the primary settings for the quiz, so we need to add all the overrides.
+ $overrides = $DB->get_records('quiz_overrides', array('quiz' => $quiz->id), 'id ASC');
+ // It is necessary to add an empty stdClass to the beginning of the array as the $oldevents
+ // list contains the original (non-override) event for the module. If this is not included
+ // the logic below will end up updating the wrong row when we try to reconcile this $overrides
+ // list against the $oldevents list.
+ array_unshift($overrides, new stdClass());
} else {
// Just do the one override.
$overrides = array($override);
$event->timesort = $timeopen;
$event->visible = instance_is_visible('quiz', $quiz);
$event->eventtype = QUIZ_EVENT_TYPE_OPEN;
+ $event->priority = null;
// Determine the event name and priority.
if ($groupid) {
$string['contents'] = 'Contents';
$string['coursepacket'] = 'Course package';
$string['coursestruct'] = 'Course structure';
-$string['crontask'] = 'Background processing for Scorm';
+$string['crontask'] = 'Background processing for SCORM';
$string['currentwindow'] = 'Current window';
$string['datadir'] = 'Filesystem error: Can\'t create course data directory';
$string['defaultdisplaysettings'] = 'Default display settings';
$string['internal'] = 'Internal (files stored in Moodle)';
$string['issuer_help'] = 'Select the OAuth 2 service that is configured to talk to the OneDrive API. If the service does not exist yet, you will need to create it.';
$string['issuer'] = 'OAuth 2 service';
-$string['mysitenotfound'] = 'You have never logged into OneDrive before. You must login to OneDrive at least once it before it can be used with Moodle.';
+$string['mysitenotfound'] = 'You have never logged into OneDrive before. You must log in to OneDrive at least once before it can be used with Moodle.';
$string['oauth2serviceslink'] = '<a href="{$a}" title="Link to OAuth 2 services configuration">OAuth 2 services configuration</a>';
$string['owner'] = 'Owned by: {$a}';
$string['pluginname'] = 'Microsoft OneDrive';
--- /dev/null
+{{!
+ 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/columns-1to1to1
+
+ Moodle columns-1to1to1 template.
+
+ The purpose of this template is to render a template with 3 columns.
+ On mobile the columns stack underneath each other.
+
+ Classes required for JS:
+ * none
+
+ Data attributes required for JS:
+ * none
+
+ Context variables required for this template:
+ * col1content Column 1 contents.
+ * col2content Column 2 contents.
+ * col3content Column 3 contents.
+
+ Example context (json):
+ {
+ "col1content": "<div class='alert alert-error'>1. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam pellentesque id urna sit amet tempor.</div>",
+ "col2content": "<div class='alert alert-success'>2. Donec lacus nisl, molestie eget sodales non, sodales et nibh. Praesent dignissim placerat sodales.</div>",
+ "col3content": "<div class='alert alert-info'>3. Praesent sit amet ante odio. In mollis nisl at mi bibendum venenatis.</div>"
+ }
+
+}}
+<div class="row">
+ <div class="col-md-4">{{$ column1 }}{{{ col1content }}}{{/ column1 }}</div>
+ <div class="col-md-4">{{$ column2 }}{{{ col2content }}}{{/ column2 }}</div>
+ <div class="col-md-4">{{$ column3 }}{{{ col3content }}}{{/ column3 }}</div>
+</div>
--- /dev/null
+{{!
+ 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/columns-1to2
+
+ Moodle columns-1to2 template.
+
+ The purpose of this template is to render 2 columns where the second column has twice the width of the first one.
+ On mobile the second column collapses underneath the first.
+
+ Classes required for JS:
+ * none
+
+ Data attributes required for JS:
+ * none
+
+ Context variables required for this template:
+ * col1content Column 1 contents.
+ * col2content Column 2 contents.
+
+ Example context (json):
+ {
+ "col1content": "<div class='alert alert-info'>1. Lorem ipsum dolor sit amet, consectetur adipiscing elit. In porttitor vulputate turpis, quis tempor arcu.</div>",
+ "col2content": "<div class='alert alert-success'>2. Vivamus ac orci in velit fringilla aliquam a a nisl. Cras luctus quam laoreet magna pulvinar aliquet.</div>"
+ }
+
+}}
+<div class="row">
+ <div class="col-md-4">{{$ column1 }}{{{ col1content }}}{{/ column1 }}</div>
+ <div class="col-md-8">{{$ column2 }}{{{ col2content }}}{{/ column2 }}</div>
+</div>
--- /dev/null
+{{!
+ 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/columns-2to1
+
+ Moodle columns-2to1 template.
+
+ The purpose of this template is to render 2 columns where the first column has twice the width of the second one.
+ On mobile the second column collapses underneath the first.
+
+ Classes required for JS:
+ * none
+
+ Data attributes required for JS:
+ * none
+
+ Context variables required for this template:
+ * col1content Column 1 contents.
+ * col2content Column 2 contents.
+
+ Example context (json):
+ {
+ "col1content": "<div class='alert alert-info'>1. Lorem ipsum dolor sit amet, consectetur adipiscing elit. In porttitor vulputate turpis, quis tempor arcu.</div>",
+ "col2content": "<div class='alert alert-success'>2. Vivamus ac orci in velit fringilla aliquam a a nisl. Cras luctus quam laoreet magna pulvinar aliquet.</div>"
+ }
+}}
+<div class="row">
+ <div class="col-md-8">{{$ column1 }}{{{ col1content }}}{{/ column1 }}</div>
+ <div class="col-md-4">{{$ column2 }}{{{ col2content }}}{{/ column2 }}</div>
+</div>
defined('MOODLE_INTERNAL') || die();
-$version = 2017050900.00; // YYYYMMDD = weekly release date of this DEV branch.
+$version = 2017050900.01; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.
/**
* Generate the XML-RPC fault response.
*
- * @param Exception $ex The exception.
+ * @param Exception|Throwable $ex The exception.
* @param int $faultcode The faultCode to be included in the fault response
* @return string The XML-RPC fault response xml containing the faultCode and faultString.
*/
- protected function generate_error(Exception $ex, $faultcode = 404) {
+ protected function generate_error($ex, $faultcode = 404) {
$error = $ex->getMessage();
if (!empty($ex->errorcode)) {