Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 401 and 402] [Versions 401 and 403]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Class containing data for my overview block.
  19   *
  20   * @package    block_myoverview
  21   * @copyright  2017 Ryan Wyllie <ryan@moodle.com>
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  namespace block_myoverview\output;
  25  defined('MOODLE_INTERNAL') || die();
  26  
  27  use renderable;
  28  use renderer_base;
  29  use templatable;
  30  use stdClass;
  31  
  32  require_once($CFG->dirroot . '/blocks/myoverview/lib.php');
  33  
  34  /**
  35   * Class containing data for my overview block.
  36   *
  37   * @copyright  2018 Bas Brands <bas@moodle.com>
  38   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  39   */
  40  class main implements renderable, templatable {
  41  
  42      /**
  43       * Store the grouping preference.
  44       *
  45       * @var string String matching the grouping constants defined in myoverview/lib.php
  46       */
  47      private $grouping;
  48  
  49      /**
  50       * Store the sort preference.
  51       *
  52       * @var string String matching the sort constants defined in myoverview/lib.php
  53       */
  54      private $sort;
  55  
  56      /**
  57       * Store the view preference.
  58       *
  59       * @var string String matching the view/display constants defined in myoverview/lib.php
  60       */
  61      private $view;
  62  
  63      /**
  64       * Store the paging preference.
  65       *
  66       * @var string String matching the paging constants defined in myoverview/lib.php
  67       */
  68      private $paging;
  69  
  70      /**
  71       * Store the display categories config setting.
  72       *
  73       * @var boolean
  74       */
  75      private $displaycategories;
  76  
  77      /**
  78       * Store the configuration values for the myoverview block.
  79       *
  80       * @var array Array of available layouts matching view/display constants defined in myoverview/lib.php
  81       */
  82      private $layouts;
  83  
  84      /**
  85       * Store a course grouping option setting
  86       *
  87       * @var boolean
  88       */
  89      private $displaygroupingallincludinghidden;
  90  
  91      /**
  92       * Store a course grouping option setting.
  93       *
  94       * @var boolean
  95       */
  96      private $displaygroupingall;
  97  
  98      /**
  99       * Store a course grouping option setting.
 100       *
 101       * @var boolean
 102       */
 103      private $displaygroupinginprogress;
 104  
 105      /**
 106       * Store a course grouping option setting.
 107       *
 108       * @var boolean
 109       */
 110      private $displaygroupingfuture;
 111  
 112      /**
 113       * Store a course grouping option setting.
 114       *
 115       * @var boolean
 116       */
 117      private $displaygroupingpast;
 118  
 119      /**
 120       * Store a course grouping option setting.
 121       *
 122       * @var boolean
 123       */
 124      private $displaygroupingfavourites;
 125  
 126      /**
 127       * Store a course grouping option setting.
 128       *
 129       * @var boolean
 130       */
 131      private $displaygroupinghidden;
 132  
 133      /**
 134       * Store a course grouping option setting.
 135       *
 136       * @var bool
 137       */
 138      private $displaygroupingcustomfield;
 139  
 140      /**
 141       * Store the custom field used by customfield grouping.
 142       *
 143       * @var string
 144       */
 145      private $customfiltergrouping;
 146  
 147      /**
 148       * Store the selected custom field value to group by.
 149       *
 150       * @var string
 151       */
 152      private $customfieldvalue;
 153  
 154      /**
 155       * main constructor.
 156       * Initialize the user preferences
 157       *
 158       * @param string $grouping Grouping user preference
 159       * @param string $sort Sort user preference
 160       * @param string $view Display user preference
 161       * @param int $paging
 162       * @param string $customfieldvalue
 163       *
 164       * @throws \dml_exception
 165       */
 166      public function __construct($grouping, $sort, $view, $paging, $customfieldvalue = null) {
 167          global $CFG;
 168          // Get plugin config.
 169          $config = get_config('block_myoverview');
 170  
 171          // Build the course grouping option name to check if the given grouping is enabled afterwards.
 172          $groupingconfigname = 'displaygrouping'.$grouping;
 173          // Check the given grouping and remember it if it is enabled.
 174          if ($grouping && $config->$groupingconfigname == true) {
 175              $this->grouping = $grouping;
 176  
 177              // Otherwise fall back to another grouping in a reasonable order.
 178              // This is done to prevent one-time UI glitches in the case when a user has chosen a grouping option previously which
 179              // was then disabled by the admin in the meantime.
 180          } else {
 181              $this->grouping = $this->get_fallback_grouping($config);
 182          }
 183          unset ($groupingconfigname);
 184  
 185          // Remember which custom field value we were using, if grouping by custom field.
 186          $this->customfieldvalue = $customfieldvalue;
 187  
 188          // Check and remember the given sorting.
 189          if ($sort) {
 190              $this->sort = $sort;
 191          } else if ($CFG->courselistshortnames) {
 192              $this->sort = BLOCK_MYOVERVIEW_SORTING_SHORTNAME;
 193          } else {
 194              $this->sort = BLOCK_MYOVERVIEW_SORTING_TITLE;
 195          }
 196          // In case sorting remembered is shortname and display extended course names not checked,
 197          // we should revert sorting to title.
 198          if (!$CFG->courselistshortnames && $sort == BLOCK_MYOVERVIEW_SORTING_SHORTNAME) {
 199              $this->sort = BLOCK_MYOVERVIEW_SORTING_TITLE;
 200          }
 201  
 202          // Check and remember the given view.
 203          $this->view = $view ? $view : BLOCK_MYOVERVIEW_VIEW_CARD;
 204  
 205          // Check and remember the given page size, `null` indicates no page size set
 206          // while a `0` indicates a paging size of `All`.
 207          if (!is_null($paging) && $paging == BLOCK_MYOVERVIEW_PAGING_ALL) {
 208              $this->paging = BLOCK_MYOVERVIEW_PAGING_ALL;
 209          } else {
 210              $this->paging = $paging ? $paging : BLOCK_MYOVERVIEW_PAGING_12;
 211          }
 212  
 213          // Check and remember if the course categories should be shown or not.
 214          if (!$config->displaycategories) {
 215              $this->displaycategories = BLOCK_MYOVERVIEW_DISPLAY_CATEGORIES_OFF;
 216          } else {
 217              $this->displaycategories = BLOCK_MYOVERVIEW_DISPLAY_CATEGORIES_ON;
 218          }
 219  
 220          // Get and remember the available layouts.
 221          $this->set_available_layouts();
 222          $this->view = $view ? $view : reset($this->layouts);
 223  
 224          // Check and remember if the particular grouping options should be shown or not.
 225          $this->displaygroupingallincludinghidden = $config->displaygroupingallincludinghidden;
 226          $this->displaygroupingall = $config->displaygroupingall;
 227          $this->displaygroupinginprogress = $config->displaygroupinginprogress;
 228          $this->displaygroupingfuture = $config->displaygroupingfuture;
 229          $this->displaygroupingpast = $config->displaygroupingpast;
 230          $this->displaygroupingfavourites = $config->displaygroupingfavourites;
 231          $this->displaygroupinghidden = $config->displaygroupinghidden;
 232          $this->displaygroupingcustomfield = ($config->displaygroupingcustomfield && $config->customfiltergrouping);
 233          $this->customfiltergrouping = $config->customfiltergrouping;
 234  
 235          // Check and remember if the grouping selector should be shown at all or not.
 236          // It will be shown if more than 1 grouping option is enabled.
 237          $displaygroupingselectors = array($this->displaygroupingallincludinghidden,
 238                  $this->displaygroupingall,
 239                  $this->displaygroupinginprogress,
 240                  $this->displaygroupingfuture,
 241                  $this->displaygroupingpast,
 242                  $this->displaygroupingfavourites,
 243                  $this->displaygroupinghidden);
 244          $displaygroupingselectorscount = count(array_filter($displaygroupingselectors));
 245          if ($displaygroupingselectorscount > 1 || $this->displaygroupingcustomfield) {
 246              $this->displaygroupingselector = true;
 247          } else {
 248              $this->displaygroupingselector = false;
 249          }
 250          unset ($displaygroupingselectors, $displaygroupingselectorscount);
 251      }
 252      /**
 253       * Determine the most sensible fallback grouping to use (in cases where the stored selection
 254       * is no longer available).
 255       * @param object $config
 256       * @return string
 257       */
 258      private function get_fallback_grouping($config) {
 259          if ($config->displaygroupingall == true) {
 260              return BLOCK_MYOVERVIEW_GROUPING_ALL;
 261          }
 262          if ($config->displaygroupingallincludinghidden == true) {
 263              return BLOCK_MYOVERVIEW_GROUPING_ALLINCLUDINGHIDDEN;
 264          }
 265          if ($config->displaygroupinginprogress == true) {
 266              return BLOCK_MYOVERVIEW_GROUPING_INPROGRESS;
 267          }
 268          if ($config->displaygroupingfuture == true) {
 269              return BLOCK_MYOVERVIEW_GROUPING_FUTURE;
 270          }
 271          if ($config->displaygroupingpast == true) {
 272              return BLOCK_MYOVERVIEW_GROUPING_PAST;
 273          }
 274          if ($config->displaygroupingfavourites == true) {
 275              return BLOCK_MYOVERVIEW_GROUPING_FAVOURITES;
 276          }
 277          if ($config->displaygroupinghidden == true) {
 278              return BLOCK_MYOVERVIEW_GROUPING_HIDDEN;
 279          }
 280          if ($config->displaygroupingcustomfield == true) {
 281              return BLOCK_MYOVERVIEW_GROUPING_CUSTOMFIELD;
 282          }
 283          // In this case, no grouping option is enabled and the grouping is not needed at all.
 284          // But it's better not to leave $this->grouping unset for any unexpected case.
 285          return BLOCK_MYOVERVIEW_GROUPING_ALLINCLUDINGHIDDEN;
 286      }
 287  
 288      /**
 289       * Set the available layouts based on the config table settings,
 290       * if none are available, defaults to the cards view.
 291       *
 292       * @throws \dml_exception
 293       *
 294       */
 295      public function set_available_layouts() {
 296  
 297          if ($config = get_config('block_myoverview', 'layouts')) {
 298              $this->layouts = explode(',', $config);
 299          } else {
 300              $this->layouts = array(BLOCK_MYOVERVIEW_VIEW_CARD);
 301          }
 302      }
 303  
 304      /**
 305       * Get the user preferences as an array to figure out what has been selected.
 306       *
 307       * @return array $preferences Array with the pref as key and value set to true
 308       */
 309      public function get_preferences_as_booleans() {
 310          $preferences = [];
 311          $preferences[$this->sort] = true;
 312          $preferences[$this->grouping] = true;
 313          // Only use the user view/display preference if it is in available layouts.
 314          if (in_array($this->view, $this->layouts)) {
 315              $preferences[$this->view] = true;
 316          } else {
 317              $preferences[reset($this->layouts)] = true;
 318          }
 319  
 320          return $preferences;
 321      }
 322  
 323      /**
 324       * Format a layout into an object for export as a Context variable to template.
 325       *
 326       * @param string $layoutname
 327       *
 328       * @return \stdClass $layout an object representation of a layout
 329       * @throws \coding_exception
 330       */
 331      public function format_layout_for_export($layoutname) {
 332          $layout = new stdClass();
 333  
 334          $layout->id = $layoutname;
 335          $layout->name = get_string($layoutname, 'block_myoverview');
 336          $layout->active = $this->view == $layoutname ? true : false;
 337          $layout->arialabel = get_string('aria:' . $layoutname, 'block_myoverview');
 338  
 339          return $layout;
 340      }
 341  
 342      /**
 343       * Get the available layouts formatted for export.
 344       *
 345       * @return array an array of objects representing available layouts
 346       */
 347      public function get_formatted_available_layouts_for_export() {
 348  
 349          return array_map(array($this, 'format_layout_for_export'), $this->layouts);
 350  
 351      }
 352  
 353      /**
 354       * Get the list of values to add to the grouping dropdown
 355       * @return object[] containing name, value and active fields
 356       */
 357      public function get_customfield_values_for_export() {
 358          global $DB, $USER;
 359          if (!$this->displaygroupingcustomfield) {
 360              return [];
 361          }
 362          $fieldid = $DB->get_field('customfield_field', 'id', ['shortname' => $this->customfiltergrouping]);
 363          if (!$fieldid) {
 364              return [];
 365          }
 366          $courses = enrol_get_all_users_courses($USER->id, true);
 367          if (!$courses) {
 368              return [];
 369          }
 370          list($csql, $params) = $DB->get_in_or_equal(array_keys($courses), SQL_PARAMS_NAMED);
 371          $select = "instanceid $csql AND fieldid = :fieldid";
 372          $params['fieldid'] = $fieldid;
 373          $distinctablevalue = $DB->sql_compare_text('value');
 374          $values = $DB->get_records_select_menu('customfield_data', $select, $params, '',
 375              "DISTINCT $distinctablevalue, $distinctablevalue AS value2");
 376          \core_collator::asort($values, \core_collator::SORT_NATURAL);
 377          $values = array_filter($values);
 378          if (!$values) {
 379              return [];
 380          }
 381          $field = \core_customfield\field_controller::create($fieldid);
 382          $isvisible = $field->get_configdata_property('visibility') == \core_course\customfield\course_handler::VISIBLETOALL;
 383          // Only visible fields to everybody supporting course grouping will be displayed.
 384          if (!$field->supports_course_grouping() || !$isvisible) {
 385              return []; // The field shouldn't have been selectable in the global settings, but just skip it now.
 386          }
 387          $values = $field->course_grouping_format_values($values);
 388          $customfieldactive = ($this->grouping === BLOCK_MYOVERVIEW_GROUPING_CUSTOMFIELD);
 389          $ret = [];
 390          foreach ($values as $value => $name) {
 391              $ret[] = (object)[
 392                  'name' => $name,
 393                  'value' => $value,
 394                  'active' => ($customfieldactive && ($this->customfieldvalue == $value)),
 395              ];
 396          }
 397          return $ret;
 398      }
 399  
 400      /**
 401       * Export this data so it can be used as the context for a mustache template.
 402       *
 403       * @param \renderer_base $output
 404       * @return array Context variables for the template
 405       * @throws \coding_exception
 406       *
 407       */
 408      public function export_for_template(renderer_base $output) {
 409          global $CFG, $USER;
 410  
 411          $nocoursesurl = $output->image_url('courses', 'block_myoverview')->out();
 412  
 413          $newcourseurl = '';
 414          $coursecat = \core_course_category::user_top();
 415          if ($coursecat && ($category = \core_course_category::get_nearest_editable_subcategory($coursecat, ['create']))) {
 416              $newcourseurl = new \moodle_url('/course/edit.php', ['category' => $category->id]);
 417          }
 418  
 419          $customfieldvalues = $this->get_customfield_values_for_export();
 420          $selectedcustomfield = '';
 421          if ($this->grouping == BLOCK_MYOVERVIEW_GROUPING_CUSTOMFIELD) {
 422              foreach ($customfieldvalues as $field) {
 423                  if ($field->value == $this->customfieldvalue) {
 424                      $selectedcustomfield = $field->name;
 425                      break;
 426                  }
 427              }
 428              // If the selected custom field value has not been found (possibly because the field has
 429              // been changed in the settings) find a suitable fallback.
 430              if (!$selectedcustomfield) {
 431                  $this->grouping = $this->get_fallback_grouping(get_config('block_myoverview'));
 432                  if ($this->grouping == BLOCK_MYOVERVIEW_GROUPING_CUSTOMFIELD) {
 433                      // If the fallback grouping is still customfield, then select the first field.
 434                      $firstfield = reset($customfieldvalues);
 435                      if ($firstfield) {
 436                          $selectedcustomfield = $firstfield->name;
 437                          $this->customfieldvalue = $firstfield->value;
 438                      }
 439                  }
 440              }
 441          }
 442          $preferences = $this->get_preferences_as_booleans();
 443          $availablelayouts = $this->get_formatted_available_layouts_for_export();
 444          $sort = '';
 445          if ($this->sort == BLOCK_MYOVERVIEW_SORTING_SHORTNAME) {
 446              $sort = 'shortname';
 447          } else {
 448              $sort = $this->sort == BLOCK_MYOVERVIEW_SORTING_TITLE ? 'fullname' : 'ul.timeaccess desc';
 449          }
 450  
 451          $defaultvariables = [
 452              'totalcoursecount' => count(enrol_get_all_users_courses($USER->id, true)),
 453              'nocoursesimg' => $nocoursesurl,
 454              'newcourseurl' => $newcourseurl,
 455              'grouping' => $this->grouping,
 456              'sort' => $sort,
 457              // If the user preference display option is not available, default to first available layout.
 458              'view' => in_array($this->view, $this->layouts) ? $this->view : reset($this->layouts),
 459              'paging' => $this->paging,
 460              'layouts' => $availablelayouts,
 461              'displaycategories' => $this->displaycategories,
 462              'displaydropdown' => (count($availablelayouts) > 1) ? true : false,
 463              'displaygroupingallincludinghidden' => $this->displaygroupingallincludinghidden,
 464              'displaygroupingall' => $this->displaygroupingall,
 465              'displaygroupinginprogress' => $this->displaygroupinginprogress,
 466              'displaygroupingfuture' => $this->displaygroupingfuture,
 467              'displaygroupingpast' => $this->displaygroupingpast,
 468              'displaygroupingfavourites' => $this->displaygroupingfavourites,
 469              'displaygroupinghidden' => $this->displaygroupinghidden,
 470              'displaygroupingselector' => $this->displaygroupingselector,
 471              'displaygroupingcustomfield' => $this->displaygroupingcustomfield && $customfieldvalues,
 472              'customfieldname' => $this->customfiltergrouping,
 473              'customfieldvalue' => $this->customfieldvalue,
 474              'customfieldvalues' => $customfieldvalues,
 475              'selectedcustomfield' => $selectedcustomfield,
 476              'showsortbyshortname' => $CFG->courselistshortnames,
 477          ];
 478          return array_merge($defaultvariables, $preferences);
 479  
 480      }
 481  }