252d37412fe42c6e5751c75e2e5cb70f1e9d05a6
[moodle.git] / calendar / classes / local / event / strategies / raw_event_retrieval_strategy.php
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/>.
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  */
25 namespace core_calendar\local\event\strategies;
27 defined('MOODLE_INTERNAL') || die();
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 {
39     public function get_raw_events(
40         array $usersfilter = null,
41         array $groupsfilter = null,
42         array $coursesfilter = null,
43         array $whereconditions = null,
44         array $whereparams = null,
45         $ordersql = null,
46         $offset = null,
47         $limitnum = null,
48         $ignorehidden = true
49     ) {
50         return $this->get_raw_events_legacy_implementation(
51             !is_null($usersfilter) ? $usersfilter : true, // True means no filter in old implementation.
52             !is_null($groupsfilter) ? $groupsfilter : true,
53             !is_null($coursesfilter) ? $coursesfilter : true,
54             $whereconditions,
55             $whereparams,
56             $ordersql,
57             $offset,
58             $limitnum,
59             $ignorehidden
60         );
61     }
63     /**
64      * The legacy implementation with minor tweaks.
65      *
66      * @param array|int|boolean $users array of users, user id or boolean for all/no user events
67      * @param array|int|boolean $groups array of groups, group id or boolean for all/no group events
68      * @param array|int|boolean $courses array of courses, course id or boolean for all/no course events
69      * @param string $whereconditions The conditions in the WHERE clause.
70      * @param array $whereparams The parameters for the WHERE clause.
71      * @param string $ordersql The ORDER BY clause.
72      * @param int $offset Offset.
73      * @param int $limitnum Limit.
74      * @param boolean $ignorehidden whether to select only visible events or all events
75      * @return array $events of selected events or an empty array if there aren't any (or there was an error)
76      */
77     protected function get_raw_events_legacy_implementation(
78         $users,
79         $groups,
80         $courses,
81         $whereconditions,
82         $whereparams,
83         $ordersql,
84         $offset,
85         $limitnum,
86         $ignorehidden
87     ) {
88         global $DB;
90         $params = array();
91         // Quick test.
92         if (empty($users) && empty($groups) && empty($courses)) {
93             return array();
94         }
96         // Array of filter conditions. To be concatenated by the OR operator.
97         $filters = [];
99         // User filter.
100         if ((is_array($users) && !empty($users)) or is_numeric($users)) {
101             // Events from a number of users.
102             list($insqlusers, $inparamsusers) = $DB->get_in_or_equal($users, SQL_PARAMS_NAMED);
103             $filters[] = "(e.userid $insqlusers AND e.courseid = 0 AND e.groupid = 0)";
104             $params = array_merge($params, $inparamsusers);
105         } else if ($users === true) {
106             // Events from ALL users.
107             $filters[] = "(e.userid != 0 AND e.courseid = 0 AND e.groupid = 0)";
108         }
109         // Boolean false (no users at all): We don't need to do anything.
111         // Group filter.
112         if ((is_array($groups) && !empty($groups)) or is_numeric($groups)) {
113             // Events from a number of groups.
114             list($insqlgroups, $inparamsgroups) = $DB->get_in_or_equal($groups, SQL_PARAMS_NAMED);
115             $filters[] = "e.groupid $insqlgroups";
116             $params = array_merge($params, $inparamsgroups);
117         } else if ($groups === true) {
118             // Events from ALL groups.
119             $filters[] = "e.groupid != 0";
120         }
121         // Boolean false (no groups at all): We don't need to do anything.
123         // Course filter.
124         if ((is_array($courses) && !empty($courses)) or is_numeric($courses)) {
125             list($insqlcourses, $inparamscourses) = $DB->get_in_or_equal($courses, SQL_PARAMS_NAMED);
126             $filters[] = "(e.groupid = 0 AND e.courseid $insqlcourses)";
127             $params = array_merge($params, $inparamscourses);
128         } else if ($courses === true) {
129             // Events from ALL courses.
130             $filters[] = "(e.groupid = 0 AND e.courseid != 0)";
131         }
133         // Security check: if, by now, we have NOTHING in $whereclause, then it means
134         // that NO event-selecting clauses were defined. Thus, we won't be returning ANY
135         // events no matter what. Allowing the code to proceed might return a completely
136         // valid query with only time constraints, thus selecting ALL events in that time frame!
137         if (empty($filters)) {
138             return array();
139         }
141         // Build our clause for the filters.
142         $filterclause = implode(' OR ', $filters);
144         // Array of where conditions for our query. To be concatenated by the AND operator.
145         $whereconditions[] = "($filterclause)";
147         // Show visible only.
148         if ($ignorehidden) {
149             $whereconditions[] = "(e.visible = 1)";
150         }
152         // Build the main query's WHERE clause.
153         $whereclause = implode(' AND ', $whereconditions);
155         // Build SQL subquery and conditions for filtered events based on priorities.
156         $subquerywhere = '';
157         $subqueryconditions = [];
159         // Get the user's courses. Otherwise, get the default courses being shown by the calendar.
160         $usercourses = calendar_get_default_courses();
162         // Set calendar filters.
163         list($usercourses, $usergroups, $user) = calendar_set_filters($usercourses, true);
164         $subqueryparams = [];
166         // Flag to indicate whether the query needs to exclude group overrides.
167         $viewgroupsonly = false;
169         if ($user) {
170             // Set filter condition for the user's events.
171             $subqueryconditions[] = "(ev.userid = :user AND ev.courseid = 0 AND ev.groupid = 0)";
172             $subqueryparams['user'] = $user;
174             foreach ($usercourses as $courseid) {
175                 if (has_capability('moodle/site:accessallgroups', \context_course::instance($courseid))) {
176                     $usergroupmembership = groups_get_all_groups($courseid, $user, 0, 'g.id');
177                     if (count($usergroupmembership) == 0) {
178                         $viewgroupsonly = true;
179                         break;
180                     }
181                 }
182             }
183         }
185         // Set filter condition for the user's group events.
186         if ($usergroups === true || $viewgroupsonly) {
187             // Fetch group events, but not group overrides.
188             $subqueryconditions[] = "(ev.groupid != 0 AND ev.eventtype = 'group')";
189         } else if (!empty($usergroups)) {
190             // Fetch group events and group overrides.
191             list($inusergroups, $inusergroupparams) = $DB->get_in_or_equal($usergroups, SQL_PARAMS_NAMED);
192             $subqueryconditions[] = "(ev.groupid $inusergroups)";
193             $subqueryparams = array_merge($subqueryparams, $inusergroupparams);
194         }
196         // Get courses to be used for the subquery.
197         $subquerycourses = [];
198         if (is_array($courses)) {
199             $subquerycourses = $courses;
200         } else if (is_numeric($courses)) {
201             $subquerycourses[] = $courses;
202         }
203         // Merge with user courses, if necessary.
204         if (!empty($usercourses)) {
205             $subquerycourses = array_merge($subquerycourses, $usercourses);
206             // Make sure we remove duplicate values.
207             $subquerycourses = array_unique($subquerycourses);
208         }
210         // Set subquery filter condition for the courses.
211         if (!empty($subquerycourses)) {
212             list($incourses, $incoursesparams) = $DB->get_in_or_equal($subquerycourses, SQL_PARAMS_NAMED);
213             $subqueryconditions[] = "(ev.groupid = 0 AND ev.courseid $incourses)";
214             $subqueryparams = array_merge($subqueryparams, $incoursesparams);
215         }
217         // Build the WHERE condition for the sub-query.
218         if (!empty($subqueryconditions)) {
219             $subquerywhere = 'WHERE ' . implode(" OR ", $subqueryconditions);
220         }
222         // Merge subquery parameters to the parameters of the main query.
223         if (!empty($subqueryparams)) {
224             $params = array_merge($params, $subqueryparams);
225         }
227         // Sub-query that fetches the list of unique events that were filtered based on priority.
228         $subquery = "SELECT ev.modulename,
229                             ev.instance,
230                             ev.eventtype,
231                             MIN(ev.priority) as priority
232                        FROM {event} ev
233                       $subquerywhere
234                    GROUP BY ev.modulename, ev.instance, ev.eventtype";
236         // Build the main query.
237         $sql = "SELECT e.*
238                   FROM {event} e
239             INNER JOIN ($subquery) fe
240                     ON e.modulename = fe.modulename
241                        AND e.instance = fe.instance
242                        AND e.eventtype = fe.eventtype
243                        AND (e.priority = fe.priority OR (e.priority IS NULL AND fe.priority IS NULL))
244              LEFT JOIN {modules} m
245                     ON e.modulename = m.name
246                  WHERE (m.visible = 1 OR m.visible IS NULL) AND $whereclause
247               ORDER BY " . ($ordersql ? $ordersql : "e.timestart");
249         if (!empty($whereparams)) {
250             $params = array_merge($params, $whereparams);
251         }
253         $events = $DB->get_records_sql($sql, $params, $offset, $limitnum);
255         return  $events === false ? [] : $events;
256     }