Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

Differences Between: [Versions 400 and 403] [Versions 401 and 403] [Versions 402 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  namespace qbank_columnsortorder;
  18  
  19  defined('MOODLE_INTERNAL') || die();
  20  
  21  require_once($CFG->libdir . '/questionlib.php');
  22  
  23  use context_system;
  24  use core_question\local\bank\column_base;
  25  use core_question\local\bank\column_manager_base;
  26  use core_question\local\bank\question_edit_contexts;
  27  use core_question\local\bank\view;
  28  use qbank_columnsortorder\local\bank\column_action_move;
  29  use qbank_columnsortorder\local\bank\column_action_remove;
  30  use qbank_columnsortorder\local\bank\column_action_resize;
  31  use qbank_columnsortorder\local\bank\preview_view;
  32  use moodle_url;
  33  
  34  /**
  35   * Class column_manager responsible for loading and saving order to the config setting.
  36   *
  37   * @package    qbank_columnsortorder
  38   * @copyright  2021 Catalyst IT Australia Pty Ltd
  39   * @author     Ghaly Marc-Alexandre <marc-alexandreghaly@catalyst-ca.net>
  40   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  41   */
  42  class column_manager extends column_manager_base {
  43      /**
  44       * @var array Column order as set in config_plugins 'class' => 'position', ie: question_type_column => 3.
  45       */
  46      public $columnorder;
  47  
  48      /**
  49       * @var array hidden columns.
  50       */
  51      public $hiddencolumns;
  52  
  53      /**
  54       * @var array columns with size.
  55       */
  56      public $colsize;
  57  
  58      /**
  59       * @var array Disabled columns in config_plugins table.
  60       */
  61      public $disabledcolumns;
  62  
  63      /**
  64       * Constructor for column_manager class.
  65       *
  66       * @param bool $globalsettings Only use the global default settings, ignoring user preferences?
  67       */
  68      public function __construct(bool $globalsettings = false) {
  69          $this->columnorder = $this->setup_property('enabledcol', $globalsettings);
  70          if (empty($this->columnorder)) {
  71              $this->columnorder = [
  72                  'core_question\local\bank\checkbox_column' . column_base::ID_SEPARATOR . 'checkbox_column',
  73                  'qbank_viewquestiontype\question_type_column' . column_base::ID_SEPARATOR . 'question_type_column',
  74                  'qbank_viewquestionname\question_name_idnumber_tags_column' . column_base::ID_SEPARATOR .
  75                  'question_name_idnumber_tags_column',
  76                      'core_question\local\bank\edit_menu_column' . column_base::ID_SEPARATOR . 'edit_menu_column',
  77                  'qbank_editquestion\question_status_column' . column_base::ID_SEPARATOR . 'question_status_column',
  78                  'qbank_history\version_number_column' . column_base::ID_SEPARATOR . 'version_number_column',
  79                  'qbank_viewcreator\creator_name_column' . column_base::ID_SEPARATOR . 'creator_name_column',
  80                  'qbank_comment\comment_count_column' . column_base::ID_SEPARATOR . 'comment_count_column',
  81              ];
  82          }
  83          $this->hiddencolumns = $this->setup_property('hiddencols', $globalsettings);
  84          $this->colsize = $this->setup_property('colsize', $globalsettings, 'json');
  85          $this->disabledcolumns = $this->setup_property('disabledcol', true); // No user preference for disabledcol.
  86  
  87          if ($this->columnorder) {
  88              $this->columnorder = array_flip($this->columnorder);
  89          }
  90          if ($this->disabledcolumns) {
  91              $this->disabledcolumns = array_flip($this->disabledcolumns);
  92          }
  93      }
  94  
  95      /**
  96       * Return the value for the given property, based the saved user preference or config setting.
  97       *
  98       * If no value is currently stored, returns an empty array.
  99       *
 100       * @param string $setting The identifier used for the saved config and user preference settings.
 101       * @param bool $global Only get the global default, ignoring the user preference?
 102       * @param string $encoding The encoding used to store the property - csv or json
 103       * @return array
 104       */
 105      private function setup_property(string $setting, bool $global = false, $encoding = 'csv'): array {
 106          $value = get_config('qbank_columnsortorder', $setting);
 107          if (!$global) {
 108              $value = get_user_preferences("qbank_columnsortorder_{$setting}", $value);
 109          }
 110          if (empty($value)) {
 111              return [];
 112          }
 113          return $encoding == 'csv' ? explode(',', $value) : json_decode($value);
 114      }
 115  
 116      /**
 117       * Sets column order in the qbank_columnsortorder plugin config.
 118       *
 119       * @param ?array $columns Column order to set. Null value clears the setting.
 120       * @param bool $global save this as a global default, rather than a user preference?
 121       */
 122      public static function set_column_order(?array $columns, bool $global = false) : void {
 123          if (!is_null($columns)) {
 124              $columns = implode(',', $columns);
 125          }
 126          self::save_preference('enabledcol', $columns, $global);
 127      }
 128  
 129      /**
 130       * Hidden Columns.
 131       *
 132       * @param ?array $columns List of hidden columns. Null value clears the setting.
 133       * @param bool $global save this as a global default, rather than a user preference?
 134       */
 135      public static function set_hidden_columns(?array $columns, bool $global = false) : void {
 136          if (!is_null($columns)) {
 137              $columns = implode(',', $columns);
 138          }
 139          self::save_preference('hiddencols', $columns, $global);
 140      }
 141  
 142      /**
 143       * Column size.
 144       *
 145       * @param ?string $sizes columns with width. Null value clears the setting.
 146       * @param bool $global save this as a global default, rather than a user preference?
 147       */
 148      public static function set_column_size(?string $sizes, bool $global = false) : void {
 149          self::save_preference('colsize', $sizes, $global);
 150      }
 151  
 152      /**
 153       * Save Preferences.
 154       *
 155       * @param string $name name of a configuration
 156       * @param ?string $value value of a configuration. Null value clears the setting.
 157       * @param bool $global save this as a global default, rather than a user preference?
 158       */
 159      private static function save_preference(string $name, ?string $value, bool $global = false): void {
 160          if ($global) {
 161              require_capability('moodle/site:config', context_system::instance());
 162              set_config($name, $value, 'qbank_columnsortorder');
 163          } else {
 164              set_user_preference("qbank_columnsortorder_{$name}", $value);
 165          }
 166      }
 167  
 168      /**
 169       * Get qbank.
 170       *
 171       * @return view
 172       */
 173      public function get_questionbank(): view {
 174          $course = (object) ['id' => 0];
 175          $context = context_system::instance();
 176          $contexts = new question_edit_contexts($context);
 177          $category = question_make_default_categories($contexts->all());
 178          $params = ['cat' => $category->id . ',' . $context->id];
 179          // Dummy call to get the objects without error.
 180          $questionbank = new preview_view(
 181              $contexts,
 182              new moodle_url('/question/bank/columnsortorder/sortcolumns.php'),
 183              $course,
 184              null,
 185              $params
 186          );
 187          return $questionbank;
 188      }
 189  
 190      /**
 191       * Get enabled columns.
 192       *
 193       * @return array
 194       */
 195      public function get_columns(): array {
 196          $columns = [];
 197          foreach ($this->get_questionbank()->get_visiblecolumns() as $key => $column) {
 198              if ($column->get_name() === 'checkbox') {
 199                  continue;
 200              }
 201              $columns[] = (object) [
 202                  'class' => get_class($column),
 203                  'name' => $column->get_title(),
 204                  'colname' => $column->get_column_name(),
 205                  'id' => $column->get_column_id(),
 206              ];
 207          }
 208          return $columns;
 209      }
 210  
 211      /**
 212       * Get disabled columns.
 213       *
 214       * @return array
 215       */
 216      public function get_disabled_columns(): array {
 217          $disabled = [];
 218          if ($this->disabledcolumns) {
 219              foreach (array_keys($this->disabledcolumns) as $column) {
 220                  [$classname, $columnname] = explode(column_base::ID_SEPARATOR, $column, 2);
 221                  $columnobject = $classname::from_column_name($this->get_questionbank(), $columnname);
 222                  $disabled[$column] = (object) [
 223                      'disabledname' => $columnobject->get_title(),
 224                  ];
 225              }
 226          }
 227          return $disabled;
 228      }
 229  
 230      /**
 231       * Updates enabled and disabled config for 'qbank_columnsortorder' plugin.
 232       *
 233       * @param array $enabledcolumns Enabled columns to set.
 234       * @param array $disabledcolumns Disabled columns to set.
 235       */
 236      protected function update_config($enabledcolumns, $disabledcolumns): void {
 237          if (!empty($enabledcolumns)) {
 238              $configenabled = implode(',', array_flip($enabledcolumns));
 239              set_config('enabledcol', $configenabled, 'qbank_columnsortorder');
 240          }
 241          if (!empty($disabledcolumns)) {
 242              $configdisabled = implode(',', array_flip($disabledcolumns));
 243              set_config('disabledcol', $configdisabled, 'qbank_columnsortorder');
 244          } else {
 245              set_config('disabledcol', null, 'qbank_columnsortorder');
 246          }
 247      }
 248  
 249      /**
 250       * Enables columns.
 251       *
 252       * @param string $plugin Plugin type and name ie: qbank_viewcreator.
 253       */
 254      public function enable_columns(string $plugin): void {
 255          $enabledcolumns = [];
 256          $disabledcolumns = [];
 257          if ($this->columnorder) {
 258              $enabledcolumns = $this->columnorder;
 259          }
 260          if ($this->disabledcolumns) {
 261              $disabledcolumns = $this->disabledcolumns;
 262              foreach ($disabledcolumns as $class => $column) {
 263                  if (strpos($class, $plugin) !== false) {
 264                      $enabledcolumns[$class] = $class;
 265                      if (isset($disabledcolumns[$class])) {
 266                          unset($disabledcolumns[$class]);
 267                      }
 268                  }
 269              }
 270          }
 271          $this->update_config($enabledcolumns, $disabledcolumns);
 272      }
 273  
 274      /**
 275       * Disables columns.
 276       *
 277       * @param string $plugin Plugin type and name ie: qbank_viewcreator.
 278       */
 279      public function disable_columns(string $plugin): void {
 280          $disabledcolumns = [];
 281          $enabledcolumns = [];
 282          $allcolumns = $this->get_columns();
 283          if ($this->disabledcolumns) {
 284              $disabledcolumns = $this->disabledcolumns;
 285          }
 286          if ($this->columnorder) {
 287              $enabledcolumns = $this->columnorder;
 288          }
 289  
 290          foreach ($allcolumns as $column) {
 291              if (str_contains($column->class, $plugin)) {
 292                  $disabledcolumns[$column->id] = $column->id;
 293                  if (isset($enabledcolumns[$column->id])) {
 294                      unset($enabledcolumns[$column->id]);
 295                  }
 296              }
 297          }
 298          $this->update_config($enabledcolumns, $disabledcolumns);
 299      }
 300  
 301      /**
 302       * Orders columns in the question bank view according to config_plugins table 'qbank_columnsortorder' config.
 303       *
 304       * @param array $ordertosort Unordered array of columns, [columnname => class]
 305       * @return array $properorder|$ordertosort Returns array ordered if 'qbank_columnsortorder' config exists.
 306       */
 307      public function get_sorted_columns($ordertosort): array {
 308          // Check if db has order set.
 309          if (!empty($this->columnorder)) {
 310              // Merge new order with old one.
 311              $columnsortorder = $this->columnorder;
 312              asort($columnsortorder);
 313              $columnorder = [];
 314              foreach ($columnsortorder as $columnid => $colposition) {
 315                  if (array_key_exists($columnid, $ordertosort)) {
 316                      $columnorder[$columnid] = $colposition;
 317                  }
 318              }
 319              $properorder = array_merge($columnorder, $ordertosort);
 320              // Always have the checkbox at first column position.
 321              $checkboxid = 'core_question\local\bank\checkbox_column' . column_base::ID_SEPARATOR . 'checkbox_column';
 322              if (isset($properorder[$checkboxid])) {
 323                  $checkboxfirstelement = $properorder[$checkboxid];
 324                  unset($properorder[$checkboxid]);
 325                  $properorder = array_merge([
 326                          $checkboxid => $checkboxfirstelement
 327                  ], $properorder);
 328              }
 329              return $properorder;
 330          }
 331          return $ordertosort;
 332      }
 333  
 334      /**
 335       * Given an array of columns, set the isvisible attribute according to $this->hiddencolumns and $this->disabledcolumns.
 336       *
 337       * @param column_base[] $columns
 338       * @return array
 339       */
 340      public function set_columns_visibility(array $columns): array {
 341          foreach ($columns as $column) {
 342              if (!is_object($column)) {
 343                  continue;
 344              }
 345              $columnid = $column->get_column_id();
 346  
 347              $column->isvisible = !in_array($columnid, $this->hiddencolumns) && !array_key_exists($columnid, $this->disabledcolumns);
 348          }
 349          return $columns;
 350      }
 351  
 352      /**
 353       * Return $this->colsize mapped as an array of column name => width, excluding empty sizes.
 354       *
 355       * @return array
 356       */
 357      public function get_colsize_map(): array {
 358          $sizes = array_reduce($this->colsize, function($result, $colsize) {
 359              $result[$colsize->column] = $colsize->width;
 360              return $result;
 361          }, []);
 362          return array_filter($sizes);
 363      }
 364  
 365      /**
 366       * Return an array of hidden columns as an array of class => column name
 367       *
 368       * @return array
 369       */
 370      public function get_hidden_columns(): array {
 371          return array_reduce($this->hiddencolumns, function($result, $hiddencolumn) {
 372              [$columnclass, $columnname] = explode(column_base::ID_SEPARATOR, $hiddencolumn, 2);
 373              $result[$hiddencolumn] = $columnclass::from_column_name($this->get_questionbank(), $columnname)->get_title();
 374              return $result;
 375          }, []);
 376      }
 377  
 378      public function get_column_width(column_base $column): string {
 379          $colsizemap = $this->get_colsize_map();
 380          $columnid = $column->get_column_id();
 381          if (array_key_exists($columnid, $colsizemap)) {
 382              return $colsizemap[$columnid] . 'px';
 383          }
 384          return parent::get_column_width($column);
 385      }
 386  
 387      public function get_column_actions(view $qbank): array {
 388          return [
 389              new column_action_move($qbank),
 390              new column_action_remove($qbank),
 391              new column_action_resize($qbank),
 392          ];
 393      }
 394  }