Merge branch 'MDL-66458-master' of git://github.com/crazyserver/moodle
authorAdrian Greeve <abgreeve@gmail.com>
Wed, 28 Aug 2019 03:25:16 +0000 (11:25 +0800)
committerAdrian Greeve <abgreeve@gmail.com>
Wed, 28 Aug 2019 03:25:16 +0000 (11:25 +0800)
38 files changed:
blocks/myoverview/amd/build/view.min.js
blocks/myoverview/amd/build/view.min.js.map
blocks/myoverview/amd/src/view.js
blocks/myoverview/classes/output/main.php
blocks/myoverview/lang/en/block_myoverview.php
blocks/myoverview/lib.php
blocks/myoverview/settings.php
blocks/myoverview/styles.css [new file with mode: 0644]
blocks/myoverview/templates/nav-grouping-selector.mustache
blocks/myoverview/tests/behat/block_myoverview_adminsettings.feature [new file with mode: 0644]
blocks/myoverview/tests/behat/block_myoverview_dashboard.feature
blocks/myoverview/tests/behat/block_myoverview_hidden.feature
blocks/myoverview/tests/behat/block_myoverview_pagelimit_persistence.feature
blocks/myoverview/tests/behat/block_myoverview_progress.feature
blocks/myoverview/tests/privacy_test.php
blocks/myoverview/version.php
blocks/recentlyaccesseditems/db/upgrade.php
comment/classes/external.php
comment/lib.php
comment/tests/externallib_test.php
comment/upgrade.txt [new file with mode: 0644]
composer.json
composer.lock
course/externallib.php
course/lib.php
course/upgrade.txt
lib/amd/build/form-autocomplete.min.js
lib/amd/build/form-autocomplete.min.js.map
lib/amd/src/form-autocomplete.js
lib/filelib.php
lib/tests/filelib_test.php
lib/upgrade.txt
lib/xmlize.php
mod/book/lib.php
mod/book/tests/lib_test.php
mod/forum/post.php
mod/glossary/lib.php
version.php

index f58ff47..01b71bc 100644 (file)
Binary files a/blocks/myoverview/amd/build/view.min.js and b/blocks/myoverview/amd/build/view.min.js differ
index ea5cedd..5de1f87 100644 (file)
Binary files a/blocks/myoverview/amd/build/view.min.js.map and b/blocks/myoverview/amd/build/view.min.js.map differ
index 3e53ea0..40d48b9 100644 (file)
@@ -67,6 +67,16 @@ function(
         NOCOURSES: 'core_course/no-courses'
     };
 
+    var GROUPINGS = {
+        GROUPING_ALLINCLUDINGHIDDEN: 'allincludinghidden',
+        GROUPING_ALL: 'all',
+        GROUPING_INPROGRESS: 'inprogress',
+        GROUPING_FUTURE: 'future',
+        GROUPING_PAST: 'past',
+        GROUPING_FAVOURITES: 'favourites',
+        GROUPING_HIDDEN: 'hidden'
+    };
+
     var NUMCOURSES_PERPAGE = [12, 24, 48, 96, 0];
 
     var loadedPages = [];
@@ -252,15 +262,104 @@ function(
         }).catch(Notification.exception);
     };
 
+    /**
+     * Get the action menu item
+     *
+     * @param {Object} root  root The course overview container
+     * @param {Number} courseId Course id.
+     * @return {Object} The hide course menu item.
+     */
+    var getHideCourseMenuItem = function(root, courseId) {
+        return root.find('[data-action="hide-course"][data-course-id="' + courseId + '"]');
+    };
+
+    /**
+     * Get the action menu item
+     *
+     * @param {Object} root  root The course overview container
+     * @param {Number} courseId Course id.
+     * @return {Object} The show course menu item.
+     */
+    var getShowCourseMenuItem = function(root, courseId) {
+        return root.find('[data-action="show-course"][data-course-id="' + courseId + '"]');
+    };
+
+    /**
+     * Hide course
+     *
+     * @param  {Object} root The course overview container
+     * @param  {Number} courseId Course id number
+     */
+    var hideCourse = function(root, courseId) {
+        var hideAction = getHideCourseMenuItem(root, courseId);
+        var showAction = getShowCourseMenuItem(root, courseId);
+        var filters = getFilterValues(root);
+
+        setCourseHiddenState(courseId, true);
+
+        // Remove the course from this view as it is now hidden and thus not covered by this view anymore.
+        // Do only if we are not in "All" view mode where really all courses are shown.
+        if (filters.grouping != GROUPINGS.GROUPING_ALLINCLUDINGHIDDEN) {
+            hideElement(root, courseId);
+        }
+
+        hideAction.addClass('hidden');
+        showAction.removeClass('hidden');
+    };
+
+    /**
+     * Show course
+     *
+     * @param  {Object} root The course overview container
+     * @param  {Number} courseId Course id number
+     */
+    var showCourse = function(root, courseId) {
+        var hideAction = getHideCourseMenuItem(root, courseId);
+        var showAction = getShowCourseMenuItem(root, courseId);
+        var filters = getFilterValues(root);
+
+        setCourseHiddenState(courseId, null);
+
+        // Remove the course from this view as it is now shown again and thus not covered by this view anymore.
+        // Do only if we are not in "All" view mode where really all courses are shown.
+        if (filters.grouping != GROUPINGS.GROUPING_ALLINCLUDINGHIDDEN) {
+            hideElement(root, courseId);
+        }
+
+        hideAction.removeClass('hidden');
+        showAction.addClass('hidden');
+    };
+
+    /**
+     * Set the courses hidden status and push to repository
+     *
+     * @param  {Number} courseId Course id to favourite.
+     * @param  {Bool} status new hidden status.
+     * @return {Promise} Repository promise.
+     */
+    var setCourseHiddenState = function(courseId, status) {
+
+        // If the given status is not hidden, the preference has to be deleted with a null value.
+        if (status === false) {
+            status = null;
+        }
+        return Repository.updateUserPreferences({
+            preferences: [
+                {
+                    type: 'block_myoverview_hidden_course_' + courseId,
+                    value: status
+                }
+            ]
+        });
+    };
+
     /**
      * Reset the loadedPages dataset to take into account the hidden element
      *
      * @param {Object} root The course overview container
-     * @param {Object} target The course that you want to hide
+     * @param {Number} id The course id number
      */
