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.
   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   * Course completion critieria - completion after specific duration from course enrolment
  19   *
  20   * @package core_completion
  21   * @category completion
  22   * @copyright 2009 Catalyst IT Ltd
  23   * @author Aaron Barnes <aaronb@catalyst.net.nz>
  24   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  25   */
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  
  29  /**
  30   * Course completion critieria - completion after specific duration from course enrolment
  31   *
  32   * @package core_completion
  33   * @category completion
  34   * @copyright 2009 Catalyst IT Ltd
  35   * @author Aaron Barnes <aaronb@catalyst.net.nz>
  36   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  37   */
  38  class completion_criteria_duration extends completion_criteria {
  39  
  40      /* @var int Criteria type constant [COMPLETION_CRITERIA_TYPE_DURATION] */
  41      public $criteriatype = COMPLETION_CRITERIA_TYPE_DURATION;
  42  
  43      /**
  44       * Finds and returns a data_object instance based on params.
  45       *
  46       * @param array $params associative arrays varname=>value
  47       * @return data_object data_object instance or false if none found.
  48       */
  49      public static function fetch($params) {
  50          $params['criteriatype'] = COMPLETION_CRITERIA_TYPE_DURATION;
  51          return self::fetch_helper('course_completion_criteria', __CLASS__, $params);
  52      }
  53  
  54      /**
  55       * Add appropriate form elements to the critieria form
  56       *
  57       * @param moodleform $mform Moodle forms object
  58       * @param stdClass $data not used
  59       */
  60      public function config_form_display(&$mform, $data = null) {
  61  
  62          $mform->addElement('checkbox', 'criteria_duration', get_string('enable'));
  63  
  64          // Populate the duration length drop down.
  65          $thresholdmenu = array(
  66              // We have strings for 1 - 6 days in the core.
  67              86400 => get_string('secondstotime86400', 'core'),
  68              172800 => get_string('secondstotime172800', 'core'),
  69              259200 => get_string('secondstotime259200', 'core'),
  70              345600 => get_string('secondstotime345600', 'core'),
  71              432000 => get_string('secondstotime432000', 'core'),
  72              518400 => get_string('secondstotime518400', 'core'),
  73              518400 => get_string('secondstotime518400', 'core'),
  74          );
  75          // Append strings for 7 - 30 days (step by 1 day).
  76          for ($i = 7; $i <= 30; $i++) {
  77              $seconds = $i * DAYSECS;
  78              $thresholdmenu[$seconds] = get_string('numdays', 'core', $i);
  79          }
  80          // Append strings for 40 - 180 days (step by 10 days).
  81          for ($i = 40; $i <= 180; $i = $i + 10) {
  82              $seconds = $i * DAYSECS;
  83              $thresholdmenu[$seconds] = get_string('numdays', 'core', $i);
  84          }
  85          // Append string for 1 year.
  86          $thresholdmenu[365 * DAYSECS] = get_string('numdays', 'core', 365);
  87  
  88          $mform->addElement('select', 'criteria_duration_days', get_string('enrolmentdurationlength', 'core_completion'), $thresholdmenu);
  89          $mform->disabledIf('criteria_duration_days', 'criteria_duration');
  90  
  91          if ($this->id) {
  92              $mform->setDefault('criteria_duration', 1);
  93              $mform->setDefault('criteria_duration_days', $this->enrolperiod);
  94          }
  95      }
  96  
  97      /**
  98       * Update the criteria information stored in the database
  99       *
 100       * @param stdClass $data Form data
 101       */
 102      public function update_config(&$data) {
 103          if (!empty($data->criteria_duration)) {
 104              $this->course = $data->id;
 105              $this->enrolperiod = $data->criteria_duration_days;
 106              $this->insert();
 107          }
 108      }
 109  
 110      /**
 111       * Get the time this user was enroled
 112       *
 113       * @param completion_completion $completion
 114       * @return int
 115       */
 116      private function get_timeenrolled($completion) {
 117          global $DB;
 118  
 119          return $DB->get_field_sql('
 120              SELECT eu.timestart
 121                FROM {user_enrolments} eu
 122                JOIN {enrol} e ON eu.enrolid = e.id
 123               WHERE e.courseid = ?
 124                 AND eu.userid = ?', array($this->course, $completion->userid));
 125      }
 126  
 127      /**
 128       * Review this criteria and decide if the user has completed
 129       *
 130       * @param completion_completion $completion The user's completion record
 131       * @param bool $mark Optionally set false to not save changes to database
 132       * @return bool
 133       */
 134      public function review($completion, $mark = true) {
 135          $timeenrolled = $this->get_timeenrolled($completion);
 136  
 137          // If duration since enrollment has passed
 138          if (!$this->enrolperiod || !$timeenrolled) {
 139              return false;
 140          }
 141  
 142          if (time() > ($timeenrolled + $this->enrolperiod)) {
 143              if ($mark) {
 144                  $completion->mark_complete();
 145              }
 146  
 147              return true;
 148          }
 149  
 150          return false;
 151      }
 152  
 153      /**
 154       * Return criteria title for display in reports
 155       *
 156       * @return string
 157       */
 158      public function get_title() {
 159          return get_string('enrolmentduration', 'completion');
 160      }
 161  
 162      /**
 163       * Return a more detailed criteria title for display in reports
 164       *
 165       * @return string
 166       */
 167      public function get_title_detailed() {
 168          return get_string('xdays', 'completion', ceil($this->enrolperiod / (60 * 60 * 24)));
 169      }
 170  
 171      /**
 172       * Return criteria type title for display in reports
 173       *
 174       * @return string
 175       */
 176      public function get_type_title() {
 177          return get_string('days', 'completion');
 178      }
 179  
 180      /**
 181       * Return criteria status text for display in reports
 182       *
 183       * @param completion_completion $completion The user's completion record
 184       * @return string
 185       */
 186      public function get_status($completion) {
 187          $timeenrolled = $this->get_timeenrolled($completion);
 188          $timeleft = $timeenrolled + $this->enrolperiod - time();
 189          $enrolperiod = ceil($this->enrolperiod / (60 * 60 * 24));
 190  
 191          $daysleft = ceil($timeleft / (60 * 60 * 24));
 192  
 193          return get_string('daysoftotal', 'completion', array(
 194                  'days' => $daysleft > 0 ? $daysleft : 0, 'total' => $enrolperiod));
 195      }
 196  
 197      /**
 198       * Find user's who have completed this criteria
 199       */
 200      public function cron() {
 201          global $DB;
 202  
 203          /*
 204           * Get all users who match meet this criteria
 205           *
 206           * We can safely ignore duplicate enrolments for
 207           * a user in a course here as we only care if
 208           * one of the enrolments has passed the set
 209           * duration.
 210           */
 211          $sql = '
 212              SELECT
 213                  c.id AS course,
 214                  cr.id AS criteriaid,
 215                  u.id AS userid,
 216                  ue.timestart AS otimestart,
 217                  (ue.timestart + cr.enrolperiod) AS ctimestart,
 218                  ue.timecreated AS otimeenrolled,
 219                  (ue.timecreated + cr.enrolperiod) AS ctimeenrolled
 220              FROM
 221                  {user} u
 222              INNER JOIN
 223                  {user_enrolments} ue
 224               ON ue.userid = u.id
 225              INNER JOIN
 226                  {enrol} e
 227               ON e.id = ue.enrolid
 228              INNER JOIN
 229                  {course} c
 230               ON c.id = e.courseid
 231              INNER JOIN
 232                  {course_completion_criteria} cr
 233               ON c.id = cr.course
 234              LEFT JOIN
 235                  {course_completion_crit_compl} cc
 236               ON cc.criteriaid = cr.id
 237              AND cc.userid = u.id
 238              WHERE
 239                  cr.criteriatype = '.COMPLETION_CRITERIA_TYPE_DURATION.'
 240              AND c.enablecompletion = 1
 241              AND cc.id IS NULL
 242              AND
 243              (
 244                  ue.timestart > 0 AND ue.timestart + cr.enrolperiod < ?
 245               OR ue.timecreated > 0 AND ue.timecreated + cr.enrolperiod < ?
 246              )
 247          ';
 248  
 249          // Loop through completions, and mark as complete
 250          $now = time();
 251          $rs = $DB->get_recordset_sql($sql, array($now, $now));
 252          foreach ($rs as $record) {
 253              $completion = new completion_criteria_completion((array) $record, DATA_OBJECT_FETCH_BY_KEY);
 254  
 255              // Use time start if not 0, otherwise use timeenrolled
 256              if ($record->otimestart) {
 257                  $completion->mark_complete($record->ctimestart);
 258              } else {
 259                  $completion->mark_complete($record->ctimeenrolled);
 260              }
 261          }
 262          $rs->close();
 263      }
 264  
 265      /**
 266       * Return criteria progress details for display in reports
 267       *
 268       * @param completion_completion $completion The user's completion record
 269       * @return array An array with the following keys:
 270       *     type, criteria, requirement, status
 271       */
 272      public function get_details($completion) {
 273          $details = array();
 274          $details['type'] = get_string('periodpostenrolment', 'completion');
 275          $details['criteria'] = get_string('remainingenroledfortime', 'completion');
 276          $details['requirement'] = get_string('xdays', 'completion', ceil($this->enrolperiod / (60*60*24)));
 277  
 278          // Get status
 279          $timeenrolled = $this->get_timeenrolled($completion);
 280          $timepassed = time() - $timeenrolled;
 281          $details['status'] = get_string('xdays', 'completion', floor($timepassed / (60*60*24)));
 282  
 283          return $details;
 284      }
 285  }