Merge branch 'MDL-60932-master' of git://github.com/aanabit/moodle
authorAdrian Greeve <abgreeve@gmail.com>
Wed, 5 Aug 2020 02:07:24 +0000 (10:07 +0800)
committerAdrian Greeve <abgreeve@gmail.com>
Wed, 5 Aug 2020 02:07:24 +0000 (10:07 +0800)
19 files changed:
admin/tool/dataprivacy/classes/data_request.php
admin/tool/dataprivacy/createdatarequest.php
admin/tool/dataprivacy/createdatarequest_form.php
admin/tool/dataprivacy/lang/en/tool_dataprivacy.php
lib/editor/atto/plugins/undo/yui/build/moodle-atto_undo-button/moodle-atto_undo-button-debug.js
lib/editor/atto/plugins/undo/yui/build/moodle-atto_undo-button/moodle-atto_undo-button-min.js
lib/editor/atto/plugins/undo/yui/build/moodle-atto_undo-button/moodle-atto_undo-button.js
lib/editor/atto/plugins/undo/yui/src/button/js/button.js
lib/table/amd/build/dynamic.min.js
lib/table/amd/build/dynamic.min.js.map
lib/table/amd/src/dynamic.js
lib/tablelib.php
message/templates/message_index.mustache
mod/feedback/classes/responses_table.php
mod/quiz/accessrule/seb/db/install.xml
theme/boost/scss/moodle/message.scss
theme/boost/style/moodle.css
theme/classic/style/moodle.css
user/tests/behat/table_column_visibility.feature [new file with mode: 0644]

index 6a94fad..02edc7c 100644 (file)
@@ -26,10 +26,11 @@ namespace tool_dataprivacy;
 
 defined('MOODLE_INTERNAL') || die();
 
+use lang_string;
 use core\persistent;
 
 /**
- * Class for loading/storing competencies from the DB.
+ * Class for loading/storing data requests from the DB.
  *
  * @copyright  2018 Jun Pataleta
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
@@ -62,6 +63,7 @@ class data_request extends persistent {
             ],
             'comments' => [
                 'type' => PARAM_TEXT,
+                'message' => new lang_string('errorinvalidrequestcomments', 'tool_dataprivacy'),
                 'default' => ''
             ],
             'commentsformat' => [
@@ -75,7 +77,10 @@ class data_request extends persistent {
                 'default' => FORMAT_PLAIN
             ],
             'userid' => [
-                'default' => 0,
+                'default' => function() {
+                    global $USER;
+                    return $USER->id;
+                },
                 'type' => PARAM_INT
             ],
             'requestedby' => [
index c29f197..a81df11 100644 (file)
@@ -67,8 +67,8 @@ if (!$manage && !\tool_dataprivacy\api::can_contact_dpo()) {
     redirect($returnurl, get_string('contactdpoviaprivacypolicy', 'tool_dataprivacy'), 0, \core\output\notification::NOTIFY_ERROR);
 }
 
-$mform = new tool_dataprivacy_data_request_form($url->out(false), ['manage' => !empty($manage)]);
-$mform->set_data(['type' => $requesttype]);
+$mform = new tool_dataprivacy_data_request_form($url->out(false), ['manage' => !empty($manage),
+    'persistent' => new \tool_dataprivacy\data_request(0, (object) ['type' => $requesttype])]);
 
 // Data request cancelled.
 if ($mform->is_cancelled()) {
index c91213c..c708a88 100644 (file)
@@ -23,6 +23,7 @@
  */
 
 use tool_dataprivacy\api;
+use tool_dataprivacy\data_request;
 use tool_dataprivacy\local\helper;
 
 defined('MOODLE_INTERNAL') || die();
@@ -36,7 +37,10 @@ require_once($CFG->libdir.'/formslib.php');
  * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
  * @package tool_dataprivacy
  */