-    var hideElement = function(root, target) {
-        var id = getCourseId(target);
-
+    var hideElement = function(root, id) {
         var pagingBar = root.find('[data-region="paging-bar"]');
         var jumpto = parseInt(pagingBar.attr('data-active-page-number'));
 
@@ -570,38 +669,15 @@ function(
 
         root.on(CustomEvents.events.activate, SELECTORS.ACTION_HIDE_COURSE, function(e, data) {
             var target = $(e.target).closest(SELECTORS.ACTION_HIDE_COURSE);
-            var id = getCourseId(target);
-
-            var request = {
-                preferences: [
-                    {
-                        type: 'block_myoverview_hidden_course_' + id,
-                        value: true
-                    }
-                ]
-            };
-            Repository.updateUserPreferences(request);
-
-            hideElement(root, target);
+            var courseId = getCourseId(target);
+            hideCourse(root, courseId);
             data.originalEvent.preventDefault();
         });
 
         root.on(CustomEvents.events.activate, SELECTORS.ACTION_SHOW_COURSE, function(e, data) {
             var target = $(e.target).closest(SELECTORS.ACTION_SHOW_COURSE);
-            var id = getCourseId(target);
-
-            var request = {
-                preferences: [
-                    {
-                        type: 'block_myoverview_hidden_course_' + id,
-                        value: null
-                    }
-                ]
-            };
-
-            Repository.updateUserPreferences(request);
-
-            hideElement(root, target);
+            var courseId = getCourseId(target);
+            showCourse(root, courseId);
             data.originalEvent.preventDefault();
         });
     };
index e1c92a9..5f4a033 100644 (file)
@@ -81,6 +81,55 @@ class main implements renderable, templatable {
      */
     private $layouts;
 
+    /**
+     * Store a course grouping option setting
+     *
+     * @var boolean
+     */
+    private $displaygroupingallincludinghidden;
+
+    /**
+     * Store a course grouping option setting.
+     *
+     * @var boolean
+     */
+    private $displaygroupingall;
+
+    /**
+     * Store a course grouping option setting.
+     *
+     * @var boolean
+     */
+    private $displaygroupinginprogress;
+
+    /**
+     * Store a course grouping option setting.
+     *
+     * @var boolean
+     */
+    private $displaygroupingfuture;
+
+    /**
+     * Store a course grouping option setting.
+     *
+     * @var boolean
+     */
+    private $displaygroupingpast;
+
+    /**
+     * Store a course grouping option setting.
+     *
+     * @var boolean
+     */
+    private $displaygroupingstarred;
+
+    /**
+     * Store a course grouping option setting.
+     *
+     * @var boolean
+     */
+    private $displaygroupinghidden;
+
     /**
      * main constructor.
      * Initialize the user preferences
@@ -92,23 +141,89 @@ class main implements renderable, templatable {
      * @throws \dml_exception
      */
     public function __construct($grouping, $sort, $view, $paging) {
-        $this->grouping = $grouping ? $grouping : BLOCK_MYOVERVIEW_GROUPING_ALL;
+        // Get plugin config.
+        $config = get_config('block_myoverview');
+
+        // Build the course grouping option name to check if the given grouping is enabled afterwards.
+        $groupingconfigname = 'displaygrouping'.$grouping;
+        // Check the given grouping and remember it if it is enabled.
+        if ($grouping && $config->$groupingconfigname == true) {
+            $this->grouping = $grouping;
+
+            // Otherwise fall back to another grouping in a reasonable order.
+            // This is done to prevent one-time UI glitches in the case when a user has chosen a grouping option previously which
+            // was then disabled by the admin in the meantime.
+        } else if ($config->displaygroupingall == true) {
+            $this->grouping = BLOCK_MYOVERVIEW_GROUPING_ALL;
+        } else if ($config->displaygroupingallincludinghidden == true) {
+            $this->grouping = BLOCK_MYOVERVIEW_GROUPING_ALLINCLUDINGHIDDEN;
+        } else if ($config->displaygroupinginprogress == true) {
+            $this->grouping = BLOCK_MYOVERVIEW_GROUPING_INPROGRESS;
+        } else if ($config->displaygroupingfuture == true) {
+            $this->grouping = BLOCK_MYOVERVIEW_GROUPING_FUTURE;
+        } else if ($config->displaygroupingpast == true) {
+            $this->grouping = BLOCK_MYOVERVIEW_GROUPING_PAST;
+        } else if ($config->displaygroupingstarred == true) {
+            $this->grouping = BLOCK_MYOVERVIEW_GROUPING_FAVOURITES;
+        } else if ($config->displaygroupinghidden == true) {
+            $this->grouping = BLOCK_MYOVERVIEW_GROUPING_HIDDEN;
+
+            // In this case, no grouping option is enabled and the grouping is not needed at all.
+            // But it's better not to leave $this->grouping unset for any unexpected case.
+        } else {
+            $this->grouping = BLOCK_MYOVERVIEW_GROUPING_ALLINCLUDINGHIDDEN;
+        }
+        unset ($groupingconfigname);
+
+        // Check and remember the given sorting.
         $this->sort = $sort ? $sort : BLOCK_MYOVERVIEW_SORTING_TITLE;
+
+        // Check and remember the given view.
+        $this->view = $view ? $view : BLOCK_MYOVERVIEW_VIEW_CARD;
+
+        // Check and remember the given page size.
         if ($paging == BLOCK_MYOVERVIEW_PAGING_ALL) {
             $this->paging = BLOCK_MYOVERVIEW_PAGING_ALL;
         } else {
             $this->paging = $paging ? $paging : BLOCK_MYOVERVIEW_PAGING_12;
         }
 
-        $config = get_config('block_myoverview');
+        // Check and remember if the course categories should be shown or not.
         if (!$config->displaycategories) {
             $this->displaycategories = BLOCK_MYOVERVIEW_DISPLAY_CATEGORIES_OFF;
         } else {
             $this->displaycategories = BLOCK_MYOVERVIEW_DISPLAY_CATEGORIES_ON;
         }
 
+        // Get and remember the available layouts.
         $this->set_available_layouts();
         $this->view = $view ? $view : reset($this->layouts);
+
+        // Check and remember if the particular grouping options should be shown or not.
+        $this->displaygroupingallincludinghidden = $config->displaygroupingallincludinghidden;
+        $this->displaygroupingall = $config->displaygroupingall;
+        $this->displaygroupinginprogress = $config->displaygroupinginprogress;
+        $this->displaygroupingfuture = $config->displaygroupingfuture;
+        $this->displaygroupingpast = $config->displaygroupingpast;
+        $this->displaygroupingstarred = $config->displaygroupingstarred;
+        $this->displaygroupinghidden = $config->displaygroupinghidden;
+
+        // Check and remember if the grouping selector should be shown at all or not.
+        // It will be shown if more than 1 grouping option is enabled.
+        $displaygroupingselectors = array($this->displaygroupingallincludinghidden,
+                $this->displaygroupingall,
+                $this->displaygroupinginprogress,
+                $this->displaygroupingfuture,
+                $this->displaygroupingpast,
+                $this->displaygroupingstarred,
+                $this->displaygroupinghidden);
+        $displaygroupingselectorscount = count(array_filter($displaygroupingselectors));
+        if ($displaygroupingselectorscount > 1) {
+            $this->displaygroupingselector = true;
+        } else {
+            $this->displaygroupingselector = false;
+        }
+        unset ($displaygroupingselectors, $displaygroupingselectorscount);
     }
 
 
@@ -204,6 +319,14 @@ class main implements renderable, templatable {
             'layouts' => $availablelayouts,
             'displaycategories' => $this->displaycategories,
             'displaydropdown' => (count($availablelayouts) > 1) ? true : false,
+            'displaygroupingallincludinghidden' => $this->displaygroupingallincludinghidden,
+            'displaygroupingall' => $this->displaygroupingall,
+            'displaygroupinginprogress' => $this->displaygroupinginprogress,
+            'displaygroupingfuture' => $this->displaygroupingfuture,
+            'displaygroupingpast' => $this->displaygroupingpast,
+            'displaygroupingstarred' => $this->displaygroupingstarred,
+            'displaygroupinghidden' => $this->displaygroupinghidden,
+            'displaygroupingselector' => $this->displaygroupingselector,
         ];
         return array_merge($defaultvariables, $preferences);
 
index 637368b..8fe7076 100644 (file)
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-$string['all'] = 'All';
-$string['allexcepthidden'] = 'All (except hidden)';
+$string['allincludinghidden'] = 'All';
+$string['all'] = 'All (except hidden)';
 $string['addtofavourites'] = 'Star this course';
 $string['aria:addtofavourites'] = 'Star for';
-$string['aria:allcourses'] = 'All courses';
-$string['aria:allcoursesexcepthidden'] = 'All courses except hidden courses';
+$string['aria:allcoursesincludinghidden'] = 'All courses';
+$string['aria:allcourses'] = 'All courses except hidden courses';
 $string['aria:card'] = 'Switch to card view';
 $string['aria:controls'] = 'Course overview controls';
 $string['aria:courseactions'] = 'Actions for current course';
@@ -45,6 +45,8 @@ $string['aria:past'] = 'Show past courses';
 $string['aria:removefromfavourites'] = 'Remove star for';
 $string['aria:summary'] = 'Switch to summary view';
 $string['aria:sortingdropdown'] = 'Sorting drop-down menu';
+$string['availablegroupings'] = 'Available filters';
+$string['availablegroupings_desc'] = 'Course filters which are available for selection by users. If none are selected, all courses will be displayed.';
 $string['card'] = 'Card';
 $string['cards'] = 'Cards';
 $string['courseprogress'] = 'Course progress:';
index 7acbaff..81090ed 100644 (file)
@@ -27,6 +27,7 @@ defined('MOODLE_INTERNAL') || die();
 /**
  * Constants for the user preferences grouping options
  */
+define('BLOCK_MYOVERVIEW_GROUPING_ALLINCLUDINGHIDDEN', 'allincludinghidden');
 define('BLOCK_MYOVERVIEW_GROUPING_ALL', 'all');
 define('BLOCK_MYOVERVIEW_GROUPING_INPROGRESS', 'inprogress');
 define('BLOCK_MYOVERVIEW_GROUPING_FUTURE', 'future');
@@ -74,6 +75,7 @@ function block_myoverview_user_preferences() {
         'default' => BLOCK_MYOVERVIEW_GROUPING_ALL,
         'type' => PARAM_ALPHA,
         'choices' => array(
+            BLOCK_MYOVERVIEW_GROUPING_ALLINCLUDINGHIDDEN,
             BLOCK_MYOVERVIEW_GROUPING_ALL,
             BLOCK_MYOVERVIEW_GROUPING_INPROGRESS,
             BLOCK_MYOVERVIEW_GROUPING_FUTURE,
index b17ea4e..b633f22 100644 (file)
@@ -27,22 +27,74 @@ defined('MOODLE_INTERNAL') || die;
 if ($ADMIN->fulltree) {
     require_once($CFG->dirroot . '/blocks/myoverview/lib.php');
 
+    // Presentation options heading.
+    $settings->add(new admin_setting_heading('block_myoverview/appearance',
+            get_string('appearance', 'admin'),
+            ''));
+
     // Display Course Categories on Dashboard course items (cards, lists, summary items).
     $settings->add(new admin_setting_configcheckbox(
-        'block_myoverview/displaycategories',
-        get_string('displaycategories', 'block_myoverview'),
-        get_string('displaycategories_help', 'block_myoverview'),
-        1));
-
-       $choices = array(BLOCK_MYOVERVIEW_VIEW_CARD => get_string('card', 'block_myoverview'),
-        BLOCK_MYOVERVIEW_VIEW_LIST => get_string('list', 'block_myoverview'),
-        BLOCK_MYOVERVIEW_VIEW_SUMMARY => get_string('summary', 'block_myoverview'));
+            'block_myoverview/displaycategories',
+            get_string('displaycategories', 'block_myoverview'),
+            get_string('displaycategories_help', 'block_myoverview'),
+            1));
 
+    // Enable / Disable available layouts.
+    $choices = array(BLOCK_MYOVERVIEW_VIEW_CARD => get_string('card', 'block_myoverview'),
+            BLOCK_MYOVERVIEW_VIEW_LIST => get_string('list', 'block_myoverview'),
+            BLOCK_MYOVERVIEW_VIEW_SUMMARY => get_string('summary', 'block_myoverview'));
     $settings->add(new admin_setting_configmulticheckbox(
-        'block_myoverview/layouts',
-        get_string('layouts', 'block_myoverview'),
-        get_string('layouts_help', 'block_myoverview'),
-        $choices,
-        $choices));
+            'block_myoverview/layouts',
+            get_string('layouts', 'block_myoverview'),
+            get_string('layouts_help', 'block_myoverview'),
+            $choices,
+            $choices));
+    unset ($choices);
+
+    // Enable / Disable course filter items.
+    $settings->add(new admin_setting_heading('block_myoverview/availablegroupings',
+            get_string('availablegroupings', 'block_myoverview'),
+            get_string('availablegroupings_desc', 'block_myoverview')));
+
+    $settings->add(new admin_setting_configcheckbox(
+            'block_myoverview/displaygroupingallincludinghidden',
+            get_string('allincludinghidden', 'block_myoverview'),
+            '',
+            0));
+
+    $settings->add(new admin_setting_configcheckbox(
+            'block_myoverview/displaygroupingall',
+            get_string('all', 'block_myoverview'),
+            '',
+            1));
+
+    $settings->add(new admin_setting_configcheckbox(
+            'block_myoverview/displaygroupinginprogress',
+            get_string('inprogress', 'block_myoverview'),
+            '',
+            1));
+
+    $settings->add(new admin_setting_configcheckbox(
+            'block_myoverview/displaygroupingpast',
+            get_string('past', 'block_myoverview'),
+            '',
+            1));
+
+    $settings->add(new admin_setting_configcheckbox(
+            'block_myoverview/displaygroupingfuture',
+            get_string('future', 'block_myoverview'),
+            '',
+            1));
+
+    $settings->add(new admin_setting_configcheckbox(
+            'block_myoverview/displaygroupingstarred',
+            get_string('favourites', 'block_myoverview'),
+            '',
+            1));
 
+    $settings->add(new admin_setting_configcheckbox(
+            'block_myoverview/displaygroupinghidden',
+            get_string('hiddencourses', 'block_myoverview'),
+            '',
+            1));
 }
diff --git a/blocks/myoverview/styles.css b/blocks/myoverview/styles.css
new file mode 100644 (file)
index 0000000..76daded
--- /dev/null
@@ -0,0 +1,5 @@
+/* Hide the first dropdown-divider if no filter option element is listed before it.
+   This can happen for some subset configurations of the block_myoverview course filter. */
+.block_myoverview button#groupingdropdown + .dropdown-menu li:first-of-type.dropdown-divider:first-of-type {
+    display: none;
+}
index fb56f6d..703482f 100644 (file)
 
     Example context (json):
     {
+        "allincludinghidden": false,
         "all": true,
         "inprogress": false,
         "future": false,
-        "past": false
+        "past": false,
+        "favourites": false,
+        "hidden": false,
+        "displaygroupingallincludinghidden": false,
+        "displaygroupingall": true,
+        "displaygroupinginprogress": true,
+        "displaygroupingfuture": true,
+        "displaygroupingpast": true,
+        "displaygroupingstarred": true,
+        "displaygroupinghidden": true,
+        "displaygroupingselector": true
     }
 }}
