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 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 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   * Raw event retrieval strategy.
  19   *
  20   * @package    core_calendar
  21   * @copyright  2017 Cameron Ball <cameron@cameron1729.xyz>
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  namespace core_calendar\local\event\strategies;
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  
  29  /**
  30   * Raw event retrieval strategy.
  31   *
  32   * This strategy is based on what used to be the calendar API's get_events function.
  33   *
  34   * @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
  35   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  36   */
  37  class raw_event_retrieval_strategy implements raw_event_retrieval_strategy_interface {
  38  
  39      public function get_raw_events(
  40          array $usersfilter = null,
  41          array $groupsfilter = null,
  42          array $coursesfilter = null,
  43          array $categoriesfilter = null,
  44          array $whereconditions = null,
  45          array $whereparams = null,
  46          $ordersql = null,
  47          $offset = null,
  48          $limitnum = null,
  49          $ignorehidden = true
  50      ) {
  51          return $this->get_raw_events_legacy_implementation(
  52              !is_null($usersfilter) ? $usersfilter : true, // True means no filter in old implementation.
  53              !is_null($groupsfilter) ? $groupsfilter : true,
  54              !is_null($coursesfilter) ? $coursesfilter : true,
  55              !is_null($categoriesfilter) ? $categoriesfilter : true,
  56              $whereconditions,
  57              $whereparams,
  58              $ordersql,
  59              $offset,
  60              $limitnum,
  61              $ignorehidden
  62          );
  63      }
  64  
  65      /**
  66       * The legacy implementation with minor tweaks.
  67       *
  68       * @param array|int|boolean $users array of users, user id or boolean for all/no user events
  69       * @param array|int|boolean $groups array of groups, group id or boolean for all/no group events
  70       * @param array|int|boolean $courses array of courses, course id or boolean for all/no course events
  71       * @param string $whereconditions The conditions in the WHERE clause.
  72       * @param array $whereparams The parameters for the WHERE clause.
  73       * @param string $ordersql The ORDER BY clause.
  74       * @param int $offset Offset.
  75       * @param int $limitnum Limit.
  76       * @param boolean $ignorehidden whether to select only visible events or all events
  77       * @return array $events of selected events or an empty array if there aren't any (or there was an error)
  78       */
  79      protected function get_raw_events_legacy_implementation(
  80          $users,
  81          $groups,
  82          $courses,
  83          $categories,
  84          $whereconditions,
  85          $whereparams,
  86          $ordersql,
  87          $offset,
  88          $limitnum,
  89          $ignorehidden
  90      ) {
  91          global $DB;
  92  
  93          $params = array();
  94          // Quick test.
  95          if (empty($users) && empty($groups) && empty($courses) && empty($categories)) {
  96              return array();
  97          }
  98  
  99          if (is_numeric($users)) {
 100              $users = array($users);
 101          }
 102          if (is_numeric($groups)) {
 103              $groups = array($groups);
 104          }
 105          if (is_numeric($courses)) {
 106              $courses = array($courses);
 107          }
 108          if (is_numeric($categories)) {
 109              $categories = array($categories);
 110          }
 111  
 112          // Array of filter conditions. To be concatenated by the OR operator.
 113          $filters = [];
 114  
 115          // User filter.
 116          if (is_array($users) && !empty($users)) {
 117              // Events from a number of users.
 118              list($insqlusers, $inparamsusers) = $DB->get_in_or_equal($users, SQL_PARAMS_NAMED);
 119              $filters[] = "(e.userid $insqlusers AND e.courseid = 0 AND e.groupid = 0 AND e.categoryid = 0)";
 120              $params = array_merge($params, $inparamsusers);
 121          } else if ($users === true) {
 122              // Events from ALL users.
 123              $filters[] = "(e.userid != 0 AND e.courseid = 0 AND e.groupid = 0 AND e.categoryid = 0)";
 124          }
 125          // Boolean false (no users at all): We don't need to do anything.
 126  
 127          // Group filter.
 128          if (is_array($groups) && !empty($groups)) {
 129              // Events from a number of groups.
 130              list($insqlgroups, $inparamsgroups) = $DB->get_in_or_equal($groups, SQL_PARAMS_NAMED);
 131              $filters[] = "e.groupid $insqlgroups";
 132              $params = array_merge($params, $inparamsgroups);
 133          } else if ($groups === true) {
 134              // Events from ALL groups.
 135              $filters[] = "e.groupid != 0";
 136          }
 137          // Boolean false (no groups at all): We don't need to do anything.
 138  
 139          // Course filter.
 140          if (is_array($courses) && !empty($courses)) {
 141              list($insqlcourses, $inparamscourses) = $DB->get_in_or_equal($courses, SQL_PARAMS_NAMED);
 142              $filters[] = "(e.groupid = 0 AND e.courseid $insqlcourses)";
 143              $params = array_merge($params, $inparamscourses);
 144          } else if ($courses === true) {
 145              // Events from ALL courses.
 146              $filters[] = "(e.groupid = 0 AND e.courseid != 0)";
 147          }
 148  
 149          // Category filter.
 150          if (is_array($categories) && !empty($categories)) {
 151              list($insqlcategories, $inparamscategories) = $DB->get_in_or_equal($categories, SQL_PARAMS_NAMED);
 152              $filters[] = "(e.groupid = 0 AND e.courseid = 0 AND e.categoryid $insqlcategories)";
 153              $params = array_merge($params, $inparamscategories);
 154          } else if ($categories === true) {
 155              // Events from ALL categories.
 156              $filters[] = "(e.groupid = 0 AND e.courseid = 0 AND e.categoryid != 0)";
 157          }
 158  
 159          // Security check: if, by now, we have NOTHING in $whereclause, then it means
 160          // that NO event-selecting clauses were defined. Thus, we won't be returning ANY
 161          // events no matter what. Allowing the code to proceed might return a completely
 162          // valid query with only time constraints, thus selecting ALL events in that time frame!
 163          if (empty($filters)) {
 164              return array();
 165          }
 166  
 167          // Build our clause for the filters.
 168          $filterclause = implode(' OR ', $filters);
 169  
 170          // Array of where conditions for our query. To be concatenated by the AND operator.
 171          $whereconditions[] = "($filterclause)";
 172  
 173          // Show visible only.
 174          if ($ignorehidden) {
 175              $whereconditions[] = "(e.visible = 1)";
 176          }
 177  
 178          // Build the main query's WHERE clause.
 179          $whereclause = implode(' AND ', $whereconditions);
 180  
 181          // Build SQL subquery and conditions for filtered events based on priorities.
 182          $subquerywhere = '';
 183          $subqueryconditions = [];
 184          $subqueryparams = [];
 185          $allusercourses = [];
 186  
 187          if (is_array($users) && !empty($users)) {
 188              $userrecords = $DB->get_records_sql("SELECT * FROM {user} WHERE id $insqlusers", $inparamsusers);
 189              foreach ($userrecords as $userrecord) {
 190                  // Get the user's courses. Otherwise, get the default courses being shown by the calendar.
 191                  $usercourses = calendar_get_default_courses(null, 'id, category, groupmode, groupmodeforce',
 192                          false, $userrecord->id);
 193  
 194                  // Set calendar filters.
 195                  list($usercourses, $usergroups, $user) = calendar_set_filters($usercourses, true, $userrecord);
 196  
 197                  $allusercourses = array_merge($allusercourses, $usercourses);
 198  
 199                  // Flag to indicate whether the query needs to exclude group overrides.
 200                  $viewgroupsonly = false;
 201  
 202                  if ($user) {
 203                      // Set filter condition for the user's events.
 204                      // Even though $user is a single scalar, we still use get_in_or_equal() because we are inside a loop.
 205                      list($inusers, $inuserparams) = $DB->get_in_or_equal($user, SQL_PARAMS_NAMED);
 206                      $subqueryconditions[] = "(ev.userid $inusers AND ev.courseid = 0 AND ev.groupid = 0 AND ev.categoryid = 0)";
 207                      $subqueryparams = array_merge($subqueryparams, $inuserparams);
 208  
 209                      foreach ($usercourses as $courseid) {
 210                          if (has_capability('moodle/site:accessallgroups', \context_course::instance($courseid), $userrecord)) {
 211                              $usergroupmembership = groups_get_all_groups($courseid, $user, 0, 'g.id');
 212                              if (count($usergroupmembership) == 0) {
 213                                  $viewgroupsonly = true;
 214                                  break;
 215                              }
 216                          }
 217                      }
 218                  }
 219  
 220                  // Set filter condition for the user's group events.
 221                  if ($usergroups === true || $viewgroupsonly) {
 222                      // Fetch group events, but not group overrides.
 223                      $subqueryconditions[] = "(ev.groupid != 0 AND ev.eventtype = 'group')";
 224                  } else if (!empty($usergroups)) {
 225                      // Fetch group events and group overrides.
 226                      list($inusergroups, $inusergroupparams) = $DB->get_in_or_equal($usergroups, SQL_PARAMS_NAMED);
 227                      $subqueryconditions[] = "(ev.groupid $inusergroups)";
 228                      $subqueryparams = array_merge($subqueryparams, $inusergroupparams);
 229                  }
 230              }
 231          } else if ($users === true) {
 232              // Events from ALL users.
 233              $subqueryconditions[] = "(ev.userid != 0 AND ev.courseid = 0 AND ev.groupid = 0 AND ev.categoryid = 0)";
 234  
 235              if (is_array($groups)) {
 236                  // Events from a number of groups.
 237                  list($insqlgroups, $inparamsgroups) = $DB->get_in_or_equal($groups, SQL_PARAMS_NAMED);
 238                  $subqueryconditions[] = "ev.groupid $insqlgroups";
 239                  $subqueryparams = array_merge($subqueryparams, $inparamsgroups);
 240              } else if ($groups === true) {
 241                  // Events from ALL groups.
 242                  $subqueryconditions[] = "ev.groupid != 0";
 243              }
 244  
 245              if ($courses === true) {
 246                  // ALL course events. It's not needed to worry about users' access as $users = true.
 247                  $subqueryconditions[] = "(ev.groupid = 0 AND ev.courseid != 0 AND ev.categoryid = 0)";
 248              }
 249          }
 250  
 251          // Get courses to be used for the subquery.
 252          $subquerycourses = [];
 253          if (is_array($courses)) {
 254              $subquerycourses = $courses;
 255          }
 256          // Merge with user courses, if necessary.
 257          if (!empty($allusercourses)) {
 258              $subquerycourses = array_merge($subquerycourses, $allusercourses);
 259              // Make sure we remove duplicate values.
 260              $subquerycourses = array_unique($subquerycourses);
 261          }
 262  
 263          // Set subquery filter condition for the courses.
 264          if (!empty($subquerycourses)) {
 265              list($incourses, $incoursesparams) = $DB->get_in_or_equal($subquerycourses, SQL_PARAMS_NAMED);
 266              $subqueryconditions[] = "(ev.groupid = 0 AND ev.courseid $incourses AND ev.categoryid = 0)";
 267              $subqueryparams = array_merge($subqueryparams, $incoursesparams);
 268          }
 269  
 270          // Set subquery filter condition for the categories.
 271          if ($categories === true) {
 272              $subqueryconditions[] = "(ev.categoryid != 0 AND ev.eventtype = 'category')";
 273          } else if (!empty($categories)) {
 274              list($incategories, $incategoriesparams) = $DB->get_in_or_equal($categories, SQL_PARAMS_NAMED);
 275              $subqueryconditions[] = "(ev.groupid = 0 AND ev.courseid = 0 AND ev.categoryid $incategories)";
 276              $subqueryparams = array_merge($subqueryparams, $incategoriesparams);
 277          }
 278  
 279          // Build the WHERE condition for the sub-query.
 280          if (!empty($subqueryconditions)) {
 281              $unionstartquery = "SELECT modulename, instance, eventtype, priority
 282                                    FROM {event} ev
 283                                   WHERE ";
 284              $subqueryunion = '('.$unionstartquery . implode(" UNION $unionstartquery ", $subqueryconditions).')';
 285          } else {
 286              $subqueryunion = '{event}';
 287          }
 288  
 289          // Merge subquery parameters to the parameters of the main query.
 290          if (!empty($subqueryparams)) {
 291              $params = array_merge($params, $subqueryparams);
 292          }
 293  
 294          // Sub-query that fetches the list of unique events that were filtered based on priority.
 295          $subquery = "SELECT ev.modulename,
 296                              ev.instance,
 297                              ev.eventtype,
 298                              MIN(ev.priority) as priority
 299                         FROM $subqueryunion ev
 300                     GROUP BY ev.modulename, ev.instance, ev.eventtype";
 301  
 302          // Build the main query.
 303          $sql = "SELECT e.*
 304                    FROM {event} e
 305              INNER JOIN ($subquery) fe
 306                      ON e.modulename = fe.modulename
 307                         AND e.instance = fe.instance
 308                         AND e.eventtype = fe.eventtype
 309                         AND (e.priority = fe.priority OR (e.priority IS NULL AND fe.priority IS NULL))
 310               LEFT JOIN {modules} m
 311                      ON e.modulename = m.name
 312                   WHERE (m.visible = 1 OR m.visible IS NULL) AND $whereclause
 313                ORDER BY " . ($ordersql ? $ordersql : "e.timestart");
 314  
 315          if (!empty($whereparams)) {
 316              $params = array_merge($params, $whereparams);
 317          }
 318  
 319          $events = $DB->get_records_sql($sql, $params, $offset, $limitnum);
 320  
 321          return  $events === false ? [] : $events;
 322      }
 323  }