theme/boost/amd/src/bootstrap/carousel.js
theme/boost/amd/src/bootstrap/collapse.js
theme/boost/amd/src/bootstrap/dropdown.js
-theme/boost/amd/src/bootstrap/index.js
theme/boost/amd/src/bootstrap/modal.js
theme/boost/amd/src/bootstrap/popover.js
theme/boost/amd/src/bootstrap/tools/sanitizer.js
theme/boost/amd/src/bootstrap/toast.js
theme/boost/amd/src/bootstrap/tooltip.js
theme/boost/amd/src/bootstrap/util.js
-theme/boost/scss/fontawesome/
\ No newline at end of file
+theme/boost/amd/src/index.js
+theme/boost/scss/fontawesome/
name: Core
-on: [push]
+on:
+ push:
+ branches-ignore:
+ - master
+ - MOODLE_[0-9]+_STABLE
+ tags-ignore:
+ - v[0-9]+.[0-9]+.[0-9]+*
env:
php: 7.4
'*/**/yui/src/*/meta/',
'*/**/build/',
].concat(thirdPartyPaths);
- grunt.file.write('.eslintignore', eslintIgnores.join('\n'));
+ grunt.file.write('.eslintignore', eslintIgnores.join('\n') + '\n');
// Generate .stylelintignore.
const stylelintIgnores = [
'theme/boost/style/moodle.css',
'theme/classic/style/moodle.css',
].concat(thirdPartyPaths);
- grunt.file.write('.stylelintignore', stylelintIgnores.join('\n'));
+ grunt.file.write('.stylelintignore', stylelintIgnores.join('\n') + '\n');
};
grunt.registerTask('ignorefiles', 'Generate ignore files for linters', handler);
theme/boost/amd/src/bootstrap/carousel.js
theme/boost/amd/src/bootstrap/collapse.js
theme/boost/amd/src/bootstrap/dropdown.js
-theme/boost/amd/src/bootstrap/index.js
theme/boost/amd/src/bootstrap/modal.js
theme/boost/amd/src/bootstrap/popover.js
theme/boost/amd/src/bootstrap/tools/sanitizer.js
theme/boost/amd/src/bootstrap/toast.js
theme/boost/amd/src/bootstrap/tooltip.js
theme/boost/amd/src/bootstrap/util.js
-theme/boost/scss/fontawesome/
\ No newline at end of file
+theme/boost/amd/src/index.js
+theme/boost/scss/fontawesome/
addons:
postgresql: "9.6"
+branches:
+ except:
+ - master
+ - /MOODLE_[0-9]+_STABLE/
+ - /^v[0-9]+\.[0-9]+\.[0-9]+.*/
+
jobs:
# Enable fast finish.
# This will fail the build if a single job fails (except those in allow_failures).
// limit for the setting name is 100.
continue;
}
- } elseif ($fieldname == 'url') {
- $fieldname = get_string('webpage');
} else {
$fieldname = get_string($fieldname);
}
// Save processors enabled/disabled status.
foreach ($allprocessors as $processor) {
$enabled = isset($form->{$processor->name});
+ if ($enabled != $processor->enabled) {
+ add_to_config_log($processor->name, $processor->enabled, $enabled, 'core');
+ }
\core_message\api::update_processor_status($processor, $enabled);
}
foreach ($newpreferences as $name => $value) {
+ $old = isset($preferences->$name) ? $preferences->$name : '';
+
+ if ($old != $value) {
+ add_to_config_log($name, $old, $value, 'core');
+ }
+
set_config($name, $value, 'message');
}
$transaction->allow_commit();
if ($error) {
$message = optional_param('error_description', '', PARAM_RAW);
if ($message) {
- print_error($message);
+ $SESSION->loginerrormsg = $message;
+ redirect(new moodle_url(get_login_url()));
} else {
- print_error($error);
+ $SESSION->loginerrormsg = $error;
+ redirect(new moodle_url(get_login_url()));
}
- die();
}
// The authorization code generated by the authorization server.
$redirecturl->param('oauth2code', $code);
redirect($redirecturl);
} else {
- print_error('invalidsesskey');
+ $SESSION->loginerrormsg = get_string('invalidsesskey', 'error');
+ redirect(new moodle_url(get_login_url()));
}
$message = get_string('purgecachesfinished', 'admin');
}
+// Redirect and/or show notification message confirming cache(s) were purged.
if (isset($message)) {
- redirect($returnurl, $message);
+ if (!$PAGE->url->compare($returnurl, URL_MATCH_BASE)) {
+ redirect($returnurl, $message);
+ }
+
+ // We are already on the purge caches page, add the notification.
+ \core\notification::add($message, \core\output\notification::NOTIFY_INFO);
}
// Otherwise, show a form to actually purge the caches.
// Import defaults section.
$temp->add(new admin_setting_heading('importsettings', new lang_string('importsettings', 'backup'), ''));
+ $temp->add(new admin_setting_configcheckbox_with_lock(
+ 'backup/backup_import_permissions',
+ new lang_string('generalpermissions', 'backup'),
+ new lang_string('configgeneralpermissions', 'backup'),
+ array('value' => 0, 'locked' => 0)));
$temp->add(new admin_setting_configcheckbox_with_lock('backup/backup_import_activities', new lang_string('generalactivities','backup'), new lang_string('configgeneralactivities','backup'), array('value'=>1, 'locked'=>0)));
$temp->add(new admin_setting_configcheckbox_with_lock('backup/backup_import_blocks', new lang_string('generalblocks','backup'), new lang_string('configgeneralblocks','backup'), array('value'=>1, 'locked'=>0)));
$temp->add(new admin_setting_configcheckbox_with_lock('backup/backup_import_filters', new lang_string('generalfilters','backup'), new lang_string('configgeneralfilters','backup'), array('value'=>1, 'locked'=>0)));
$temp->add(new admin_setting_configcheckbox_with_lock('restore/restore_general_role_assignments',
new lang_string('generalroleassignments', 'backup'),
new lang_string('configrestoreroleassignments', 'backup'), array('value' => 1, 'locked' => 0)));
+ $temp->add(new admin_setting_configcheckbox_with_lock('restore/restore_general_permissions',
+ new lang_string('generalpermissions', 'backup'),
+ new lang_string('configrestorepermissions', 'backup'), array('value' => 1, 'locked' => 0)));
$temp->add(new admin_setting_configcheckbox_with_lock('restore/restore_general_activities',
new lang_string('generalactivities', 'backup'),
new lang_string('configrestoreactivities', 'backup'), array('value' => 1, 'locked' => 0)));
'country' => new lang_string('country'),
'moodlenetprofile' => new lang_string('moodlenetprofile', 'user'),
'timezone' => new lang_string('timezone'),
- 'webpage' => new lang_string('webpage'),
- 'icqnumber' => new lang_string('icqnumber'),
- 'skypeid' => new lang_string('skypeid'),
- 'yahooid' => new lang_string('yahooid'),
- 'aimid' => new lang_string('aimid'),
- 'msnid' => new lang_string('msnid'),
'firstaccess' => new lang_string('firstaccess'),
'lastaccess' => new lang_string('lastaccess'),
'lastip' => new lang_string('lastip'),
--- /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/>.
+
+/**
+ * Javascript module for contacting the site DPO
+ *
+ * @module tool_dataprivacy/contactdpo
+ * @package tool_dataprivacy
+ * @copyright 2021 Paul Holden <paulh@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+import ModalForm from 'core_form/modalform';
+import Notification from 'core/notification';
+import {get_string as getString} from 'core/str';
+import {add as addToast} from 'core/toast';
+
+const SELECTORS = {
+ CONTACT_DPO: '[data-action="contactdpo"]',
+};
+
+/**
+ * Initialize module
+ */
+export const init = () => {
+ const triggerElement = document.querySelector(SELECTORS.CONTACT_DPO);
+
+ triggerElement.addEventListener('click', event => {
+ event.preventDefault();
+
+ const modalForm = new ModalForm({
+ modalConfig: {
+ title: getString('contactdataprotectionofficer', 'tool_dataprivacy'),
+ },
+ formClass: 'tool_dataprivacy\\form\\contactdpo',
+ saveButtonText: getString('send', 'tool_dataprivacy'),
+ returnFocus: triggerElement,
+ });
+
+ // Show a toast notification when the form is submitted.
+ modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, event => {
+ if (event.detail.result) {
+ getString('requestsubmitted', 'tool_dataprivacy').then(addToast).catch();
+ } else {
+ const warningMessages = event.detail.warnings.map(warning => warning.message);
+ Notification.addNotification({
+ type: 'error',
+ message: warningMessages.join('<br>')
+ });
+ }
+ });
+
+ modalForm.show();
+ });
+};
* @copyright 2018 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-define([
- 'jquery',
- 'core/ajax',
- 'core/notification',
- 'core/str',
- 'core/modal_factory',
- 'core/modal_events',
- 'core/templates',
- 'core/pending'],
-function($, Ajax, Notification, Str, ModalFactory, ModalEvents, Templates, Pending) {
- /**
- * List of action selectors.
- *
- * @type {{CANCEL_REQUEST: string}}
- * @type {{CONTACT_DPO: string}}
- */
- var ACTIONS = {
- CANCEL_REQUEST: '[data-action="cancel"]',
- CONTACT_DPO: '[data-action="contactdpo"]',
- };
+import Ajax from 'core/ajax';
+import Notification from 'core/notification';
+import Pending from 'core/pending';
+import {get_strings as getStrings} from 'core/str';
- /**
- * MyRequestActions class.
- */
- var MyRequestActions = function() {
- this.registerEvents();
- };
+const SELECTORS = {
+ CANCEL_REQUEST: '[data-action="cancel"][data-requestid]',
+};
- /**
- * Register event listeners.
- */
- MyRequestActions.prototype.registerEvents = function() {
- $(ACTIONS.CANCEL_REQUEST).click(function(e) {
- e.preventDefault();
-
- var requestId = $(this).data('requestid');
- var stringkeys = [
- {
- key: 'cancelrequest',
- component: 'tool_dataprivacy'
- },
- {
- key: 'cancelrequestconfirmation',
- component: 'tool_dataprivacy'
- }
- ];
-
- Str.get_strings(stringkeys).then(function(langStrings) {
- var title = langStrings[0];
- var confirmMessage = langStrings[1];
- return ModalFactory.create({
- title: title,
- body: confirmMessage,
- type: ModalFactory.types.SAVE_CANCEL
- }).then(function(modal) {
- modal.setSaveButtonText(title);
-
- // Handle save event.
- modal.getRoot().on(ModalEvents.save, function() {
- // Cancel the request.
- var params = {
- 'requestid': requestId
- };
-
- var request = {
- methodname: 'tool_dataprivacy_cancel_data_request',
- args: params
- };
-
- Ajax.call([request])[0].done(function(data) {
- if (data.result) {
- window.location.reload();
- } else {
- Notification.addNotification({
- message: data.warnings[0].message,
- type: 'error'
- });
- }
- }).fail(Notification.exception);
- });
-
- // Handle hidden event.
- modal.getRoot().on(ModalEvents.hidden, function() {
- // Destroy when hidden.
- modal.destroy();
- });
-
- return modal;
- });
- }).done(function(modal) {
- // Show the modal!
- modal.show();
-
- }).fail(Notification.exception);
- });
-
- $(ACTIONS.CONTACT_DPO).click(function(e) {
- var pendingPromise = new Pending('dataprivacy/crud:initModal:contactdpo');
- e.preventDefault();
+/**
+ * Initialize module
+ */
+export const init = () => {
+ document.addEventListener('click', event => {
+ const triggerElement = event.target.closest(SELECTORS.CANCEL_REQUEST);
+ if (triggerElement === null) {
+ return;
+ }
- var replyToEmail = $(this).data('replytoemail');
+ event.preventDefault();
- var keys = [
- {
- key: 'contactdataprotectionofficer',
- component: 'tool_dataprivacy'
- },
- {
- key: 'send',
- component: 'tool_dataprivacy'
- },
- ];
+ const requiredStrings = [
+ {key: 'cancelrequest', component: 'tool_dataprivacy'},
+ {key: 'cancelrequestconfirmation', component: 'tool_dataprivacy'},
+ ];
- var sendButtonText = '';
- Str.get_strings(keys).then(function(langStrings) {
- var modalTitle = langStrings[0];
- sendButtonText = langStrings[1];
- var context = {
- 'replytoemail': replyToEmail
+ getStrings(requiredStrings).then(([cancelRequest, cancelConfirm]) => {
+ return Notification.confirm(cancelRequest, cancelConfirm, cancelRequest, null, () => {
+ const pendingPromise = new Pending('tool/dataprivacy:cancelRequest');
+ const request = {
+ methodname: 'tool_dataprivacy_cancel_data_request',
+ args: {requestid: triggerElement.dataset.requestid}
};
- return ModalFactory.create({
- title: modalTitle,
- body: Templates.render('tool_dataprivacy/contact_dpo', context),
- type: ModalFactory.types.SAVE_CANCEL,
- large: true
- });
- }).then(function(modal) {
- modal.setSaveButtonText(sendButtonText);
-
- // Show the modal!
- modal.show();
- // Handle send event.
- modal.getRoot().on(ModalEvents.save, function(e) {
- var message = $('#message').val().trim();
- if (message.length === 0) {
- e.preventDefault();
- // Show validation error when the message is empty.
- $('[data-region="messageinput"]').addClass('has-danger notifyproblem');
- $('#id_error_message').removeAttr('hidden');
+ Ajax.call([request])[0].then(response => {
+ if (response.result) {
+ window.location.reload();
} else {
- // Send the message.
- sendMessageToDPO(message);
+ Notification.addNotification({
+ type: 'error',
+ message: response.warnings[0].message
+ });
}
- });
-
- // Handle hidden event.
- modal.getRoot().on(ModalEvents.hidden, function() {
- // Destroy when hidden.
- modal.destroy();
- });
-
- return;
- }).then(pendingPromise.resolve)
- .catch(Notification.exception);
- });
- };
-
- /**
- * Send message to the Data Protection Officer.
- *
- * @param {String} message The message to send.
- */
- function sendMessageToDPO(message) {
- var request = {
- methodname: 'tool_dataprivacy_contact_dpo',
- args: {
- message: message
- }
- };
-
- var requestType = 'success';
- Ajax.call([request])[0].then(function(data) {
- if (data.result) {
- return Str.get_string('requestsubmitted', 'tool_dataprivacy');
- }
- requestType = 'error';
- return data.warnings.join('<br>');
-
- }).done(function(message) {
- Notification.addNotification({
- message: message,
- type: requestType
+ return pendingPromise.resolve();
+ }).catch(Notification.exception);
});
-
- }).fail(Notification.exception);
- }
-
- return /** @alias module:tool_dataprivacy/myrequestactions */ {
- // Public variables and functions.
-
- /**
- * Initialise the unified user filter.
- *
- * @method init
- * @return {MyRequestActions}
- */
- 'init': function() {
- return new MyRequestActions();
- }
- };
-});
+ }).catch();
+ });
+};
$warnings[] = [
'item' => $dpo->id,
'warningcode' => 'errorsendingtodpo',
- 'message' => get_string('errorsendingmessagetodpo', 'tool_dataprivacy')
+ 'message' => get_string('errorsendingmessagetodpo', 'tool_dataprivacy',
+ fullname($dpo))
];
}
}
--- /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/>.
+
+
+namespace tool_dataprivacy\form;
+
+use context;
+use context_user;
+use moodle_exception;
+use moodle_url;
+use core_form\dynamic_form;
+use tool_dataprivacy\api;
+use tool_dataprivacy\external;
+
+/**
+ * Contact DPO modal form
+ *
+ * @package tool_dataprivacy
+ * @copyright 2021 Paul Holden <paulh@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class contactdpo extends dynamic_form {
+
+ /**
+ * Form definition
+ */
+ protected function definition() {
+ global $USER;
+
+ $mform = $this->_form;
+
+ $mform->addElement('static', 'replyto', get_string('replyto', 'tool_dataprivacy'), s($USER->email));
+
+ $mform->addElement('textarea', 'message', get_string('message', 'tool_dataprivacy'), 'cols="60" rows="8"');
+ $mform->setType('message', PARAM_TEXT);
+ $mform->addRule('message', get_string('required'), 'required', null, 'client');
+ }
+
+ /**
+ * Return form context
+ *
+ * @return context
+ */
+ protected function get_context_for_dynamic_submission(): context {
+ global $USER;
+
+ return context_user::instance($USER->id);
+ }
+
+ /**
+ * Check if current user has access to this form, otherwise throw exception
+ *
+ * @throws moodle_exception
+ */
+ protected function check_access_for_dynamic_submission(): void {
+ if (!api::can_contact_dpo()) {
+ throw new moodle_exception('errorcontactdpodisabled', 'tool_dataprivacy');
+ }
+ }
+
+ /**
+ * Process the form submission, used if form was submitted via AJAX
+ *
+ * @return array
+ */
+ public function process_dynamic_submission() {
+ return external::contact_dpo($this->get_data()->message);
+ }
+
+ /**
+ * Load in existing data as form defaults (not applicable)
+ */
+ public function set_data_for_dynamic_submission(): void {
+ return;
+ }
+
+ /**
+ * Returns url to set in $PAGE->set_url() when form is being rendered or submitted via AJAX
+ *
+ * @return moodle_url
+ */
+ protected function get_page_url_for_dynamic_submission(): moodle_url {
+ global $USER;
+
+ return new moodle_url('/user/profile.php', ['id' => $USER->id]);
+ }
+}
/**
* Render the contact DPO link.
*
- * @param string $replytoemail The Reply-to email address
* @return string The HTML for the link.
- * @throws coding_exception
*/
- public function render_contact_dpo_link($replytoemail) {
+ public function render_contact_dpo_link() {
$params = [
'data-action' => 'contactdpo',
- 'data-replytoemail' => $replytoemail,
];
return html_writer::link('#', get_string('contactdataprotectionofficer', 'tool_dataprivacy'), $params);
}
$string['emailsalutation'] = 'Dear {$a},';
$string['errorcannotrequestdeleteforself'] = 'You don\'t have permission to create deletion request for yourself.';
$string['errorcannotrequestdeleteforother'] = 'You don\'t have permission to create deletion request for this user.';
+$string['errorcontactdpodisabled'] = 'Contacting the privacy officer is disabled';
$string['errorinvalidrequestcomments'] = 'The comments field may contain plain text only.';
$string['errorinvalidrequestcreationmethod'] = 'Invalid request creation method!';
$string['errorinvalidrequeststatus'] = 'Invalid request status!';
// Contact data protection officer link.
if (\tool_dataprivacy\api::can_contact_dpo() && $iscurrentuser) {
$renderer = $PAGE->get_renderer('tool_dataprivacy');
- $content = $renderer->render_contact_dpo_link($USER->email);
+ $content = $renderer->render_contact_dpo_link();
$node = new core_user\output\myprofile\node('privacyandpolicies', 'contactdpo', null, null, null, $content);
$category->add_node($node);
- $PAGE->requires->js_call_amd('tool_dataprivacy/myrequestactions', 'init');
+
+ // Require our Javascript module to handle contact DPO interaction.
+ $PAGE->requires->js_call_amd('tool_dataprivacy/contactdpo', 'init');
$url = new moodle_url('/admin/tool/dataprivacy/mydatarequests.php');
$node = new core_user\output\myprofile\node('privacyandpolicies', 'datarequests',
+++ /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 tool_dataprivacy/contact_dpo
-
- The purpose of this template is to enable the user to contact the site's DPO via email.
-
- Classes required for JS:
- * none
-
- Data attributes required for JS:
- * none
-
- Context variables required for this template:
- * userid int The user's ID.
- * email string The user's email address.
-
- Example context (json):
- {
- "userid": 1,
- "replytoemail": "martha@example.com"
- }
-}}
-<div class="container">
- <div class="row mb-2">
- <label class="col-md-3 col-form-label">{{#str}}replyto, tool_dataprivacy{{/str}}</label>
- <div class="col-md-9 col-form-label">{{replytoemail}}</div>
- </div>
- <div class="row" data-region="messageinput">
- <label for="message" class="col-md-3 col-form-label">
- {{#str}}message, tool_dataprivacy{{/str}}
- <span class="float-sm-right text-nowrap">
- <abbr class="initialism text-danger" title="{{#str}}required{{/str}}">{{#pix}}req, core, {{#str}}required{{/str}}{{/pix}}</abbr>
- </span>
- </label>
- <div class="col-md-9">
- <textarea class="form-control" id="message" cols="60" rows="8"></textarea>
- <div class="form-control-feedback" id="id_error_message" hidden="hidden">
- {{#str}}required, moodle{{/str}}
- </div>
- </div>
- </div>
-</div>
-
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | s1@example.com |
- And I log in as "admin"
- And I set the following administration settings values:
- | contactdataprotectionofficer | 1 |
- And I log out
@javascript
Scenario: Contacting the privacy officer
- Given I log in as "student1"
+ Given the following config values are set as admin:
+ | contactdataprotectionofficer | 1 | tool_dataprivacy |
+ When I log in as "student1"
And I follow "Profile" in the user menu
- And I should see "Contact the privacy officer"
And I click on "Contact the privacy officer" "link"
And I set the field "Message" to "Hello DPO!"
And I click on "Send" "button" in the "Contact the privacy officer" "dialogue"
- And I should see "Your request has been submitted to the privacy officer"
+ Then I should see "Your request has been submitted to the privacy officer"
And I click on "Data requests" "link"
And I should see "Hello DPO!" in the "General inquiry" "table_row"
+
+ Scenario: Contacting the privacy officer when not enabled
+ When I log in as "student1"
+ And I follow "Profile" in the user menu
+ Then "Contact the privacy officer" "link" should not exist
--- /dev/null
+@tool @tool_dataprivacy
+Feature: Manage my own data requests
+ In order to manage my own data requests
+ As a user
+ I need to be able to view and cancel all my data requests
+
+ Background:
+ Given the following "users" exist:
+ | username | firstname | lastname | email |
+ | student1 | Student | 1 | s1@example.com |
+ And the following config values are set as admin:
+ | contactdataprotectionofficer | 1 | tool_dataprivacy |
+
+ @javascript
+ Scenario: Cancel my own data request
+ Given I log in as "student1"
+ And I follow "Profile" in the user menu
+ And I click on "Contact the privacy officer" "link"
+ And I set the field "Message" to "Hello DPO!"
+ And I click on "Send" "button" in the "Contact the privacy officer" "dialogue"
+ And I should see "Your request has been submitted to the privacy officer"
+ When I click on "Data requests" "link"
+ And I open the action menu in "Hello DPO!" "table_row"
+ And I choose "Cancel" in the open action menu
+ And I click on "Cancel request" "button" in the "Cancel request" "dialogue"
+ Then I should see "Cancelled" in the "Hello DPO!" "table_row"
This template does not have an example context because it includes ajax functionality.
}}
<div data-region="user-competency-full-info" data-node="user-competency" data-competencyid="{{usercompetency.competencyid}}" data-userid="{{usercompetency.userid}}" data-region-id="{{uniqid}}">
-<div data-region="competency-summary">
- {{#competency}}
- {{> tool_lp/competency_summary }}
- {{/competency}}
- <dl>
- {{#usercompetency}}
- <dt>{{#str}}reviewstatus, tool_lp{{/str}}</dt>
- <dd data-region="user-competency-status">{{statusname}}
+ <div data-region="competency-summary">
+ {{#competency}}
+ {{> tool_lp/competency_summary }}
+ {{/competency}}
+ <dl>
+ {{#usercompetency}}
+ <dt>{{#str}}reviewstatus, tool_lp{{/str}}</dt>
+ <dd data-region="user-competency-status">{{statusname}}
- {{#isstatusinreview}}
- - {{reviewer.fullname}}
- {{/isstatusinreview}}
+ {{#isstatusinreview}}
+ - {{reviewer.fullname}}
+ {{/isstatusinreview}}
- {{#isrequestreviewallowed}}<button class="btn btn-secondary" data-action="request-review">{{#str}}requestreview, tool_lp{{/str}}</button>{{/isrequestreviewallowed}}
- {{#iscancelreviewrequestallowed}}<button class="btn btn-secondary" data-action="cancel-review-request">{{#str}}cancelreviewrequest, tool_lp{{/str}}</button>{{/iscancelreviewrequestallowed}}
- {{#isstartreviewallowed}}<button class="btn btn-secondary" data-action="start-review">{{#str}}startreview, tool_lp{{/str}}</button>{{/isstartreviewallowed}}
- {{#isstopreviewallowed}}<button class="btn btn-secondary" data-action="stop-review">{{#str}}stopreview, tool_lp{{/str}}</button>{{/isstopreviewallowed}}
- </dd>
- <dt>{{#str}}proficient, tool_lp{{/str}}</dt>
- <dd>
- <span class="badge {{#proficiency}}badge-success{{/proficiency}}{{^proficiency}}badge-danger{{/proficiency}} float-left">
- {{proficiencyname}}
- </span>
- </dd>
- <dt>{{#str}}rating, tool_lp{{/str}}</dt>
- <dd>{{gradename}}
- {{#cangrade}}
- <button class="btn btn-secondary" id="rate_{{uniqid}}">{{#str}}rate, tool_lp{{/str}}</button>
- {{/cangrade}}
- </dd>
- {{#js}}
- require(['jquery', 'tool_lp/grade_user_competency_inline', 'tool_lp/user_competency_info', 'tool_lp/user_competency_workflow'], function($, mod, info, UserCompWorkflow) {
+ {{#isrequestreviewallowed}}<button class="btn btn-secondary" data-action="request-review">{{#str}}requestreview, tool_lp{{/str}}</button>{{/isrequestreviewallowed}}
+ {{#iscancelreviewrequestallowed}}<button class="btn btn-secondary" data-action="cancel-review-request">{{#str}}cancelreviewrequest, tool_lp{{/str}}</button>{{/iscancelreviewrequestallowed}}
+ {{#isstartreviewallowed}}<button class="btn btn-secondary" data-action="start-review">{{#str}}startreview, tool_lp{{/str}}</button>{{/isstartreviewallowed}}
+ {{#isstopreviewallowed}}<button class="btn btn-secondary" data-action="stop-review">{{#str}}stopreview, tool_lp{{/str}}</button>{{/isstopreviewallowed}}
+ </dd>
+ <dt>{{#str}}proficient, tool_lp{{/str}}</dt>
+ <dd>
+ <span class="badge {{#proficiency}}badge-success{{/proficiency}}{{^proficiency}}badge-danger{{/proficiency}} float-left">
+ {{proficiencyname}}
+ </span>
+ </dd>
+ <dt>{{#str}}rating, tool_lp{{/str}}</dt>
+ <dd>{{gradename}}
+ {{#cangrade}}
+ <button class="btn btn-secondary" id="rate_{{uniqid}}">{{#str}}rate, tool_lp{{/str}}</button>
+ {{/cangrade}}
+ </dd>
+ {{#js}}
+ require(['jquery', 'tool_lp/grade_user_competency_inline', 'tool_lp/user_competency_info', 'tool_lp/user_competency_workflow'], function($, mod, info, UserCompWorkflow) {
- var competencyElement = $('[data-region-id="{{uniqid}}"]');
- var infoReloader = new info(competencyElement, '{{competency.competency.id}}', '{{user.id}}');
+ var competencyElement = $('[data-region-id="{{uniqid}}"]');
+ var infoReloader = new info(competencyElement, '{{competency.competency.id}}', '{{user.id}}');
- var ucw = new UserCompWorkflow();
- ucw.registerEvents('[data-region="user-competency-status"]');
- ucw.on('status-changed', infoReloader.reload.bind(infoReloader));
- ucw.on('error-occured', infoReloader.reload.bind(infoReloader));
+ var ucw = new UserCompWorkflow();
+ ucw.registerEvents('[data-region="user-competency-status"]');
+ ucw.on('status-changed', infoReloader.reload.bind(infoReloader));
+ ucw.on('error-occured', infoReloader.reload.bind(infoReloader));
- var inlineGrader = new mod('#rate_{{uniqid}}', '{{competency.scaleid}}', '{{competency.competency.id}}', '{{user.id}}', '{{plan.id}}', '', '{{#str}}chooserating, tool_lp{{/str}}');
- inlineGrader.on('competencyupdated', infoReloader.reload.bind(infoReloader));
- });
- {{/js}}
- {{/usercompetency}}
- </dl>
- {{#commentarea}}
- {{#canpostorhascomments}}
- {{>tool_lp/comment_area}}
- {{/canpostorhascomments}}
- {{/commentarea}}
- <dl data-region="evidence-listing">
- <dt>{{#str}}evidence, tool_lp{{/str}}</dt>
- <dd>
- {{#evidence}}
- {{> tool_lp/evidence_summary }}
- {{/evidence}}
- {{^evidence}}
- <p>{{#str}}noevidence, tool_lp{{/str}}</p>
- {{/evidence}}
- </dd>
- </dl>
+ var inlineGrader = new mod('#rate_{{uniqid}}', '{{competency.scaleid}}', '{{competency.competency.id}}', '{{user.id}}', '{{plan.id}}', '', '{{#str}}chooserating, tool_lp{{/str}}');
+ inlineGrader.on('competencyupdated', infoReloader.reload.bind(infoReloader));
+ });
+ {{/js}}
+ {{/usercompetency}}
+ </dl>
+ {{#commentarea}}
+ {{#canpostorhascomments}}
+ {{>tool_lp/comment_area}}
+ {{/canpostorhascomments}}
+ {{/commentarea}}
+ <dl data-region="evidence-listing">
+ <dt>{{#str}}evidence, tool_lp{{/str}}</dt>
+ <dd>
+ {{#evidence}}
+ {{> tool_lp/evidence_summary }}
+ {{/evidence}}
+ {{^evidence}}
+ <p>{{#str}}noevidence, tool_lp{{/str}}</p>
+ {{/evidence}}
+ </dd>
+ </dl>
+ </div>
</div>
$mform->addElement('checkbox', 'basicauth', get_string('usebasicauth', 'tool_oauth2'));
$mform->addHelpButton('basicauth', 'usebasicauth', 'tool_oauth2');
+ // Base Url.
+ $mform->addElement('text', 'baseurl', get_string('issuerbaseurl', 'tool_oauth2'));
+ $mform->addRule('baseurl', get_string('maximumchars', '', 1024), 'maxlength', 1024, 'client');
+ $mform->addHelpButton('baseurl', 'issuerbaseurl', 'tool_oauth2');
+ if ($this->type && $this->type == 'nextcloud') {
+ $mform->addRule('baseurl', null, 'required', null, 'client');
+ }
+
+ // Image.
+ $mform->addElement('text', 'image', get_string('issuerimage', 'tool_oauth2'), 'maxlength="1024"');
+ $mform->addRule('image', get_string('maximumchars', '', 1024), 'maxlength', 1024, 'client');
+ $mform->addHelpButton('image', 'issuername', 'tool_oauth2');
+
+ // Show on login page.
+ $options = [
+ \core\oauth2\issuer::EVERYWHERE => get_string('issueruseineverywhere', 'tool_oauth2'),
+ \core\oauth2\issuer::LOGINONLY => get_string('issueruseinloginonly', 'tool_oauth2'),
+ \core\oauth2\issuer::SERVICEONLY => get_string('issueruseininternalonly', 'tool_oauth2'),
+ ];
+ $mform->addElement('select', 'showonloginpage', get_string('issuerusein', 'tool_oauth2'), $options);
+ $mform->addHelpButton('showonloginpage', 'issuerusein', 'tool_oauth2');
+
+ // Name on login page.
+ $mform->addElement('text', 'loginpagename', get_string('issuerloginpagename', 'tool_oauth2'));
+ $mform->addRule('loginpagename', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
+ $mform->addHelpButton('loginpagename', 'issuerloginpagename', 'tool_oauth2');
+ $mform->hideIf('loginpagename', 'showonloginpage', 'eq', \core\oauth2\issuer::SERVICEONLY);
+
// Login scopes.
$mform->addElement('text', 'loginscopes', get_string('issuerloginscopes', 'tool_oauth2'));
- $mform->addRule('loginscopes', null, 'required', null, 'client');
$mform->addRule('loginscopes', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
$mform->addHelpButton('loginscopes', 'issuerloginscopes', 'tool_oauth2');
+ $mform->hideIf('loginscopes', 'showonloginpage', 'eq', \core\oauth2\issuer::SERVICEONLY);
// Login scopes offline.
$mform->addElement('text', 'loginscopesoffline', get_string('issuerloginscopesoffline', 'tool_oauth2'));
- $mform->addRule('loginscopesoffline', null, 'required', null, 'client');
$mform->addRule('loginscopesoffline', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
$mform->addHelpButton('loginscopesoffline', 'issuerloginscopesoffline', 'tool_oauth2');
+ $mform->hideIf('loginscopesoffline', 'showonloginpage', 'eq', \core\oauth2\issuer::SERVICEONLY);
// Login params.
$mform->addElement('text', 'loginparams', get_string('issuerloginparams', 'tool_oauth2'));
$mform->addRule('loginparams', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
$mform->addHelpButton('loginparams', 'issuerloginparams', 'tool_oauth2');
+ $mform->hideIf('loginparams', 'showonloginpage', 'eq', \core\oauth2\issuer::SERVICEONLY);
// Login params offline.
$mform->addElement('text', 'loginparamsoffline', get_string('issuerloginparamsoffline', 'tool_oauth2'));
$mform->addRule('loginparamsoffline', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
$mform->addHelpButton('loginparamsoffline', 'issuerloginparamsoffline', 'tool_oauth2');
-
- // Base Url.
- $mform->addElement('text', 'baseurl', get_string('issuerbaseurl', 'tool_oauth2'));
- $mform->addRule('baseurl', get_string('maximumchars', '', 1024), 'maxlength', 1024, 'client');
- $mform->addHelpButton('baseurl', 'issuerbaseurl', 'tool_oauth2');
- if ($this->type && $this->type == 'nextcloud') {
- $mform->addRule('baseurl', null, 'required', null, 'client');
- }
+ $mform->hideIf('loginparamsoffline', 'showonloginpage', 'eq', \core\oauth2\issuer::SERVICEONLY);
// Allowed Domains.
$mform->addElement('text', 'alloweddomains', get_string('issueralloweddomains', 'tool_oauth2'));
$mform->addRule('alloweddomains', get_string('maximumchars', '', 1024), 'maxlength', 1024, 'client');
$mform->addHelpButton('alloweddomains', 'issueralloweddomains', 'tool_oauth2');
-
- // Image.
- $mform->addElement('text', 'image', get_string('issuerimage', 'tool_oauth2'), 'maxlength="1024"');
- $mform->addRule('image', get_string('maximumchars', '', 1024), 'maxlength', 1024, 'client');
- $mform->addHelpButton('image', 'issuername', 'tool_oauth2');
-
- // Show on login page.
- $mform->addElement('checkbox', 'showonloginpage', get_string('issuershowonloginpage', 'tool_oauth2'));
- $mform->addHelpButton('showonloginpage', 'issuershowonloginpage', 'tool_oauth2');
+ $mform->hideIf('alloweddomains', 'showonloginpage', 'eq', \core\oauth2\issuer::SERVICEONLY);
if ($this->showrequireconfirm) {
// Require confirmation email for new accounts.
$mform->addElement('advcheckbox', 'requireconfirmation', get_string('issuerrequireconfirmation', 'tool_oauth2'));
$mform->addHelpButton('requireconfirmation', 'issuerrequireconfirmation', 'tool_oauth2');
+ $mform->hideIf('requireconfirmation', 'showonloginpage', 'eq', \core\oauth2\issuer::SERVICEONLY);
}
if ($this->type == 'imsobv2p1' || $issuer->get('servicetype') == 'imsobv2p1') {
$mform->getElement('servicetype')->setValue($this->type);
}
}
+
+ /**
+ * Define extra validation mechanims.
+ *
+ * The data here:
+ * - does not include {@see self::$fieldstoremove}.
+ * - does include {@see self::$foreignfields}.
+ * - was converted to map persistent-like data, e.g. array $description to string $description + int $descriptionformat.
+ *
+ * You can modify the $errors parameter in order to remove some validation errors should you
+ * need to. However, the best practice is to return new or overriden errors. Only modify the
+ * errors passed by reference when you have no other option.
+ *
+ * Do not add any logic here, it is only intended to be used by child classes.
+ *
+ * @param stdClass $data Data to validate.
+ * @param array $files Array of files.
+ * @param array $errors Currently reported errors.
+ * @return array of additional errors, or overridden errors.
+ */
+ protected function extra_validation($data, $files, array &$errors) {
+ $errors = [];
+ if ($data->showonloginpage != \core\oauth2\issuer::SERVICEONLY) {
+ if (!strlen(trim($data->loginscopes))) {
+ $errors['loginscopes'] = get_string('required');
+ }
+ if (!strlen(trim($data->loginscopesoffline))) {
+ $errors['loginscopesoffline'] = get_string('required');
+ }
+ }
+ return $errors;
+ }
}
$table = new html_table();
$table->head = [
get_string('name'),
- get_string('configuredstatus', 'tool_oauth2'),
- get_string('loginissuer', 'tool_oauth2'),
+ get_string('issuerusedforlogin', 'tool_oauth2'),
+ get_string('logindisplay', 'tool_oauth2'),
+ get_string('issuerusedforinternal', 'tool_oauth2'),
get_string('discoverystatus', 'tool_oauth2') . ' ' . $this->help_icon('discovered', 'tool_oauth2'),
get_string('systemauthstatus', 'tool_oauth2') . ' ' . $this->help_icon('systemaccountconnected', 'tool_oauth2'),
get_string('edit'),
$namecell = new html_table_cell($name);
$namecell->header = true;
- // Configured.
- if ($issuer->is_configured()) {
- $configured = $this->pix_icon('yes', get_string('configured', 'tool_oauth2'), 'tool_oauth2');
+ // Login issuer.
+ if ((int)$issuer->get('showonloginpage') == issuer::SERVICEONLY) {
+ $loginissuer = $this->pix_icon('no', get_string('notloginissuer', 'tool_oauth2'), 'tool_oauth2');
+ $logindisplayas = '';
} else {
- $configured = $this->pix_icon('no', get_string('notconfigured', 'tool_oauth2'), 'tool_oauth2');
+ $logindisplayas = s($issuer->get_display_name());
+ if ($issuer->get('id') && $issuer->is_configured() && !empty($issuer->get_endpoint_url('userinfo'))) {
+ $loginissuer = $this->pix_icon('yes', get_string('loginissuer', 'tool_oauth2'), 'tool_oauth2');
+ } else {
+ $loginissuer = $this->pix_icon('notconfigured', get_string('notconfigured', 'tool_oauth2'), 'tool_oauth2');
+ }
}
- $configuredstatuscell = new html_table_cell($configured);
+ $loginissuerstatuscell = new html_table_cell($loginissuer);
- // Login issuer.
- if (!empty($issuer->get('showonloginpage'))) {
- $loginissuer = $this->pix_icon('yes', get_string('loginissuer', 'tool_oauth2'), 'tool_oauth2');
+ // Internal services issuer.
+ if ((int)$issuer->get('showonloginpage') == issuer::LOGINONLY) {
+ $serviceissuer = $this->pix_icon('no', get_string('issuersservicesnotallow', 'tool_oauth2'), 'tool_oauth2');
+ } else if ($issuer->get('id') && $issuer->is_configured()) {
+ $serviceissuer = $this->pix_icon('yes', get_string('issuersservicesallow', 'tool_oauth2'), 'tool_oauth2');
} else {
- $loginissuer = $this->pix_icon('no', get_string('notloginissuer', 'tool_oauth2'), 'tool_oauth2');
+ $serviceissuer = $this->pix_icon('notconfigured', get_string('notconfigured', 'tool_oauth2'), 'tool_oauth2');
}
- $loginissuerstatuscell = new html_table_cell($loginissuer);
+ $internalissuerstatuscell = new html_table_cell($serviceissuer);
// Discovered.
if (!empty($issuer->get('scopessupported'))) {
$row = new html_table_row([
$namecell,
- $configuredstatuscell,
$loginissuerstatuscell,
+ $logindisplayas,
+ $internalissuerstatuscell,
$discoverystatuscell,
$systemauthstatuscell,
$editcell,
]);
+ if (!$issuer->get('enabled')) {
+ $row->attributes['class'] = 'dimmed_text';
+ }
+
$data[] = $row;
$index++;
}
if ($issuer) {
$PAGE->navbar->add(get_string('editissuer', 'tool_oauth2', s($issuer->get('name'))));
} else {
- $PAGE->navbar->add(get_string('createnewservice', 'tool_oauth2') . get_string('custom_service', 'tool_oauth2'));
+ $PAGE->navbar->add(get_string('createnewservice', 'tool_oauth2') . ' ' . get_string('custom_service', 'tool_oauth2'));
}
$showrequireconfirm = false;
if ($issuer) {
echo $OUTPUT->heading(get_string('editissuer', 'tool_oauth2', s($issuer->get('name'))));
} else {
- echo $OUTPUT->heading(get_string('createnewservice', 'tool_oauth2') . get_string('custom_service', 'tool_oauth2'));
+ echo $OUTPUT->heading(get_string('createnewservice', 'tool_oauth2') . ' ' . get_string('custom_service', 'tool_oauth2'));
}
$mform->display();
echo $OUTPUT->footer();
redirect($PAGE->url, get_string('changessaved'), null, \core\output\notification::NOTIFY_SUCCESS);
} else {
echo $OUTPUT->header();
- echo $OUTPUT->heading(get_string('createnewservice', 'tool_oauth2') . get_string($type . '_service', 'tool_oauth2'));
+ echo $OUTPUT->heading(get_string('createnewservice', 'tool_oauth2') . ' ' . get_string($type . '_service', 'tool_oauth2'));
$mform->display();
echo $OUTPUT->footer();
}
$mform = new \tool_oauth2\form\issuer(null, ['persistent' => $issuer, 'type' => $type,
'showrequireconfirm' => $showrequireconfirm]);
- $PAGE->navbar->add(get_string('createnewservice', 'tool_oauth2') . get_string($type . '_service', 'tool_oauth2'));
+ $PAGE->navbar->add(get_string('createnewservice', 'tool_oauth2') . ' ' . get_string($type . '_service', 'tool_oauth2'));
echo $OUTPUT->header();
- echo $OUTPUT->heading(get_string('createnewservice', 'tool_oauth2') . get_string($type . '_service', 'tool_oauth2'));
+ echo $OUTPUT->heading(get_string('createnewservice', 'tool_oauth2') . ' ' . get_string($type . '_service', 'tool_oauth2'));
$mform->display();
echo $OUTPUT->footer();
echo $OUTPUT->header();
echo $OUTPUT->heading(get_string('pluginname', 'tool_oauth2'));
echo $OUTPUT->doc_link('OAuth2_Services', get_string('serviceshelp', 'tool_oauth2'));
- $issuers = core\oauth2\api::get_all_issuers();
+ $issuers = core\oauth2\api::get_all_issuers(true);
echo $renderer->issuers_table($issuers);
echo $renderer->container_start();
- echo get_string('createnewservice', 'tool_oauth2');
+ echo get_string('createnewservice', 'tool_oauth2') . ' ';
// Google template.
$docs = 'admin/tool/oauth2/issuers/google';
$string['createfromtemplate'] = 'Create an OAuth 2 service from a template';
$string['createfromtemplatedesc'] = 'Choose one of the OAuth 2 service templates below to create an OAuth service with a valid configuration for one of the known service types. This will create the OAuth 2 service, with all the correct end points and parameters required for authentication, though you will still need to enter the client ID and secret for the new service before it can be used.';
$string['createnewendpoint'] = 'Create new endpoint for issuer "{$a}"';
-$string['createnewservice'] = 'Create new service: ';
+$string['createnewservice'] = 'Create new service:';
$string['createnewuserfieldmapping'] = 'Create new user field mapping for issuer "{$a}"';
$string['custom_service'] = 'Custom';
$string['deleteconfirm'] = 'Are you sure you want to delete the identity issuer "{$a}"? Any plugins relying on this issuer will stop working.';
$string['endpointurl'] = 'URL';
$string['facebook_service'] = 'Facebook';
$string['google_service'] = 'Google';
-$string['imsobv2p1_service'] = 'IMS OBv2.1';
+$string['imsobv2p1_service'] = 'OpenBadges';
$string['issuersetup'] = 'Detailed instructions on configuring the common OAuth 2 services';
$string['issuersetuptype'] = 'Detailed instructions on setting up the {$a} OAuth 2 provider';
$string['issueralloweddomains_help'] = 'If set, this setting is a comma separated list of domains that logins will be restricted to when using this provider.';
$string['issuerenabled'] = 'Identity issuer enabled';
$string['issuerimage_help'] = 'An image URL used to show a logo for this issuer. May be displayed on login page.';
$string['issuerimage'] = 'Logo URL';
+$string['issuerloginpagename'] = 'Name displayed on the login page';
+$string['issuerloginpagename_help'] = 'If specified, this name will be used on the login page instead of the service name above';
$string['issuerloginparams'] = 'Additional parameters included in a login request.';
$string['issuerloginparams_help'] = 'Some systems require additional parameters for a login request in order to read the user\'s basic profile.';
$string['issuerloginparamsoffline'] = 'Additional parameters included in a login request for offline access.';
$string['issuerrequireconfirmation_help'] = 'Require that all users verify their email address before they can log in with OAuth. This applies to newly created accounts as part of the login process, or when an existing Moodle account is connected to an OAuth login via matching email addresses.';
$string['issuerrequireconfirmation'] = 'Require email verification';
$string['issuers'] = 'Issuers';
+$string['issuersservicesallow'] = 'Allow services';
+$string['issuersservicesnotallow'] = 'Do not allow services';
+$string['issuerusein'] = 'This service will be used';
+$string['issuerusein_help'] = 'OAuth 2 services can be used in some internal services, on the login page, or both, if needed';
+$string['issueruseineverywhere'] = 'Login page and internal services';
+$string['issueruseininternalonly'] = 'Internal services only';
+$string['issueruseinloginonly'] = 'Login page only';
+$string['issuerusedforlogin'] = 'Login';
+$string['issuerusedforinternal'] = 'Internal services';
+$string['logindisplay'] = 'Display on login page as';
$string['loginissuer'] = 'Allow login';
$string['microsoft_service'] = 'Microsoft';
$string['nextcloud_service'] = 'Nextcloud';
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 25.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#FFA500;}
+</style>
+<path class="st0" d="M9.1,12.9v-1.7c0-0.1,0-0.2-0.1-0.2s-0.1-0.1-0.2-0.1H7.2c-0.1,0-0.1,0-0.2,0.1s-0.1,0.1-0.1,0.2v1.7
+ c0,0.1,0,0.2,0.1,0.2s0.1,0.1,0.2,0.1h1.7c0.1,0,0.1,0,0.2-0.1S9.1,13,9.1,12.9z M9.1,9.5l0.2-4.1c0-0.1,0-0.1-0.1-0.2
+ C9.1,5.2,9,5.2,9,5.2H7c-0.1,0-0.1,0-0.2,0.1c-0.1,0-0.1,0.1-0.1,0.2l0.2,4.1c0,0.1,0,0.1,0.1,0.1c0.1,0,0.1,0.1,0.2,0.1h1.6
+ c0.1,0,0.2,0,0.2-0.1C9.1,9.7,9.1,9.6,9.1,9.5z M9,1.3l6.8,12.5c0.2,0.4,0.2,0.7,0,1.1c-0.1,0.2-0.2,0.3-0.4,0.4s-0.4,0.2-0.6,0.2
+ H1.2c-0.2,0-0.4-0.1-0.6-0.2S0.3,15,0.2,14.9c-0.2-0.4-0.2-0.7,0-1.1L7,1.3c0.1-0.2,0.2-0.3,0.4-0.4S7.8,0.7,8,0.7s0.4,0.1,0.6,0.2
+ C8.8,0.9,8.9,1.1,9,1.3z"/>
+</svg>
When I press "Save changes"
Then I should see "Changes saved"
And I should see "Testing service"
- And "Configured" "icon" should exist in the "Testing service" "table_row"
And "Allow login" "icon" should exist in the "Testing service" "table_row"
+ And "Allow services" "icon" should exist in the "Testing service" "table_row"
And "Service discovery successful" "icon" should exist in the "Testing service" "table_row"
And I click on "Configure endpoints" "link" in the "Testing service" "table_row"
And I should see "https://accounts.google.com/.well-known/openid-configuration" in the "discovery_endpoint" "table_row"
When I press "Save changes"
Then I should see "Changes saved"
And I should see "Testing service"
- And "Configured" "icon" should exist in the "Testing service" "table_row"
And "Allow login" "icon" should exist in the "Testing service" "table_row"
+ And "Allow services" "icon" should exist in the "Testing service" "table_row"
And I should see "-" in the "Testing service" "table_row"
And I click on "Configure endpoints" "link" in the "Testing service" "table_row"
And I should see "authorization_endpoint"
When I press "Save changes"
Then I should see "Changes saved"
And I should see "Testing service"
- And "Configured" "icon" should exist in the "Testing service" "table_row"
And "Allow login" "icon" should exist in the "Testing service" "table_row"
+ And "Allow services" "icon" should exist in the "Testing service" "table_row"
And I should see "-" in the "Testing service" "table_row"
And I click on "Configure endpoints" "link" in the "Testing service" "table_row"
And I should see "authorization_endpoint"
When I press "Save changes"
Then I should see "Changes saved"
And I should see "Testing service"
- And "Configured" "icon" should exist in the "Testing service" "table_row"
And "Do not allow login" "icon" should exist in the "Testing service" "table_row"
+ And "Allow services" "icon" should exist in the "Testing service" "table_row"
And I should see "-" in the "Testing service" "table_row"
And I click on "Configure endpoints" "link" in the "Testing service" "table_row"
And I should see "authorization_endpoint"
And I should see "Identity issuer deleted"
And I should not see "Testing service modified"
- Scenario: Create, edit and delete standard service for IMS OBv2.1
- Given I press "IMS OBv2.1"
- And I should see "Create new service: IMS OBv2.1"
+ Scenario: Create, edit and delete standard service for OpenBadges
+ Given I press "OpenBadges"
+ And I should see "Create new service: OpenBadges"
And I set the following fields to these values:
| Client ID | thisistheclientid |
| Client secret | supersecret |
| Service base URL | https://dc.imsglobal.org/ |
When I press "Save changes"
Then I should see "Changes saved"
- And I should see "IMS OBv2.1"
- And "Configured" "icon" should exist in the "IMS OBv2.1" "table_row"
- And "Do not allow login" "icon" should exist in the "IMS OBv2.1" "table_row"
- And "Service discovery successful" "icon" should exist in the "IMS OBv2.1" "table_row"
+ And I should see "OpenBadges"
+ And "Allow services" "icon" should exist in the "OpenBadges" "table_row"
+ And "Do not allow login" "icon" should exist in the "OpenBadges" "table_row"
+ And "Service discovery successful" "icon" should exist in the "OpenBadges" "table_row"
And the "src" attribute of "table.admintable th img" "css_element" should contain "IMS-Global-Logo.png"
- And I click on "Configure endpoints" "link" in the "IMS OBv2.1" "table_row"
+ And I click on "Configure endpoints" "link" in the "OpenBadges" "table_row"
And I should see "https://dc.imsglobal.org/.well-known/badgeconnect.json" in the "discovery_endpoint" "table_row"
And I should see "authorization_endpoint"
And I follow "OAuth 2 services"
- And I click on "Configure user field mappings" "link" in the "IMS OBv2.1" "table_row"
+ And I click on "Configure user field mappings" "link" in the "OpenBadges" "table_row"
And I should not see "given_name"
And I should not see "middle_name"
And I follow "OAuth 2 services"
- And I click on "Edit" "link" in the "IMS OBv2.1" "table_row"
+ And I click on "Edit" "link" in the "OpenBadges" "table_row"
And I set the following fields to these values:
| Name | IMS Global |
And I press "Save changes"
When I press "Save changes"
Then I should see "Changes saved"
And I should see "Google custom"
- And "Configured" "icon" should exist in the "Google custom" "table_row"
And "Do not allow login" "icon" should exist in the "Google custom" "table_row"
+ And "Allow services" "icon" should exist in the "Google custom" "table_row"
And "Service discovery successful" "icon" should exist in the "Google custom" "table_row"
And the "src" attribute of "table.admintable th img" "css_element" should contain "favicon.ico"
And I click on "Configure endpoints" "link" in the "Google custom" "table_row"
When I press "Save changes"
Then I should see "Could not discover end points for identity issuer: Invalid custom service"
And I should see "URL: https://dc.imsglobal.org/.well-known/openid-configuration"
- And "Configured" "icon" should exist in the "Invalid custom service" "table_row"
+ And "Allow services" "icon" should exist in the "Invalid custom service" "table_row"
And "Do not allow login" "icon" should exist in the "Invalid custom service" "table_row"
And I should see "-" in the "Invalid custom service" "table_row"
And I click on "Configure endpoints" "link" in the "Invalid custom service" "table_row"
| Name | Valid custom service |
| Service base URL | https://accounts.google.com/ |
And I press "Save changes"
- And "Configured" "icon" should exist in the "Valid custom" "table_row"
And "Do not allow login" "icon" should exist in the "Valid custom" "table_row"
+ And "Allow services" "icon" should exist in the "Valid custom" "table_row"
And "Service discovery successful" "icon" should exist in the "Valid custom" "table_row"
And I click on "Edit" "link" in the "Valid custom service" "table_row"
And I set the following fields to these values:
When I press "Save changes"
And I should see "Changes saved"
And I should see "Empty custom service"
- And "Configured" "icon" should exist in the "Empty custom service" "table_row"
+ And "Allow services" "icon" should exist in the "Empty custom service" "table_row"
And "Do not allow login" "icon" should exist in the "Empty custom service" "table_row"
And I should see "-" in the "Empty custom service" "table_row"
And I click on "Configure endpoints" "link" in the "Empty custom service" "table_row"
| Name | Valid custom service |
| Service base URL | https://accounts.google.com |
And I press "Save changes"
- And "Configured" "icon" should exist in the "Valid custom" "table_row"
And "Do not allow login" "icon" should exist in the "Valid custom" "table_row"
+ And "Allow services" "icon" should exist in the "Valid custom" "table_row"
And "Service discovery successful" "icon" should exist in the "Valid custom" "table_row"
And I click on "Edit" "link" in the "Valid custom service" "table_row"
And I set the following fields to these values:
And I press "Continue"
And I should see "Identity issuer deleted"
And I should not see "Empty custom service"
+
+ Scenario: Create a standard service for Google and test form and UI for login only, services only and both
+ Given I press "Google"
+ And I should see "Create new service: Google"
+ # Create using 'Login page only' option.
+ And I set the following fields to these values:
+ | Name | Testing service |
+ | Client ID | thisistheclientid |
+ | Client secret | supersecret |
+ | This service will be used | Login page only |
+ When I press "Save changes"
+ Then I should see "Changes saved"
+ And I should see "Testing service"
+ And "Allow login" "icon" should exist in the "Testing service" "table_row"
+ And "Do not allow services" "icon" should exist in the "Testing service" "table_row"
+ And "Service discovery successful" "icon" should exist in the "Testing service" "table_row"
+ # Change to 'Internal services only'.
+ And I click on "Edit" "link" in the "Testing service" "table_row"
+ And I set the following fields to these values:
+ | This service will be used | Internal services only |
+ And I press "Save changes"
+ And I should see "Changes saved"
+ And "Do not allow login" "icon" should exist in the "Testing service" "table_row"
+ And "Allow services" "icon" should exist in the "Testing service" "table_row"
+ # Change to 'Login page and internal services' and add a display name.
+ And I click on "Edit" "link" in the "Testing service" "table_row"
+ And I set the following fields to these values:
+ | This service will be used | Login page and internal services |
+ | Name displayed on the login page | Google new display name |
+ And I press "Save changes"
+ And I should see "Changes saved"
+ And "Allow login" "icon" should exist in the "Testing service" "table_row"
+ And "Allow services" "icon" should exist in the "Testing service" "table_row"
+ And I should see "Google new display name" in the "Testing service" "table_row"
+
+ Scenario: Create a login page only custom OIDC service
+ Given I press "Custom"
+ And I should see "Create new service: Custom"
+ And I set the following fields to these values:
+ | Name | Empty custom service |
+ | Client ID | thisistheclientid |
+ | Client secret | supersecret |
+ | This service will be used | Login page only |
+ | Name displayed on the login page | Custom display name |
+ When I press "Save changes"
+ And I should see "Changes saved"
+ And I should see "Empty custom service"
+ And I should see "Custom display name" in the "Empty custom service" "table_row"
+ And "Not configured" "icon" should exist in the "Empty custom service" "table_row"
+ And "Do not allow services" "icon" should exist in the "Empty custom service" "table_row"
+ And I click on "Edit" "link" in the "Empty custom service" "table_row"
+ And I set the following fields to these values:
+ | Service base URL | https://accounts.google.com |
+ And I press "Save changes"
+ And "Allow login" "icon" should exist in the "Empty custom service" "table_row"
+ And "Do not allow services" "icon" should exist in the "Empty custom service" "table_row"
$this->standardfields = array('id', 'username', 'email', 'emailstop',
'city', 'country', 'lang', 'timezone', 'mailformat',
'maildisplay', 'maildigest', 'htmleditor', 'autosubscribe',
- 'institution', 'department', 'idnumber', 'skype',
- 'msn', 'aim', 'yahoo', 'icq', 'phone1', 'phone2', 'address',
- 'url', 'description', 'descriptionformat', 'password',
+ 'institution', 'department', 'idnumber', 'phone1', 'phone2', 'address',
+ 'description', 'descriptionformat', 'password',
'auth', // Watch out when changing auth type or using external auth plugins!
'oldusername', // Use when renaming users - this is the original username.
'suspended', // 1 means suspend user account, 0 means activate user account, nothing means keep as is.
$mform->addHelpButton('description', 'userdescription');
$mform->setAdvanced('description');
- $mform->addElement('text', 'url', get_string('webpage'), 'maxlength="255" size="50"');
- $mform->setType('url', PARAM_URL);
- $mform->setAdvanced('url');
-
$mform->addElement('text', 'idnumber', get_string('idnumber'), 'maxlength="255" size="25"');
$mform->setType('idnumber', core_user::get_property_type('idnumber'));
$mform->setForceLtr('idnumber');
$filename = 'tour_export_' . $tour->get_id() . '_' . time() . '.json';
// Force download.
- header('Last-Modified: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT');
- header('Cache-Control: private, must-revalidate, pre-check=0, post-check=0, max-age=0');
- header('Expires: ' . gmdate('D, d M Y H:i:s', 0) . 'GMT');
- header('Pragma: no-cache');
- header('Accept-Ranges: none');
- header('Content-disposition: attachment; filename=' . $filename);
- header('Content-length: ' . strlen($exportstring));
- header('Content-type: text/calendar; charset=utf-8');
-
- echo $exportstring;
- die;
+ send_file($exportstring, $filename, 0, 0, true, true);
}
/**
'phone1' => 'phone1',
'phone2' => 'phone2',
'city' => 'city',
- 'url' => 'url',
- 'icq' => 'icq',
- 'skype' => 'skype',
- 'aim' => 'aim',
- 'yahoo' => 'yahoo',
- 'msn' => 'msn',
'country' => 'country');
if ($extrafields = $DB->get_records('user_info_field')) {
\core\event\prediction_action_started::create($eventdata)->trigger();
}
+ /**
+ * Get the executed actions.
+ *
+ * Actions could be filtered by actionname.
+ *
+ * @param array $actionnamefilter Limit the results obtained to this list of action names.
+ * @param int $userid the user id. Current user by default.
+ * @return array of actions.
+ */
+ public function get_executed_actions(array $actionnamefilter = null, int $userid = 0): array {
+ global $USER, $DB;
+
+ $conditions[] = "predictionid = :predictionid";
+ $params['predictionid'] = $this->get_prediction_data()->id;
+ if (!$userid) {
+ $userid = $USER->id;
+ }
+ $conditions[] = "userid = :userid";
+ $params['userid'] = $userid;
+ if ($actionnamefilter) {
+ list($actionsql, $actionparams) = $DB->get_in_or_equal($actionnamefilter, SQL_PARAMS_NAMED);
+ $conditions[] = "actionname $actionsql";
+ $params = $params + $actionparams;
+ }
+ return $DB->get_records_select('analytics_prediction_actions', implode(' AND ', $conditions), $params);
+ }
+
/**
* format_calculations
*
$this->assertEquals(2, $DB->count_records('analytics_prediction_actions'));
}
+ /**
+ * Data provider for test_get_executed_actions.
+ *
+ * @return array
+ */
+ public function execute_actions_provider(): array {
+ return [
+ 'Empty actions with no filter' => [
+ [],
+ [],
+ 0
+ ],
+ 'Empty actions with filter' => [
+ [],
+ [\core_analytics\prediction::ACTION_FIXED],
+ 0
+ ],
+ 'Multiple actions with no filter' => [
+ [
+ \core_analytics\prediction::ACTION_FIXED,
+ \core_analytics\prediction::ACTION_FIXED,
+ \core_analytics\prediction::ACTION_INCORRECTLY_FLAGGED
+ ],
+ [],
+ 3
+ ],
+ 'Multiple actions applying filter' => [
+ [
+ \core_analytics\prediction::ACTION_FIXED,
+ \core_analytics\prediction::ACTION_FIXED,
+ \core_analytics\prediction::ACTION_INCORRECTLY_FLAGGED
+ ],
+ [\core_analytics\prediction::ACTION_FIXED],
+ 2
+ ],
+ 'Multiple actions not applying filter' => [
+ [
+ \core_analytics\prediction::ACTION_FIXED,
+ \core_analytics\prediction::ACTION_FIXED,
+ \core_analytics\prediction::ACTION_INCORRECTLY_FLAGGED
+ ],
+ [\core_analytics\prediction::ACTION_NOT_APPLICABLE],
+ 0
+ ],
+ 'Multiple actions with multiple filter' => [
+ [
+ \core_analytics\prediction::ACTION_FIXED,
+ \core_analytics\prediction::ACTION_FIXED,
+ \core_analytics\prediction::ACTION_INCORRECTLY_FLAGGED
+ ],
+ [\core_analytics\prediction::ACTION_FIXED, \core_analytics\prediction::ACTION_INCORRECTLY_FLAGGED],
+ 3
+ ],
+ ];
+ }
+
+ /**
+ * Tests for get_executed_actions() function.
+ *
+ * @dataProvider execute_actions_provider
+ * @param array $actionstoexecute An array of actions to execute
+ * @param array $actionnamefilter Actions to filter
+ * @param int $returned Number of actions returned
+ *
+ * @covers \core_analytics\prediction::get_executed_actions
+ */
+ public function test_get_executed_actions(array $actionstoexecute, array $actionnamefilter, int $returned) {
+
+ $this->setUser($this->teacher2);
+ list($ignored, $predictions) = $this->model->get_predictions($this->context, true);
+ $prediction = reset($predictions);
+ $target = $this->model->get_target();
+ foreach($actionstoexecute as $action) {
+ $prediction->action_executed($action, $target);
+ }
+
+ $filteredactions = $prediction->get_executed_actions($actionnamefilter);
+ $this->assertCount($returned, $filteredactions);
+ }
+
/**
* test_get_predictions
*/
by updating the lib/db/analytics.php file and bumping the core version.
* Final deprecation - get_analysables(). Please see get_analysables_interator() instead.
get_analysables_iterator() needs to be overridden by the child class.
+* A new function get_executed_actions() has been added to \core_analytics\prediction class
+ to get all (or filtered by action name) executed actions of a prediction
=== 3.8 ===
$externalfields = [
'address' => 'privacy:metadata:mnet_external:address',
- 'aim' => 'privacy:metadata:mnet_external:aim',
'alternatename' => 'privacy:metadata:mnet_external:alternatename',
'autosubscribe' => 'privacy:metadata:mnet_external:autosubscribe',
'calendartype' => 'privacy:metadata:mnet_external:calendartype',
'firstaccess' => 'privacy:metadata:mnet_external:firstaccess',
'firstname' => 'privacy:metadata:mnet_external:firstname',
'firstnamephonetic' => 'privacy:metadata:mnet_external:firstnamephonetic',
- 'icq' => 'privacy:metadata:mnet_external:icq',
'id' => 'privacy:metadata:mnet_external:id',
'idnumber' => 'privacy:metadata:mnet_external:idnumber',
'imagealt' => 'privacy:metadata:mnet_external:imagealt',
'maildigest' => 'privacy:metadata:mnet_external:maildigest',
'maildisplay' => 'privacy:metadata:mnet_external:maildisplay',
'middlename' => 'privacy:metadata:mnet_external:middlename',
- 'msn' => 'privacy:metadata:mnet_external:msn',
'phone1' => 'privacy:metadata:mnet_external:phone1',
'pnone2' => 'privacy:metadata:mnet_external:phone2',
'picture' => 'privacy:metadata:mnet_external:picture',
'policyagreed' => 'privacy:metadata:mnet_external:policyagreed',
- 'skype' => 'privacy:metadata:mnet_external:skype',
'suspended' => 'privacy:metadata:mnet_external:suspended',
'timezone' => 'privacy:metadata:mnet_external:timezone',
'trackforums' => 'privacy:metadata:mnet_external:trackforums',
'trustbitmask' => 'privacy:metadata:mnet_external:trustbitmask',
- 'url' => 'privacy:metadata:mnet_external:url',
'username' => 'privacy:metadata:mnet_external:username',
- 'yahoo' => 'privacy:metadata:mnet_external:yahoo',
];
$collection->add_external_location_link('moodle', $externalfields, 'privacy:metadata:external:moodle');
$string['privacy:metadata:external:mahara'] = 'This plugin can send data externally to a linked Mahara application.';
$string['privacy:metadata:external:moodle'] = 'This plugin can send data externally to a linked Moodle application.';
$string['privacy:metadata:mnet_external:address'] = 'The address of the user.';
-$string['privacy:metadata:mnet_external:aim'] = 'The AIM identifier of the user';
$string['privacy:metadata:mnet_external:alternatename'] = 'An alternative name for the user.';
$string['privacy:metadata:mnet_external:autosubscribe'] = 'A preference as to if the user should be auto-subscribed to forums the user posts in.';
$string['privacy:metadata:mnet_external:calendartype'] = 'A user preference for the type of calendar to use.';
$string['privacy:metadata:mnet_external:firstaccess'] = 'The time that this user first accessed the site.';
$string['privacy:metadata:mnet_external:firstname'] = 'The first name of the user.';
$string['privacy:metadata:mnet_external:firstnamephonetic'] = 'The phonetic details about the user\'s first name.';
-$string['privacy:metadata:mnet_external:icq'] = 'The ICQ number of the user.';
$string['privacy:metadata:mnet_external:id'] = 'The user ID';
$string['privacy:metadata:mnet_external:idnumber'] = 'An identification number given by the institution';
$string['privacy:metadata:mnet_external:imagealt'] = 'Alternative text for the user\'s image.';
$string['privacy:metadata:mnet_external:maildigest'] = 'A setting for the mail digest for this user.';
$string['privacy:metadata:mnet_external:maildisplay'] = 'A preference for the user about displaying their email address to other users.';
$string['privacy:metadata:mnet_external:middlename'] = 'The middle name of the user';
-$string['privacy:metadata:mnet_external:msn'] = 'The MSN identifier of the user';
$string['privacy:metadata:mnet_external:phone1'] = 'A phone number for the user.';
$string['privacy:metadata:mnet_external:phone2'] = 'An additional phone number for the user.';
$string['privacy:metadata:mnet_external:picture'] = 'The picture details associated with this user.';
$string['privacy:metadata:mnet_external:policyagreed'] = 'A flag to determine if the user has agreed to the site policy.';
-$string['privacy:metadata:mnet_external:skype'] = 'The Skype identifier of the user';
$string['privacy:metadata:mnet_external:suspended'] = 'A flag to show if the user has been suspended on this system.';
$string['privacy:metadata:mnet_external:timezone'] = 'The timezone of the user';
$string['privacy:metadata:mnet_external:trackforums'] = 'A preference for forums and tracking them.';
$string['privacy:metadata:mnet_external:trustbitmask'] = 'The trust bit mask';
-$string['privacy:metadata:mnet_external:url'] = 'A URL related to this user.';
$string['privacy:metadata:mnet_external:username'] = 'The username for this user.';
-$string['privacy:metadata:mnet_external:yahoo'] = 'The Yahoo identifier of the user';
$string['privacy:metadata:mnet_log'] = 'Details of remote actions carried out by a local user logged in a remote system.';
$string['privacy:metadata:mnet_log:action'] = 'Action carried out by the user.';
$string['privacy:metadata:mnet_log:cmid'] = 'ID of the course module.';
$string['privacy:metadata:mnet_session:useragent'] = 'User agent used to access the remote system';
$string['privacy:metadata:mnet_session:userid'] = 'ID of the user jumping to remote system.';
$string['privacy:metadata:mnet_session:username'] = 'Username of the user jumping to remote system.';
-$string['unknownhost'] = 'Unknown host';
\ No newline at end of file
+$string['unknownhost'] = 'Unknown host';
+
+// Deprecated since Moodle 4.0.
+$string['privacy:metadata:mnet_external:aim'] = 'The AIM identifier of the user';
+$string['privacy:metadata:mnet_external:icq'] = 'The ICQ number of the user.';
+$string['privacy:metadata:mnet_external:msn'] = 'The MSN identifier of the user';
+$string['privacy:metadata:mnet_external:skype'] = 'The Skype identifier of the user';
+$string['privacy:metadata:mnet_external:url'] = 'A URL related to this user.';
+$string['privacy:metadata:mnet_external:yahoo'] = 'The Yahoo identifier of the user';
$user->mnethostid = $CFG->mnet_localhost_id;
$user->lastname = isset($userinfo['lastname']) ? $userinfo['lastname'] : '';
$user->firstname = isset($userinfo['firstname']) ? $userinfo['firstname'] : '';
- $user->url = isset($userinfo['url']) ? $userinfo['url'] : '';
$user->alternatename = isset($userinfo['alternatename']) ? $userinfo['alternatename'] : '';
$user->secret = random_string(15);
$user->mnethostid = $CFG->mnet_localhost_id;
$user->lastname = isset($userinfo['lastname']) ? $userinfo['lastname'] : '';
$user->firstname = isset($userinfo['firstname']) ? $userinfo['firstname'] : '';
- $user->url = isset($userinfo['url']) ? $userinfo['url'] : '';
$user->alternatename = isset($userinfo['alternatename']) ? $userinfo['alternatename'] : '';
$user->secret = random_string(15);
return false;
}
- /**
- * Do some checks on the identity provider before showing it on the login page.
- * @param core\oauth2\issuer $issuer
- * @return boolean
- */
- private function is_ready_for_login_page(\core\oauth2\issuer $issuer) {
- return $issuer->get('enabled') &&
- $issuer->is_configured() &&
- !empty($issuer->get('showonloginpage'));
- }
-
/**
* Return a list of identity providers to display on the login page.
*
* @return array List of arrays with keys url, iconurl and name.
*/
public function loginpage_idp_list($wantsurl) {
- $providers = \core\oauth2\api::get_all_issuers();
+ $providers = \core\oauth2\api::get_all_issuers(true);
$result = [];
if (empty($wantsurl)) {
$wantsurl = '/';
}
foreach ($providers as $idp) {
- if ($this->is_ready_for_login_page($idp)) {
+ if ($idp->is_available_for_login()) {
$params = ['id' => $idp->get('id'), 'wantsurl' => $wantsurl, 'sesskey' => sesskey()];
$url = new moodle_url('/auth/oauth2/login.php', $params);
$icon = $idp->get('image');
- $result[] = ['url' => $url, 'iconurl' => $icon, 'name' => $idp->get('name')];
+ $result[] = ['url' => $url, 'iconurl' => $icon, 'name' => $idp->get_display_name()];
}
}
return $result;
use html_table_cell;
use html_table_row;
use html_writer;
-use auth\oauth2\linked_login;
+use auth_oauth2\linked_login;
use moodle_url;
defined('MOODLE_INTERNAL') || die();
/**
* This function will render one beautiful table with all the linked_logins.
*
- * @param \auth\oauth2\linked_login[] $linkedlogins - list of all linked logins.
+ * @param linked_login[] $linkedlogins - list of all linked logins.
* @return string HTML to output.
*/
public function linked_logins_table($linkedlogins) {
$issuerid = required_param('issuerid', PARAM_INT);
$issuer = \core\oauth2\api::get_issuer($issuerid);
- if (!$issuer->is_authentication_supported() || !$issuer->get('showonloginpage') || !$issuer->get('enabled')) {
+ if (!$issuer->is_available_for_login()) {
throw new \moodle_exception('issuernologin', 'auth_oauth2');
}
auth_oauth2\api::clean_orphaned_linked_logins();
-$issuers = \core\oauth2\api::get_all_issuers();
+$issuers = \core\oauth2\api::get_all_issuers(true);
$anyshowinloginpage = false;
$issuerbuttons = array();
foreach ($issuers as $issuer) {
- if (!$issuer->is_authentication_supported() || !$issuer->get('showonloginpage') || !$issuer->get('enabled')) {
+ if (!$issuer->is_available_for_login()) {
continue;
}
$anyshowinloginpage = true;
$addparams = ['action' => 'new', 'issuerid' => $issuer->get('id'), 'sesskey' => sesskey(), 'logout' => true];
$addurl = new moodle_url('/auth/oauth2/linkedlogins.php', $addparams);
- $issuerbuttons[$issuer->get('id')] = $renderer->single_button($addurl, get_string('createnewlinkedlogin', 'auth_oauth2', s($issuer->get('name'))));
+ $issuerbuttons[$issuer->get('id')] = $renderer->single_button($addurl, get_string('createnewlinkedlogin', 'auth_oauth2',
+ s($issuer->get_display_name())));
}
if (!$anyshowinloginpage) {
}
$issuer = new \core\oauth2\issuer($issuerid);
+if (!$issuer->is_available_for_login()) {
+ throw new \moodle_exception('issuernologin', 'auth_oauth2');
+}
$returnparams = ['wantsurl' => $wantsurl, 'sesskey' => sesskey(), 'id' => $issuerid];
$returnurl = new moodle_url('/auth/oauth2/login.php', $returnparams);
'email' => \core_user\fields::get_display_name('email'),
'city' => \core_user\fields::get_display_name('city'),
'country' => \core_user\fields::get_display_name('country'),
- 'url' => \core_user\fields::get_display_name('url'),
- 'icq' => \core_user\fields::get_display_name('icq'),
- 'skype' => \core_user\fields::get_display_name('skype'),
- 'aim' => \core_user\fields::get_display_name('aim'),
- 'yahoo' => \core_user\fields::get_display_name('yahoo'),
- 'msn' => \core_user\fields::get_display_name('msn'),
'idnumber' => \core_user\fields::get_display_name('idnumber'),
'institution' => \core_user\fields::get_display_name('institution'),
'department' => \core_user\fields::get_display_name('department'),
$this->add_step(new backup_course_logs_structure_step('course_logs', 'logs.xml'));
// New log stores.
$this->add_step(new backup_course_logstores_structure_step('course_logstores', 'logstores.xml'));
+ // Last access to course logs.
+ $this->add_step(new backup_course_loglastaccess_structure_step('course_loglastaccess', 'loglastaccess.xml'));
}
// Generate the course competencies.
$this->add_setting($roleassignments);
$users->add_dependency($roleassignments);
+ // Define permission.
+ if ($this->plan->get_mode() == backup::MODE_IMPORT) {
+ $permissions = new backup_permissions_setting('permissions', base_setting::IS_BOOLEAN, false);
+ $permissions->set_ui(new backup_setting_ui_checkbox($permissions, get_string('rootsettingpermissions', 'backup')));
+ $this->add_setting($permissions);
+ }
+
// Define activities
$activities = new backup_activities_setting('activities', base_setting::IS_BOOLEAN, true);
$activities->set_ui(new backup_setting_ui_checkbox($activities, get_string('rootsettingactivities', 'backup')));
*/
class backup_users_setting extends backup_generic_setting {}
+/**
+ * root setting to control if backup will include permission information by roles
+ */
+class backup_permissions_setting extends backup_generic_setting {
+}
+
/**
* root setting to control if backup will include group information depends on @backup_users_setting
*
// Then, the fields potentially needing anonymization
$anonfields = array(
- 'username', 'idnumber', 'email', 'icq', 'skype',
- 'yahoo', 'aim', 'msn', 'phone1',
+ 'username', 'idnumber', 'email', 'phone1',
'phone2', 'institution', 'department', 'address',
'city', 'country', 'lastip', 'picture',
- 'url', 'description', 'descriptionformat', 'imagealt', 'auth');
+ 'description', 'descriptionformat', 'imagealt', 'auth');
$anonfields = array_merge($anonfields, \core_user\fields::get_name_fields());
// Add anonymized fields to $userfields with custom final element
}
}
+/**
+ * Structure step in charge of constructing the loglastaccess.xml file for the course logs.
+ *
+ * This backup step will backup the logs of the user_lastaccess table.
+ */
+class backup_course_loglastaccess_structure_step extends backup_structure_step {
+
+ /**
+ * This function creates the structures for the loglastaccess.xml file.
+ * Expected structure would look like this.
+ * <loglastaccesses>
+ * <loglastaccess id=2>
+ * <userid>5</userid>
+ * <timeaccess>1616887341</timeaccess>
+ * </loglastaccess>
+ * </loglastaccesses>
+ *
+ * @return backup_nested_element
+ */
+ protected function define_structure() {
+
+ // To know if we are including userinfo.
+ $userinfo = $this->get_setting_value('users');
+
+ // Define the structure of logstores container.
+ $lastaccesses = new backup_nested_element('lastaccesses');
+ $lastaccess = new backup_nested_element('lastaccess', array('id'), array('userid', 'timeaccess'));
+
+ // Define build tree.
+ $lastaccesses->add_child($lastaccess);
+
+ // This element should only happen if we are including user info.
+ if ($userinfo) {
+ // Define sources.
+ $lastaccess->set_source_sql('
+ SELECT id, userid, timeaccess
+ FROM {user_lastaccess}
+ WHERE courseid = ?',
+ array(backup::VAR_COURSEID));
+
+ // Define userid annotation to user.
+ $lastaccess->annotate_ids('user', 'userid');
+ }
+
+ // Return the root element (lastaccessess).
+ return $lastaccesses;
+ }
+}
+
/**
* Structure step in charge of constructing the logstores.xml file for the activity logs.
*
$this->add_step(new restore_course_logs_structure_step('course_logs', 'course/logs.xml'));
// New log stores.
$this->add_step(new restore_course_logstores_structure_step('course_logstores', 'course/logstores.xml'));
+ // Last access to course logs.
+ $this->add_step(new restore_course_loglastaccess_structure_step('course_loglastaccess', 'course/loglastaccess.xml'));
}
// Review all the executed tasks having one after_restore method
$this->add_setting($roleassignments);
$users->add_dependency($roleassignments);
+ // Define permissions.
+ $defaultvalue = false; // Safer default.
+ $changeable = false;
+ // Enable when available, or key doesn't exist (backward compatibility).
+ if (!array_key_exists('permissions', $rootsettings) || !empty($rootsettings['permissions'])) {
+ $defaultvalue = true;
+ $changeable = true;
+ }
+ $permissions = new restore_permissions_setting('permissions', base_setting::IS_BOOLEAN, $defaultvalue);
+ $permissions->set_ui(new backup_setting_ui_checkbox($permissions, get_string('rootsettingpermissions', 'backup')));
+ $permissions->get_ui()->set_changeable($changeable);
+ $this->add_setting($permissions);
+
// Define activitites
$defaultvalue = false; // Safer default
$changeable = false;
*/
class restore_users_setting extends restore_generic_setting {}
+/**
+ * root setting to control if restore will create override permission information by roles
+ */
+class restore_permissions_setting extends restore_generic_setting {
+}
+
/**
* root setting to control if restore will create groups/grouping information. Depends on @restore_users_setting
*
if ($this->get_setting_value('role_assignments')) {
$paths[] = new restore_path_element('assignment', '/roles/role_assignments/assignment');
}
- $paths[] = new restore_path_element('override', '/roles/role_overrides/override');
+ if ($this->get_setting_value('permissions')) {
+ $paths[] = new restore_path_element('override', '/roles/role_overrides/override');
+ }
return $paths;
}
}
}
+/**
+ * Structure step in charge of restoring the loglastaccess.xml file for the course logs.
+ *
+ * This restore step will rebuild the table for user_lastaccess table.
+ */
+class restore_course_loglastaccess_structure_step extends restore_structure_step {
+
+ /**
+ * Conditionally decide if this step should be executed.
+ *
+ * This function checks the following parameter:
+ *
+ * 1. the loglastaccess.xml file exists
+ *
+ * @return bool true is safe to execute, false otherwise
+ */
+ protected function execute_condition() {
+ // Check it is included in the backup.
+ $fullpath = $this->task->get_taskbasepath();
+ $fullpath = rtrim($fullpath, '/') . '/' . $this->filename;
+ if (!file_exists($fullpath)) {
+ // Not found, can't restore loglastaccess.xml information.
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Return the elements to be processed on restore of loglastaccess.
+ *
+ * @return restore_path_element[] array of elements to be processed on restore.
+ */
+ protected function define_structure() {
+
+ $paths = array();
+ // To know if we are including userinfo.
+ $userinfo = $this->get_setting_value('users');
+
+ if ($userinfo) {
+ $paths[] = new restore_path_element('lastaccess', '/lastaccesses/lastaccess');
+ }
+ // Return the paths wrapped.
+ return $paths;
+ }
+
+ /**
+ * Process the 'lastaccess' elements.
+ *
+ * @param array $data element data
+ */
+ protected function process_lastaccess($data) {
+ global $DB;
+
+ $data = (object)$data;
+
+ $data->courseid = $this->get_courseid();
+ if (!$data->userid = $this->get_mappingid('user', $data->userid)) {
+ return; // Nothing to do, not able to find the user to set the lastaccess time.
+ }
+
+ // Check if record does exist.
+ $exists = $DB->get_record('user_lastaccess', array('courseid' => $data->courseid, 'userid' => $data->userid));
+ if ($exists) {
+ // If the time of last access of the restore is newer, then replace and update.
+ if ($exists->timeaccess < $data->timeaccess) {
+ $exists->timeaccess = $data->timeaccess;
+ $DB->update_record('user_lastaccess', $exists);
+ }
+ } else {
+ $DB->insert_record('user_lastaccess', $data);
+ }
+ }
+}
+
/**
* Structure step in charge of restoring the logstores.xml file for the activity logs.
*
$user->id
);
$controller->execute_plan();
+ $controller->destroy(); // Unset all structures, close files...
return $controller->get_backupid();
}
--- /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/>.
+
+/**
+ * Backup restore base tests.
+ *
+ * @package core_backup
+ * @copyright Tomo Tsuyuki <tomotsuyuki@catalyst-au.net>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
+require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
+
+/**
+ * Basic testcase class for backup / restore functionality.
+ */
+abstract class core_backup_backup_restore_base_testcase extends advanced_testcase {
+
+ /**
+ * Setup test data.
+ */
+ protected function setUp(): void {
+ $this->resetAfterTest();
+ $this->setAdminUser();
+ }
+
+ /**
+ * Backup the course by general mode.
+ *
+ * @param stdClass $course Course for backup.
+ * @return string Hash string ID from the backup.
+ * @throws coding_exception
+ * @throws moodle_exception
+ */
+ protected function perform_backup($course): string {
+ global $CFG, $USER;
+
+ $coursecontext = context_course::instance($course->id);
+
+ // Start backup process.
+ $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE,
+ backup::INTERACTIVE_NO, backup::MODE_GENERAL, $USER->id);
+ $bc->execute_plan();
+ $backupid = $bc->get_backupid();
+ $bc->destroy();
+
+ // Get the backup file.
+ $fs = get_file_storage();
+ $files = $fs->get_area_files($coursecontext->id, 'backup', 'course', false, 'id ASC');
+ $backupfile = reset($files);
+
+ // Extract backup file.
+ $path = $CFG->tempdir . DIRECTORY_SEPARATOR . "backup" . DIRECTORY_SEPARATOR . $backupid;
+
+ $fp = get_file_packer('application/vnd.moodle.backup');
+ $fp->extract_to_pathname($backupfile, $path);
+
+ return $backupid;
+ }
+
+ /**
+ * Restore from backupid to course.
+ *
+ * @param string $backupid Hash string ID from backup.
+ * @param stdClass $course Course which is restored for.
+ * @throws restore_controller_exception
+ */
+ protected function perform_restore($backupid, $course): void {
+ global $USER;
+
+ // Set up restore.
+ $rc = new restore_controller($backupid, $course->id,
+ backup::INTERACTIVE_NO, backup::MODE_GENERAL, $USER->id, backup::TARGET_EXISTING_ADDING);
+ // Execute restore.
+ $rc->execute_precheck();
+ $rc->execute_plan();
+ $rc->destroy();
+ }
+
+ /**
+ * Import course from course1 to course2.
+ *
+ * @param stdClass $course1 Course to be backuped up.
+ * @param stdClass $course2 Course to be restored.
+ * @throws restore_controller_exception
+ */
+ protected function perform_import($course1, $course2): void {
+ global $USER;
+
+ // Start backup process.
+ $bc = new backup_controller(backup::TYPE_1COURSE, $course1->id, backup::FORMAT_MOODLE,
+ backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id);
+ $backupid = $bc->get_backupid();
+ $bc->execute_plan();
+ $bc->destroy();
+
+ // Set up restore.
+ $rc = new restore_controller($backupid, $course2->id,
+ backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id, backup::TARGET_EXISTING_ADDING);
+ // Execute restore.
+ $rc->execute_precheck();
+ $rc->execute_plan();
+ $rc->destroy();
+ }
+
+}
--- /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/>.
+
+/**
+ * Backup restore permission tests.
+ *
+ * @package core_backup
+ * @copyright Tomo Tsuyuki <tomotsuyuki@catalyst-au.net>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once('backup_restore_base_testcase.php');
+require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
+require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
+
+/**
+ * Testcase class for permission backup / restore functionality.
+ */
+class core_backup_backup_restore_permission_testcase extends core_backup_backup_restore_base_testcase {
+
+ /** @var stdClass A test course which is restored/imported from. */
+ protected $course1;
+
+ /** @var stdClass A test course which is restored/imported to. */
+ protected $course2;
+
+ /** @var stdClass A user for using in this test. */
+ protected $user;
+
+ /** @var string Capability name for using in this test. */
+ protected $capabilityname;
+
+ /** @var context_course Context instance for course1. */
+ protected $course1context;
+
+ /** @var context_course Context instance for course2. */
+ protected $course2context;
+
+ /**
+ * Setup test data.
+ */
+ protected function setUp(): void {
+ global $DB;
+
+ parent::setUp();
+ // Create a course with some availability data set.
+ $generator = $this->getDataGenerator();
+ $this->course1 = $generator->create_course();
+ $this->course1context = context_course::instance($this->course1->id);
+ $this->course2 = $generator->create_course();
+ $this->course2context = context_course::instance($this->course2->id);
+ $this->capabilityname = 'enrol/manual:enrol';
+ $this->user = $generator->create_user();
+
+ // Set additional permission for course 1.
+ $teacherrole = $DB->get_record('role', ['shortname' => 'teacher'], '*', MUST_EXIST);
+ role_change_permission($teacherrole->id, $this->course1context, $this->capabilityname, CAP_ALLOW);
+
+ // Enrol to the courses.
+ $generator->enrol_user($this->user->id, $this->course1->id, $teacherrole->id);
+ $generator->enrol_user($this->user->id, $this->course2->id, $teacherrole->id);
+ }
+
+ /**
+ * Test having settings.
+ */
+ public function test_having_settings(): void {
+ $this->assertEquals(0, get_config('backup', 'backup_import_permissions'));
+ $this->assertEquals(1, get_config('restore', 'restore_general_permissions'));
+ }
+
+ /**
+ * Test for restore with permission.
+ */
+ public function test_backup_restore_with_permission(): void {
+
+ // Set default setting to restore with permission.
+ set_config('restore_general_permissions', 1, 'restore');
+
+ // Confirm course1 has the capability for the user.
+ $this->assertTrue(has_capability($this->capabilityname, $this->course1context, $this->user));
+
+ // Confirm course2 does not have the capability for the user.
+ $this->assertFalse(has_capability($this->capabilityname, $this->course2context, $this->user));
+
+ // Perform backup and restore.
+ $backupid = $this->perform_backup($this->course1);
+ $this->perform_restore($backupid, $this->course2);
+
+ // Confirm course2 has the capability for the user.
+ $this->assertTrue(has_capability($this->capabilityname, $this->course2context, $this->user));
+ }
+
+ /**
+ * Test for backup / restore without restore permission.
+ */
+ public function test_backup_restore_without_permission(): void {
+
+ // Set default setting to restore without permission.
+ set_config('restore_general_permissions', 0, 'restore');
+
+ // Perform backup and restore.
+ $backupid = $this->perform_backup($this->course1);
+ $this->perform_restore($backupid, $this->course2);
+
+ // Confirm course2 does not have the capability for the user.
+ $this->assertFalse(has_capability($this->capabilityname, $this->course2context, $this->user));
+ }
+
+ /**
+ * Test for import with permission.
+ */
+ public function test_backup_import_with_permission(): void {
+
+ // Set default setting to restore with permission.
+ set_config('backup_import_permissions', 1, 'backup');
+
+ // Perform import.
+ $this->perform_import($this->course1, $this->course2);
+
+ // Confirm course2 does not have the capability for the user.
+ $this->assertTrue(has_capability($this->capabilityname, $this->course2context, $this->user));
+ }
+
+ /**
+ * Test for import without permission.
+ */
+ public function test_backup_import_without_permission(): void {
+
+ // Set default setting to restore without permission.
+ set_config('backup_import_permissions', 0, 'backup');
+
+ // Perform import.
+ $this->perform_import($this->course1, $this->course2);
+
+ // Confirm course2 does not have the capability for the user.
+ $this->assertFalse(has_capability($this->capabilityname, $this->course2context, $this->user));
+ }
+
+}
This files describes API changes in /backup/*,
information provided here is intended especially for developers.
-=== 4.0 ===
+=== 3.11 ===
+
+ * New setting called "Include override permissions" has been implemented. The default
+ settings is OFF for import, and ON for restore.
+
+=== 3.10 ===
+
* Local plugins can now hook into a backup and restore process of grade items by
using define_grade_item_plugin_structure method (See MDL-69418).
'backup_import_blocks' => 'blocks',
'backup_import_filters' => 'filters',
'backup_import_calendarevents' => 'calendarevents',
+ 'backup_import_permissions' => 'permissions',
'backup_import_questionbank' => 'questionbank',
'backup_import_groups' => 'groups',
'backup_import_competencies' => 'competencies',
'restore_general_users' => 'users',
'restore_general_enrolments' => 'enrolments',
'restore_general_role_assignments' => 'role_assignments',
+ 'restore_general_permissions' => 'permissions',
'restore_general_activities' => 'activities',
'restore_general_blocks' => 'blocks',
'restore_general_filters' => 'filters',
return 'anon' . $counter . '@doesntexist.invalid'; // Just a counter.
}
- public static function process_user_icq($value) {
- return ''; // Clean icq
- }
-
- public static function process_user_skype($value) {
- return ''; // Clean skype
- }
-
- public static function process_user_yahoo($value) {
- return ''; // Clean yahoo
- }
-
- public static function process_user_aim($value) {
- return ''; // Clean aim
- }
-
- public static function process_user_msn($value) {
- return ''; // Clean msn
- }
-
public static function process_user_phone1($value) {
return ''; // Clean phone1
}
return 0; // No picture
}
- public static function process_user_url($value) {
- return ''; // No url
- }
-
public static function process_user_description($value) {
return ''; // No user description
}
-@core @core_backup @core_contentbank
+@core @core_backup @core_contentbank @core_h5p @contenttype_h5p @_file_upload @javascript
Feature: Import course content bank content
In order to import content from a course contentbank
As a teacher
I need to confirm that errors will not happen
Background:
- Given the following "courses" exist:
+ Given I log in as "admin"
+ And I am on site homepage
+ And I turn editing mode on
+ And I add the "Navigation" block if not present
+ And I configure the "Navigation" block
+ And I set the following fields to these values:
+ | Page contexts | Display throughout the entire site |
+ And I press "Save changes"
+ And I navigate to "H5P > Manage H5P content types" in site administration
+ And I upload "h5p/tests/fixtures/ipsums.h5p" file to "H5P content type" filemanager
+ And I click on "Upload H5P content types" "button" in the "#fitem_id_uploadlibraries" "css_element"
+ And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
| Course 2 | C2 | 0 |
| teacher1 | C1 | editingteacher |
| teacher1 | C2 | editingteacher |
And the following "contentbank content" exist:
- | contextlevel | reference | contenttype | user | contentname |
- | Course | C1 | contenttype_h5p | teacher1 | ipsums.h5p |
+ | contextlevel | reference | contenttype | user | contentname | filepath |
+ | Course | C1 | contenttype_h5p | teacher1 | ipsums.h5p | /h5p/tests/fixtures/ipsums.h5p |
+ And I log out
And I log in as "teacher1"
Scenario: Import content bank content to another course
Given I am on "Course 2" course homepage
+ And I expand "Site pages" node
And I click on "Content bank" "link"
And I should not see "ipsums.h5p"
When I import "Course 1" course into "Course 2" course using this options:
+ And I expand "Site pages" node
And I click on "Content bank" "link"
Then I should see "ipsums.h5p"
And I am on "Course 1" course homepage
+ And I expand "Site pages" node
And I click on "Content bank" "link"
And I should see "ipsums.h5p"
Scenario: User could configure not to import content bank
Given I am on "Course 2" course homepage
+ And I expand "Site pages" node
And I click on "Content bank" "link"
And I should not see "ipsums.h5p"
When I import "Course 1" course into "Course 2" course using this options:
| Initial | Include content bank content | 0 |
+ And I expand "Site pages" node
And I click on "Content bank" "link"
Then I should not see "ipsums.h5p"
And I am on "Course 1" course homepage
+ And I expand "Site pages" node
And I click on "Content bank" "link"
And I should see "ipsums.h5p"
As a teacher
I need to import a course contents into another course selecting what I want to import
- Scenario: Import course's contents to another course
+ Background:
Given the following "courses" exist:
| fullname | shortname | category |
- | Course 1 | C1 | 0 |
- | Course 2 | C2 | 0 |
+ | Course 1 | C1 | 0 |
+ | Course 2 | C2 | 0 |
And the following "users" exist:
- | username | firstname | lastname | email |
- | teacher1 | Teacher | 1 | teacher1@example.com |
+ | username | firstname | lastname | email |
+ | teacher1 | Teacher | 1 | teacher1@example.com |
And the following "course enrolments" exist:
- | user | course | role |
- | teacher1 | C1 | editingteacher |
- | teacher1 | C2 | editingteacher |
- And I log in as "teacher1"
+ | user | course | role |
+ | teacher1 | C1 | editingteacher |
+ | teacher1 | C2 | editingteacher |
+
+ Scenario: Import course's contents to another course
+ Given I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
And I add a "Database" to section "1" and I fill the form with:
| Name | Test database name |
And I should see "Test forum name"
And I should see "Comments" in the "Comments" "block"
And I should see "Recent blog entries"
+
+ Scenario: Import process with permission option
+ Given the following "permission overrides" exist:
+ | capability | permission | role | contextlevel | reference |
+ | enrol/manual:enrol | Allow | teacher | Course | C1 |
+ And I log in as "teacher1"
+ When I import "Course 1" course into "Course 2" course using this options:
+ | Initial | Include override permissions | 1 |
+ And I navigate to "Users > Permissions" in current page administration
+ Then I should see "Non-editing teacher (1)"
+ And I set the field "Advanced role override" to "Non-editing teacher (1)"
+ And I press "Go"
+ And "enrol/manual:enrol" capability has "Allow" permission
+
+ Scenario: Import process without permission option
+ Given the following "permission overrides" exist:
+ | capability | permission | role | contextlevel | reference |
+ | enrol/manual:enrol | Allow | teacher | Course | C1 |
+ And I log in as "teacher1"
+ When I import "Course 1" course into "Course 2" course using this options:
+ | Initial | Include override permissions | 0 |
+ And I navigate to "Users > Permissions" in current page administration
+ Then I should see "Non-editing teacher (0)"
And I should not see "Topic 16"
And I should see "Test URL name" in the "Topic 3" "section"
And I should see "Test forum name" in the "Topic 1" "section"
+
+ @javascript
+ Scenario: Restore a backup with override permission
+ Given the following "permission overrides" exist:
+ | capability | permission | role | contextlevel | reference |
+ | enrol/manual:enrol | Allow | teacher | Course | C1 |
+ And I backup "Course 1" course using this options:
+ | Confirmation | Filename | test_backup.mbz |
+ When I restore "test_backup.mbz" backup into a new course using this options:
+ | Settings | Include override permissions | 1 |
+ Then I navigate to "Users > Permissions" in current page administration
+ And I should see "Non-editing teacher (1)"
+ And I set the field "Advanced role override" to "Non-editing teacher (1)"
+ And "enrol/manual:enrol" capability has "Allow" permission
+
+ @javascript
+ Scenario: Restore a backup without override permission
+ Given the following "permission overrides" exist:
+ | capability | permission | role | contextlevel | reference |
+ | enrol/manual:enrol | Allow | teacher | Course | C1 |
+ And I backup "Course 1" course using this options:
+ | Confirmation | Filename | test_backup.mbz |
+ When I restore "test_backup.mbz" backup into a new course using this options:
+ | Settings | Include override permissions | 0 |
+ Then I navigate to "Users > Permissions" in current page administration
+ And I should see "Non-editing teacher (0)"
$returnurl = new moodle_url('/badges/backpack-connect.php',
['action' => 'authorization', 'sesskey' => sesskey(), 'backpackid' => $backpackid]);
+ // If scope is not passed as parameter, use the issuer supported scopes.
+ if (empty($scope)) {
+ $scope = $issuer->get('scopessupported');
+ }
$client = new core_badges\oauth2\client($issuer, $returnurl, $scope, $externalbackpack);
if ($client) {
if (!$client->is_logged_in()) {
$callbackurl = self::callback_url();
if ($granttype == 'authorization_code') {
+ $this->basicauth = true;
$params = array('code' => $code,
'grant_type' => $granttype,
'redirect_uri' => $callbackurl->out(false),
);
}
if ($this->basicauth) {
- $idsecret = urlencode($this->clientid) . ':' . urlencode($this->clientsecret);
+ $idsecret = $this->clientid . ':' . $this->clientsecret;
$this->setHeader('Authorization: Basic ' . base64_encode($idsecret));
} else {
$params['client_id'] = $this->clientid;
}
// Requests can either use http GET or POST.
$response = $this->post($this->token_url(), $this->build_post_data($params));
- $r = json_decode($response);
if ($this->info['http_code'] !== 200) {
- throw new moodle_exception('Could not upgrade oauth token');
+ $debuginfo = !empty($this->error) ? $this->error : $response;
+ throw new moodle_exception('oauth2refreshtokenerror', 'core_error', '', $this->info['http_code'], $debuginfo);
}
+ $r = json_decode($response);
+
if (is_null($r)) {
throw new moodle_exception("Could not decode JSON token response");
}
$missing = array();
// Note: cannot use user_get_default_fields() here because it is not possible to decide which fields user can modify.
- $dfields = array('firstname', 'lastname', 'email', 'address', 'phone1', 'phone2', 'icq', 'skype', 'yahoo',
- 'aim', 'msn', 'department', 'institution', 'description', 'picture', 'city', 'url', 'country');
+ $dfields = array('firstname', 'lastname', 'email', 'address', 'phone1', 'phone2',
+ 'department', 'institution', 'description', 'picture', 'city', 'country');
$sql = "SELECT uf.id as fieldid, uf.name as name, ic.id as categoryid, ic.name as categoryname, uf.datatype
FROM {user_info_field} uf
if (!empty($CFG->badges_allowexternalbackpack) && ($expiration > $now)
&& $userbackpack = badges_get_user_backpack($USER->id)) {
- $assertion = null;
if (badges_open_badges_backpack_api($userbackpack->id) == OPEN_BADGES_V2P1) {
$assertion = new moodle_url('/badges/backpack-export.php', array('hash' => $ibadge->hash));
} else {
$assertion = new moodle_url('/badges/backpack-add.php', array('hash' => $ibadge->hash));
}
- if (!is_null(assertion)) {
- $attributes = ['class' => 'btn btn-secondary m-1', 'role' => 'button'];
- $tobackpack = html_writer::link($assertion, get_string('addtobackpack', 'badges'), $attributes);
- $output .= $tobackpack;
- }
+ $attributes = ['class' => 'btn btn-secondary m-1', 'role' => 'button'];
+ $tobackpack = html_writer::link($assertion, get_string('addtobackpack', 'badges'), $attributes);
+ $output .= $tobackpack;
}
}
$output .= html_writer::end_tag('div');
$criteria_overall = award_criteria::build(array('criteriatype' => BADGE_CRITERIA_TYPE_OVERALL, 'badgeid' => $badge->id));
$criteria_overall->save(array('agg' => BADGE_CRITERIA_AGGREGATION_ANY));
$criteria_overall1 = award_criteria::build(array('criteriatype' => BADGE_CRITERIA_TYPE_PROFILE, 'badgeid' => $badge->id));
- $criteria_overall1->save(array('agg' => BADGE_CRITERIA_AGGREGATION_ALL, 'field_address' => 'address', 'field_aim' => 'aim',
- 'field_' . $customprofileid => $customprofileid));
+ $criteria_overall1->save(array('agg' => BADGE_CRITERIA_AGGREGATION_ALL, 'field_address' => 'address',
+ 'field_department' => 'department', 'field_' . $customprofileid => $customprofileid));
// Assert the badge will not be issued to the user as is.
$badge = new badge($this->coursebadge);
// Set the required fields and make sure the badge got issued.
$this->user->address = 'Test address';
- $this->user->aim = '999999999';
+ $this->user->department = 'sillywalks';
$sink = $this->redirectEmails();
profile_save_data((object)array('id' => $this->user->id, 'profile_field_newfield' => 'X'));
user_update_user($this->user, false);
$data->useremail = obfuscate_mailto($USER->email, '');
}
- if (!empty($this->config->display_icq) && !empty($USER->icq)) {
- $data->usericq = s($USER->icq);
- }
-
- if (!empty($this->config->display_skype) && !empty($USER->skype)) {
- $data->userskype = s($USER->skype);
- }
-
- if (!empty($this->config->display_yahoo) && !empty($USER->yahoo)) {
- $data->useryahoo = s($USER->yahoo);
- }
-
- if (!empty($this->config->display_aim) && !empty($USER->aim)) {
- $data->useraim = s($USER->aim);
- }
-
- if (!empty($this->config->display_msn) && !empty($USER->msn)) {
- $data->usermsn = s($USER->msn);
- }
-
if (!empty($this->config->display_phone1) && !empty($USER->phone1)) {
$data->userphone1 = s($USER->phone1);
}
$mform->setDefault('config_display_email', '1');
}
- $mform->addElement('selectyesno', 'config_display_icq', get_string('display_icq', 'block_myprofile'));
- if (isset($this->block->config->display_icq)) {
- $mform->setDefault('config_display_icq', $this->block->config->display_icq);
- } else {
- $mform->setDefault('config_display_icq', '0');
- }
-
- $mform->addElement('selectyesno', 'config_display_skype', get_string('display_skype', 'block_myprofile'));
- if (isset($this->block->config->display_skype)) {
- $mform->setDefault('config_display_skype', $this->block->config->display_skype);
- } else {
- $mform->setDefault('config_display_skype', '0');
- }
-
- $mform->addElement('selectyesno', 'config_display_yahoo', get_string('display_yahoo', 'block_myprofile'));
- if (isset($this->block->config->display_yahoo)) {
- $mform->setDefault('config_display_yahoo', $this->block->config->display_yahoo);
- } else {
- $mform->setDefault('config_display_yahoo', '0');
- }
-
- $mform->addElement('selectyesno', 'config_display_aim', get_string('display_aim', 'block_myprofile'));
- if (isset($this->block->config->display_aim)) {
- $mform->setDefault('config_display_aim', $this->block->config->display_aim);
- } else {
- $mform->setDefault('config_display_aim', '0');
- }
-
- $mform->addElement('selectyesno', 'config_display_msn', get_string('display_msn', 'block_myprofile'));
- if (isset($this->block->config->display_msn)) {
- $mform->setDefault('config_display_msn', $this->block->config->display_msn);
- } else {
- $mform->setDefault('config_display_msn', '0');
- }
-
$mform->addElement('selectyesno', 'config_display_phone1', get_string('display_phone1', 'block_myprofile'));
if (isset($this->block->config->display_phone1)) {
$mform->setDefault('config_display_phone1', $this->block->config->display_phone1);
$string['display_country'] = 'Display country';
$string['display_city'] = 'Display city';
$string['display_email'] = 'Display email';
-$string['display_icq'] = 'Display ICQ';
-$string['display_skype'] = 'Display Skype';
-$string['display_yahoo'] = 'Display Yahoo';
-$string['display_aim'] = 'Display AIM';
-$string['display_msn'] = 'Display MSN';
$string['display_phone1'] = 'Display phone';
$string['display_phone2'] = 'Display mobile phone';
$string['display_institution'] = 'Display institution';
$string['myprofile_settings'] = 'Visible user information';
$string['pluginname'] = 'Logged in user';
$string['privacy:metadata'] = 'The Logged in user block only shows information about the logged in user and does not store data itself.';
+
+// Deprecated since Moodle 4.0.
+$string['display_icq'] = 'Display ICQ';
+$string['display_skype'] = 'Display Skype';
+$string['display_yahoo'] = 'Display Yahoo';
+$string['display_aim'] = 'Display AIM';
+$string['display_msn'] = 'Display MSN';
\ No newline at end of file
* usercountry
* usercity
* useremail
- * usericq
- * userskype
- * useryahoo
- * useraim
- * usermsn
* userphone1
* userphone2
* userinstitution
"usercountry": "Australia",
"usercity": "Perth",
"useremail": "<a href=''>john.doe@example.com</a>",
- "usericq": "12345",
- "userskype": "john.doe",
- "useryahoo": "12345",
- "useraim": "12345",
- "usermsn": "12345",
"userphone1": "123456789",
"userphone2": "123456789",
"userinstitution": "Institution",
{{{ useremail }}}
</div>
{{/useremail}}
- {{#usericq}}
- <div class="myprofileitem icq">
- <span>ICQ:</span>
- {{ usericq }}
- </div>
- {{/usericq}}
- {{#userskype}}
- <div class="myprofileitem skype">
- <span>Skype:</span>
- {{ userskype }}
- </div>
- {{/userskype}}
- {{#useryahoo}}
- <div class="myprofileitem yahoo">
- <span>Yahoo:</span>
- {{ useryahoo }}
- </div>
- {{/useryahoo}}
- {{#useraim}}
- <div class="myprofileitem aim">
- <span>AIM:</span>
- {{ useraim }}
- </div>
- {{/useraim}}
- {{#usermsn}}
- <div class="myprofileitem msn">
- <span>MSN:</span>
- {{ usermsn }}
- </div>
- {{/usermsn}}
{{#userphone1}}
<div class="myprofileitem phone1">
<span>{{#str}} phone1 {{/str}}:</span>
And I press "Save changes"
And I should see "teacher1@example.com" in the "Logged in user" "block"
- Scenario: Configure the logged in user block to show / hide the users ICQ
- Given the following "users" exist:
- | username | firstname | lastname | email | icq |
- | teacher1 | Teacher | One | teacher1@example.com | myicq |
- And I log in as "teacher1"
- And I press "Customise this page"
- When I add the "Logged in user" block
- And I configure the "Logged in user" block
- And I set the following fields to these values:
- | Display ICQ | No |
- And I press "Save changes"
- Then I should see "Teacher One" in the "Logged in user" "block"
- And I should not see "myicq" in the "Logged in user" "block"
- And I configure the "Logged in user" block
- And I set the following fields to these values:
- | Display ICQ | Yes |
- And I press "Save changes"
- And I should see "myicq" in the "Logged in user" "block"
-
- Scenario: Configure the logged in user block to show / hide the users Skype
- Given the following "users" exist:
- | username | firstname | lastname | email | skype |
- | teacher1 | Teacher | One | teacher1@example.com | myskype |
- And I log in as "teacher1"
- And I press "Customise this page"
- When I add the "Logged in user" block
- And I configure the "Logged in user" block
- And I set the following fields to these values:
- | Display Skype | No |
- And I press "Save changes"
- Then I should see "Teacher One" in the "Logged in user" "block"
- And I should not see "myskype" in the "Logged in user" "block"
- And I configure the "Logged in user" block
- And I set the following fields to these values:
- | Display Skype | Yes |
- And I press "Save changes"
- And I should see "myskype" in the "Logged in user" "block"
-
- Scenario: Configure the logged in user block to show / hide the users Yahoo
- Given the following "users" exist:
- | username | firstname | lastname | email | yahoo |
- | teacher1 | Teacher | One | teacher1@example.com | myyahoo |
- And I log in as "teacher1"
- And I press "Customise this page"
- When I add the "Logged in user" block
- And I configure the "Logged in user" block
- And I set the following fields to these values:
- | Display Yahoo | No |
- And I press "Save changes"
- Then I should see "Teacher One" in the "Logged in user" "block"
- And I should not see "myyahoo" in the "Logged in user" "block"
- And I configure the "Logged in user" block
- And I set the following fields to these values:
- | Display Yahoo | Yes |
- And I press "Save changes"
- And I should see "myyahoo" in the "Logged in user" "block"
-
- Scenario: Configure the logged in user block to show / hide the users AIM
- Given the following "users" exist:
- | username | firstname | lastname | email | aim |
- | teacher1 | Teacher | One | teacher1@example.com | myaim |
- And I log in as "teacher1"
- And I press "Customise this page"
- When I add the "Logged in user" block
- And I configure the "Logged in user" block
- And I set the following fields to these values:
- | Display AIM | No |
- And I press "Save changes"
- Then I should see "Teacher One" in the "Logged in user" "block"
- And I should not see "myaim" in the "Logged in user" "block"
- And I configure the "Logged in user" block
- And I set the following fields to these values:
- | Display AIM | Yes |
- And I press "Save changes"
- And I should see "myaim" in the "Logged in user" "block"
-
- Scenario: Configure the logged in user block to show / hide the users MSN
- Given the following "users" exist:
- | username | firstname | lastname | email | msn |
- | teacher1 | Teacher | One | teacher1@example.com | mymsn |
- And I log in as "teacher1"
- And I press "Customise this page"
- When I add the "Logged in user" block
- And I configure the "Logged in user" block
- And I set the following fields to these values:
- | Display MSN | No |
- And I press "Save changes"
- Then I should see "Teacher One" in the "Logged in user" "block"
- And I should not see "mymsn" in the "Logged in user" "block"
- And I configure the "Logged in user" block
- And I set the following fields to these values:
- | Display MSN | Yes |
- And I press "Save changes"
- And I should see "mymsn" in the "Logged in user" "block"
-
Scenario: Configure the logged in user block to show / hide the users phone
Given the following "users" exist:
| username | firstname | lastname | email | phone1 |
* @return string
*/
public function get_type_title() {
- return get_string('grade');
+ return get_string('gradenoun');
}
/**
--- /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/>.
+
+/**
+ * Module to handle AJAX interactions with content bank upload files.
+ *
+ * @module core_contentbank/upload
+ * @copyright 2021 Sara Arjona <sara@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+import ModalForm from 'core_form/modalform';
+import {get_string as getString} from 'core/str';
+
+/**
+ * Initialize upload files to the content bank form as Modal form.
+ *
+ * @param {String} elementSelector
+ * @param {String} formClass
+ * @param {Integer} contextId
+ * @param {Integer} contentId
+ */
+export const initModal = (elementSelector, formClass, contextId, contentId) => {
+ const element = document.querySelector(elementSelector);
+ element.addEventListener('click', function(e) {
+ e.preventDefault();
+ const form = new ModalForm({
+ formClass,
+ args: {
+ contextid: contextId,
+ id: contentId,
+ },
+ modalConfig: {title: getString('upload', 'contentbank')},
+ returnFocus: e.target,
+ });
+ form.addEventListener(form.events.FORM_SUBMITTED, (event) => {
+ document.location = event.detail.returnurl;
+ });
+ form.show();
+ });
+};
--- /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/>.
+
+namespace core_contentbank\form;
+
+/**
+ * Upload files to content bank form
+ *
+ * @package core_contentbank
+ * @copyright 2020 Amaia Anabitarte <amaia@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class upload_files extends \core_form\dynamic_form {
+
+ /**
+ * Add elements to this form.
+ */
+ public function definition() {
+ $mform = $this->_form;
+
+ $mform->addElement('hidden', 'contextid');
+ $mform->setType('contextid', PARAM_INT);
+
+ $mform->addElement('hidden', 'id');
+ $mform->setType('id', PARAM_INT);
+
+ $mform->addElement('filepicker', 'file', get_string('file', 'core_contentbank'), null, $this->get_options());
+ $mform->addHelpButton('file', 'file', 'core_contentbank');
+ $mform->addRule('file', null, 'required');
+ }
+
+ /**
+ * Validate incoming data.
+ *
+ * @param array $data
+ * @param array $files
+ * @return array
+ */
+ public function validation($data, $files) {
+ $errors = array();
+ $draftitemid = $data['file'];
+ $options = $this->get_options();
+ if (file_is_draft_area_limit_reached($draftitemid, $options['areamaxbytes'])) {
+ $errors['file'] = get_string('userquotalimit', 'error');
+ }
+ return $errors;
+ }
+
+ /**
+ * Check if current user has access to this form, otherwise throw exception
+ *
+ * Sometimes permission check may depend on the action and/or id of the entity.
+ * If necessary, form data is available in $this->_ajaxformdata or
+ * by calling $this->optional_param()
+ */
+ protected function check_access_for_dynamic_submission(): void {
+ require_capability('moodle/contentbank:upload', $this->get_context_for_dynamic_submission());
+
+ // Check the context used by the content bank is allowed.
+ $cb = new \core_contentbank\contentbank();
+ if (!$cb->is_context_allowed($this->get_context_for_dynamic_submission())) {
+ throw new \moodle_exception('contextnotallowed', 'core_contentbank');
+ }
+
+ // If $id is defined, the file content will be replaced (instead of uploading a new one).
+ // Check that the user has the right permissions to replace this content file.
+ $id = $this->optional_param('id', null, PARAM_INT);
+ if ($id) {
+ $content = $cb->get_content_from_id($id);
+ $contenttype = $content->get_content_type_instance();
+ if (!$contenttype->can_manage($content) || !$contenttype->can_upload()) {
+ throw new \moodle_exception('nopermissions', 'error', '', null, get_string('replacecontent', 'contentbank'));
+ }
+ }
+ }
+
+ /**
+ * Returns form context
+ *
+ * If context depends on the form data, it is available in $this->_ajaxformdata or
+ * by calling $this->optional_param()
+ *
+ * @return \context
+ */
+ protected function get_context_for_dynamic_submission(): \context {
+ $contextid = $this->optional_param('contextid', null, PARAM_INT);
+ return \context::instance_by_id($contextid, MUST_EXIST);
+ }
+
+ /**
+ * File upload options
+ *
+ * @return array
+ * @throws \coding_exception
+ */
+ protected function get_options(): array {
+ global $CFG;
+
+ $maxbytes = $CFG->userquota;
+ $maxareabytes = $CFG->userquota;
+ if (has_capability('moodle/user:ignoreuserquota', $this->get_context_for_dynamic_submission())) {
+ $maxbytes = USER_CAN_IGNORE_FILE_SIZE_LIMITS;
+ $maxareabytes = FILE_AREA_MAX_BYTES_UNLIMITED;
+ }
+
+ $cb = new \core_contentbank\contentbank();
+ $id = $this->optional_param('id', null, PARAM_INT);
+ if ($id) {
+ $content = $cb->get_content_from_id($id);
+ $contenttype = $content->get_content_type_instance();
+ $extensions = $contenttype->get_manageable_extensions();
+ $acceptedtypes = implode(',', $extensions);
+ } else {
+ $acceptedtypes = $cb->get_supported_extensions_as_string($this->get_context_for_dynamic_submission());
+ }
+
+ return ['subdirs' => 1, 'maxbytes' => $maxbytes, 'maxfiles' => -1, 'accepted_types' => $acceptedtypes,
+ 'areamaxbytes' => $maxareabytes];
+ }
+
+ /**
+ * Process the form submission, used if form was submitted via AJAX
+ *
+ * This method can return scalar values or arrays that can be json-encoded, they will be passed to the caller JS.
+ *
+ * Submission data can be accessed as: $this->get_data()
+ *
+ * @return mixed
+ */
+ public function process_dynamic_submission() {
+ global $USER;
+
+ // Get the file and create the content based on it.
+ $usercontext = \context_user::instance($USER->id);
+ $fs = get_file_storage();
+ $files = $fs->get_area_files($usercontext->id, 'user', 'draft', $this->get_data()->file, 'itemid, filepath,
+ filename', false);
+ if (!empty($files)) {
+ $file = reset($files);
+ $cb = new \core_contentbank\contentbank();
+ try {
+ if ($this->get_data()->id) {
+ $content = $cb->get_content_from_id($this->get_data()->id);
+ $contenttype = $content->get_content_type_instance();
+ $content = $contenttype->replace_content($file, $content);
+ } else {
+ $content = $cb->create_content_from_file($this->get_context_for_dynamic_submission(), $USER->id, $file);
+ }
+ $params = ['id' => $content->get_id(), 'contextid' => $this->get_context_for_dynamic_submission()->id];
+ $url = new \moodle_url('/contentbank/view.php', $params);
+ } catch (\Exception $e) {
+ // Redirect to the right page (depending on if content is new or existing) and display an error.
+ if ($this->get_data()->id) {
+ $content = $cb->get_content_from_id($this->get_data()->id);
+ $params = [
+ 'id' => $content->get_id(),
+ 'contextid' => $this->get_context_for_dynamic_submission()->id,
+ 'errormsg' => 'notvalidpackage',
+ ];
+ $url = new \moodle_url('/contentbank/view.php', $params);
+ } else {
+ $url = new \moodle_url('/contentbank/index.php', [
+ 'contextid' => $this->get_context_for_dynamic_submission()->id,
+ 'errormsg' => 'notvalidpackage'],
+ );
+ }
+ }
+
+ return ['returnurl' => $url->out(false)];
+ }
+
+ return null;
+ }
+
+ /**
+ * Load in existing data as form defaults
+ *
+ * Can be overridden to retrieve existing values from db by entity id and also
+ * to preprocess editor and filemanager elements
+ *
+ * Example:
+ * $this->set_data(get_entity($this->_ajaxformdata['id']));
+ */
+ public function set_data_for_dynamic_submission(): void {
+ $data = (object)[
+ 'contextid' => $this->optional_param('contextid', null, PARAM_INT),
+ 'id' => $this->optional_param('id', null, PARAM_INT),
+ ];
+ $this->set_data($data);
+ }
+
+ /**
+ * Returns url to set in $PAGE->set_url() when form is being rendered or submitted via AJAX
+ *
+ * This is used in the form elements sensitive to the page url, such as Atto autosave in 'editor'
+ *
+ * If the form has arguments (such as 'id' of the element being edited), the URL should
+ * also have respective argument.
+ *
+ * @return \moodle_url
+ */
+ protected function get_page_url_for_dynamic_submission(): \moodle_url {
+ $params = ['contextid' => $this->get_context_for_dynamic_submission()->id];
+
+ $id = $this->optional_param('id', null, PARAM_INT);
+ if ($id) {
+ $url = '/contentbank/view.php';
+ $params['id'] = $id;
+ } else {
+ $url = '/contentbank/index.php';
+ }
+
+ return new \moodle_url($url, $params);
+ }
+}
namespace contenttype_h5p;
-use stdClass;
-use html_writer;
-
/**
* H5P Content manager class
*