Commit | Line | Data |
---|---|---|
ce02a9bf | 1 | <?php |
ce02a9bf | 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/>. | |
3b120e46 | 16 | |
17 | /** | |
6fbd60ef | 18 | * Functions for interacting with the message system |
3b120e46 | 19 | * |
6fbd60ef AD |
20 | * @package core_message |
21 | * @copyright 2008 Luis Rodrigues and Martin Dougiamas | |
22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
3b120e46 | 23 | */ |
24 | ||
78bfb562 PS |
25 | defined('MOODLE_INTERNAL') || die(); |
26 | ||
1fcf0ca8 | 27 | require_once(__DIR__ . '/../message/lib.php'); |
2e075f41 | 28 | |
3b120e46 | 29 | /** |
7c7d3afa | 30 | * Called when a message provider wants to send a message. |
75e4f98c | 31 | * This functions checks the message recipient's message processor configuration then |
6fbd60ef | 32 | * sends the message to the configured processors |
7c7d3afa | 33 | * |
21cde6ac | 34 | * Required parameters of the $eventdata object: |
305adb10 AD |
35 | * component string component name. must exist in message_providers |
36 | * name string message type name. must exist in message_providers | |
76a8aef9 RK |
37 | * userfrom object|int the user sending the message |
38 | * userto object|int the message recipient | |
fe983847 | 39 | * subject string the message subject |
21cde6ac DM |
40 | * fullmessage string the full message in a given format |
41 | * fullmessageformat int the format if the full message (FORMAT_MOODLE, FORMAT_HTML, ..) | |
42 | * fullmessagehtml string the full version (the message processor will choose with one to use) | |
43 | * smallmessage string the small version of the message | |
44 | * | |
45 | * Optional parameters of the $eventdata object: | |
46 | * notification bool should the message be considered as a notification rather than a personal message | |
47 | * contexturl string if this is a notification then you can specify a url to view the event. For example the forum post the user is being notified of. | |
48 | * contexturlname string the display text for contexturl | |
7c7d3afa | 49 | * |
349f98ad PS |
50 | * Note: processor failure is is not reported as false return value, |
51 | * earlier versions did not do it consistently either. | |
52 | * | |
6fbd60ef | 53 | * @category message |
cc350fd9 | 54 | * @param \core\message\message $eventdata information about the message (component, userfrom, userto, ...) |
349f98ad | 55 | * @return mixed the integer ID of the new message or false if there was a problem with submitted data |
3b120e46 | 56 | */ |
cc4952e9 | 57 | function message_send(\core\message\message $eventdata) { |
0eddf920 | 58 | global $CFG, $DB, $SITE; |
3b120e46 | 59 | |
3a00a167 | 60 | //new message ID to return |
61 | $messageid = false; | |
62 | ||
fd0f7ac5 RT |
63 | // Fetch default (site) preferences |
64 | $defaultpreferences = get_message_output_default_preferences(); | |
65 | $preferencebase = $eventdata->component.'_'.$eventdata->name; | |
39d2c688 DM |
66 | |
67 | // If the message provider is disabled via preferences, then don't send the message. | |
fd0f7ac5 RT |
68 | if (!empty($defaultpreferences->{$preferencebase.'_disable'})) { |
69 | return $messageid; | |
70 | } | |
71 | ||
5e12b369 JL |
72 | // By default a message is a notification. Only personal/private messages aren't notifications. |
73 | if (!isset($eventdata->notification)) { | |
74 | $eventdata->notification = 1; | |
75 | } | |
76 | ||
52f9cf20 JD |
77 | if (!is_object($eventdata->userfrom)) { |
78 | $eventdata->userfrom = core_user::get_user($eventdata->userfrom); | |
79 | } | |
80 | if (!$eventdata->userfrom) { | |
81 | debugging('Attempt to send msg from unknown user', DEBUG_NORMAL); | |
82 | return false; | |
83 | } | |
84 | ||
85 | // Legacy messages (FROM a single user TO a single user) must be converted into conversation messages. | |
86 | // Then, these will be passed through the conversation messages code below. | |
87 | if (!$eventdata->notification && !$eventdata->convid) { | |
88 | // If messaging is disabled at the site level, then the 'instantmessage' provider is always disabled. | |
89 | // Given this is the only 'message' type message provider, we can exit now if this is the case. | |
90 | // Don't waste processing time trying to work out the other conversation member, if it's an individual | |
91 | // conversation, just throw a generic debugging notice and return. | |
92 | if (empty($CFG->messaging) || $eventdata->component !== 'moodle' || $eventdata->name !== 'instantmessage') { | |
93 | debugging('Attempt to send msg from a provider '.$eventdata->component.'/'.$eventdata->name. | |
94 | ' that is inactive or not allowed for the user id='.$eventdata->userto->id, DEBUG_NORMAL); | |
95 | return false; | |
0eddf920 | 96 | } |
52f9cf20 JD |
97 | |
98 | if (!is_object($eventdata->userto)) { | |
99 | $eventdata->userto = core_user::get_user($eventdata->userto); | |
100 | } | |
101 | if (!$eventdata->userto) { | |
102 | debugging('Attempt to send msg to unknown user', DEBUG_NORMAL); | |
0eddf920 JD |
103 | return false; |
104 | } | |
105 | ||
52f9cf20 JD |
106 | // Verify all necessary data fields are present. |
107 | if (!isset($eventdata->userto->auth) or !isset($eventdata->userto->suspended) | |
108 | or !isset($eventdata->userto->deleted) or !isset($eventdata->userto->emailstop)) { | |
109 | ||
110 | debugging('Necessary properties missing in userto object, fetching full record', DEBUG_DEVELOPER); | |
111 | $eventdata->userto = core_user::get_user($eventdata->userto->id); | |
112 | } | |
113 | ||
114 | $usertoisrealuser = (core_user::is_real_user($eventdata->userto->id) != false); | |
115 | // If recipient is internal user (noreply user), and emailstop is set then don't send any msg. | |
116 | if (!$usertoisrealuser && !empty($eventdata->userto->emailstop)) { | |
117 | debugging('Attempt to send msg to internal (noreply) user', DEBUG_NORMAL); | |
118 | return false; | |
119 | } | |
120 | ||
121 | if (!$conversationid = \core_message\api::get_conversation_between_users([$eventdata->userfrom->id, | |
122 | $eventdata->userto->id])) { | |
123 | $conversation = \core_message\api::create_conversation( | |
124 | \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, | |
125 | [ | |
126 | $eventdata->userfrom->id, | |
127 | $eventdata->userto->id | |
128 | ] | |
129 | ); | |
130 | } | |
131 | // We either have found a conversation, or created one. | |
132 | $conversationid = $conversationid ? $conversationid : $conversation->id; | |
133 | $eventdata->convid = $conversationid; | |
134 | } | |
135 | ||
136 | // This is a message directed to a conversation, not a specific user as was the way in legacy messaging. | |
137 | // The above code has adapted the legacy messages into conversation messages. | |
138 | // We must call send_message_to_conversation(), which handles per-member processor iteration and triggers | |
139 | // a per-conversation event. | |
140 | // All eventdata for messages should now have a convid, as we fixed this above. | |
141 | if (!$eventdata->notification) { | |
142 | ||
0eddf920 JD |
143 | // Only one message will be saved to the DB. |
144 | $conversationid = $eventdata->convid; | |
145 | $table = 'messages'; | |
146 | $tabledata = new stdClass(); | |
147 | $tabledata->courseid = $eventdata->courseid; | |
148 | $tabledata->useridfrom = $eventdata->userfrom->id; | |
149 | $tabledata->conversationid = $conversationid; | |
150 | $tabledata->subject = $eventdata->subject; | |
151 | $tabledata->fullmessage = $eventdata->fullmessage; | |
152 | $tabledata->fullmessageformat = $eventdata->fullmessageformat; | |
153 | $tabledata->fullmessagehtml = $eventdata->fullmessagehtml; | |
154 | $tabledata->smallmessage = $eventdata->smallmessage; | |
155 | $tabledata->timecreated = time(); | |
156 | ||
86e274d9 JD |
157 | if ($messageid = message_handle_phpunit_redirection($eventdata, $table, $tabledata)) { |
158 | return $messageid; | |
0eddf920 JD |
159 | } |
160 | ||
161 | // Cache messages. | |
162 | if (!empty($eventdata->convid)) { | |
163 | // Cache the timecreated value of the last message in this conversation. | |
164 | $cache = cache::make('core', 'message_time_last_message_between_users'); | |
165 | $key = \core_message\helper::get_last_message_time_created_cache_key($eventdata->convid); | |
166 | $cache->set($key, $tabledata->timecreated); | |
167 | } | |
168 | ||
169 | // Store unread message just in case we get a fatal error any time later. | |
170 | $tabledata->id = $DB->insert_record($table, $tabledata); | |
171 | $eventdata->savedmessageid = $tabledata->id; | |
172 | ||
173 | return \core\message\manager::send_message_to_conversation($eventdata, $tabledata); | |
174 | } | |
175 | ||
52f9cf20 | 176 | // Else the message is a notification. |
349f98ad | 177 | if (!is_object($eventdata->userto)) { |
3bcf6b3c | 178 | $eventdata->userto = core_user::get_user($eventdata->userto); |
fe983847 | 179 | } |
349f98ad PS |
180 | if (!$eventdata->userto) { |
181 | debugging('Attempt to send msg to unknown user', DEBUG_NORMAL); | |
182 | return false; | |
183 | } | |
349f98ad | 184 | |
39d2c688 DM |
185 | // If the provider's component is disabled or the user can't receive messages from it, don't send the message. |
186 | $isproviderallowed = false; | |
187 | foreach (message_get_providers_for_user($eventdata->userto->id) as $provider) { | |
188 | if ($provider->component === $eventdata->component && $provider->name === $eventdata->name) { | |
189 | $isproviderallowed = true; | |
190 | break; | |
191 | } | |
192 | } | |
193 | if (!$isproviderallowed) { | |
194 | debugging('Attempt to send msg from a provider '.$eventdata->component.'/'.$eventdata->name. | |
195 | ' that is inactive or not allowed for the user id='.$eventdata->userto->id, DEBUG_NORMAL); | |
196 | return false; | |
197 | } | |
198 | ||
349f98ad PS |
199 | // Verify all necessary data fields are present. |
200 | if (!isset($eventdata->userto->auth) or !isset($eventdata->userto->suspended) | |
201 | or !isset($eventdata->userto->deleted) or !isset($eventdata->userto->emailstop)) { | |
202 | ||
203 | debugging('Necessary properties missing in userto object, fetching full record', DEBUG_DEVELOPER); | |
204 | $eventdata->userto = core_user::get_user($eventdata->userto->id); | |
205 | } | |
3bcf6b3c RT |
206 | |
207 | $usertoisrealuser = (core_user::is_real_user($eventdata->userto->id) != false); | |
208 | // If recipient is internal user (noreply user), and emailstop is set then don't send any msg. | |
209 | if (!$usertoisrealuser && !empty($eventdata->userto->emailstop)) { | |
210 | debugging('Attempt to send msg to internal (noreply) user', DEBUG_NORMAL); | |
211 | return false; | |
212 | } | |
213 | ||
1560760f | 214 | //after how long inactive should the user be considered logged off? |
3b120e46 | 215 | if (isset($CFG->block_online_users_timetosee)) { |
216 | $timetoshowusers = $CFG->block_online_users_timetosee * 60; | |
217 | } else { | |
1560760f | 218 | $timetoshowusers = 300;//5 minutes |
3b120e46 | 219 | } |
220 | ||
1560760f | 221 | // Work out if the user is logged in or not |
bc68fc9a | 222 | if (!empty($eventdata->userto->lastaccess) && (time()-$timetoshowusers) < $eventdata->userto->lastaccess) { |
3b120e46 | 223 | $userstate = 'loggedin'; |
1560760f AD |
224 | } else { |
225 | $userstate = 'loggedoff'; | |
3b120e46 | 226 | } |
117bd748 | 227 | |
883ce421 | 228 | // Check if we are creating a notification or message. |
52f9cf20 JD |
229 | $table = 'notifications'; |
230 | ||
231 | $tabledata = new stdClass(); | |
232 | $tabledata->useridfrom = $eventdata->userfrom->id; | |
233 | $tabledata->useridto = $eventdata->userto->id; | |
234 | $tabledata->subject = $eventdata->subject; | |
235 | $tabledata->fullmessage = $eventdata->fullmessage; | |
236 | $tabledata->fullmessageformat = $eventdata->fullmessageformat; | |
237 | $tabledata->fullmessagehtml = $eventdata->fullmessagehtml; | |
238 | $tabledata->smallmessage = $eventdata->smallmessage; | |
239 | $tabledata->eventtype = $eventdata->name; | |
240 | $tabledata->component = $eventdata->component; | |
241 | $tabledata->timecreated = time(); | |
242 | if (!empty($eventdata->contexturl)) { | |
243 | $tabledata->contexturl = (string)$eventdata->contexturl; | |
14a0e7dd | 244 | } else { |
52f9cf20 | 245 | $tabledata->contexturl = null; |
14a0e7dd AD |
246 | } |
247 | ||
52f9cf20 JD |
248 | if (!empty($eventdata->contexturlname)) { |
249 | $tabledata->contexturlname = (string)$eventdata->contexturlname; | |
250 | } else { | |
251 | $tabledata->contexturlname = null; | |
252 | } | |
3b120e46 | 253 | |
86e274d9 JD |
254 | if ($messageid = message_handle_phpunit_redirection($eventdata, $table, $tabledata)) { |
255 | return $messageid; | |
4c9e03f0 PS |
256 | } |
257 | ||
685daf1a | 258 | // Fetch enabled processors. |
52f9cf20 | 259 | $processors = get_message_processors(true); |
2e075f41 RK |
260 | |
261 | // Preset variables | |
262 | $processorlist = array(); | |
2e075f41 RK |
263 | // Fill in the array of processors to be used based on default and user preferences |
264 | foreach ($processors as $processor) { | |
3bcf6b3c RT |
265 | // Skip adding processors for internal user, if processor doesn't support sending message to internal user. |
266 | if (!$usertoisrealuser && !$processor->object->can_send_to_any_users()) { | |
267 | continue; | |
268 | } | |
269 | ||
2e075f41 RK |
270 | // First find out permissions |
271 | $defaultpreference = $processor->name.'_provider_'.$preferencebase.'_permitted'; | |
e8fc7940 | 272 | if (isset($defaultpreferences->{$defaultpreference})) { |
2e075f41 RK |
273 | $permitted = $defaultpreferences->{$defaultpreference}; |
274 | } else { | |
ba4c451f FM |
275 | // MDL-25114 They supplied an $eventdata->component $eventdata->name combination which doesn't |
276 | // exist in the message_provider table (thus there is no default settings for them). | |
277 | $preferrormsg = "Could not load preference $defaultpreference. Make sure the component and name you supplied | |
278 | to message_send() are valid."; | |
279 | throw new coding_exception($preferrormsg); | |
60dd7688 | 280 | } |
2e075f41 RK |
281 | |
282 | // Find out if user has configured this output | |
bb3546f3 | 283 | // Some processors cannot function without settings from the user |
b46ca3d7 | 284 | $userisconfigured = $processor->object->is_user_configured($eventdata->userto); |
2e075f41 | 285 | |
be34802d | 286 | // DEBUG: notify if we are forcing unconfigured output |
b46ca3d7 | 287 | if ($permitted == 'forced' && !$userisconfigured) { |
2e075f41 RK |
288 | debugging('Attempt to force message delivery to user who has "'.$processor->name.'" output unconfigured', DEBUG_NORMAL); |
289 | } | |
290 | ||
291 | // Populate the list of processors we will be using | |
52f9cf20 | 292 | if ($permitted == 'forced' && $userisconfigured) { |
d8aa5ec7 | 293 | // An admin is forcing users to use this message processor. Use this processor unconditionally. |
2e075f41 | 294 | $processorlist[] = $processor->name; |
bb3546f3 | 295 | } else if ($permitted == 'permitted' && $userisconfigured && !$eventdata->userto->emailstop) { |
d8aa5ec7 AD |
296 | // User has not disabled notifications |
297 | // See if user set any notification preferences, otherwise use site default ones | |
2e075f41 | 298 | $userpreferencename = 'message_provider_'.$preferencebase.'_'.$userstate; |
349f98ad | 299 | if ($userpreference = get_user_preferences($userpreferencename, null, $eventdata->userto)) { |
2e075f41 RK |
300 | if (in_array($processor->name, explode(',', $userpreference))) { |
301 | $processorlist[] = $processor->name; | |
302 | } | |
e8fc7940 | 303 | } else if (isset($defaultpreferences->{$userpreferencename})) { |
2e075f41 RK |
304 | if (in_array($processor->name, explode(',', $defaultpreferences->{$userpreferencename}))) { |
305 | $processorlist[] = $processor->name; | |
306 | } | |
307 | } | |
308 | } | |
2044a2b2 | 309 | } |
3b120e46 | 310 | |
349f98ad | 311 | // Store unread message just in case we get a fatal error any time later. |
883ce421 MN |
312 | $tabledata->id = $DB->insert_record($table, $tabledata); |
313 | $eventdata->savedmessageid = $tabledata->id; | |
ab1bc5d2 | 314 | |
349f98ad | 315 | // Let the manager do the sending or buffering when db transaction in progress. |
883ce421 | 316 | return \core\message\manager::send_message($eventdata, $tabledata, $processorlist); |
3b120e46 | 317 | } |
318 | ||
86e274d9 JD |
319 | /** |
320 | * Helper method containing the PHPUnit specific code, used to redirect and capture messages/notifications. | |
321 | * | |
322 | * @param \core\message\message $eventdata the message object | |
323 | * @param string $table the table to store the tabledata in, either messages or notifications. | |
324 | * @param stdClass $tabledata the data to be stored when creating the message/notification. | |
325 | * @return int the id of the stored message. | |
326 | */ | |
327 | function message_handle_phpunit_redirection(\core\message\message $eventdata, string $table, \stdClass $tabledata) { | |
328 | global $DB; | |
329 | if (PHPUNIT_TEST and class_exists('phpunit_util')) { | |
330 | // Add some more tests to make sure the normal code can actually work. | |
331 | $componentdir = core_component::get_component_directory($eventdata->component); | |
332 | if (!$componentdir or !is_dir($componentdir)) { | |
333 | throw new coding_exception('Invalid component specified in message-send(): '.$eventdata->component); | |
334 | } | |
335 | if (!file_exists("$componentdir/db/messages.php")) { | |
336 | throw new coding_exception("$eventdata->component does not contain db/messages.php necessary for message_send()"); | |
337 | } | |
338 | $messageproviders = null; | |
339 | include("$componentdir/db/messages.php"); | |
340 | if (!isset($messageproviders[$eventdata->name])) { | |
341 | throw new coding_exception("Missing messaging defaults for event '$eventdata->name' in '$eventdata->component' " . | |
342 | "messages.php file"); | |
343 | } | |
344 | unset($componentdir); | |
345 | unset($messageproviders); | |
346 | // Now ask phpunit if it wants to catch this message. | |
347 | if (phpunit_util::is_redirecting_messages()) { | |
348 | $messageid = $DB->insert_record($table, $tabledata); | |
349 | $message = $DB->get_record($table, array('id' => $messageid)); | |
350 | ||
351 | if ($eventdata->notification) { | |
352 | // Add the useridto attribute for BC. | |
353 | $message->useridto = $eventdata->userto->id; | |
354 | ||
355 | // Mark the notification as read. | |
356 | \core_message\api::mark_notification_as_read($message); | |
357 | } else { | |
358 | // Add the useridto attribute for BC. | |
359 | if (isset($eventdata->userto)) { | |
360 | $message->useridto = $eventdata->userto->id; | |
361 | } | |
362 | // Mark the message as read for each of the other users. | |
363 | $sql = "SELECT u.* | |
364 | FROM {message_conversation_members} mcm | |
365 | JOIN {user} u | |
366 | ON (mcm.conversationid = :convid AND u.id = mcm.userid AND u.id != :userid)"; | |
367 | $otherusers = $DB->get_records_sql($sql, ['convid' => $eventdata->convid, 'userid' => $eventdata->userfrom->id]); | |
368 | foreach ($otherusers as $othermember) { | |
369 | \core_message\api::mark_message_as_read($othermember->id, $message); | |
370 | } | |
371 | } | |
372 | ||
373 | // Unit tests need this detail. | |
374 | $message->notification = $eventdata->notification; | |
375 | phpunit_util::message_sent($message); | |
376 | return $messageid; | |
377 | } | |
378 | } | |
379 | } | |
380 | ||
120b3758 | 381 | /** |
6fbd60ef | 382 | * Updates the message_providers table with the current set of message providers |
ebe0c008 | 383 | * |
6fbd60ef AD |
384 | * @param string $component For example 'moodle', 'mod_forum' or 'block_quiz_results' |
385 | * @return boolean True on success | |
120b3758 | 386 | */ |
387 | function message_update_providers($component='moodle') { | |
388 | global $DB; | |
389 | ||
390 | // load message providers from files | |
391 | $fileproviders = message_get_providers_from_file($component); | |
392 | ||
393 | // load message providers from the database | |
394 | $dbproviders = message_get_providers_from_db($component); | |
395 | ||
396 | foreach ($fileproviders as $messagename => $fileprovider) { | |
397 | ||
398 | if (!empty($dbproviders[$messagename])) { // Already exists in the database | |
298925d4 | 399 | // check if capability has changed |
120b3758 | 400 | if ($dbproviders[$messagename]->capability == $fileprovider['capability']) { // Same, so ignore |
401 | // exact same message provider already present in db, ignore this entry | |
402 | unset($dbproviders[$messagename]); | |
403 | continue; | |
404 | ||
405 | } else { // Update existing one | |
365a5941 | 406 | $provider = new stdClass(); |
7c7d3afa PS |
407 | $provider->id = $dbproviders[$messagename]->id; |
408 | $provider->capability = $fileprovider['capability']; | |
120b3758 | 409 | $DB->update_record('message_providers', $provider); |
410 | unset($dbproviders[$messagename]); | |
411 | continue; | |
412 | } | |
413 | ||
414 | } else { // New message provider, add it | |
415 | ||
365a5941 | 416 | $provider = new stdClass(); |
120b3758 | 417 | $provider->name = $messagename; |
418 | $provider->component = $component; | |
419 | $provider->capability = $fileprovider['capability']; | |
420 | ||
7a04c476 | 421 | $transaction = $DB->start_delegated_transaction(); |
120b3758 | 422 | $DB->insert_record('message_providers', $provider); |
7a04c476 RK |
423 | message_set_default_message_preference($component, $messagename, $fileprovider); |
424 | $transaction->allow_commit(); | |
120b3758 | 425 | } |
426 | } | |
427 | ||
428 | foreach ($dbproviders as $dbprovider) { // Delete old ones | |
429 | $DB->delete_records('message_providers', array('id' => $dbprovider->id)); | |
298925d4 RK |
430 | $DB->delete_records_select('config_plugins', "plugin = 'message' AND ".$DB->sql_like('name', '?', false), array("%_provider_{$component}_{$dbprovider->name}_%")); |
431 | $DB->delete_records_select('user_preferences', $DB->sql_like('name', '?', false), array("message_provider_{$component}_{$dbprovider->name}_%")); | |
007bfe8b | 432 | cache_helper::invalidate_by_definition('core', 'config', array(), 'message'); |
120b3758 | 433 | } |
31afb0a4 RK |
434 | |
435 | return true; | |
120b3758 | 436 | } |
437 | ||
afed8d0f RK |
438 | /** |
439 | * This function populates default message preferences for all existing providers | |
440 | * when the new message processor is added. | |
441 | * | |
442 | * @param string $processorname The name of message processor plugin (e.g. 'email', 'jabber') | |
6fbd60ef | 443 | * @throws invalid_parameter_exception if $processorname does not exist in the database |
afed8d0f RK |
444 | */ |
445 | function message_update_processors($processorname) { | |
446 | global $DB; | |
447 | ||
448 | // validate if our processor exists | |
449 | $processor = $DB->get_records('message_processors', array('name' => $processorname)); | |
450 | if (empty($processor)) { | |
451 | throw new invalid_parameter_exception(); | |
452 | } | |
453 | ||
454 | $providers = $DB->get_records_sql('SELECT DISTINCT component FROM {message_providers}'); | |
455 | ||
456 | $transaction = $DB->start_delegated_transaction(); | |
457 | foreach ($providers as $provider) { | |
458 | // load message providers from files | |
459 | $fileproviders = message_get_providers_from_file($provider->component); | |
460 | foreach ($fileproviders as $messagename => $fileprovider) { | |
461 | message_set_default_message_preference($provider->component, $messagename, $fileprovider, $processorname); | |
462 | } | |
463 | } | |
464 | $transaction->allow_commit(); | |
465 | } | |
466 | ||
7a04c476 | 467 | /** |
6fbd60ef | 468 | * Setting default messaging preferences for particular message provider |
ebe0c008 | 469 | * |
7a04c476 RK |
470 | * @param string $component The name of component (e.g. moodle, mod_forum, etc.) |
471 | * @param string $messagename The name of message provider | |
472 | * @param array $fileprovider The value of $messagename key in the array defined in plugin messages.php | |
6fbd60ef | 473 | * @param string $processorname The optional name of message processor |
7a04c476 | 474 | */ |
afed8d0f | 475 | function message_set_default_message_preference($component, $messagename, $fileprovider, $processorname='') { |
7a04c476 RK |
476 | global $DB; |
477 | ||
478 | // Fetch message processors | |
afed8d0f RK |
479 | $condition = null; |
480 | // If we need to process a particular processor, set the select condition | |
481 | if (!empty($processorname)) { | |
482 | $condition = array('name' => $processorname); | |
483 | } | |
484 | $processors = $DB->get_records('message_processors', $condition); | |
7a04c476 RK |
485 | |
486 | // load default messaging preferences | |
487 | $defaultpreferences = get_message_output_default_preferences(); | |
488 | ||
ebe0c008 | 489 | // Setting default preference |
7a04c476 RK |
490 | $componentproviderbase = $component.'_'.$messagename; |
491 | $loggedinpref = array(); | |
492 | $loggedoffpref = array(); | |
ebe0c008 | 493 | // set 'permitted' preference first for each messaging processor |
7a04c476 RK |
494 | foreach ($processors as $processor) { |
495 | $preferencename = $processor->name.'_provider_'.$componentproviderbase.'_permitted'; | |
ebe0c008 | 496 | // if we do not have this setting yet, set it |
e8fc7940 | 497 | if (!isset($defaultpreferences->{$preferencename})) { |
7a04c476 RK |
498 | // determine plugin default settings |
499 | $plugindefault = 0; | |
500 | if (isset($fileprovider['defaults'][$processor->name])) { | |
501 | $plugindefault = $fileprovider['defaults'][$processor->name]; | |
502 | } | |
503 | // get string values of the settings | |
504 | list($permitted, $loggedin, $loggedoff) = translate_message_default_setting($plugindefault, $processor->name); | |
505 | // store default preferences for current processor | |
506 | set_config($preferencename, $permitted, 'message'); | |
507 | // save loggedin/loggedoff settings | |
508 | if ($loggedin) { | |
509 | $loggedinpref[] = $processor->name; | |
510 | } | |
511 | if ($loggedoff) { | |
512 | $loggedoffpref[] = $processor->name; | |
513 | } | |
514 | } | |
515 | } | |
ebe0c008 | 516 | // now set loggedin/loggedoff preferences |
7a04c476 RK |
517 | if (!empty($loggedinpref)) { |
518 | $preferencename = 'message_provider_'.$componentproviderbase.'_loggedin'; | |
afed8d0f RK |
519 | if (isset($defaultpreferences->{$preferencename})) { |
520 | // We have the default preferences for this message provider, which | |
521 | // likely means that we have been adding a new processor. Add defaults | |
522 | // to exisitng preferences. | |
523 | $loggedinpref = array_merge($loggedinpref, explode(',', $defaultpreferences->{$preferencename})); | |
524 | } | |
7a04c476 RK |
525 | set_config($preferencename, join(',', $loggedinpref), 'message'); |
526 | } | |
527 | if (!empty($loggedoffpref)) { | |
528 | $preferencename = 'message_provider_'.$componentproviderbase.'_loggedoff'; | |
afed8d0f RK |
529 | if (isset($defaultpreferences->{$preferencename})) { |
530 | // We have the default preferences for this message provider, which | |
531 | // likely means that we have been adding a new processor. Add defaults | |
532 | // to exisitng preferences. | |
533 | $loggedoffpref = array_merge($loggedoffpref, explode(',', $defaultpreferences->{$preferencename})); | |
534 | } | |
7a04c476 RK |
535 | set_config($preferencename, join(',', $loggedoffpref), 'message'); |
536 | } | |
537 | } | |
538 | ||
120b3758 | 539 | /** |
a8134ff6 | 540 | * Returns the active providers for the user specified, based on capability |
ebe0c008 | 541 | * |
a8134ff6 | 542 | * @param int $userid id of user |
6fbd60ef | 543 | * @return array An array of message providers |
120b3758 | 544 | */ |
a8134ff6 | 545 | function message_get_providers_for_user($userid) { |
9f05a1a6 | 546 | global $DB, $CFG; |
120b3758 | 547 | |
7fcd3c30 | 548 | $providers = get_message_providers(); |
120b3758 | 549 | |
2cf5ab10 TH |
550 | // Ensure user is not allowed to configure instantmessage if it is globally disabled. |
551 | if (!$CFG->messaging) { | |
552 | foreach ($providers as $providerid => $provider) { | |
553 | if ($provider->name == 'instantmessage') { | |
554 | unset($providers[$providerid]); | |
555 | break; | |
120b3758 | 556 | } |
557 | } | |
2cf5ab10 | 558 | } |
12188796 | 559 | |
2cf5ab10 TH |
560 | // If the component is an enrolment plugin, check it is enabled |
561 | foreach ($providers as $providerid => $provider) { | |
56da374e | 562 | list($type, $name) = core_component::normalize_component($provider->component); |
2cf5ab10 | 563 | if ($type == 'enrol' && !enrol_is_enabled($name)) { |
9f05a1a6 | 564 | unset($providers[$providerid]); |
2cf5ab10 TH |
565 | } |
566 | } | |
567 | ||
568 | // Now we need to check capabilities. We need to eliminate the providers | |
569 | // where the user does not have the corresponding capability anywhere. | |
570 | // Here we deal with the common simple case of the user having the | |
571 | // capability in the system context. That handles $CFG->defaultuserroleid. | |
572 | // For the remaining providers/capabilities, we need to do a more complex | |
573 | // query involving all overrides everywhere. | |
574 | $unsureproviders = array(); | |
575 | $unsurecapabilities = array(); | |
576 | $systemcontext = context_system::instance(); | |
577 | foreach ($providers as $providerid => $provider) { | |
578 | if (empty($provider->capability) || has_capability($provider->capability, $systemcontext, $userid)) { | |
579 | // The provider is relevant to this user. | |
12188796 AD |
580 | continue; |
581 | } | |
582 | ||
2cf5ab10 TH |
583 | $unsureproviders[$providerid] = $provider; |
584 | $unsurecapabilities[$provider->capability] = 1; | |
585 | unset($providers[$providerid]); | |
586 | } | |
587 | ||
588 | if (empty($unsureproviders)) { | |
589 | // More complex checks are not required. | |
590 | return $providers; | |
591 | } | |
592 | ||
593 | // Now check the unsure capabilities. | |
594 | list($capcondition, $params) = $DB->get_in_or_equal( | |
595 | array_keys($unsurecapabilities), SQL_PARAMS_NAMED); | |
596 | $params['userid'] = $userid; | |
597 | ||
598 | $sql = "SELECT DISTINCT rc.capability, 1 | |
599 | ||
600 | FROM {role_assignments} ra | |
601 | JOIN {context} actx ON actx.id = ra.contextid | |
602 | JOIN {role_capabilities} rc ON rc.roleid = ra.roleid | |
603 | JOIN {context} cctx ON cctx.id = rc.contextid | |
604 | ||
605 | WHERE ra.userid = :userid | |
606 | AND rc.capability $capcondition | |
607 | AND rc.permission > 0 | |
5c4685f0 DP |
608 | AND (".$DB->sql_concat('actx.path', "'/'")." LIKE ".$DB->sql_concat('cctx.path', "'/%'"). |
609 | " OR ".$DB->sql_concat('cctx.path', "'/'")." LIKE ".$DB->sql_concat('actx.path', "'/%'").")"; | |
2cf5ab10 TH |
610 | |
611 | if (!empty($CFG->defaultfrontpageroleid)) { | |
612 | $frontpagecontext = context_course::instance(SITEID); | |
613 | ||
614 | list($capcondition2, $params2) = $DB->get_in_or_equal( | |
615 | array_keys($unsurecapabilities), SQL_PARAMS_NAMED); | |
616 | $params = array_merge($params, $params2); | |
617 | $params['frontpageroleid'] = $CFG->defaultfrontpageroleid; | |
618 | $params['frontpagepathpattern'] = $frontpagecontext->path . '/'; | |
619 | ||
620 | $sql .= " | |
5f7b0f2b | 621 | UNION |
2cf5ab10 TH |
622 | |
623 | SELECT DISTINCT rc.capability, 1 | |
624 | ||
625 | FROM {role_capabilities} rc | |
626 | JOIN {context} cctx ON cctx.id = rc.contextid | |
627 | ||
628 | WHERE rc.roleid = :frontpageroleid | |
629 | AND rc.capability $capcondition2 | |
630 | AND rc.permission > 0 | |
5c4685f0 | 631 | AND ".$DB->sql_concat('cctx.path', "'/'")." LIKE :frontpagepathpattern"; |
2cf5ab10 TH |
632 | } |
633 | ||
634 | $relevantcapabilities = $DB->get_records_sql_menu($sql, $params); | |
635 | ||
636 | // Add back any providers based on the detailed capability check. | |
637 | foreach ($unsureproviders as $providerid => $provider) { | |
638 | if (array_key_exists($provider->capability, $relevantcapabilities)) { | |
639 | $providers[$providerid] = $provider; | |
9f05a1a6 | 640 | } |
120b3758 | 641 | } |
642 | ||
643 | return $providers; | |
644 | } | |
645 | ||
646 | /** | |
647 | * Gets the message providers that are in the database for this component. | |
ebe0c008 | 648 | * |
6fbd60ef | 649 | * This is an internal function used within messagelib.php |
120b3758 | 650 | * |
75e4f98c | 651 | * @see message_update_providers() |
6fbd60ef AD |
652 | * @param string $component A moodle component like 'moodle', 'mod_forum', 'block_quiz_results' |
653 | * @return array An array of message providers | |
120b3758 | 654 | */ |
655 | function message_get_providers_from_db($component) { | |
656 | global $DB; | |
657 | ||
7c7d3afa | 658 | return $DB->get_records('message_providers', array('component'=>$component), '', 'name, id, component, capability'); // Name is unique per component |
120b3758 | 659 | } |
660 | ||
661 | /** | |
6fbd60ef | 662 | * Loads the messages definitions for a component from file |
ebe0c008 | 663 | * |
6fbd60ef AD |
664 | * If no messages are defined for the component, return an empty array. |
665 | * This is an internal function used within messagelib.php | |
120b3758 | 666 | * |
6fbd60ef AD |
667 | * @see message_update_providers() |
668 | * @see message_update_processors() | |
669 | * @param string $component A moodle component like 'moodle', 'mod_forum', 'block_quiz_results' | |
670 | * @return array An array of message providers or empty array if not exists | |
120b3758 | 671 | */ |
672 | function message_get_providers_from_file($component) { | |
b0d1d941 | 673 | $defpath = core_component::get_component_directory($component).'/db/messages.php'; |
120b3758 | 674 | |
675 | $messageproviders = array(); | |
676 | ||
677 | if (file_exists($defpath)) { | |
678 | require($defpath); | |
679 | } | |
680 | ||
681 | foreach ($messageproviders as $name => $messageprovider) { // Fix up missing values if required | |
682 | if (empty($messageprovider['capability'])) { | |
683 | $messageproviders[$name]['capability'] = NULL; | |
684 | } | |
7a04c476 RK |
685 | if (empty($messageprovider['defaults'])) { |
686 | $messageproviders[$name]['defaults'] = array(); | |
687 | } | |
120b3758 | 688 | } |
689 | ||
690 | return $messageproviders; | |
691 | } | |
692 | ||
693 | /** | |
6fbd60ef | 694 | * Remove all message providers for particular component and corresponding settings |
ebe0c008 | 695 | * |
6fbd60ef | 696 | * @param string $component A moodle component like 'moodle', 'mod_forum', 'block_quiz_results' |
ebe0c008 | 697 | * @return void |
120b3758 | 698 | */ |
8e265315 | 699 | function message_provider_uninstall($component) { |
03dd0575 | 700 | global $DB; |
a813a748 | 701 | |
7a04c476 RK |
702 | $transaction = $DB->start_delegated_transaction(); |
703 | $DB->delete_records('message_providers', array('component' => $component)); | |
704 | $DB->delete_records_select('config_plugins', "plugin = 'message' AND ".$DB->sql_like('name', '?', false), array("%_provider_{$component}_%")); | |
705 | $DB->delete_records_select('user_preferences', $DB->sql_like('name', '?', false), array("message_provider_{$component}_%")); | |
706 | $transaction->allow_commit(); | |
007bfe8b SH |
707 | // Purge all messaging settings from the caches. They are stored by plugin so we have to clear all message settings. |
708 | cache_helper::invalidate_by_definition('core', 'config', array(), 'message'); | |
2044a2b2 | 709 | } |
8e265315 RK |
710 | |
711 | /** | |
6fbd60ef | 712 | * Uninstall a message processor |
8e265315 | 713 | * |
6fbd60ef | 714 | * @param string $name A message processor name like 'email', 'jabber' |
8e265315 RK |
715 | */ |
716 | function message_processor_uninstall($name) { | |
717 | global $DB; | |
718 | ||
719 | $transaction = $DB->start_delegated_transaction(); | |
720 | $DB->delete_records('message_processors', array('name' => $name)); | |
3f9d9e28 | 721 | $DB->delete_records_select('config_plugins', "plugin = ?", array("message_{$name}")); |
8e265315 RK |
722 | // delete permission preferences only, we do not care about loggedin/loggedoff |
723 | // defaults, they will be removed on the next attempt to update the preferences | |
724 | $DB->delete_records_select('config_plugins', "plugin = 'message' AND ".$DB->sql_like('name', '?', false), array("{$name}_provider_%")); | |
725 | $transaction->allow_commit(); | |
007bfe8b SH |
726 | // Purge all messaging settings from the caches. They are stored by plugin so we have to clear all message settings. |
727 | cache_helper::invalidate_by_definition('core', 'config', array(), array('message', "message_{$name}")); | |
8e265315 | 728 | } |