Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.0.x will end 8 May 2023 (12 months).
  • Bug fixes for security issues in 4.0.x will end 13 November 2023 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: the minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is also supported.

Differences Between: [Versions 310 and 400] [Versions 311 and 400] [Versions 39 and 400] [Versions 400 and 401] [Versions 400 and 402] [Versions 400 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   * List of grade letters.
  19   *
  20   * @package   core_grades
  21   * @copyright 2008 Nicolas Connault
  22   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  require_once '../../../config.php';
  26  require_once $CFG->dirroot.'/grade/lib.php';
  27  require_once $CFG->libdir.'/gradelib.php';
  28  
  29  $contextid = optional_param('id', SYSCONTEXTID, PARAM_INT);
  30  $action   = optional_param('action', '', PARAM_ALPHA);
  31  $edit     = optional_param('edit', false, PARAM_BOOL); //are we editing?
  32  
  33  $url = new moodle_url('/grade/edit/letter/index.php', array('id' => $contextid));
  34  
  35  list($context, $course, $cm) = get_context_info_array($contextid);
  36  $contextid = null;//now we have a context object throw away the $contextid from the params
  37  
  38  //if viewing
  39  if (!$edit) {
  40      if (!has_capability('moodle/grade:manage', $context) and !has_capability('moodle/grade:manageletters', $context)) {
  41          print_error('nopermissiontoviewletergrade');
  42      }
  43  } else {//else we're editing
  44      require_capability('moodle/grade:manageletters', $context);
  45      navigation_node::override_active_url($url);
  46      $url->param('edit', 1);
  47      $PAGE->navbar->add(get_string('editgradeletters', 'grades'), $url);
  48  }
  49  $PAGE->set_url($url);
  50  
  51  $returnurl = null;
  52  $editparam = null;
  53  if ($context->contextlevel == CONTEXT_SYSTEM or $context->contextlevel == CONTEXT_COURSECAT) {
  54      require_once $CFG->libdir.'/adminlib.php';
  55  
  56      admin_externalpage_setup('letters');
  57  
  58      $admin = true;
  59      $returnurl = "$CFG->wwwroot/grade/edit/letter/index.php";
  60      $editparam = '?edit=1';
  61      $PAGE->set_primary_active_tab('siteadminnode');
  62  } else if ($context->contextlevel == CONTEXT_COURSE) {
  63  
  64      $PAGE->set_pagelayout('standard');//calling this here to make blocks display
  65  
  66      require_login($context->instanceid, false, $cm);
  67  
  68      $admin = false;
  69      $returnurl = $CFG->wwwroot.'/grade/edit/letter/index.php?id='.$context->id;
  70      $editparam = '&edit=1';
  71  
  72      $gpr = new grade_plugin_return(array('type'=>'edit', 'plugin'=>'letter', 'courseid'=>$course->id));
  73  } else {
  74      print_error('invalidcourselevel');
  75  }
  76  
  77  $strgrades = get_string('grades');
  78  $pagename  = get_string('letters', 'grades');
  79  
  80  $letters = grade_get_letters($context);
  81  $num = count($letters) + 3;
  82  
  83  $override = $DB->record_exists('grade_letters', array('contextid' => $context->id));
  84  
  85  //if were viewing the letters
  86  if (!$edit) {
  87      $heading = get_string('gradeletters', 'grades');
  88      $actionbar = new \core_grades\output\grade_letters_action_bar($context);
  89  
  90      if ($admin) {
  91          echo $OUTPUT->header();
  92          $renderer = $PAGE->get_renderer('core_grades');
  93          echo $renderer->render_action_bar($actionbar);
  94          echo $OUTPUT->heading($heading);
  95      } else {
  96          print_grade_page_head($course->id, 'letter', 'view', $heading, false, false,
  97              true, null, null, null, $actionbar);
  98      }
  99  
 100      $data = array();
 101  
 102      $max = 100;
 103      foreach($letters as $boundary=>$letter) {
 104          $line = array();
 105          $line[] = format_float($max,2).' %';
 106          $line[] = format_float($boundary,2).' %';
 107          $line[] = format_string($letter);
 108          $data[] = $line;
 109          $max = $boundary - 0.01;
 110      }
 111  
 112      if (!empty($override)) {
 113          echo $OUTPUT->notification(get_string('gradeletteroverridden', 'grades'), 'notifymessage');
 114      }
 115  
 116      $table = new html_table();
 117      $table->id = 'grade-letters-view';
 118      $table->head  = array(get_string('max', 'grades'), get_string('min', 'grades'), get_string('letter', 'grades'));
 119      $table->size  = array('30%', '30%', '40%');
 120      $table->align = array('left', 'left', 'left');
 121      $table->width = '30%';
 122      $table->data  = $data;
 123      $table->tablealign  = 'center';
 124      echo html_writer::table($table);
 125  
 126  } else { //else we're editing
 127      require_once ('edit_form.php');
 128  
 129      $data = new stdClass();
 130      $data->id = $context->id;
 131  
 132      $i = 1;
 133      foreach ($letters as $boundary=>$letter) {
 134          $gradelettername = 'gradeletter'.$i;
 135          $gradeboundaryname = 'gradeboundary'.$i;
 136  
 137          $data->$gradelettername   = $letter;
 138          $data->$gradeboundaryname = $boundary;
 139          $i++;
 140      }
 141      $data->override = $override;
 142  
 143      $mform = new edit_letter_form($returnurl.$editparam, array('num'=>$num, 'admin'=>$admin));
 144      $mform->set_data($data);
 145  
 146      if ($mform->is_cancelled()) {
 147          redirect($returnurl);
 148  
 149      } else if ($data = $mform->get_data()) {
 150  
 151          // Make sure we are updating the cache.
 152          $cache = cache::make('core', 'grade_letters');
 153  
 154          if (!$admin and empty($data->override)) {
 155              $records = $DB->get_records('grade_letters', array('contextid' => $context->id));
 156              foreach ($records as $record) {
 157                  $DB->delete_records('grade_letters', array('id' => $record->id));
 158                  // Trigger the letter grade deleted event.
 159                  $event = \core\event\grade_letter_deleted::create(array(
 160                      'objectid' => $record->id,
 161                      'context' => $context,
 162                  ));
 163                  $event->trigger();
 164              }
 165  
 166              // Make sure we clear the cache for this context.
 167              $cache->delete($context->id);
 168              redirect($returnurl);
 169          }
 170  
 171          $letters = array();
 172          for ($i=1; $i < $num+1; $i++) {
 173              $gradelettername = 'gradeletter'.$i;
 174              $gradeboundaryname = 'gradeboundary'.$i;
 175  
 176              if (property_exists($data, $gradeboundaryname) and $data->$gradeboundaryname != -1) {
 177                  $letter = trim($data->$gradelettername);
 178                  if ($letter == '') {
 179                      continue;
 180                  }
 181  
 182                  $boundary = floatval($data->$gradeboundaryname);
 183                  if ($boundary < 0 || $boundary > 100) {
 184                      continue;    // Skip if out of range.
 185                  }
 186  
 187                  // The keys need to be strings so floats are not truncated.
 188                  $letters[number_format($boundary, 5)] = $letter;
 189              }
 190          }
 191  
 192          $pool = array();
 193          if ($records = $DB->get_records('grade_letters', array('contextid' => $context->id), 'lowerboundary ASC')) {
 194              foreach ($records as $r) {
 195                  // Will re-use the lowerboundary to avoid duplicate during the update process.
 196                  $pool[number_format($r->lowerboundary, 5)] = $r;
 197              }
 198          }
 199  
 200          foreach ($letters as $boundary => $letter) {
 201              $record = new stdClass();
 202              $record->letter        = $letter;
 203              $record->lowerboundary = $boundary;
 204              $record->contextid     = $context->id;
 205  
 206              if (isset($pool[$boundary])) {
 207                  // Re-use the existing boundary to avoid key constraint.
 208                  if ($letter != $pool[$boundary]->letter) {
 209                      // The letter has been assigned to another boundary, we update it.
 210                      $record->id = $pool[$boundary]->id;
 211                      $DB->update_record('grade_letters', $record);
 212                      // Trigger the letter grade updated event.
 213                      $event = \core\event\grade_letter_updated::create(array(
 214                          'objectid' => $record->id,
 215                          'context' => $context,
 216                      ));
 217                      $event->trigger();
 218                  }
 219                  unset($pool[$boundary]);    // Remove the letter from the pool.
 220              } else if ($candidate = array_pop($pool)) {
 221                  // The boundary is new, we update a random record from the pool.
 222                  $record->id = $candidate->id;
 223                  $DB->update_record('grade_letters', $record);
 224                  // Trigger the letter grade updated event.
 225                  $event = \core\event\grade_letter_updated::create(array(
 226                      'objectid' => $record->id,
 227                      'context' => $context,
 228                  ));
 229                  $event->trigger();
 230              } else {
 231                  // No records were found, this must be a new letter.
 232                  $newid = $DB->insert_record('grade_letters', $record);
 233                  // Trigger the letter grade added event.
 234                  $event = \core\event\grade_letter_created::create(array(
 235                      'objectid' => $newid,
 236                      'context' => $context,
 237                  ));
 238                  $event->trigger();
 239              }
 240          }
 241  
 242          // Cache the changed letters.
 243          if (!empty($letters)) {
 244  
 245              // For some reason, the cache saves it in the order in which they were entered
 246              // but we really want to order them in descending order so we sort it here.
 247              krsort($letters);
 248              $cache->set($context->id, $letters);
 249          }
 250  
 251          // Delete the unused records.
 252          foreach($pool as $leftover) {
 253              $DB->delete_records('grade_letters', array('id' => $leftover->id));
 254              // Trigger the letter grade deleted event.
 255              $event = \core\event\grade_letter_deleted::create(array(
 256                  'objectid' => $leftover->id,
 257                  'context' => $context,
 258              ));
 259              $event->trigger();
 260          }
 261  
 262          redirect($returnurl);
 263      }
 264  
 265      print_grade_page_head($COURSE->id, 'letter', 'edit', get_string('editgradeletters', 'grades'),
 266          false, false, false);
 267  
 268      $mform->display();
 269  }
 270  
 271  echo $OUTPUT->footer();