MDL-27642 messages: ensure message processors are removed when plugin is being uninst...
[moodle.git] / lib / messagelib.php
CommitLineData
ce02a9bf 1<?php
3b120e46 2
ce02a9bf 3// This file is part of Moodle - http://moodle.org/
4//
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
3b120e46 17
18/**
120b3758 19 * messagelib.php - Contains generic messaging functions for the message system
3b120e46 20 *
78bfb562
PS
21 * @package core
22 * @subpackage message
23 * @copyright Luis Rodrigues and Martin Dougiamas
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3b120e46 25 */
26
78bfb562
PS
27defined('MOODLE_INTERNAL') || die();
28
2e075f41
RK
29require_once(dirname(dirname(__FILE__)) . '/message/lib.php');
30
3b120e46 31/**
7c7d3afa 32 * Called when a message provider wants to send a message.
3b120e46 33 * This functions checks the user's processor configuration to send the given type of message,
34 * then tries to send it.
7c7d3afa
PS
35 *
36 * Required parameter $eventdata structure:
305adb10
AD
37 * component string component name. must exist in message_providers
38 * name string message type name. must exist in message_providers
76a8aef9
RK
39 * userfrom object|int the user sending the message
40 * userto object|int the message recipient
fe983847 41 * subject string the message subject
7c7d3afa
PS
42 * fullmessage - the full message in a given format
43 * fullmessageformat - the format if the full message (FORMAT_MOODLE, FORMAT_HTML, ..)
44 * fullmessagehtml - the full version (the message processor will choose with one to use)
45 * smallmessage - the small version of the message
14a0e7dd
AD
46 * contexturl - 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.
47 * contexturlname - the display text for contexturl
7c7d3afa 48 *
305adb10 49 * @param object $eventdata information about the message (component, userfrom, userto, ...)
3a00a167 50 * @return int|false the ID of the new message or false if there was a problem with a processor
3b120e46 51 */
7c7d3afa 52function message_send($eventdata) {
3b120e46 53 global $CFG, $DB;
54
3a00a167 55 //new message ID to return
56 $messageid = false;
57
e484041b
PS
58 //TODO: we need to solve problems with database transactions here somehow, for now we just prevent transactions - sorry
59 $DB->transactions_forbidden();
7c7d3afa 60
fe983847 61 if (is_int($eventdata->userto)) {
2af2210c 62 $eventdata->userto = $DB->get_record('user', array('id' => $eventdata->userto));
fe983847
AD
63 }
64 if (is_int($eventdata->userfrom)) {
2af2210c 65 $eventdata->userfrom = $DB->get_record('user', array('id' => $eventdata->userfrom));
fe983847
AD
66 }
67
1560760f 68 //after how long inactive should the user be considered logged off?
3b120e46 69 if (isset($CFG->block_online_users_timetosee)) {
70 $timetoshowusers = $CFG->block_online_users_timetosee * 60;
71 } else {
1560760f 72 $timetoshowusers = 300;//5 minutes
3b120e46 73 }
74
1560760f 75 // Work out if the user is logged in or not
bc68fc9a 76 if (!empty($eventdata->userto->lastaccess) && (time()-$timetoshowusers) < $eventdata->userto->lastaccess) {
3b120e46 77 $userstate = 'loggedin';
1560760f
AD
78 } else {
79 $userstate = 'loggedoff';
3b120e46 80 }
117bd748 81
1560760f 82 // Create the message object
365a5941 83 $savemessage = new stdClass();
3b120e46 84 $savemessage->useridfrom = $eventdata->userfrom->id;
85 $savemessage->useridto = $eventdata->userto->id;
86 $savemessage->subject = $eventdata->subject;
87 $savemessage->fullmessage = $eventdata->fullmessage;
88 $savemessage->fullmessageformat = $eventdata->fullmessageformat;
89 $savemessage->fullmessagehtml = $eventdata->fullmessagehtml;
90 $savemessage->smallmessage = $eventdata->smallmessage;
ff5b4081 91
a813a748
AD
92 if (!empty($eventdata->notification)) {
93 $savemessage->notification = $eventdata->notification;
ff5b4081 94 } else {
a813a748 95 $savemessage->notification = 0;
ff5b4081
AD
96 }
97
14a0e7dd
AD
98 if (!empty($eventdata->contexturl)) {
99 $savemessage->contexturl = $eventdata->contexturl;
100 } else {
101 $savemessage->contexturl = null;
102 }
103
104 if (!empty($eventdata->contexturlname)) {
105 $savemessage->contexturlname = $eventdata->contexturlname;
106 } else {
107 $savemessage->contexturlname = null;
108 }
109
ff5b4081 110 $savemessage->timecreated = time();
3b120e46 111
2e075f41
RK
112 // Fetch enabled processors
113 $processors = get_message_processors(true);
114 // Fetch default (site) preferences
115 $defaultpreferences = get_message_output_default_preferences();
116
117 // Preset variables
118 $processorlist = array();
119 $preferencebase = $eventdata->component.'_'.$eventdata->name;
120 // Fill in the array of processors to be used based on default and user preferences
121 foreach ($processors as $processor) {
122 // First find out permissions
123 $defaultpreference = $processor->name.'_provider_'.$preferencebase.'_permitted';
124 if (array_key_exists($defaultpreference, $defaultpreferences)) {
125 $permitted = $defaultpreferences->{$defaultpreference};
126 } else {
a6de5ed5 127 //MDL-25114 They supplied an $eventdata->component $eventdata->name combination which doesn't
2e075f41 128 //exist in the message_provider table (thus there is no default settings for them)
60dd7688 129 $preferrormsg = get_string('couldnotfindpreference', 'message', $preferencename);
a6de5ed5 130 throw new coding_exception($preferrormsg,'blah');
60dd7688 131 }
2e075f41
RK
132
133 // Find out if user has configured this output
134 $is_user_configured = $processor->object->is_user_configured($eventdata->userto);
135
136 // DEBUG: noify if we are forcing unconfigured output
137 if ($permitted == 'forced' && !$is_user_configured) {
138 debugging('Attempt to force message delivery to user who has "'.$processor->name.'" output unconfigured', DEBUG_NORMAL);
139 }
140
141 // Populate the list of processors we will be using
142 if ($permitted == 'forced' && $is_user_configured) {
143 // We force messages for this processor, so use this processor unconditionally if user has configured it
144 $processorlist[] = $processor->name;
145 } else if ($permitted == 'permitted' && $is_user_configured) {
146 // User settings are permitted, see if user set any, othervice use site default ones
147 $userpreferencename = 'message_provider_'.$preferencebase.'_'.$userstate;
148 if ($userpreference = get_user_preferences($userpreferencename, null, $eventdata->userto->id)) {
149 if (in_array($processor->name, explode(',', $userpreference))) {
150 $processorlist[] = $processor->name;
151 }
152 } else if (array_key_exists($userpreferencename, $defaultpreferences)) {
153 if (in_array($processor->name, explode(',', $defaultpreferences->{$userpreferencename}))) {
154 $processorlist[] = $processor->name;
155 }
156 }
157 }
2044a2b2 158 }
3b120e46 159
2e075f41 160 if (empty($processorlist) && $savemessage->notification) {
a813a748 161 //if they have deselected all processors and its a notification mark it read. The user doesnt want to be bothered
6c4e8db0 162 $savemessage->timeread = time();
3a00a167 163 $messageid = $DB->insert_record('message_read', $savemessage);
3b120e46 164 } else { // Process the message
4d8b36aa 165 // Store unread message just in case we can not send it
3a00a167 166 $messageid = $savemessage->id = $DB->insert_record('message', $savemessage);
fe983847 167 $eventdata->savedmessageid = $savemessage->id;
117bd748 168
4d8b36aa 169 // Try to deliver the message to each processor
2e075f41 170 if (!empty($processorlist)) {
a813a748 171 foreach ($processorlist as $procname) {
2e075f41
RK
172 if (!$processors[$procname]->object->send_message($eventdata)) {
173 debugging('Error calling message processor '.$procname);
3a00a167 174 $messageid = false;
3b120e46 175 }
3b120e46 176 }
2e075f41 177
388025cb
AD
178 //if messaging is disabled and they previously had forum notifications handled by the popup processor
179 //or any processor that puts a row in message_working then the notification will remain forever
180 //unread. To prevent this mark the message read if messaging is disabled
181 if (empty($CFG->messaging)) {
182 require_once($CFG->dirroot.'/message/lib.php');
3a00a167 183 $messageid = message_mark_message_read($savemessage, time());
388025cb
AD
184 } else if ( $DB->count_records('message_working', array('unreadmessageid' => $savemessage->id)) == 0){
185 //if there is no more processors that want to process this we can move message to message_read
a813a748 186 require_once($CFG->dirroot.'/message/lib.php');
3a00a167 187 $messageid = message_mark_message_read($savemessage, time(), true);
a813a748 188 }
4d8b36aa 189 }
3b120e46 190 }
191
3a00a167 192 return $messageid;
3b120e46 193}
194
120b3758 195
196/**
197 * This code updates the message_providers table with the current set of providers
17da2e6f 198 * @param $component - examples: 'moodle', 'mod_forum', 'block_quiz_results'
120b3758 199 * @return boolean
200 */
201function message_update_providers($component='moodle') {
202 global $DB;
203
204 // load message providers from files
205 $fileproviders = message_get_providers_from_file($component);
206
207 // load message providers from the database
208 $dbproviders = message_get_providers_from_db($component);
209
210 foreach ($fileproviders as $messagename => $fileprovider) {
211
212 if (!empty($dbproviders[$messagename])) { // Already exists in the database
117bd748 213
120b3758 214 if ($dbproviders[$messagename]->capability == $fileprovider['capability']) { // Same, so ignore
215 // exact same message provider already present in db, ignore this entry
216 unset($dbproviders[$messagename]);
217 continue;
218
219 } else { // Update existing one
365a5941 220 $provider = new stdClass();
7c7d3afa
PS
221 $provider->id = $dbproviders[$messagename]->id;
222 $provider->capability = $fileprovider['capability'];
120b3758 223 $DB->update_record('message_providers', $provider);
224 unset($dbproviders[$messagename]);
225 continue;
226 }
227
228 } else { // New message provider, add it
229
365a5941 230 $provider = new stdClass();
120b3758 231 $provider->name = $messagename;
232 $provider->component = $component;
233 $provider->capability = $fileprovider['capability'];
234
235 $DB->insert_record('message_providers', $provider);
236 }
237 }
238
239 foreach ($dbproviders as $dbprovider) { // Delete old ones
240 $DB->delete_records('message_providers', array('id' => $dbprovider->id));
241 }
242
243 return true;
244}
245
246/**
247 * Returns the active providers for the current user, based on capability
248 * @return array of message providers
249 */
250function message_get_my_providers() {
251 global $DB;
252
253 $systemcontext = get_context_instance(CONTEXT_SYSTEM);
254
a813a748 255 $providers = $DB->get_records('message_providers', null, 'name');
120b3758 256
257 // Remove all the providers we aren't allowed to see now
258 foreach ($providers as $providerid => $provider) {
259 if (!empty($provider->capability)) {
260 if (!has_capability($provider->capability, $systemcontext)) {
261 unset($providers[$providerid]); // Not allowed to see this
262 }
263 }
264 }
265
266 return $providers;
267}
268
269/**
270 * Gets the message providers that are in the database for this component.
271 * @param $component - examples: 'moodle', 'mod/forum', 'block/quiz_results'
272 * @return array of message providers
273 *
274 * INTERNAL - to be used from messagelib only
275 */
276function message_get_providers_from_db($component) {
277 global $DB;
278
7c7d3afa 279 return $DB->get_records('message_providers', array('component'=>$component), '', 'name, id, component, capability'); // Name is unique per component
120b3758 280}
281
282/**
283 * Loads the messages definitions for the component (from file). If no
284 * messages are defined for the component, we simply return an empty array.
17da2e6f 285 * @param $component - examples: 'moodle', 'mod_forum', 'block_quiz_results'
120b3758 286 * @return array of message providerss or empty array if not exists
287 *
288 * INTERNAL - to be used from messagelib only
289 */
290function message_get_providers_from_file($component) {
17da2e6f 291 $defpath = get_component_directory($component).'/db/messages.php';
120b3758 292
293 $messageproviders = array();
294
295 if (file_exists($defpath)) {
296 require($defpath);
297 }
298
299 foreach ($messageproviders as $name => $messageprovider) { // Fix up missing values if required
300 if (empty($messageprovider['capability'])) {
301 $messageproviders[$name]['capability'] = NULL;
302 }
303 }
304
305 return $messageproviders;
306}
307
308/**
117bd748 309 * Remove all message providers
3bcbd80e 310 * @param $component - examples: 'moodle', 'mod_forum', 'block_quiz_results'
120b3758 311 */
312function message_uninstall($component) {
03dd0575 313 global $DB;
120b3758 314 return $DB->delete_records('message_providers', array('component' => $component));
315}
316
2044a2b2 317/**
318 * Set default message preferences.
319 * @param $user - User to set message preferences
320 */
7c7d3afa 321function message_set_default_message_preferences($user) {
2044a2b2 322 global $DB;
117bd748 323
46505ee7
AD
324 //check for the pre 2.0 disable email setting
325 $useemail = empty($user->emailstop);
69554090 326
a813a748 327 //look for the pre-2.0 preference if it exists
2ab19e2a
AD
328 $oldpreference = get_user_preferences('message_showmessagewindow', -1, $user->id);
329 //if they elected to see popups or the preference didnt exist
330 $usepopups = (intval($oldpreference)==1 || intval($oldpreference)==-1);
331
46505ee7
AD
332 $defaultonlineprocessor = 'none';
333 $defaultofflineprocessor = 'none';
334
335 if ($useemail) {
336 $defaultonlineprocessor = 'email';
337 $defaultofflineprocessor = 'email';
338 } else if ($usepopups) {
339 $defaultonlineprocessor = 'popup';
340 $defaultofflineprocessor = 'popup';
341 }
342
343 $offlineprocessortouse = $onlineprocessortouse = null;
a813a748 344
2044a2b2 345 $providers = $DB->get_records('message_providers');
346 $preferences = array();
69554090 347
7c7d3afa 348 foreach ($providers as $providerid => $provider) {
69554090 349
46505ee7
AD
350 //force some specific defaults for IMs
351 if ($provider->name=='instantmessage' && $usepopups && $useemail) {
f5b9b38c
AD
352 $onlineprocessortouse = 'popup';
353 $offlineprocessortouse = 'email,popup';
69554090
AD
354 } else {
355 $onlineprocessortouse = $defaultonlineprocessor;
356 $offlineprocessortouse = $defaultofflineprocessor;
357 }
358
359 $preferences['message_provider_'.$provider->component.'_'.$provider->name.'_loggedin'] = $onlineprocessortouse;
360 $preferences['message_provider_'.$provider->component.'_'.$provider->name.'_loggedoff'] = $offlineprocessortouse;
117bd748 361 }
7c7d3afa 362 return set_user_preferences($preferences, $user->id);
2044a2b2 363}