+{{#displaygroupingselector}}
 <div class="dropdown mb-1 mr-auto">
     <button id="groupingdropdown" type="button" class="btn btn-outline-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="{{#str}} aria:groupingdropdown, block_myoverview {{/str}}">
         {{#pix}} i/filter {{/pix}}
         <span class="d-sm-inline-block" data-active-item-text>
-            {{#all}}{{#str}} allexcepthidden, block_myoverview {{/str}}{{/all}}
+            {{#allincludinghidden}}{{#str}} allincludinghidden, block_myoverview {{/str}}{{/allincludinghidden}}
+            {{#all}}{{#str}} all, block_myoverview {{/str}}{{/all}}
             {{#inprogress}}{{#str}} inprogress, block_myoverview {{/str}}{{/inprogress}}
             {{#future}}{{#str}} future, block_myoverview {{/str}}{{/future}}
             {{#past}}{{#str}} past, block_myoverview {{/str}}{{/past}}
         </span>
     </button>
     <ul class="dropdown-menu" data-show-active-item data-active-item-text aria-labelledby="groupingdropdown">
+        {{#displaygroupingallincludinghidden}}
         <li>
-            <a class="dropdown-item {{#all}}active{{/all}}" href="#" data-filter="grouping" data-value="all" data-pref="all" aria-label="{{#str}} aria:allcoursesexcepthidden, block_myoverview {{/str}}" aria-controls="courses-view-{{uniqid}}">
-                {{#str}} allexcepthidden, block_myoverview {{/str}}
+            <a class="dropdown-item {{#allincludinghidden}}active{{/allincludinghidden}}" href="#" data-filter="grouping" data-value="allincludinghidden" data-pref="allincludinghidden" aria-label="{{#str}} aria:allcoursesincludinghidden, block_myoverview {{/str}}" aria-controls="courses-view-{{uniqid}}">
+                {{#str}} allincludinghidden, block_myoverview {{/str}}
             </a>
         </li>
+        {{/displaygroupingallincludinghidden}}
+        {{#displaygroupingall}}
+        <li class="dropdown-divider" role="presentation">
+            <span class="filler">&nbsp;</span>
+        </li>
+        <li>
+            <a class="dropdown-item {{#all}}active{{/all}}" href="#" data-filter="grouping" data-value="all" data-pref="all" aria-label="{{#str}} aria:allcourses, block_myoverview {{/str}}" aria-controls="courses-view-{{uniqid}}">
+                {{#str}} all, block_myoverview {{/str}}
+            </a>
+        </li>
+        {{/displaygroupingall}}
+        {{#displaygroupinginprogress}}
         <li class="dropdown-divider" role="presentation">
             <span class="filler">&nbsp;</span>
         </li>
                 {{#str}} inprogress, block_myoverview {{/str}}
             </a>
         </li>
+        {{/displaygroupinginprogress}}
+        {{#displaygroupingfuture}}
+            {{^displaygroupinginprogress}}
+            <li class="dropdown-divider" role="presentation">
+                <span class="filler">&nbsp;</span>
+            </li>
+            {{/displaygroupinginprogress}}
         <li>
             <a class="dropdown-item {{#future}}active{{/future}}" href="#" data-filter="grouping" data-value="future" data-pref="future" aria-label="{{#str}} aria:future, block_myoverview {{/str}}" aria-controls="courses-view-{{uniqid}}">
                 {{#str}} future, block_myoverview {{/str}}
             </a>
         </li>
+        {{/displaygroupingfuture}}
+        {{#displaygroupingpast}}
+            {{^displaygroupinginprogress}}
+                {{^displaygroupingfuture}}
+                <li class="dropdown-divider" role="presentation">
+                    <span class="filler">&nbsp;</span>
+                </li>
+                {{/displaygroupingfuture}}
+            {{/displaygroupinginprogress}}
         <li>
             <a class="dropdown-item {{#past}}active{{/past}}" href="#" data-filter="grouping" data-value="past" data-pref="past" aria-label="{{#str}} aria:past, block_myoverview {{/str}}" aria-controls="courses-view-{{uniqid}}">
                 {{#str}} past, block_myoverview {{/str}}
             </a>
         </li>
+        {{/displaygroupingpast}}
+        {{#displaygroupingstarred}}
         <li class="dropdown-divider" role="presentation">
             <span class="filler">&nbsp;</span>
         </li>
             <a class="dropdown-item {{#favourites}}active{{/favourites}}" href="#" data-filter="grouping" data-value="favourites"  data-pref="favourites" aria-label="{{#str}} aria:favourites, block_myoverview {{/str}}" aria-controls="courses-view-{{uniqid}}">
                 {{#str}} favourites, block_myoverview {{/str}}
             </a>
-        </li>
+        {{/displaygroupingstarred}}
+        {{#displaygroupinghidden}}
         <li class="dropdown-divider" role="presentation">
             <span class="filler">&nbsp;</span>
         </li>
                 {{#str}} hiddencourses, block_myoverview {{/str}}
             </a>
         </li>
+        {{/displaygroupinghidden}}
     </ul>
 </div>
+{{/displaygroupingselector}}
+{{^displaygroupingselector}}
+<div class="mb-1 mr-auto">
+    <span class="filler">&nbsp;</span>
+</div>
+{{/displaygroupingselector}}
diff --git a/blocks/myoverview/tests/behat/block_myoverview_adminsettings.feature b/blocks/myoverview/tests/behat/block_myoverview_adminsettings.feature
new file mode 100644 (file)
index 0000000..5e69256
--- /dev/null
@@ -0,0 +1,226 @@
+@block @block_myoverview @javascript
+Feature: The my overview block allows admins to easily configure the students' course list
+  In order to adapt the my overview block to my users' needs
+  As an admin
+  I can configure the appearance of the my overview block
+
+  Background:
+    Given the following "users" exist:
+      | username | firstname | lastname | email                | idnumber |
+      | student1 | Student   | X        | student1@example.com | S1       |
+    And the following "categories" exist:
+      | name        | category | idnumber |
+      | Category 1  | 0        | CAT1     |
+    And the following "courses" exist:
+      | fullname | shortname | category | startdate                   | enddate         |
+      | Course 1 | C1        | 0        | ##1 month ago##             | ##15 days ago## |
+      | Course 2 | C2        | 0        | ##yesterday##               | ##tomorrow## |
+      | Course 3 | C3        | 0        | ##yesterday##               | ##tomorrow## |
+      | Course 4 | C4        | CAT1     | ##yesterday##               | ##tomorrow## |
+      | Course 5 | C5        | 0        | ##first day of next month## | ##last day of next month## |
+    And the following "course enrolments" exist:
+      | user | course | role |
+      | student1 | C1 | student |
+      | student1 | C2 | student |
+      | student1 | C3 | student |
+      | student1 | C4 | student |
+      | student1 | C5 | student |
+
+  Scenario: Enable 'All' course filter option
+    Given I log in as "admin"
+    And I navigate to "Plugins > Blocks > Course overview" in site administration
+    And I set the field "All" to "1"
+    And I press "Save"
+    And I log out
+    Then I log in as "student1"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
+    # We have to check for the data attribute instead of the list element text as we would get false positives from the "All (except hidden)" element otherwise
+    Then "[data-value='allincludinghidden']" "css_element" should exist in the ".block_myoverview .dropdown-menu" "css_element"
+    And I log out
+
+  Scenario: Disable 'All' course filter option
+    Given I log in as "admin"
+    And I navigate to "Plugins > Blocks > Course overview" in site administration
+    And I set the field "All" to "0"
+    And I press "Save"
+    And I log out
+    Then I log in as "student1"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
+    # We have to check for the data attribute instead of the list element text as we would get false negatives "All (except hidden)" element otherwise
+    Then "[data-value='allincludinghidden']" "css_element" should not exist in the ".block_myoverview .dropdown-menu" "css_element"
+    And I log out
+
+  Scenario: Enable 'All (except hidden)' course filter option
+    Given I log in as "admin"
+    And I navigate to "Plugins > Blocks > Course overview" in site administration
+    And I set the field "All (except hidden)" to "1"
+    And I press "Save"
+    And I log out
+    Then I log in as "student1"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
+    Then "All (except hidden)" "list_item" should exist in the ".block_myoverview .dropdown-menu" "css_element"
+    And I log out
+
+  Scenario: Disable 'All (except hidden)' course filter option
+    Given I log in as "admin"
+    And I navigate to "Plugins > Blocks > Course overview" in site administration
+    And I set the field "All (except hidden)" to "0"
+    And I press "Save"
+    And I log out
+    Then I log in as "student1"
+    # 'All (except hidden)' option has been disabled, so the button is falling back to the 'In progress' option which is the next enabled option.
+    And I click on "In progress" "button" in the "Course overview" "block"
+    Then "All (except hidden)" "list_item" should not exist in the ".block_myoverview .dropdown-menu" "css_element"
+    And I log out
+
+  Scenario: Enable 'In progress' course filter option
+    Given I log in as "admin"
+    And I navigate to "Plugins > Blocks > Course overview" in site administration
+    And I set the field "In progress" to "1"
+    And I press "Save"
+    And I log out
+    Then I log in as "student1"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
+    Then "In progress" "list_item" should exist in the ".block_myoverview .dropdown-menu" "css_element"
+    And I log out
+
+  Scenario: Disable 'In progress' course filter option
+    Given I log in as "admin"
+    And I navigate to "Plugins > Blocks > Course overview" in site administration
+    And I set the field "In progress" to "0"
+    And I press "Save"
+    And I log out
+    Then I log in as "student1"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
+    Then "In progress" "list_item" should not exist in the ".block_myoverview .dropdown-menu" "css_element"
+    And I log out
+
+  Scenario: Enable 'Future' course filter option
+    Given I log in as "admin"
+    And I navigate to "Plugins > Blocks > Course overview" in site administration
+    And I set the field "Future" to "1"
+    And I press "Save"
+    And I log out
+    Then I log in as "student1"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
+    Then "Future" "list_item" should exist in the ".block_myoverview .dropdown-menu" "css_element"
+    And I log out
+
+  Scenario: Disable 'Future' course filter option
+    Given I log in as "admin"
+    And I navigate to "Plugins > Blocks > Course overview" in site administration
+    And I set the field "Future" to "0"
+    And I press "Save"
+    And I log out
+    Then I log in as "student1"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
+    Then "Future" "list_item" should not exist in the ".block_myoverview .dropdown-menu" "css_element"
+    And I log out
+
+  Scenario: Enable 'Past' course filter option
+    Given I log in as "admin"
+    And I navigate to "Plugins > Blocks > Course overview" in site administration
+    And I set the field "Past" to "1"
+    And I press "Save"
+    And I log out
+    Then I log in as "student1"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
+    Then "Past" "list_item" should exist in the ".block_myoverview .dropdown-menu" "css_element"
+    And I log out
+
+  Scenario: Disable 'Past' course filter option
+    Given I log in as "admin"
+    And I navigate to "Plugins > Blocks > Course overview" in site administration
+    And I set the field "Past" to "0"
+    And I press "Save"
+    And I log out
+    Then I log in as "student1"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
+    Then "Past" "list_item" should not exist in the ".block_myoverview .dropdown-menu" "css_element"
+    And I log out
+
+  Scenario: Enable 'Starred' course filter option
+    Given I log in as "admin"
+    And I navigate to "Plugins > Blocks > Course overview" in site administration
+    And I set the field "Starred" to "1"
+    And I press "Save"
+    And I log out
+    Then I log in as "student1"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
+    Then "Starred" "list_item" should exist in the ".block_myoverview .dropdown-menu" "css_element"
+    And I log out
+
+  Scenario: Disable 'Starred' course filter option
+    Given I log in as "admin"
+    And I navigate to "Plugins > Blocks > Course overview" in site administration
+    And I set the field "Starred" to "0"
+    And I press "Save"
+    And I log out
+    Then I log in as "student1"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
+    Then "Starred" "list_item" should not exist in the ".block_myoverview .dropdown-menu" "css_element"
+    And I log out
+
+  Scenario: Enable 'Hidden' course filter option
+    Given I log in as "admin"
+    And I navigate to "Plugins > Blocks > Course overview" in site administration
+    And I set the field "Hidden" to "1"
+    And I press "Save"
+    And I log out
+    Then I log in as "student1"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
+    Then "Hidden" "list_item" should exist in the ".block_myoverview .dropdown-menu" "css_element"
+    And I log out
+
+  Scenario: Disable 'Hidden' course filter option
+    Given I log in as "admin"
+    And I navigate to "Plugins > Blocks > Course overview" in site administration
+    And I set the field "Hidden" to "0"
+    And I press "Save"
+    And I log out
+    Then I log in as "student1"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
+    Then "Hidden" "list_item" should not exist in the ".block_myoverview .dropdown-menu" "css_element"
+    And I log out
+
+  Scenario: Disable all course filter options
+    Given I log in as "admin"
+    And I navigate to "Plugins > Blocks > Course overview" in site administration
+    And I set the field "All" to "0"
+    And I set the field "All (except hidden)" to "0"
+    And I set the field "In progress" to "0"
+    And I set the field "Future" to "0"
+    And I set the field "Past" to "0"
+    And I set the field "Starred" to "0"
+    And I set the field "Hidden" to "0"
+    And I press "Save"
+    And I log out
+    And I log in as "student1"
+    Then "button#groupingdropdown" "css_element" should not exist in the ".block_myoverview" "css_element"
+    And I should see "Course 1" in the "Course overview" "block"
+    And I should see "Course 2" in the "Course overview" "block"
+    And I should see "Course 3" in the "Course overview" "block"
+    And I should see "Course 4" in the "Course overview" "block"
+    And I should see "Course 5" in the "Course overview" "block"
+    And I log out
+
+  Scenario: Disable all but one course filter option
+    Given I log in as "admin"
+    And I navigate to "Plugins > Blocks > Course overview" in site administration
+    And I set the field "All" to "0"
+    And I set the field "All (except hidden)" to "0"
+    And I set the field "In progress" to "1"
+    And I set the field "Future" to "0"
+    And I set the field "Past" to "0"
+    And I set the field "Starred" to "0"
+    And I set the field "Hidden" to "0"
+    And I press "Save"
+    And I log out
+    And I log in as "student1"
+    Then "button#groupingdropdown" "css_element" should not exist in the ".block_myoverview" "css_element"
+    And I should see "Course 2" in the "Course overview" "block"
+    And I should see "Course 3" in the "Course overview" "block"
+    And I should see "Course 4" in the "Course overview" "block"
+    And I should not see "Course 1" in the "Course overview" "block"
+    And I should not see "Course 5" in the "Course overview" "block"
+    And I log out
index b63804b..9101de8 100644 (file)
@@ -28,7 +28,7 @@ Feature: The my overview block allows users to easily access their courses
 
   Scenario: View past courses
     Given I log in as "student1"
-    And I click on "All" "button" in the "Course overview" "block"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
     When I click on "Past" "link" in the "Course overview" "block"
     Then I should see "Course 1" in the "Course overview" "block"
     And I should not see "Course 2" in the "Course overview" "block"
@@ -39,7 +39,7 @@ Feature: The my overview block allows users to easily access their courses
 
   Scenario: View future courses
     Given I log in as "student1"
-    And I click on "All" "button" in the "Course overview" "block"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
     When I click on "Future" "link" in the "Course overview" "block"
     Then I should see "Course 5" in the "Course overview" "block"
     And I should not see "Course 1" in the "Course overview" "block"
@@ -50,7 +50,7 @@ Feature: The my overview block allows users to easily access their courses
 
   Scenario: View inprogress courses
     Given I log in as "student1"
-    And I click on "All" "button" in the "Course overview" "block"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
     When I click on "In progress" "link" in the "Course overview" "block"
     Then I should see "Course 2" in the "Course overview" "block"
     Then I should see "Course 3" in the "Course overview" "block"
@@ -59,10 +59,25 @@ Feature: The my overview block allows users to easily access their courses
     And I should not see "Course 5" in the "Course overview" "block"
     And I log out
 
-  Scenario: View all courses
+  Scenario: View all (except hidden) courses
     Given I log in as "student1"
-    And I click on "All" "button" in the "Course overview" "block"
-    When I click on "All" "link" in the "Course overview" "block"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
+    When I click on "All (except hidden)" "link" in the "Course overview" "block"
+    Then I should see "Course 1" in the "Course overview" "block"
+    Then I should see "Course 2" in the "Course overview" "block"
+    Then I should see "Course 3" in the "Course overview" "block"
+    Then I should see "Course 4" in the "Course overview" "block"
+    Then I should see "Course 5" in the "Course overview" "block"
+    And I log out
+
+  Scenario: View all (including hidden) courses
+    Given the following config values are set as admin:
+      | config                            | value | plugin           |
+      | displaygroupingallincludinghidden | 1     | block_myoverview |
+    And I log in as "student1"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
+    # We have to click on the data attribute instead of the button element text as we might risk to click on the false positive "All (except hidden)" element instead
+    When I click on "[data-value='allincludinghidden']" "css_element" in the "Course overview" "block"
     Then I should see "Course 1" in the "Course overview" "block"
     Then I should see "Course 2" in the "Course overview" "block"
     Then I should see "Course 3" in the "Course overview" "block"
@@ -72,7 +87,7 @@ Feature: The my overview block allows users to easily access their courses
 
   Scenario: View inprogress courses - test persistence
     Given I log in as "student1"
-    And I click on "All" "button" in the "Course overview" "block"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
     And I click on "In progress" "link" in the "Course overview" "block"
     And I reload the page
     Then I should see "In progress" in the "Course overview" "block"
@@ -83,12 +98,12 @@ Feature: The my overview block allows users to easily access their courses
     And I should not see "Course 5" in the "Course overview" "block"
     And I log out
 
-  Scenario: View all courses - w/ persistence
+  Scenario: View all (except hidden) courses - w/ persistence
     Given I log in as "student1"
-    And I click on "All" "button" in the "Course overview" "block"
-    When I click on "All" "link" in the "Course overview" "block"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
+    When I click on "All (except hidden)" "link" in the "Course overview" "block"
     And I reload the page
-    Then I should see "All" in the "Course overview" "block"
+    Then I should see "All (except hidden)" in the "Course overview" "block"
     Then I should see "Course 1" in the "Course overview" "block"
     Then I should see "Course 2" in the "Course overview" "block"
     Then I should see "Course 3" in the "Course overview" "block"
@@ -98,7 +113,7 @@ Feature: The my overview block allows users to easily access their courses
 
   Scenario: View past courses - w/ persistence
     Given I log in as "student1"
-    And I click on "All" "button" in the "Course overview" "block"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
     When I click on "Past" "link" in the "Course overview" "block"
     And I reload the page
     Then I should see "Past" in the "Course overview" "block"
@@ -111,7 +126,7 @@ Feature: The my overview block allows users to easily access their courses
 
   Scenario: View future courses - w/ persistence
     Given I log in as "student1"
-    And I click on "All" "button" in the "Course overview" "block"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
     When I click on "Future" "link" in the "Course overview" "block"
     And I reload the page
     Then I should see "Future" in the "Course overview" "block"
@@ -164,7 +179,7 @@ Feature: The my overview block allows users to easily access their courses
 
   Scenario: View inprogress courses with hide persistent functionality
     Given I log in as "student1"
-    And I click on "All" "button" in the "Course overview" "block"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
     When I click on "In progress" "link" in the "Course overview" "block"
     And I click on ".coursemenubtn" "css_element" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
     And I click on "Hide from view" "link" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
@@ -178,7 +193,7 @@ Feature: The my overview block allows users to easily access their courses
 
   Scenario: View past courses with hide persistent functionality
     Given I log in as "student1"
-    And I click on "All" "button" in the "Course overview" "block"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
     When I click on "Past" "link" in the "Course overview" "block"
     And I click on ".coursemenubtn" "css_element" in the "//div[@class='card dashboard-card' and contains(.,'Course 1')]" "xpath_element"
     And I click on "Hide from view" "link" in the "//div[@class='card dashboard-card' and contains(.,'Course 1')]" "xpath_element"
@@ -192,7 +207,7 @@ Feature: The my overview block allows users to easily access their courses
 
   Scenario: View future courses with hide persistent functionality
     Given I log in as "student1"
-    And I click on "All" "button" in the "Course overview" "block"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
     When I click on "Future" "link" in the "Course overview" "block"
     And I click on ".coursemenubtn" "css_element" in the "//div[@class='card dashboard-card' and contains(.,'Course 5')]" "xpath_element"
     And I click on "Hide from view" "link" in the "//div[@class='card dashboard-card' and contains(.,'Course 5')]" "xpath_element"
@@ -204,6 +219,38 @@ Feature: The my overview block allows users to easily access their courses
     And I should not see "Course 4" in the "Course overview" "block"
     And I log out
 
+  Scenario: View all (except hidden) courses with hide persistent functionality
+    Given I log in as "student1"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
+    When I click on "All (except hidden)" "link" in the "Course overview" "block"
+    And I click on ".coursemenubtn" "css_element" in the "//div[@class='card dashboard-card' and contains(.,'Course 5')]" "xpath_element"
+    And I click on "Hide from view" "link" in the "//div[@class='card dashboard-card' and contains(.,'Course 5')]" "xpath_element"
+    And I reload the page
+    Then I should not see "Course 5" in the "Course overview" "block"
+    And I should see "Course 1" in the "Course overview" "block"
+    And I should see "Course 2" in the "Course overview" "block"
+    And I should see "Course 3" in the "Course overview" "block"
+    And I should see "Course 4" in the "Course overview" "block"
+    And I log out
+
+  Scenario: View all (including hidden) courses with hide persistent functionality
+    Given the following config values are set as admin:
+      | config                            | value | plugin           |
+      | displaygroupingallincludinghidden | 1     | block_myoverview |
+    And I log in as "student1"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
+    # We have to click on the data attribute instead of the button element text as we might risk to click on the false positive "All (except hidden)" element instead
+    When I click on "[data-value='allincludinghidden']" "css_element" in the "Course overview" "block"
+    And I click on ".coursemenubtn" "css_element" in the "//div[@class='card dashboard-card' and contains(.,'Course 5')]" "xpath_element"
+    And I click on "Hide from view" "link" in the "//div[@class='card dashboard-card' and contains(.,'Course 5')]" "xpath_element"
+    And I reload the page
+    Then I should see "Course 5" in the "Course overview" "block"
+    And I should see "Course 1" in the "Course overview" "block"
+    And I should see "Course 2" in the "Course overview" "block"
+    And I should see "Course 3" in the "Course overview" "block"
+    And I should see "Course 4" in the "Course overview" "block"
+    And I log out
+
   Scenario: Show course category in cards display
     Given I log in as "student1"
     And I click on "Display drop-down menu" "button" in the "Course overview" "block"
index c9c4845..e494c75 100644 (file)
@@ -25,7 +25,9 @@ Feature: The my overview block allows users to hide their courses
 
   Scenario: Test hide toggle functionality
     Given I log in as "student1"
-    When I click on ".coursemenubtn" "css_element" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
+    When I click on "All (except hidden)" "link" in the "Course overview" "block"
+    And I click on ".coursemenubtn" "css_element" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
     And I click on "Hide from view" "link" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
     And I reload the page
     Then I should not see "Course 2" in the "Course overview" "block"
@@ -33,13 +35,15 @@ Feature: The my overview block allows users to hide their courses
 
   Scenario: Test hide toggle functionality w/ favorites
     Given I log in as "student1"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
+    When I click on "All (except hidden)" "link" in the "Course overview" "block"
     And I click on ".coursemenubtn" "css_element" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
     And I click on "Star this course" "link" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
     And I click on ".coursemenubtn" "css_element" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
     And I click on "Hide from view" "link" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
     When I reload the page
     Then I should not see "Course 2" in the "Course overview" "block"
-    And I click on "All" "button" in the "Course overview" "block"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
     And I click on "Starred" "link" in the "Course overview" "block"
     Then I should not see "Course 2" in the "Course overview" "block"
     And I click on "Starred" "button" in the "Course overview" "block"
@@ -49,25 +53,29 @@ Feature: The my overview block allows users to hide their courses
 
   Scenario: Test show toggle functionality
     Given I log in as "student1"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
+    When I click on "All (except hidden)" "link" in the "Course overview" "block"
     And I click on ".coursemenubtn" "css_element" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
     And I click on "Hide from view" "link" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
-    When I click on "All" "button" in the "Course overview" "block"
+    When I click on "All (except hidden)" "button" in the "Course overview" "block"
     And I click on "Hidden" "link" in the "Course overview" "block"
     When I click on ".coursemenubtn" "css_element" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
     And I click on "Show this course" "link" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
     And I reload the page
     And I click on "Hidden" "button" in the "Course overview" "block"
-    When I click on "All" "link" in the "Course overview" "block"
+    When I click on "All (except hidden)" "link" in the "Course overview" "block"
     Then I should see "Course 2" in the "Course overview" "block"
     And I log out
 
   Scenario: Test show toggle functionality w/ favorites
     Given I log in as "student1"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
+    When I click on "All (except hidden)" "link" in the "Course overview" "block"
     And I click on ".coursemenubtn" "css_element" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
     And I click on "Star this course" "link" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
     And I click on ".coursemenubtn" "css_element" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
     And I click on "Hide from view" "link" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
-    And I click on "All" "button" in the "Course overview" "block"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
     And I click on "Hidden" "link" in the "Course overview" "block"
     And I should see "Course 2" in the "Course overview" "block"
     And I click on ".coursemenubtn" "css_element" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
@@ -75,9 +83,39 @@ Feature: The my overview block allows users to hide their courses
     When I reload the page
     Then I should not see "Course 2" in the "Course overview" "block"
     And I click on "Hidden" "button" in the "Course overview" "block"
-    And I click on "All" "link" in the "Course overview" "block"
+    And I click on "All (except hidden)" "link" in the "Course overview" "block"
     Then I should see "Course 2" in the "Course overview" "block"
-    And I click on "All" "button" in the "Course overview" "block"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
     And I click on "Starred" "link" in the "Course overview" "block"
     Then I should see "Course 2" in the "Course overview" "block"
+    And I log out
+
+  Scenario: Test a course is hidden directly with "All (except hidden)" courses
+    Given I log in as "student1"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
+    When I click on "All (except hidden)" "link" in the "Course overview" "block"
+    And I click on ".coursemenubtn" "css_element" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
+    And I click on "Hide from view" "link" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
+    Then I should not see "Course 2" in the "Course overview" "block"
+    And I log out
+
+  Scenario: Test a course is never hidden with "All (including hidden)" courses
+    Given the following config values are set as admin:
+      | config                            | value | plugin           |
+      | displaygroupingallincludinghidden | 1     | block_myoverview |
+    And I log in as "student1"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
+    # We have to click on the data attribute instead of the button element text as we might risk to click on the false positive "All (except hidden)" element instead
+    When I click on "[data-value='allincludinghidden']" "css_element" in the "Course overview" "block"
+    And I click on ".coursemenubtn" "css_element" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
+    And I click on "Hide from view" "link" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
+    Then I should see "Course 2" in the "Course overview" "block"
+    And I click on ".coursemenubtn" "css_element" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
+    And I should not see "Hide from view" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
+    And I should see "Show this course" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
+    And I click on "Show this course" "link" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
+    Then I should see "Course 2" in the "Course overview" "block"
+    And I click on ".coursemenubtn" "css_element" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
+    And I should see "Hide from view" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
+    And I should not see "Show this course" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
     And I log out
\ No newline at end of file
index 6e0e107..84f2e4e 100644 (file)
@@ -50,7 +50,7 @@ Feature: The my overview block allows users to persistence of their page limits
     Given I log in as "student1"
     When I click on "[data-toggle='dropdown']" "css_element" in the "Course overview" "block"
     And I click on "All" "link"
-    And I click on "All" "button" in the "Course overview" "block"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
     And I click on "In progress" "link" in the "Course overview" "block"
     Then I should see "Course 13"
     And I should see "All" in the "[data-action='limit-toggle']" "css_element"
index 06813f5..3c2c1c9 100644 (file)
@@ -22,8 +22,8 @@ Feature: Course overview block show users their progress on courses
 
   Scenario: Course progress percentage should not be displayed if completion is not enabled
     Given I log in as "student1"
-    And I click on "All" "button" in the "Course overview" "block"
-    When I click on "All" "link" in the "Course overview" "block"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
+    When I click on "All (except hidden)" "link" in the "Course overview" "block"
     Then I should not see "0%" in the "Course overview" "block"
     And I log out
 
@@ -38,12 +38,12 @@ Feature: Course overview block show users their progress on courses
     And I press "Save and return to course"
     And I log out
     When I log in as "student1"
-    And I click on "All" "button" in the "Course overview" "block"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
     Then I should see "Course 1" in the "Course overview" "block"
     And I should see "0%" in the "Course overview" "block"
     And I am on "Course 1" course homepage
     And I follow "Test choice 1"
     And I follow "Dashboard" in the user menu
-    And I click on "All" "button" in the "Course overview" "block"
+    And I click on "All (except hidden)" "button" in the "Course overview" "block"
     And I should see "100%" in the "Course overview" "block"
     And I log out
index 5b5b488..12a7387 100644 (file)
@@ -72,6 +72,7 @@ class block_myoverview_privacy_testcase extends \core_privacy\tests\provider_tes
         return array(
             array('block_myoverview_user_sort_preference', 'lastaccessed', ''),
             array('block_myoverview_user_sort_preference', 'title', ''),
+            array('block_myoverview_user_grouping_preference', 'allincludinghidden', ''),
             array('block_myoverview_user_grouping_preference', 'all', ''),
             array('block_myoverview_user_grouping_preference', 'inprogress', ''),
             array('block_myoverview_user_grouping_preference', 'future', ''),
index 4c93e24..6b5751d 100644 (file)
@@ -24,6 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2019070400;         // The current plugin version (Date: YYYYMMDDXX).
+$plugin->version   = 2019070401;         // The current plugin version (Date: YYYYMMDDXX).
 $plugin->requires  = 2019051100;         // Requires this Moodle version.
 $plugin->component = 'block_myoverview'; // Full name of the plugin (used for diagnostics).
index cc895b3..8da5ad1 100644 (file)
@@ -50,8 +50,22 @@ function xmldb_block_recentlyaccesseditems_upgrade($oldversion, $block) {
     // Automatically generated Moodle v3.7.0 release upgrade line.
     // Put any upgrade step following this.
     if ($oldversion < 2019052001) {
-        $sql = "courseid NOT IN (SELECT c.id from {course} c) OR cmid NOT IN (SELECT cm.id from {course_modules} cm)";
-        $DB->delete_records_select("block_recentlyaccesseditems", $sql);
+        // Query the items to be deleted as a list of IDs. We cannot delete directly from this as a
+        // subquery because MySQL does not support delete with subqueries.
+        $fordeletion = $DB->get_fieldset_sql("
+                SELECT rai.id
+                  FROM {block_recentlyaccesseditems} rai
+             LEFT JOIN {course} c ON c.id = rai.courseid
+             LEFT JOIN {course_modules} cm ON cm.id = rai.cmid
+                 WHERE c.id IS NULL OR cm.id IS NULL");
+
+        // Delete the array in chunks of 500 (Oracle does not support more than 1000 parameters,
+        // let's leave some leeway, there are likely only one chunk anyway).
+        $chunks = array_chunk($fordeletion, 500);
+        foreach ($chunks as $chunk) {
+            $DB->delete_records_list('block_recentlyaccesseditems', 'id', $chunk);
+        }
+
         upgrade_block_savepoint(true, 2019052001, 'recentlyaccesseditems', false);
     }
 
index 06f312d..b76483d 100644 (file)
@@ -49,12 +49,13 @@ class core_comment_external extends external_api {
 
         return new external_function_parameters(
             array(
-                'contextlevel' => new external_value(PARAM_ALPHA, 'contextlevel system, course, user...'),
-                'instanceid'   => new external_value(PARAM_INT, 'the Instance id of item associated with the context level'),
-                'component'    => new external_value(PARAM_COMPONENT, 'component'),
-                'itemid'       => new external_value(PARAM_INT, 'associated id'),
-                'area'         => new external_value(PARAM_AREA, 'string comment area', VALUE_DEFAULT, ''),
-                'page'         => new external_value(PARAM_INT, 'page number (0 based)', VALUE_DEFAULT, 0),
+                'contextlevel'  => new external_value(PARAM_ALPHA, 'contextlevel system, course, user...'),
+                'instanceid'    => new external_value(PARAM_INT, 'the Instance id of item associated with the context level'),
+                'component'     => new external_value(PARAM_COMPONENT, 'component'),
+                'itemid'        => new external_value(PARAM_INT, 'associated id'),
+                'area'          => new external_value(PARAM_AREA, 'string comment area', VALUE_DEFAULT, ''),
+                'page'          => new external_value(PARAM_INT, 'page number (0 based)', VALUE_DEFAULT, 0),
+                'sortdirection' => new external_value(PARAM_ALPHA, 'Sort direction: ASC or DESC', VALUE_DEFAULT, 'DESC'),
             )
         );
     }
@@ -68,22 +69,33 @@ class core_comment_external extends external_api {
      * @param int $itemid the item id
      * @param string $area comment area
      * @param int $page page number
+     * @param string $sortdirection sort direction
      * @return array of comments and warnings
      * @since Moodle 2.9
      */
-    public static function get_comments($contextlevel, $instanceid, $component, $itemid, $area = '', $page = 0) {
+    public static function get_comments($contextlevel, $instanceid, $component, $itemid, $area = '', $page = 0,
+            $sortdirection = 'DESC') {
+        global $CFG;
 
         $warnings = array();
         $arrayparams = array(
-            'contextlevel' => $contextlevel,
-            'instanceid'   => $instanceid,
-            'component'    => $component,
-            'itemid'       => $itemid,
-            'area'         => $area,
-            'page'         => $page
+            'contextlevel'  => $contextlevel,
+            'instanceid'    => $instanceid,
+            'component'     => $component,
+            'itemid'        => $itemid,
+            'area'          => $area,
+            'page'          => $page,
+            'sortdirection' => $sortdirection,
         );
         $params = self::validate_parameters(self::get_comments_parameters(), $arrayparams);
 
+        $sortdirection = strtoupper($params['sortdirection']);
+        $directionallowedvalues = array('ASC', 'DESC');
+        if (!in_array($sortdirection, $directionallowedvalues)) {
+            throw new invalid_parameter_exception('Invalid value for sortdirection parameter (value: ' . $sortdirection . '),' .
+                'allowed values are: ' . implode(',', $directionallowedvalues));
+        }
+
         $context = self::get_context_from_params($params);
         self::validate_context($context);
 
@@ -96,7 +108,7 @@ class core_comment_external extends external_api {
         $args->component = $params['component'];
 
         $commentobject = new comment($args);
-        $comments = $commentobject->get_comments($params['page']);
+        $comments = $commentobject->get_comments($params['page'], $sortdirection);
 
         // False means no permissions to see comments.
         if ($comments === false) {
@@ -117,6 +129,8 @@ class core_comment_external extends external_api {
 
         $results = array(
             'comments' => $comments,
+            'count' => $commentobject->count(),
+            'perpage' => (!empty($CFG->commentsperpage)) ? $CFG->commentsperpage : 15,
             'warnings' => $warnings
         );
         return $results;
@@ -148,6 +162,8 @@ class core_comment_external extends external_api {
                         ), 'comment'
                     ), 'List of comments'
                 ),
+                'count' => new external_value(PARAM_INT,  'Total number of comments.', VALUE_OPTIONAL),
+                'perpage' => new external_value(PARAM_INT,  'Number of comments per page.', VALUE_OPTIONAL),
                 'warnings' => new external_warnings()
             )
         );
index f8e9f23..463fa00 100644 (file)
@@ -537,9 +537,10 @@ class comment {
      * Return matched comments
      *
      * @param  int $page
+     * @param  str $sortdirection sort direction, ASC or DESC
      * @return array
      */
-    public function get_comments($page = '') {
+    public function get_comments($page = '', $sortdirection = 'DESC') {
         global $DB, $CFG, $USER, $OUTPUT;
         if (!$this->can_view()) {
             return false;
@@ -557,6 +558,7 @@ class comment {
             $params['component'] = $component;
         }
 
+        $sortdirection = ($sortdirection === 'ASC') ? 'ASC' : 'DESC';
         $sql = "SELECT $ufields, c.id AS cid, c.content AS ccontent, c.format AS cformat, c.timecreated AS ctimecreated
                   FROM {comments} c
                   JOIN {user} u ON u.id = c.userid
@@ -564,7 +566,7 @@ class comment {
                        c.commentarea = :commentarea AND
                        c.itemid = :itemid AND
                        $componentwhere
-              ORDER BY c.timecreated DESC";
+              ORDER BY c.timecreated $sortdirection";
         $params['contextid'] = $this->contextid;
         $params['commentarea'] = $this->commentarea;
         $params['itemid'] = $this->itemid;
index c6a7433..e9eb0c2 100644 (file)
@@ -123,11 +123,34 @@ class core_comment_externallib_testcase extends externallib_advanced_testcase {
 
         $this->assertCount(0, $result['warnings']);
         $this->assertCount(2, $result['comments']);
+        $this->assertEquals(2, $result['count']);
+        $this->assertEquals(15, $result['perpage']);
 
         $this->assertEquals($user->id, $result['comments'][0]['userid']);
         $this->assertEquals($user->id, $result['comments'][1]['userid']);
 
-        $this->assertEquals($cmtid2, $result['comments'][0]['id']);
+        $this->assertEquals($cmtid2, $result['comments'][0]['id']); // Default ordering newer first.
         $this->assertEquals($cmtid1, $result['comments'][1]['id']);
+
+        // Test sort direction and pagination.
+        $CFG->commentsperpage = 1;
+        $result = core_comment_external::get_comments($contextlevel, $instanceid, $component, $itemid, $area, $page, 'ASC');
+        $result = external_api::clean_returnvalue(core_comment_external::get_comments_returns(), $result);
+
+        $this->assertCount(0, $result['warnings']);
+        $this->assertCount(1, $result['comments']); // Only one per page.
+        $this->assertEquals(2, $result['count']);
+        $this->assertEquals($CFG->commentsperpage, $result['perpage']);
+        $this->assertEquals($cmtid1, $result['comments'][0]['id']); // Comments order older first.
+
+        // Next page.
+        $result = core_comment_external::get_comments($contextlevel, $instanceid, $component, $itemid, $area, $page + 1, 'ASC');
+        $result = external_api::clean_returnvalue(core_comment_external::get_comments_returns(), $result);
+
+        $this->assertCount(0, $result['warnings']);
+        $this->assertCount(1, $result['comments']);
+        $this->assertEquals(2, $result['count']);
+        $this->assertEquals($CFG->commentsperpage, $result['perpage']);
+        $this->assertEquals($cmtid2, $result['comments'][0]['id']);
     }
 }
diff --git a/comment/upgrade.txt b/comment/upgrade.txt
new file mode 100644 (file)
index 0000000..2510a99
--- /dev/null
@@ -0,0 +1,7 @@
+This files describes API changes in /comment/* ,
+information provided here is intended especially for developers.
+
+=== 3.8 ===
+  * External function get_comments now returns the total count of comments and the number of comments per page.
+    It also has a new parameter to indicate the sorting direction (defaulted to DESC).
+
index 5a86efa..597a77a 100644 (file)
@@ -13,7 +13,7 @@
     "require-dev": {
         "phpunit/phpunit": "7.5.*",
         "phpunit/dbunit": "4.0.*",
-        "moodlehq/behat-extension": "3.38.1",
+        "moodlehq/behat-extension": "3.38.2",
         "mikey179/vfsstream": "^1.6",
         "instaclick/php-webdriver": "dev-local as 1.x-dev"
     }
index 9f51b08..30350b9 100644 (file)
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "413289581153e5427c3f4fc277185fb0",
+    "content-hash": "f3e6814cafec1673c1fa51dea8a41306",
     "packages": [],
     "packages-dev": [
         {
         },
         {
             "name": "moodlehq/behat-extension",
-            "version": "v3.38.1",
+            "version": "v3.38.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/moodlehq/moodle-behat-extension.git",
-                "reference": "a3c38c2864e7259b1de834218abfe49eecc03417"
+                "reference": "ee293554b4e75b7444576bfe0c9f6779fb1c04cb"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/moodlehq/moodle-behat-extension/zipball/a3c38c2864e7259b1de834218abfe49eecc03417",
-                "reference": "a3c38c2864e7259b1de834218abfe49eecc03417",
+                "url": "https://api.github.com/repos/moodlehq/moodle-behat-extension/zipball/ee293554b4e75b7444576bfe0c9f6779fb1c04cb",
+                "reference": "ee293554b4e75b7444576bfe0c9f6779fb1c04cb",
                 "shasum": ""
             },
             "require": {
                 "behat/mink-extension": "~2.2",
                 "behat/mink-goutte-driver": "~1.2",
                 "behat/mink-selenium2-driver": "~1.3",
-                "php": ">=5.4.4",
+                "php": ">=7.1.0",
                 "symfony/process": "2.8.*"
             },
             "type": "library",
             "authors": [
                 {
                     "name": "David MonllaĆ³",
+                    "role": "Developer",
                     "email": "david.monllao@gmail.com",
-                    "homepage": "http://moodle.com",
-                    "role": "Developer"
+                    "homepage": "http://moodle.com"
                 }
             ],
             "description": "Moodle behat extension",
                 "Behat",
                 "moodle"
             ],
-            "time": "2019-07-18T08:33:39+00:00"
+            "time": "2019-08-26T22:08:54+00:00"
         },
         {
             "name": "myclabs/deep-copy",
             "authors": [
                 {
                     "name": "Arne Blankerts",
-                    "email": "arne@blankerts.de",
-                    "role": "Developer"
+                    "role": "Developer",
+                    "email": "arne@blankerts.de"
                 },
                 {
                     "name": "Sebastian Heuer",
-                    "email": "sebastian@phpeople.de",
-                    "role": "Developer"
+                    "role": "Developer",
+                    "email": "sebastian@phpeople.de"
                 },
                 {
                     "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "Developer"
+                    "role": "Developer",
+                    "email": "sebastian@phpunit.de"
                 }
             ],
             "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
             "authors": [
                 {
                     "name": "Arne Blankerts",
-                    "email": "arne@blankerts.de",
-                    "role": "Developer"
+                    "role": "Developer",
+                    "email": "arne@blankerts.de"
                 },
                 {
                     "name": "Sebastian Heuer",
-                    "email": "sebastian@phpeople.de",
-                    "role": "Developer"
+                    "role": "Developer",
+                    "email": "sebastian@phpeople.de"
                 },
                 {
                     "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "Developer"
+                    "role": "Developer",
+                    "email": "sebastian@phpunit.de"
                 }
             ],
             "description": "Library for handling version information and constraints",
             "authors": [
                 {
                     "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
+                    "role": "lead",
+                    "email": "sebastian@phpunit.de"
                 }
             ],
             "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
             "authors": [
                 {
                     "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
+                    "role": "lead",
+                    "email": "sebastian@phpunit.de"
                 }
             ],
             "description": "FilterIterator implementation that filters files based on a list of suffixes.",
             "authors": [
                 {
                     "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
+                    "role": "lead",
+                    "email": "sebastian@phpunit.de"
                 }
             ],
             "description": "Simple template engine.",
             "authors": [
                 {
                     "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
+                    "role": "lead",
+                    "email": "sebastian@phpunit.de"
                 }
             ],
             "description": "Utility class for timing",
         },
         {
             "name": "phpunit/phpunit",
-            "version": "7.5.14",
+            "version": "7.5.15",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/phpunit.git",
-                "reference": "2834789aeb9ac182ad69bfdf9ae91856a59945ff"
+                "reference": "d79c053d972856b8b941bb233e39dc521a5093f0"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2834789aeb9ac182ad69bfdf9ae91856a59945ff",
-                "reference": "2834789aeb9ac182ad69bfdf9ae91856a59945ff",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d79c053d972856b8b941bb233e39dc521a5093f0",
+                "reference": "d79c053d972856b8b941bb233e39dc521a5093f0",
                 "shasum": ""
             },
             "require": {
             "authors": [
                 {
                     "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
+                    "role": "lead",
+                    "email": "sebastian@phpunit.de"
                 }
             ],
             "description": "The PHP Unit Testing framework.",
                 "testing",
                 "xunit"
             ],
-            "time": "2019-07-15T06:24:08+00:00"
+            "time": "2019-08-21T07:05:16+00:00"
         },
         {
             "name": "psr/container",
             "authors": [
                 {
                     "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
+                    "role": "lead",
+                    "email": "sebastian@phpunit.de"
                 }
             ],
             "description": "Library that helps with managing the version number of Git-hosted PHP projects",
         },
         {
             "name": "symfony/browser-kit",
-            "version": "v4.3.3",
+            "version": "v4.3.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/browser-kit.git",
-                "reference": "a29dd02a1f3f81b9a15c7730cc3226718ddb55ca"
+                "reference": "9e5dddb637b13db82e35695a8603fe6e118cc119"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/browser-kit/zipball/a29dd02a1f3f81b9a15c7730cc3226718ddb55ca",
-                "reference": "a29dd02a1f3f81b9a15c7730cc3226718ddb55ca",
+                "url": "https://api.github.com/repos/symfony/browser-kit/zipball/9e5dddb637b13db82e35695a8603fe6e118cc119",
+                "reference": "9e5dddb637b13db82e35695a8603fe6e118cc119",
                 "shasum": ""
             },
             "require": {
             ],
             "description": "Symfony BrowserKit Component",
             "homepage": "https://symfony.com",
-            "time": "2019-06-11T15:41:59+00:00"
+            "time": "2019-08-26T08:26:39+00:00"
         },
         {
             "name": "symfony/class-loader",
-            "version": "v3.4.30",
+            "version": "v3.4.31",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/class-loader.git",
-                "reference": "4459eef5298dedfb69f771186a580062b8516497"
+                "reference": "e212b06996819a2bce026a63da03b7182d05a690"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/class-loader/zipball/4459eef5298dedfb69f771186a580062b8516497",
-                "reference": "4459eef5298dedfb69f771186a580062b8516497",
+                "url": "https://api.github.com/repos/symfony/class-loader/zipball/e212b06996819a2bce026a63da03b7182d05a690",
+                "reference": "e212b06996819a2bce026a63da03b7182d05a690",
                 "shasum": ""
             },
             "require": {
             ],
             "description": "Symfony ClassLoader Component",
             "homepage": "https://symfony.com",
-            "time": "2019-01-16T09:39:14+00:00"
+            "time": "2019-08-20T13:31:17+00:00"
         },
         {
             "name": "symfony/config",
-            "version": "v4.3.3",
+            "version": "v4.3.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/config.git",
-                "reference": "a17a2aea43950ce83a0603ed301bac362eb86870"
+                "reference": "07d49c0f823e0bc367c6d84e35b61419188a5ece"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/config/zipball/a17a2aea43950ce83a0603ed301bac362eb86870",
-                "reference": "a17a2aea43950ce83a0603ed301bac362eb86870",
+                "url": "https://api.github.com/repos/symfony/config/zipball/07d49c0f823e0bc367c6d84e35b61419188a5ece",
+                "reference": "07d49c0f823e0bc367c6d84e35b61419188a5ece",
                 "shasum": ""
             },
             "require": {
             ],
             "description": "Symfony Config Component",
             "homepage": "https://symfony.com",
-            "time": "2019-07-18T10:34:59+00:00"
+            "time": "2019-08-26T08:26:39+00:00"
         },
         {
             "name": "symfony/console",
         },
         {
             "name": "symfony/css-selector",
-            "version": "v3.4.30",
+            "version": "v3.4.31",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/css-selector.git",
-                "reference": "8ca29297c29b64fb3a1a135e71cb25f67f9fdccf"
+                "reference": "e18c5c4b35e7f17513448a25d02f7af34a4bdb41"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/css-selector/zipball/8ca29297c29b64fb3a1a135e71cb25f67f9fdccf",
-                "reference": "8ca29297c29b64fb3a1a135e71cb25f67f9fdccf",
+                "url": "https://api.github.com/repos/symfony/css-selector/zipball/e18c5c4b35e7f17513448a25d02f7af34a4bdb41",
+                "reference": "e18c5c4b35e7f17513448a25d02f7af34a4bdb41",
                 "shasum": ""
             },
             "require": {
             ],
             "description": "Symfony CssSelector Component",
             "homepage": "https://symfony.com",
-            "time": "2019-01-16T09:39:14+00:00"
+            "time": "2019-08-20T13:31:17+00:00"
         },
         {
             "name": "symfony/debug",
-            "version": "v3.4.30",
+            "version": "v3.4.31",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/debug.git",
-                "reference": "bc977cb2681d75988ab2d53d14c4245c6c04f82f"
+                "reference": "0b600300918780001e2821db77bc28b677794486"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/debug/zipball/bc977cb2681d75988ab2d53d14c4245c6c04f82f",
-                "reference": "bc977cb2681d75988ab2d53d14c4245c6c04f82f",
+                "url": "https://api.github.com/repos/symfony/debug/zipball/0b600300918780001e2821db77bc28b677794486",
+                "reference": "0b600300918780001e2821db77bc28b677794486",
                 "shasum": ""
             },
             "require": {
             ],
             "description": "Symfony Debug Component",
             "homepage": "https://symfony.com",
-            "time": "2019-07-23T08:39:19+00:00"
+            "time": "2019-08-20T13:31:17+00:00"
         },
         {
             "name": "symfony/dependency-injection",
         },
         {
             "name": "symfony/dom-crawler",
-            "version": "v4.3.3",
+            "version": "v4.3.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/dom-crawler.git",
-                "reference": "291397232a2eefb3347eaab9170409981eaad0e2"
+                "reference": "cc686552948d627528c0e2e759186dff67c2610e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/291397232a2eefb3347eaab9170409981eaad0e2",
-                "reference": "291397232a2eefb3347eaab9170409981eaad0e2",
+                "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/cc686552948d627528c0e2e759186dff67c2610e",
+                "reference": "cc686552948d627528c0e2e759186dff67c2610e",
                 "shasum": ""
             },
             "require": {
             ],
             "description": "Symfony DomCrawler Component",
             "homepage": "https://symfony.com",
-            "time": "2019-06-13T11:03:18+00:00"
+            "time": "2019-08-26T08:26:39+00:00"
         },
         {
             "name": "symfony/event-dispatcher",
-            "version": "v3.4.30",
+            "version": "v3.4.31",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/event-dispatcher.git",
-                "reference": "f18fdd6cc7006441865e698420cee26bac94741f"
+                "reference": "3e922c4c3430b9de624e8a285dada5e61e230959"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/f18fdd6cc7006441865e698420cee26bac94741f",
-                "reference": "f18fdd6cc7006441865e698420cee26bac94741f",
+                "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/3e922c4c3430b9de624e8a285dada5e61e230959",
+                "reference": "3e922c4c3430b9de624e8a285dada5e61e230959",
                 "shasum": ""
             },
             "require": {
             ],
             "description": "Symfony EventDispatcher Component",
             "homepage": "https://symfony.com",
-            "time": "2019-06-25T07:45:31+00:00"
+            "time": "2019-08-23T08:05:57+00:00"
         },
         {
             "name": "symfony/filesystem",
-            "version": "v4.3.3",
+            "version": "v4.3.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/filesystem.git",
-                "reference": "b9896d034463ad6fd2bf17e2bf9418caecd6313d"
+                "reference": "9abbb7ef96a51f4d7e69627bc6f63307994e4263"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/filesystem/zipball/b9896d034463ad6fd2bf17e2bf9418caecd6313d",
-                "reference": "b9896d034463ad6fd2bf17e2bf9418caecd6313d",
+                "url": "https://api.github.com/repos/symfony/filesystem/zipball/9abbb7ef96a51f4d7e69627bc6f63307994e4263",
+                "reference": "9abbb7ef96a51f4d7e69627bc6f63307994e4263",
                 "shasum": ""
             },
             "require": {
             ],
             "description": "Symfony Filesystem Component",
             "homepage": "https://symfony.com",
-            "time": "2019-06-23T08:51:25+00:00"
+            "time": "2019-08-20T14:07:54+00:00"
         },
         {
             "name": "symfony/polyfill-ctype",
             "authors": [
                 {
                     "name": "Arne Blankerts",
-                    "email": "arne@blankerts.de",
-                    "role": "Developer"
+                    "role": "Developer",
+                    "email": "arne@blankerts.de"
                 }
             ],
             "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
         },
         {
             "name": "webmozart/assert",
-            "version": "1.4.0",
+            "version": "1.5.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/webmozart/assert.git",
-                "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9"
+                "reference": "88e6d84706d09a236046d686bbea96f07b3a34f4"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/webmozart/assert/zipball/83e253c8e0be5b0257b881e1827274667c5c17a9",
-                "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9",
+                "url": "https://api.github.com/repos/webmozart/assert/zipball/88e6d84706d09a236046d686bbea96f07b3a34f4",
+                "reference": "88e6d84706d09a236046d686bbea96f07b3a34f4",
                 "shasum": ""
             },
             "require": {
                 "symfony/polyfill-ctype": "^1.8"
             },
             "require-dev": {
-                "phpunit/phpunit": "^4.6",
-                "sebastian/version": "^1.0.1"
+                "phpunit/phpunit": "^4.8.36 || ^7.5.13"
             },
             "type": "library",
             "extra": {
                 "check",
                 "validate"
             ],
-            "time": "2018-12-25T11:19:39+00:00"
+            "time": "2019-08-24T08:43:50+00:00"
         }
     ],
     "aliases": [
index 7f3a170..8877119 100644 (file)
@@ -3741,6 +3741,8 @@ class core_course_external extends external_api {
         $sort = $params['sort'];
 
         switch($classification) {
+            case COURSE_TIMELINE_ALLINCLUDINGHIDDEN:
+                break;
             case COURSE_TIMELINE_ALL:
                 break;
             case COURSE_TIMELINE_PAST:
@@ -3764,10 +3766,16 @@ class core_course_external extends external_api {
         $hiddencourses = get_hidden_courses_on_timeline();
         $courses = [];
 
-        // If the timeline requires the hidden courses then restrict the result to only $hiddencourses else exclude.
-        if ($classification == COURSE_TIMELINE_HIDDEN) {
+        // If the timeline requires really all courses, get really all courses.
+        if ($classification == COURSE_TIMELINE_ALLINCLUDINGHIDDEN) {
+            $courses = course_get_enrolled_courses_for_logged_in_user(0, $offset, $sort, $fields, COURSE_DB_QUERY_LIMIT);
+
+            // Otherwise if the timeline requires the hidden courses then restrict the result to only $hiddencourses.
+        } else if ($classification == COURSE_TIMELINE_HIDDEN) {
             $courses = course_get_enrolled_courses_for_logged_in_user(0, $offset, $sort, $fields,
                 COURSE_DB_QUERY_LIMIT, $hiddencourses);
+
+            // Otherwise get the requested courses and exclude the hidden courses.
         } else {
             $courses = course_get_enrolled_courses_for_logged_in_user(0, $offset, $sort, $fields,
                 COURSE_DB_QUERY_LIMIT, [], $hiddencourses);
index dc8b162..3eec792 100644 (file)
@@ -56,6 +56,7 @@ define('FIRSTUSEDEXCELROW', 3);
 define('MOD_CLASS_ACTIVITY', 0);
 define('MOD_CLASS_RESOURCE', 1);
 
+define('COURSE_TIMELINE_ALLINCLUDINGHIDDEN', 'allincludinghidden');
 define('COURSE_TIMELINE_ALL', 'all');
 define('COURSE_TIMELINE_PAST', 'past');
 define('COURSE_TIMELINE_INPROGRESS', 'inprogress');
@@ -2558,7 +2559,7 @@ function update_course($data, $editoroptions = NULL) {
                 // The summary might be very long, we don't wan't to fill up the log record with the full text.
                 $updatedfields[$field] = '(updated)';
             }
-        } else if ($field == 'tags') {
+        } else if ($field == 'tags' && !empty($CFG->usetags)) {
             // Tags might not have the same array keys, just check the values.
             if (array_values($data->$field) !== array_values($value)) {
                 $updatedfields[$field] = $data->$field;
@@ -4320,9 +4321,9 @@ function course_filter_courses_by_timeline_classification(
 ) : array {
 
     if (!in_array($classification,
-            [COURSE_TIMELINE_ALL, COURSE_TIMELINE_PAST, COURSE_TIMELINE_INPROGRESS,
+            [COURSE_TIMELINE_ALLINCLUDINGHIDDEN, COURSE_TIMELINE_ALL, COURSE_TIMELINE_PAST, COURSE_TIMELINE_INPROGRESS,
                 COURSE_TIMELINE_FUTURE, COURSE_TIMELINE_HIDDEN])) {
-        $message = 'Classification must be one of COURSE_TIMELINE_ALL, COURSE_TIMELINE_PAST, '
+        $message = 'Classification must be one of COURSE_TIMELINE_ALLINCLUDINGHIDDEN, COURSE_TIMELINE_ALL, COURSE_TIMELINE_PAST, '
             . 'COURSE_TIMELINE_INPROGRESS or COURSE_TIMELINE_FUTURE';
         throw new moodle_exception($message);
     }
@@ -4336,7 +4337,7 @@ function course_filter_courses_by_timeline_classification(
         $pref = get_user_preferences('block_myoverview_hidden_course_' . $course->id, 0);
 
         // Added as of MDL-63457 toggle viewability for each user.
-        if (($classification == COURSE_TIMELINE_HIDDEN && $pref) ||
+        if ($classification == COURSE_TIMELINE_ALLINCLUDINGHIDDEN || ($classification == COURSE_TIMELINE_HIDDEN && $pref) ||
             (($classification == COURSE_TIMELINE_ALL || $classification == course_classify_for_timeline($course)) && !$pref)) {
             $filteredcourses[] = $course;
             $filtermatches++;
index 10407f9..d48e163 100644 (file)
@@ -2,8 +2,12 @@ This files describes API changes in /course/*,
 information provided here is intended especially for developers.
 
 === 3.8 ===
+
 * The following functions have been finally deprecated and can not be used any more:
   - core_course_external::get_activities_overview
+* External function core_course_external::get_enrolled_courses_by_timeline_classification now also supports the classification
+  'allincludinghidden' which delivers all courses including hidden courses. The classification 'all' still returns all courses
+  without hidden courses.
 
 === 3.7 ===
 
index 549a653..1688749 100644 (file)
Binary files a/lib/amd/build/form-autocomplete.min.js and b/lib/amd/build/form-autocomplete.min.js differ
index db0aeaf..11e43c8 100644 (file)
Binary files a/lib/amd/build/form-autocomplete.min.js.map and b/lib/amd/build/form-autocomplete.min.js.map differ
index b91dd0b..43e3227 100644 (file)
@@ -718,6 +718,7 @@ function($, log, str, templates, notification, LoadingIcon) {
             window.setTimeout(function() {
                 // Get the current element with focus.
                 var focusElement = $(document.activeElement);
+                var timeoutPromise = $.Deferred();
 
                 // Only close the menu if the input hasn't regained focus and if the element still exists,
                 // and regain focus if the scrollbar is clicked.
@@ -727,18 +728,22 @@ function($, log, str, templates, notification, LoadingIcon) {
                     inputElement.focus(); // Probably the scrollbar is clicked. Regain focus.
                 } else if (!focusElement.is(inputElement) && $(document.getElementById(state.inputId)).length) {
                     if (options.tags) {
-                        pendingPromise.then(function() {
+                        timeoutPromise.then(function() {
                             return createItem(options, state, originalSelect);
                         })
                         .catch();
                     }
-                    pendingPromise.then(function() {
+                    timeoutPromise.then(function() {
                         return closeSuggestions(state);
                     })
                     .catch();
                 }
 
-                pendingPromise.resolve();
+                timeoutPromise.then(function() {
+                    return pendingPromise.resolve();
+                })
+                .catch();
+                timeoutPromise.resolve();
             }, 500);
         });
         if (options.showSuggestions) {
index 6512933..c541731 100644 (file)
@@ -1044,7 +1044,8 @@ function file_save_draft_area_files($draftitemid, $contextid, $component, $filea
 
     // Do not merge files, leave it as it was.
     if ($draftitemid === IGNORE_FILE_MERGE) {
-        return null;
+        // Safely return $text, no need to rewrite pluginfile because this is mostly comming from an external client like the app.
+        return $text;
     }
 
     $usercontext = context_user::instance($USER->id);
index a1b428a..f6e0c1b 100644 (file)
@@ -869,6 +869,11 @@ class core_filelib_testcase extends advanced_testcase {
         // Save without merge.
         file_save_draft_area_files(IGNORE_FILE_MERGE, $usercontext->id, 'user', 'private', 0);
         $this->assertCount(2, $fs->get_area_files($usercontext->id, 'user', 'private'));
+        // Save again, this time including some inline text.
+        $inlinetext = 'Some text <img src="@@PLUGINFILE@@/file.png">';
+        $text = file_save_draft_area_files(IGNORE_FILE_MERGE, $usercontext->id, 'user', 'private', 0, null, $inlinetext);
+        $this->assertCount(2, $fs->get_area_files($usercontext->id, 'user', 'private'));
+        $this->assertEquals($inlinetext, $text);
     }
 
     /**
index 19185a5..c02d911 100644 (file)
@@ -77,6 +77,7 @@ attribute on forms to avoid collisions in forms loaded in AJAX requests.
   and get_reduced_timeout(). These allow for timeouts to be increased by a setting in config.php.
 * The $draftitemid parameter of file_save_draft_area_files() function now supports the constant IGNORE_FILE_MERGE:
   When the parameter is set to that constant, the function won't process file merging, keeping the original state of the file area.
+  Notice also than when $text is set, pluginfile rewrite won't be processed so the text will not be modified.
 * Introduced new callback for plugin developers '<component>_pre_processor_message_send($procname, $proceventdata)':
   This will allow any plugin to manipulate messages or notifications before they are sent by a processor (email, mobile...)
 * New capability 'moodle/category:viewcourselist' in category context that controls whether user is able to browse list of courses
index a8ec8c8..78cf82b 100644 (file)
@@ -39,9 +39,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-// Add setuplib in case it has not been loaded yet as it's required by \xml_format_exception.
-require_once($CFG->libdir.'/setuplib.php');
-
 class xml_format_exception extends moodle_exception {
     /** @var string */
     public $errorstring;
index e28942d..8f7fd86 100644 (file)
@@ -564,7 +564,7 @@ function book_export_contents($cm, $baseurl) {
     $currentchapter = 0;
 
     foreach ($chapters as $chapter) {
-        if ($chapter->hidden) {
+        if ($chapter->hidden && !has_capability('mod/book:viewhiddenchapters', $context)) {
             continue;
         }
 
@@ -573,6 +573,7 @@ function book_export_contents($cm, $baseurl) {
             "title"     => format_string($chapter->title, true, array('context' => $context)),
             "href"      => $chapter->id . "/index.html",
             "level"     => 0,
+            "hidden"    => $chapter->hidden,
             "subitems"  => array()
         );
 
index 49f740a..247f29d 100644 (file)
@@ -49,9 +49,13 @@ class mod_book_lib_testcase extends advanced_testcase {
         require_once($CFG->dirroot . '/course/externallib.php');
 
         $user = $this->getDataGenerator()->create_user();
+        $teacher = $this->getDataGenerator()->create_user();
         $course = $this->getDataGenerator()->create_course(array('enablecomment' => 1));
         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
+        $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
+
         $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id);
+        $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id);
 
         // Test book with 3 chapters.
         $book = $this->getDataGenerator()->create_module('book', array('course' => $course->id));
@@ -87,6 +91,88 @@ class mod_book_lib_testcase extends advanced_testcase {
         // Now, test the function via the external API.
         $contents = core_course_external::get_course_contents($course->id, array());
         $contents = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $contents);
+
+        $this->assertCount(4, $contents[0]['modules'][0]['contents']);
+
+        $this->assertEquals('content', $contents[0]['modules'][0]['contents'][0]['type']);
+        $this->assertEquals('structure', $contents[0]['modules'][0]['contents'][0]['filename']);
+
+        $this->assertEquals('file', $contents[0]['modules'][0]['contents'][1]['type']);
+        $this->assertEquals('Chapter 1', $contents[0]['modules'][0]['contents'][1]['content']);
+
+        $this->assertEquals('file', $contents[0]['modules'][0]['contents'][2]['type']);
+        $this->assertEquals('Chapter 2', $contents[0]['modules'][0]['contents'][2]['content']);
+
+        $this->assertEquals('file', $contents[0]['modules'][0]['contents'][3]['type']);
+        $this->assertEquals('Chapter 3', $contents[0]['modules'][0]['contents'][3]['content']);
+
+        $this->assertEquals('book', $contents[0]['modules'][0]['modname']);
+        $this->assertEquals($cm->id, $contents[0]['modules'][0]['id']);
+        $this->assertCount(2, $contents[0]['modules'][0]['contents'][1]['tags']);
+        $this->assertEquals('Cats', $contents[0]['modules'][0]['contents'][1]['tags'][0]['rawname']);
+        $this->assertEquals('Dogs', $contents[0]['modules'][0]['contents'][1]['tags'][1]['rawname']);
+
+        // As a teacher.
+        $this->setUser($teacher);
+
+        $contents = book_export_contents($cm, '');
+        // As a teacher, the hidden chapter must be included in the structure.
+        $this->assertCount(5, $contents);
+
+        $this->assertEquals('structure', $contents[0]['filename']);
+        // Check structure is correct.
+        $foundhiddenchapter = false;
+        $chapters = json_decode($contents[0]['content']);
+        foreach ($chapters as $chapter) {
+            if ($chapter->title == 'Chapter 4' && $chapter->hidden == 1) {
+                $foundhiddenchapter = true;
+            }
+        }
+        $this->assertTrue($foundhiddenchapter);
+
+        $this->assertEquals('index.html', $contents[1]['filename']);
+        $this->assertEquals('Chapter 1', $contents[1]['content']);
+        $this->assertCount(2, $contents[1]['tags']);
+        $this->assertEquals('Cats', $contents[1]['tags'][0]['rawname']);
+        $this->assertEquals($tag->id, $contents[1]['tags'][0]['id']);
+        $this->assertEquals('Dogs', $contents[1]['tags'][1]['rawname']);
+        $this->assertEquals('index.html', $contents[2]['filename']);
+        $this->assertEquals('Chapter 2', $contents[2]['content']);
+        $this->assertEquals('index.html', $contents[3]['filename']);
+        $this->assertEquals('Chapter 3', $contents[3]['content']);
+        $this->assertEquals('index.html', $contents[4]['filename']);
+        $this->assertEquals('Chapter 4', $contents[4]['content']);
+
+        // Now, test the function via the external API.
+        $contents = core_course_external::get_course_contents($course->id, array());
+        $contents = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $contents);
+
+        $this->assertCount(5, $contents[0]['modules'][0]['contents']);
+
+        $this->assertEquals('content', $contents[0]['modules'][0]['contents'][0]['type']);
+        $this->assertEquals('structure', $contents[0]['modules'][0]['contents'][0]['filename']);
+        // Check structure is correct.
+        $foundhiddenchapter = false;
+        $chapters = json_decode($contents[0]['modules'][0]['contents'][0]['content']);
+        foreach ($chapters as $chapter) {
+            if ($chapter->title == 'Chapter 4' && $chapter->hidden == 1) {
+                $foundhiddenchapter = true;
+            }
+        }
+        $this->assertTrue($foundhiddenchapter);
+
+        $this->assertEquals('file', $contents[0]['modules'][0]['contents'][1]['type']);
+        $this->assertEquals('Chapter 1', $contents[0]['modules'][0]['contents'][1]['content']);
+
+        $this->assertEquals('file', $contents[0]['modules'][0]['contents'][2]['type']);
+        $this->assertEquals('Chapter 2', $contents[0]['modules'][0]['contents'][2]['content']);
+
+        $this->assertEquals('file', $contents[0]['modules'][0]['contents'][3]['type']);
+        $this->assertEquals('Chapter 3', $contents[0]['modules'][0]['contents'][3]['content']);
+
+        $this->assertEquals('file', $contents[0]['modules'][0]['contents'][4]['type']);
+        $this->assertEquals('Chapter 4', $contents[0]['modules'][0]['contents'][4]['content']);
+
         $this->assertEquals('book', $contents[0]['modules'][0]['modname']);
         $this->assertEquals($cm->id, $contents[0]['modules'][0]['id']);
         $this->assertCount(2, $contents[0]['modules'][0]['contents'][1]['tags']);
index 49af7e2..342326c 100644 (file)
@@ -156,7 +156,7 @@ if (!empty($forum)) {
                 $urlfactory->get_course_url_from_forum($forumentity),
                 get_string('activityiscurrentlyhidden'),
                 null,
-                \core\output\notice::NOTIFY_ERROR
+                \core\output\notification::NOTIFY_ERROR
             );
     }
 
@@ -387,7 +387,7 @@ if (!empty($forum)) {
                 $urlfactory->get_discussion_view_url_from_discussion($discussionentity),
                 get_string('cannotdeletepost', 'forum'),
                 null,
-                \core\output\notice::NOTIFY_ERROR
+                \core\output\notification::NOTIFY_ERROR
             );
     }
 
@@ -403,14 +403,14 @@ if (!empty($forum)) {
                     $urlfactory->get_discussion_view_url_from_discussion($discussionentity),
                     get_string('couldnotdeleteratings', 'rating'),
                     null,
-                    \core\output\notice::NOTIFY_ERROR
+                    \core\output\notification::NOTIFY_ERROR
                 );
         } else if ($replycount && !has_capability('mod/forum:deleteanypost', $modcontext)) {
             redirect(
                     $urlfactory->get_discussion_view_url_from_discussion($discussionentity),
-                    get_string('couldnotdeletereplies', 'rating'),
+                    get_string('couldnotdeletereplies', 'forum'),
                     null,
-                    \core\output\notice::NOTIFY_ERROR
+                    \core\output\notification::NOTIFY_ERROR
                 );
         } else {
             if (!$postentity->has_parent()) {
@@ -418,9 +418,9 @@ if (!empty($forum)) {
                 if ($forum->type == 'single') {
                     redirect(
                             $urlfactory->get_discussion_view_url_from_discussion($discussionentity),
-                            get_string('cannotdeletediscussioninsinglediscussion', 'rating'),
+                            get_string('cannotdeletediscussioninsinglediscussion', 'forum'),
                             null,
-                            \core\output\notice::NOTIFY_ERROR
+                            \core\output\notification::NOTIFY_ERROR
                         );
                 }
                 forum_delete_discussion($discussion, false, $course, $cm, $forum);
@@ -452,7 +452,7 @@ if (!empty($forum)) {
                             $urlfactory->get_discussion_view_url_from_post($postentity),
                             get_string('errorwhiledelete', 'forum'),
                             null,
-                            \core\output\notice::NOTIFY_ERROR
+                            \core\output\notification::NOTIFY_ERROR
                         );
                 }
 
@@ -485,9 +485,9 @@ if (!empty($forum)) {
             if (!has_capability('mod/forum:deleteanypost', $modcontext)) {
                 redirect(
                         forum_go_back_to($urlfactory->get_view_post_url_from_post($postentity)),
-                        get_string('couldnotdeletereplies', 'rating'),
+                        get_string('couldnotdeletereplies', 'forum'),
                         null,
-                        \core\output\notice::NOTIFY_ERROR
+                        \core\output\notification::NOTIFY_ERROR
                     );
             }
 
index 6de89ca..50bcddc 100644 (file)
@@ -2425,8 +2425,8 @@ function glossary_generate_export_file($glossary, $ignored = "", $hook = 0) {
  * @return string
  */
 function glossary_read_imported_file($file_content) {
-    require_once "../../lib/xmlize.php";
     global $CFG;
+    require_once "../../lib/xmlize.php";
 
     return xmlize($file_content, 0);
 }
index 578a0fa..a92da48 100644 (file)
 
 defined('MOODLE_INTERNAL') || die();
 
-$version  = 2019082300.00;              // YYYYMMDD      = weekly release date of this DEV branch.
+$version  = 2019082400.00;              // YYYYMMDD      = weekly release date of this DEV branch.
                                         //         RR    = release increments - 00 in DEV branches.
                                         //           .XX = incremental changes.
 
-$release  = '3.8dev (Build: 20190823)'; // Human-friendly version name
+$release  = '3.8dev (Build: 20190824)'; // Human-friendly version name
 
 $branch   = '38';                       // This version's branch.
 $maturity = MATURITY_ALPHA;             // This version's maturity level.