-class tool_dataprivacy_data_request_form extends moodleform {
+class tool_dataprivacy_data_request_form extends \core\form\persistent {
+
+    /** @var string Name of the persistent class. */
+    protected static $persistentclass = data_request::class;
 
     /** @var bool Flag to indicate whether this form is being rendered for managing data requests or for regular requests. */
     protected $manage = false;
@@ -96,14 +100,13 @@ class tool_dataprivacy_data_request_form extends moodleform {
             api::DATAREQUEST_TYPE_EXPORT => get_string('requesttypeexport', 'tool_dataprivacy'),
             api::DATAREQUEST_TYPE_DELETE => get_string('requesttypedelete', 'tool_dataprivacy')
         ];
+
         $mform->addElement('select', 'type', get_string('requesttype', 'tool_dataprivacy'), $options);
-        $mform->setType('type', PARAM_INT);
         $mform->addHelpButton('type', 'requesttype', 'tool_dataprivacy');
 
         // Request comments text area.
         $textareaoptions = ['cols' => 60, 'rows' => 10];
         $mform->addElement('textarea', 'comments', get_string('requestcomments', 'tool_dataprivacy'), $textareaoptions);
-        $mform->setType('type', PARAM_ALPHANUM);
         $mform->addHelpButton('comments', 'requestcomments', 'tool_dataprivacy');
 
         // Action buttons.
@@ -129,34 +132,49 @@ class tool_dataprivacy_data_request_form extends moodleform {
         }
     }
 
+    /**
+     * Get the default data. Unset the default userid if managing data requests
+     *
+     * @return stdClass
+     */
+    protected function get_default_data() {
+        $data = parent::get_default_data();
+        if ($this->manage) {
+            unset($data->userid);
+        }
+
+        return $data;
+    }
+
     /**
      * Form validation.
      *
-     * @param array $data
+     * @param stdClass $data
      * @param array $files
+     * @param array $errors
      * @return array
      * @throws coding_exception
      * @throws dml_exception
      */
-    public function validation($data, $files) {
+    public function extra_validation($data, $files, array &$errors) {
         global $USER;
-        $errors = [];
 
         $validrequesttypes = [
             api::DATAREQUEST_TYPE_EXPORT,
             api::DATAREQUEST_TYPE_DELETE
         ];
-        if (!in_array($data['type'], $validrequesttypes)) {
+        if (!in_array($data->type, $validrequesttypes)) {
             $errors['type'] = get_string('errorinvalidrequesttype', 'tool_dataprivacy');
         }
 
-        if (api::has_ongoing_request($data['userid'], $data['type'])) {
+        $userid = $data->userid;
+
+        if (api::has_ongoing_request($userid, $data->type)) {
             $errors['type'] = get_string('errorrequestalreadyexists', 'tool_dataprivacy');
         }
 
         // Check if current user can create data deletion request.
-        $userid = $data['userid'];
-        if ($data['type'] == api::DATAREQUEST_TYPE_DELETE) {
+        if ($data->type == api::DATAREQUEST_TYPE_DELETE) {
             if ($userid == $USER->id) {
                 if (!api::can_create_data_deletion_request_for_self()) {
                     $errors['type'] = get_string('errorcannotrequestdeleteforself', 'tool_dataprivacy');
index b5f3e60..63d33fe 100644 (file)
@@ -135,6 +135,7 @@ $string['effectiveretentionperioduser'] = '{$a} (since the last time the user ac
 $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['errorinvalidrequestcomments'] = 'Please ensure your comment contains plain text only.';
 $string['errorinvalidrequestcreationmethod'] = 'Invalid request creation method!';
 $string['errorinvalidrequeststatus'] = 'Invalid request status!';
 $string['errorinvalidrequesttype'] = 'Invalid request type!';
index 1a3e43e..81c39d1 100644 (file)
Binary files a/lib/editor/atto/plugins/undo/yui/build/moodle-atto_undo-button/moodle-atto_undo-button-debug.js and b/lib/editor/atto/plugins/undo/yui/build/moodle-atto_undo-button/moodle-atto_undo-button-debug.js differ
index fc5786d..7031821 100644 (file)
Binary files a/lib/editor/atto/plugins/undo/yui/build/moodle-atto_undo-button/moodle-atto_undo-button-min.js and b/lib/editor/atto/plugins/undo/yui/build/moodle-atto_undo-button/moodle-atto_undo-button-min.js differ
index 1a3e43e..81c39d1 100644 (file)
Binary files a/lib/editor/atto/plugins/undo/yui/build/moodle-atto_undo-button/moodle-atto_undo-button.js and b/lib/editor/atto/plugins/undo/yui/build/moodle-atto_undo-button/moodle-atto_undo-button.js differ
index e72ef44..aa3ce8a 100644 (file)
@@ -255,6 +255,12 @@ Y.namespace('M.atto_undo').Button = Y.Base.create('button', Y.M.editor_atto.Edit
      */
     _redoHandler: function(e) {
         e.preventDefault();
+
+        // Don't do anything if redo stack is empty.
+        if (this._redoStack.length === 0) {
+            return;
+        }
+
         var html = this._getHTML(),
             redo = this._getRedo();
 
index 11ddc73..457bab1 100644 (file)
Binary files a/lib/table/amd/build/dynamic.min.js and b/lib/table/amd/build/dynamic.min.js differ
index 089870b..e2275f1 100644 (file)
Binary files a/lib/table/amd/build/dynamic.min.js.map and b/lib/table/amd/build/dynamic.min.js.map differ
index 899809b..a0246dd 100644 (file)
@@ -336,12 +336,13 @@ export const getLastInitial = tableRoot => getTableData(tableRoot).tableLastInit
  * @param {HTMLElement} tableRoot
  * @param {String} columnToHide
  * @param {Bool} refreshContent
+ * @returns {Promise}
  */
 export const hideColumn = (tableRoot, columnToHide, refreshContent = true) => {
     const hiddenColumns = JSON.parse(tableRoot.dataset.tableHiddenColumns);
     hiddenColumns.push(columnToHide);
 
-    updateTable(tableRoot, {hiddenColumns}, refreshContent);
+    return updateTable(tableRoot, {hiddenColumns}, refreshContent);
 };
 
 /**
@@ -350,12 +351,13 @@ export const hideColumn = (tableRoot, columnToHide, refreshContent = true) => {
  * @param {HTMLElement} tableRoot
  * @param {String} columnToShow
  * @param {Bool} refreshContent
+ * @returns {Promise}
  */
 export const showColumn = (tableRoot, columnToShow, refreshContent = true) => {
     let hiddenColumns = JSON.parse(tableRoot.dataset.tableHiddenColumns);
     hiddenColumns = hiddenColumns.filter(columnName => columnName !== columnToShow);
 
-    updateTable(tableRoot, {hiddenColumns}, refreshContent);
+    return updateTable(tableRoot, {hiddenColumns}, refreshContent);
 };
 
 /**
index 78bade9..63510a6 100644 (file)
@@ -1444,23 +1444,19 @@ class flexible_table {
             }
         }
 
-        // Now, update the column attributes for collapsed columns
-        foreach (array_keys($this->columns) as $column) {
-            if (!empty($this->prefs['collapse'][$column])) {
-                $this->column_style[$column]['width'] = '10px';
-            }
-        }
+        $this->set_hide_show_preferences();
+        $this->set_sorting_preferences();
+        $this->set_initials_preferences();
 
-        // Now, update the column attributes for collapsed columns
+        // Now, reduce the width of collapsed columns and remove the width from columns that should be expanded.
         foreach (array_keys($this->columns) as $column) {
             if (!empty($this->prefs['collapse'][$column])) {
                 $this->column_style[$column]['width'] = '10px';
+            } else {
+                unset($this->column_style[$column]['width']);
             }
         }
 
-        $this->set_sorting_preferences();
-        $this->set_initials_preferences();
-
         if (empty($this->baseurl)) {
             debugging('You should set baseurl when using flexible_table.');
             global $PAGE;
index f47b39c..33e3ab6 100644 (file)
@@ -43,7 +43,7 @@
 >
     <div class="container-fluid">
         <div class="row-fluid h-100 no-gutters">
-            <div class="col-4 d-flex flex-column">
+            <div class="col-4 d-flex flex-column conversationcontainer">
                 <div class="border-right h-100">
                     <div class="panel-header-container" data-region="panel-header-container">
                         {{> core_message/message_drawer_view_overview_header }}
index 383a322..663c3c5 100644 (file)
@@ -194,7 +194,11 @@ class mod_feedback_responses_table extends table_sql {
         if (preg_match('/^val(\d+)$/', $column, $matches)) {
             $items = $this->feedbackstructure->get_items();
             $itemobj = feedback_get_item_class($items[$matches[1]]->typ);
-            return trim($itemobj->get_printval($items[$matches[1]], (object) ['value' => $row->$column] ));
+            $printval = $itemobj->get_printval($items[$matches[1]], (object) ['value' => $row->$column]);
+            if ($this->is_downloading()) {
+                $printval = html_entity_decode($printval, ENT_QUOTES);
+            }
+            return trim($printval);
         }
         return $row->$column;
     }
index 1969ad3..ffbf932 100644 (file)
@@ -39,7 +39,7 @@
         <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
         <KEY NAME="quizid" TYPE="foreign-unique" FIELDS="quizid" REFTABLE="quiz" REFFIELDS="id"/>
         <KEY NAME="cmid" TYPE="foreign-unique" FIELDS="cmid" REFTABLE="course_modules" REFFIELDS="id"/>
-        <KEY NAME="templateid" TYPE="foreign" FIELDS="templateid" REFTABLE="quizacces_seb_template" REFFIELDS="id"/>
+        <KEY NAME="templateid" TYPE="foreign" FIELDS="templateid" REFTABLE="quizaccess_seb_template" REFFIELDS="id"/>
         <KEY NAME="usermodified" TYPE="foreign" FIELDS="usermodified" REFTABLE="user" REFFIELDS="id"/>
       </KEYS>
     </TABLE>
index 8388761..fff7cd4 100644 (file)
@@ -651,15 +651,25 @@ $message-day-color: color-yiq($message-app-bg) !default;
         overflow-y: auto;
     }
 }
-#page-message-index #region-main {
-    height: 100%;
-    div[role="main"] {
+#page-message-index {
+    #page-header {
+        display: none;
+    }
+    #region-main {
         height: 100%;
-        #maincontent {
-            margin-top: -1px;
+        margin-top: 0;
+        .conversationcontainer {
+            max-height: calc(100vh - 50px);
+            overflow: auto;
         }
-        .message-app.main {
+        div[role="main"] {
             height: 100%;
+            #maincontent {
+                margin-top: -1px;
+            }
+            .message-app.main {
+                height: 100%;
+            }
         }
     }
 }
index fba0b2c..ac1f653 100644 (file)
@@ -15316,8 +15316,15 @@ a.ygtvspacer:hover {
   .message-app .lazy-load-list {
     overflow-y: auto; }
 
+#page-message-index #page-header {
+  display: none; }
+
 #page-message-index #region-main {
-  height: 100%; }
+  height: 100%;
+  margin-top: 0; }
+  #page-message-index #region-main .conversationcontainer {
+    max-height: calc(100vh - 50px);
+    overflow: auto; }
   #page-message-index #region-main div[role="main"] {
     height: 100%; }
     #page-message-index #region-main div[role="main"] #maincontent {
index 6272692..cd3beff 100644 (file)
@@ -15534,8 +15534,15 @@ a.ygtvspacer:hover {
   .message-app .lazy-load-list {
     overflow-y: auto; }
 
+#page-message-index #page-header {
+  display: none; }
+
 #page-message-index #region-main {
-  height: 100%; }
+  height: 100%;
+  margin-top: 0; }
+  #page-message-index #region-main .conversationcontainer {
+    max-height: calc(100vh - 50px);
+    overflow: auto; }
   #page-message-index #region-main div[role="main"] {
     height: 100%; }
     #page-message-index #region-main div[role="main"] #maincontent {
diff --git a/user/tests/behat/table_column_visibility.feature b/user/tests/behat/table_column_visibility.feature
new file mode 100644 (file)
index 0000000..f7dd23b
--- /dev/null
@@ -0,0 +1,49 @@
+@core @core_user
+Feature: The visibility of table columns can be toggled
+  In order to customise my view of participants data
+  As a user
+  I need to be able to hide and show columns in the participants table
+
+  Background:
+    Given the following "courses" exist:
+      | fullname | shortname | category | groupmode |
+      | Course 1 | C1        | 0        | 1         |
+    And the following "users" exist:
+      | username | firstname | lastname | email               |
+      | t1       | Agatha    | T        | agatha@example.com  |
+      | s1       | Matilda   | W        | matilda@example.com |
+      | s2       | Mick      | H        | mick@example.com    |
+    And the following "course enrolments" exist:
+      | user | course | role           |
+      | t1   | C1     | editingteacher |
+      | s1   | C1     | student        |
+      | s2   | C1     | student        |
+
+  @javascript
+  Scenario: The visibility of columns can be individually toggled within the participants table
+    Given I log in as "t1"
+    And I am on "Course 1" course homepage
+    And I navigate to course participants
+    And I should see "Email address" in the "participants" "table"
+    And I should see "matilda@example.com" in the "participants" "table"
+    And I should see "Roles" in the "participants" "table"
+    And I should see "Student" in the "participants" "table"
+    When I follow "Hide Email address"
+    Then I should not see "Email address" in the "participants" "table"
+    And I should not see "matilda@example.com" in the "participants" "table"
+    And I should see "Roles" in the "participants" "table"
+    And I should see "Student" in the "participants" "table"
+    And I follow "Hide Roles"
+    And I should not see "Roles" in the "participants" "table"
+    And I should not see "Student" in the "participants" "table"
+    And I should not see "matilda@example.com" in the "participants" "table"
+    And I follow "Show Email address"
+    And I should see "Email address" in the "participants" "table"
+    And I should see "matilda@example.com" in the "participants" "table"
+    And I should not see "Roles" in the "participants" "table"
+    And I should not see "Student" in the "participants" "table"
+    And I follow "Show Roles"
+    And I should see "Roles" in the "participants" "table"
+    And I should see "Student" in the "participants" "table"
+    And I should see "Email address" in the "participants" "table"
+    And I should see "matilda@example.com" in the "participants" "table"