Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

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

   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->string_for_js('overridenoneconfirm', 'gradereport_singleview');
 272          $PAGE->requires->js_init_call('M.gradereport_singleview.init', array(), false, $module);
 273      }
 274  
 275      /**
 276       * Process the data from a form submission.
 277       *
 278       * @param array $data
 279       * @return array of warnings
 280       */
 281      public function process($data) {
 282          $warnings = array();
 283  
 284          $fields = $this->definition();
 285  
 286          // Avoiding execution timeouts when updating
 287          // a large amount of grades.
 288          $progress = 0;
 289          $progressbar = new \core\progress\display_if_slow();
 290          $progressbar->start_html();
 291          $progressbar->start_progress(get_string('savegrades', 'gradereport_singleview'), count((array) $data) - 1);
 292          $changecount = array();
 293          // This array is used to determine if the override should be excluded from being counted as a change.
 294          $ignorevalues = [];
 295  
 296          foreach ($data as $varname => $throw) {
 297              $progressbar->progress($progress);
 298              $progress++;
 299              if (preg_match("/(\w+)_(\d+)_(\d+)/", $varname, $matches)) {
 300                  $itemid = $matches[2];
 301                  $userid = $matches[3];
 302              } else {
 303                  continue;
 304              }
 305  
 306              $gradeitem = grade_item::fetch(array(
 307                  'id' => $itemid, 'courseid' => $this->courseid
 308              ));
 309  
 310              if (preg_match('/^old[oe]{1}/', $varname)) {
 311                  $elementname = preg_replace('/^old/', '', $varname);
 312                  if (!isset($data->$elementname)) {
 313                      // Decrease the progress because we've increased the
 314                      // size of the array we are iterating through.
 315                      $progress--;
 316                      $data->$elementname = false;
 317                  }
 318              }
 319  
 320              if (!in_array($matches[1], $fields)) {
 321                  continue;
 322              }
 323  
 324              if (!$gradeitem) {
 325                  continue;
 326              }
 327  
 328              $grade = $this->fetch_grade_or_default($gradeitem, $userid);
 329  
 330              $classname = '\\gradereport_singleview\\local\\ui\\' . $matches[1];
 331              $element = new $classname($grade);
 332  
 333              $name = $element->get_name();
 334              $oldname = "old$name";
 335  
 336              $posted = $data->$name;
 337  
 338              $format = $element->determine_format();
 339  
 340              if ($format->is_textbox() and trim($data->$name) === '') {
 341                  $data->$name = null;
 342              }
 343  
 344              // Same value; skip.
 345              if (isset($data->$oldname) && $data->$oldname == $posted) {
 346                  continue;
 347              }
 348  
 349              // If the user submits Exclude grade elements without the proper.
 350              // permissions then we should refuse to update.
 351              if ($matches[1] === 'exclude' && !has_capability('moodle/grade:manage', $this->context)){
 352                  $warnings[] = get_string('nopermissions', 'error', get_string('grade:manage', 'role'));
 353                  continue;
 354              }
 355  
 356              $msg = $element->set($posted);
 357              // Value to check against our list of matchelements to ignore.
 358              $check = explode('_', $varname, 2);
 359  
 360              // Optional type.
 361              if (!empty($msg)) {
 362                  $warnings[] = $msg;
 363                  if ($element instanceof \gradereport_singleview\local\ui\finalgrade) {
 364                      // Add this value to this list so that the override object that is coming next will also be skipped.
 365                      $ignorevalues[$check[1]] = $check[1];
 366                      // This item wasn't changed so don't add to the changecount.
 367                      continue;
 368                  }
 369              }
 370              // Check to see if this value has already been skipped.
 371              if (array_key_exists($check[1], $ignorevalues)) {
 372                  continue;
 373              }
 374              if (preg_match('/_(\d+)_(\d+)/', $varname, $matchelement)) {
 375                  $changecount[$matchelement[0]] = 1;
 376              }
 377          }
 378  
 379          // Some post-processing.
 380          $eventdata = new stdClass;
 381          $eventdata->warnings = $warnings;
 382          $eventdata->post_data = $data;
 383          $eventdata->instance = $this;
 384          $eventdata->changecount = $changecount;
 385  
 386          $progressbar->end_html();
 387  
 388          return $eventdata;
 389      }
 390  
 391      /**
 392       * By default there are no options.
 393       * @return array
 394       */
 395      public function options() {
 396          return array();
 397      }
 398  
 399      /**
 400       * Should we show the group selector?
 401       * @return bool
 402       */
 403      public function display_group_selector() {
 404          return true;
 405      }
 406  
 407      /**
 408       * Should we show the next prev selector?
 409       * @return bool
 410       */
 411      public function supports_next_prev() {
 412          return true;
 413      }
 414  
 415      /**
 416       * Load a valid list of users for this gradebook as the screen "items".
 417       * @return array $users A list of enroled users.
 418       */
 419      protected function load_users() {
 420          global $CFG;
 421  
 422          // Create a graded_users_iterator because it will properly check the groups etc.
 423          $defaultgradeshowactiveenrol = !empty($CFG->grade_report_showonlyactiveenrol);
 424          $showonlyactiveenrol = get_user_preferences('grade_report_showonlyactiveenrol', $defaultgradeshowactiveenrol);
 425          $showonlyactiveenrol = $showonlyactiveenrol || !has_capability('moodle/course:viewsuspendedusers', $this->context);
 426  
 427          require_once($CFG->dirroot.'/grade/lib.php');
 428          $gui = new \graded_users_iterator($this->course, null, $this->groupid);
 429          $gui->require_active_enrolment($showonlyactiveenrol);
 430          $gui->init();
 431  
 432          // Flatten the users.
 433          $users = array();
 434          while ($user = $gui->next_user()) {
 435              $users[$user->user->id] = $user->user;
 436          }
 437          $gui->close();
 438          return $users;
 439      }
 440  
 441      /**
 442       * Allow selection of number of items to display per page.
 443       * @return string
 444       */
 445      public function perpage_select() {
 446          global $PAGE, $OUTPUT;
 447  
 448          $options = array_combine(self::$validperpage, self::$validperpage);
 449  
 450          $url = new moodle_url($PAGE->url);
 451          $url->remove_params(['page', 'perpage']);
 452  
 453          $out = '';
 454          $select = new \single_select($url, 'perpage', $options, $this->perpage, null, 'perpagechanger');
 455          $select->label = get_string('itemsperpage', 'gradereport_singleview');
 456          $out .= $OUTPUT->render($select);
 457  
 458          return $out;
 459      }
 460  }