Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 39 and 310] [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 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   * Abstract class used as a base for the 3 screens.
  19   *
  20   * @package   gradereport_singleview
  21   * @copyright 2014 Moodle Pty Ltd (http://moodle.com)
  22   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  namespace gradereport_singleview\local\screen;
  26  
  27  use context_course;
  28  use moodle_url;
  29  use html_writer;
  30  use grade_structure;
  31  use grade_grade;
  32  use grade_item;
  33  use stdClass;
  34  
  35  defined('MOODLE_INTERNAL') || die;
  36  
  37  /**
  38   * Abstract class used as a base for the 3 screens.
  39   *
  40   * @package   gradereport_singleview
  41   * @copyright 2014 Moodle Pty Ltd (http://moodle.com)
  42   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  43   */
  44  abstract class screen {
  45  
  46      /** @var int $courseid The id of the course */
  47      protected $courseid;
  48  
  49      /** @var int $itemid Either a user id or a grade_item id */
  50      protected $itemid;
  51  
  52      /** @var int $groupid The currently set groupid (if set) */
  53      protected $groupid;
  54  
  55      /** @var course_context $context The course context */
  56      protected $context;
  57  
  58      /** @var int $page The page number */
  59      protected $page;
  60  
  61      /** @var int $perpage Results per page */
  62      protected $perpage;
  63  
  64      /** @var array $items List of items on the page, they could be users or grade_items */
  65      protected $items;
  66  
  67      /** @var array $validperpage List of allowed values for 'perpage' setting */
  68      protected static $validperpage = [20, 50, 100, 200, 400, 1000, 5000];
  69  
  70      /**
  71       * Constructor
  72       *
  73       * @param int $courseid The course id
  74       * @param int $itemid The item id
  75       * @param int $groupid The group id
  76       */
  77      public function __construct($courseid, $itemid, $groupid = null) {
  78          global $DB;
  79  
  80          $this->courseid = $courseid;
  81          $this->itemid = $itemid;
  82          $this->groupid = $groupid;
  83  
  84          $this->context = context_course::instance($this->courseid);
  85          $this->course = $DB->get_record('course', array('id' => $courseid));
  86  
  87          $this->page = optional_param('page', 0, PARAM_INT);
  88  
  89          $cache = \cache::make_from_params(\cache_store::MODE_SESSION, 'gradereport_singleview', 'perpage');
  90          $perpage = optional_param('perpage', null, PARAM_INT);
  91          if (!in_array($perpage, self::$validperpage)) {
  92              // Get from cache.
  93              $perpage = $cache->get(get_class($this));
  94          } else {
  95              // Save to cache.
  96              $cache->set(get_class($this), $perpage);
  97          }
  98          if ($perpage) {
  99              $this->perpage = $perpage;
 100          } else {
 101              $this->perpage = 100;
 102          }
 103  
 104          $this->init(empty($itemid));
 105      }
 106  
 107      /**
 108       * Cache the grade_structure class
 109       */
 110      public function setup_structure() {
 111          $this->structure = new grade_structure();
 112          $this->structure->modinfo = get_fast_modinfo($this->course);
 113      }
 114  
 115      /**
 116       * Create a nice link from a thing (user or grade_item).
 117       *
 118       * @param string $screen
 119       * @param int $itemid
 120       * @param bool $display Should we wrap this in an anchor ?
 121       * @return string The link
 122       */
 123      public function format_link($screen, $itemid, $display = null) {
 124          $url = new moodle_url('/grade/report/singleview/index.php', array(
 125              'id' => $this->courseid,
 126              'item' => $screen,
 127              'itemid' => $itemid,
 128              'group' => $this->groupid,
 129          ));
 130  
 131          if ($display) {
 132              return html_writer::link($url, $display);
 133          } else {
 134              return $url;
 135          }
 136      }
 137  
 138      /**
 139       * Get the grade_grade
 140       *
 141       * @param grade_item $item The grade_item
 142       * @param int $userid The user id
 143       * @return grade_grade
 144       */
 145      public function fetch_grade_or_default($item, $userid) {
 146          $grade = grade_grade::fetch(array(
 147              'itemid' => $item->id, 'userid' => $userid
 148          ));
 149  
 150          if (!$grade) {
 151              $default = new stdClass;
 152  
 153              $default->userid = $userid;
 154              $default->itemid = $item->id;
 155              $default->feedback = '';
 156  
 157              $grade = new grade_grade($default, false);
 158          }
 159  
 160          $grade->grade_item = $item;
 161  
 162          return $grade;
 163      }
 164  
 165      /**
 166       * Make the HTML element that toggles all the checkboxes on or off.
 167       *
 168       * @param string $key A unique key for this control - inserted in the classes.
 169       * @return string
 170       */
 171      public function make_toggle($key) {
 172          $attrs = array('href' => '#');
 173  
 174          // Do proper lang strings for title attributes exist for the given key?
 175          $strmanager = \get_string_manager();
 176          $titleall = get_string('all');
 177          $titlenone = get_string('none');
 178          if ($strmanager->string_exists(strtolower($key) . 'all', 'gradereport_singleview')) {
 179              $titleall = get_string(strtolower($key) . 'all', 'gradereport_singleview');
 180          }
 181          if ($strmanager->string_exists(strtolower($key) . 'none', 'gradereport_singleview')) {
 182              $titlenone = get_string(strtolower($key) . 'none', 'gradereport_singleview');
 183          }
 184  
 185          $all = html_writer::tag('a', get_string('all'), $attrs + array(
 186              'class' => 'include all ' . $key,
 187              'title' => $titleall
 188          ));
 189  
 190          $none = html_writer::tag('a', get_string('none'), $attrs + array(
 191              'class' => 'include none ' . $key,
 192              'title' => $titlenone
 193          ));
 194  
 195          return html_writer::tag('span', "$all / $none", array(
 196              'class' => 'inclusion_links'
 197          ));
 198      }
 199  
 200      /**
 201       * Make a toggle link with some text before it.
 202       *
 203       * @param string $key A unique key for this control - inserted in the classes.
 204       * @return string
 205       */
 206      public function make_toggle_links($key) {
 207          return get_string($key, 'gradereport_singleview') . ' ' .
 208              $this->make_toggle($key);
 209      }
 210  
 211      /**
 212       * Get the default heading for the screen.
 213       *
 214       * @return string
 215       */
 216      public function heading() {
 217          return get_string('entrypage', 'gradereport_singleview');
 218      }
 219  
 220      /**
 221       * Override this to init the screen.
 222       *
 223       * @param boolean $selfitemisempty True if no item has been selected yet.
 224       */
 225      public abstract function init($selfitemisempty = false);
 226  
 227      /**
 228       * Get the type of items in the list.
 229       *
 230       * @return string
 231       */
 232      public abstract function item_type();
 233  
 234      /**
 235       * Get the entire screen as a string.
 236       *
 237       * @return string
 238       */
 239      public abstract function html();
 240  
 241      /**
 242       * Does this screen support paging?
 243       *
 244       * @return bool
 245       */
 246      public function supports_paging() {
 247          return true;
 248      }
 249  
 250      /**
 251       * Default pager
 252       *
 253       * @return string
 254       */
 255      public function pager() {
 256          return '';
 257      }
 258  
 259      /**
 260       * Initialise the js for this screen.
 261       */
 262      public function js() {
 263          global $PAGE;
 264  
 265          $module = array(
 266              'name' => 'gradereport_singleview',
 267              'fullpath' => '/grade/report/singleview/js/singleview.js',
 268              'requires' => array('base', 'dom', 'event', 'event-simulate', 'io-base')
 269          );
 270  
 271          $PAGE->requires->js_init_call('M.gradereport_singleview.init', array(), false, $module);
 272      }
 273  
 274      /**
 275       * Process the data from a form submission.
 276       *
 277       * @param array $data
 278       * @return array of warnings
 279       */
 280      public function process($data) {
 281          $warnings = array();
 282  
 283          $fields = $this->definition();
 284  
 285          // Avoiding execution timeouts when updating
 286          // a large amount of grades.
 287          $progress = 0;
 288          $progressbar = new \core\progress\display_if_slow();
 289          $progressbar->start_html();
 290          $progressbar->start_progress(get_string('savegrades', 'gradereport_singleview'), count((array) $data) - 1);
 291          $changecount = array();
 292          // This array is used to determine if the override should be excluded from being counted as a change.
 293          $ignorevalues = [];
 294  
 295          foreach ($data as $varname => $throw) {
 296              $progressbar->progress($progress);
 297              $progress++;
 298              if (preg_match("/(\w+)_(\d+)_(\d+)/", $varname, $matches)) {
 299                  $itemid = $matches[2];
 300                  $userid = $matches[3];
 301              } else {
 302                  continue;
 303              }
 304  
 305              $gradeitem = grade_item::fetch(array(
 306                  'id' => $itemid, 'courseid' => $this->courseid
 307              ));
 308  
 309              if (preg_match('/^old[oe]{1}/', $varname)) {
 310                  $elementname = preg_replace('/^old/', '', $varname);
 311                  if (!isset($data->$elementname)) {
 312                      // Decrease the progress because we've increased the
 313                      // size of the array we are iterating through.
 314                      $progress--;
 315                      $data->$elementname = false;
 316                  }
 317              }
 318  
 319              if (!in_array($matches[1], $fields)) {
 320                  continue;
 321              }
 322  
 323              if (!$gradeitem) {
 324                  continue;
 325              }
 326  
 327              $grade = $this->fetch_grade_or_default($gradeitem, $userid);
 328  
 329              $classname = '\\gradereport_singleview\\local\\ui\\' . $matches[1];
 330              $element = new $classname($grade);
 331  
 332              $name = $element->get_name();
 333              $oldname = "old$name";
 334  
 335              $posted = $data->$name;
 336  
 337              $format = $element->determine_format();
 338  
 339              if ($format->is_textbox() and trim($data->$name) === '') {
 340                  $data->$name = null;
 341              }
 342  
 343              // Same value; skip.
 344              if (isset($data->$oldname) && $data->$oldname == $posted) {
 345                  continue;
 346              }
 347  
 348              // If the user submits Exclude grade elements without the proper.
 349              // permissions then we should refuse to update.
 350              if ($matches[1] === 'exclude' && !has_capability('moodle/grade:manage', $this->context)){
 351                  $warnings[] = get_string('nopermissions', 'error', get_string('grade:manage', 'role'));
 352                  continue;
 353              }
 354  
 355              $msg = $element->set($posted);
 356              // Value to check against our list of matchelements to ignore.
 357              $check = explode('_', $varname, 2);
 358  
 359              // Optional type.
 360              if (!empty($msg)) {
 361                  $warnings[] = $msg;
 362                  if ($element instanceof \gradereport_singleview\local\ui\finalgrade) {
 363                      // Add this value to this list so that the override object that is coming next will also be skipped.
 364                      $ignorevalues[$check[1]] = $check[1];
 365                      // This item wasn't changed so don't add to the changecount.
 366                      continue;
 367                  }
 368              }
 369              // Check to see if this value has already been skipped.
 370              if (array_key_exists($check[1], $ignorevalues)) {
 371                  continue;
 372              }
 373              if (preg_match('/_(\d+)_(\d+)/', $varname, $matchelement)) {
 374                  $changecount[$matchelement[0]] = 1;
 375              }
 376          }
 377  
 378          // Some post-processing.
 379          $eventdata = new stdClass;
 380          $eventdata->warnings = $warnings;
 381          $eventdata->post_data = $data;
 382          $eventdata->instance = $this;
 383          $eventdata->changecount = $changecount;
 384  
 385          $progressbar->end_html();
 386  
 387          return $eventdata;
 388      }
 389  
 390      /**
 391       * By default there are no options.
 392       * @return array
 393       */
 394      public function options() {
 395          return array();
 396      }
 397  
 398      /**
 399       * Should we show the group selector?
 400       * @return bool
 401       */
 402      public function display_group_selector() {
 403          return true;
 404      }
 405  
 406      /**
 407       * Should we show the next prev selector?
 408       * @return bool
 409       */
 410      public function supports_next_prev() {
 411          return true;
 412      }
 413  
 414      /**
 415       * Load a valid list of users for this gradebook as the screen "items".
 416       * @return array $users A list of enroled users.
 417       */
 418      protected function load_users() {
 419          global $CFG;
 420  
 421          // Create a graded_users_iterator because it will properly check the groups etc.
 422          $defaultgradeshowactiveenrol = !empty($CFG->grade_report_showonlyactiveenrol);
 423          $showonlyactiveenrol = get_user_preferences('grade_report_showonlyactiveenrol', $defaultgradeshowactiveenrol);
 424          $showonlyactiveenrol = $showonlyactiveenrol || !has_capability('moodle/course:viewsuspendedusers', $this->context);
 425  
 426          require_once($CFG->dirroot.'/grade/lib.php');
 427          $gui = new \graded_users_iterator($this->course, null, $this->groupid);
 428          $gui->require_active_enrolment($showonlyactiveenrol);
 429          $gui->init();
 430  
 431          // Flatten the users.
 432          $users = array();
 433          while ($user = $gui->next_user()) {
 434              $users[$user->user->id] = $user->user;
 435          }
 436          $gui->close();
 437          return $users;
 438      }
 439  
 440      /**
 441       * Allow selection of number of items to display per page.
 442       * @return string
 443       */
 444      public function perpage_select() {
 445          global $PAGE, $OUTPUT;
 446  
 447          $options = array_combine(self::$validperpage, self::$validperpage);
 448  
 449          $url = new moodle_url($PAGE->url);
 450          $url->remove_params(['page', 'perpage']);
 451  
 452          $out = '';
 453          $select = new \single_select($url, 'perpage', $options, $this->perpage, null, 'perpagechanger');
 454          $select->label = get_string('itemsperpage', 'gradereport_singleview');
 455          $out .= $OUTPUT->render($select);
 456  
 457          return $out;
 458      }
 459  }