MDL-27171 messages: allow using user IDs instead of objects in message_send function
[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
3b120e46 29/**
7c7d3afa 30 * Called when a message provider wants to send a message.
3b120e46 31 * This functions checks the user's processor configuration to send the given type of message,
32 * then tries to send it.
7c7d3afa
PS
33 *
34 * Required parameter $eventdata structure:
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
7c7d3afa
PS
40 * fullmessage - the full message in a given format
41 * fullmessageformat - the format if the full message (FORMAT_MOODLE, FORMAT_HTML, ..)
42 * fullmessagehtml - the full version (the message processor will choose with one to use)
43 * smallmessage - the small version of the message
14a0e7dd
AD
44 * 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.
45 * contexturlname - the display text for contexturl
7c7d3afa 46 *
305adb10 47 * @param object $eventdata information about the message (component, userfrom, userto, ...)
3a00a167 48 * @return int|false the ID of the new message or false if there was a problem with a processor
3b120e46 49 */
7c7d3afa 50function message_send($eventdata) {
3b120e46 51 global $CFG, $DB;
52
3a00a167 53 //new message ID to return
54 $messageid = false;
55
e484041b
PS
56 //TODO: we need to solve problems with database transactions here somehow, for now we just prevent transactions - sorry
57 $DB->transactions_forbidden();
7c7d3afa 58
fe983847 59 if (is_int($eventdata->userto)) {
2af2210c 60 $eventdata->userto = $DB->get_record('user', array('id' => $eventdata->userto));
fe983847
AD
61 }
62 if (is_int($eventdata->userfrom)) {
2af2210c 63 $eventdata->userfrom = $DB->get_record('user', array('id' => $eventdata->userfrom));
fe983847
AD
64 }
65
1560760f 66 //after how long inactive should the user be considered logged off?
3b120e46 67 if (isset($CFG->block_online_users_timetosee)) {
68 $timetoshowusers = $CFG->block_online_users_timetosee * 60;
69 } else {
1560760f 70 $timetoshowusers = 300;//5 minutes
3b120e46 71 }
72
1560760f 73 // Work out if the user is logged in or not
bc68fc9a 74 if (!empty($eventdata->userto->lastaccess) && (time()-$timetoshowusers) < $eventdata->userto->lastaccess) {
3b120e46 75 $userstate = 'loggedin';
1560760f
AD
76 } else {
77 $userstate = 'loggedoff';
3b120e46 78 }
117bd748 79
1560760f 80 // Create the message object
365a5941 81 $savemessage = new stdClass();
3b120e46 82 $savemessage->useridfrom = $eventdata->userfrom->id;
83 $savemessage->useridto = $eventdata->userto->id;
84 $savemessage->subject = $eventdata->subject;
85 $savemessage->fullmessage = $eventdata->fullmessage;
86 $savemessage->fullmessageformat = $eventdata->fullmessageformat;
87 $savemessage->fullmessagehtml = $eventdata->fullmessagehtml;
88 $savemessage->smallmessage = $eventdata->smallmessage;
ff5b4081 89
a813a748
AD
90 if (!empty($eventdata->notification)) {
91 $savemessage->notification = $eventdata->notification;
ff5b4081 92 } else {
a813a748 93 $savemessage->notification = 0;
ff5b4081
AD
94 }
95
14a0e7dd
AD
96 if (!empty($eventdata->contexturl)) {
97 $savemessage->contexturl = $eventdata->contexturl;
98 } else {
99 $savemessage->contexturl = null;
100 }
101
102 if (!empty($eventdata->contexturlname)) {
103 $savemessage->contexturlname = $eventdata->contexturlname;
104 } else {
105 $savemessage->contexturlname = null;
106 }
107
ff5b4081 108 $savemessage->timecreated = time();
3b120e46 109
1560760f
AD
110 // Find out what processors are defined currently
111 // When a user doesn't have settings none gets return, if he doesn't want contact "" gets returned
112 $preferencename = 'message_provider_'.$eventdata->component.'_'.$eventdata->name.'_'.$userstate;
117bd748 113
a813a748 114 $processor = get_user_preferences($preferencename, null, $eventdata->userto->id);
7c7d3afa
PS
115 if ($processor == NULL) { //this user never had a preference, save default
116 if (!message_set_default_message_preferences($eventdata->userto)) {
818dc096 117 print_error('cannotsavemessageprefs', 'message');
2044a2b2 118 }
a813a748 119 $processor = get_user_preferences($preferencename, NULL, $eventdata->userto->id);
60dd7688 120 if (empty($processor)) {
a6de5ed5 121 //MDL-25114 They supplied an $eventdata->component $eventdata->name combination which doesn't
60dd7688
AD
122 //exist in the message_provider table
123 $preferrormsg = get_string('couldnotfindpreference', 'message', $preferencename);
a6de5ed5 124 throw new coding_exception($preferrormsg,'blah');
60dd7688 125 }
2044a2b2 126 }
3b120e46 127
a813a748
AD
128 if ($processor=='none' && $savemessage->notification) {
129 //if they have deselected all processors and its a notification mark it read. The user doesnt want to be bothered
6c4e8db0 130 $savemessage->timeread = time();
3a00a167 131 $messageid = $DB->insert_record('message_read', $savemessage);
3b120e46 132 } else { // Process the message
4d8b36aa 133 // Store unread message just in case we can not send it
3a00a167 134 $messageid = $savemessage->id = $DB->insert_record('message', $savemessage);
fe983847 135 $eventdata->savedmessageid = $savemessage->id;
117bd748 136
4d8b36aa 137 // Try to deliver the message to each processor
a813a748
AD
138 if ($processor!='none') {
139 $processorlist = explode(',', $processor);
140 foreach ($processorlist as $procname) {
141 $processorfile = $CFG->dirroot. '/message/output/'.$procname.'/message_output_'.$procname.'.php';
142
143 if (is_readable($processorfile)) {
144 include_once($processorfile); // defines $module with version etc
145 $processclass = 'message_output_' . $procname;
146
147 if (class_exists($processclass)) {
148 $pclass = new $processclass();
149
fe983847 150 if (!$pclass->send_message($eventdata)) {
a813a748 151 debugging('Error calling message processor '.$procname);
3a00a167 152 $messageid = false;
a813a748 153 }
117bd748 154 }
a813a748 155 } else {
a46c9a2a 156 debugging('Error finding message processor '.$procname);
3a00a167 157 $messageid = false;
3b120e46 158 }
3b120e46 159 }
388025cb
AD
160
161 //if messaging is disabled and they previously had forum notifications handled by the popup processor
162 //or any processor that puts a row in message_working then the notification will remain forever
163 //unread. To prevent this mark the message read if messaging is disabled
164 if (empty($CFG->messaging)) {
165 require_once($CFG->dirroot.'/message/lib.php');
3a00a167 166 $messageid = message_mark_message_read($savemessage, time());
388025cb
AD
167 } else if ( $DB->count_records('message_working', array('unreadmessageid' => $savemessage->id)) == 0){
168 //if there is no more processors that want to process this we can move message to message_read
a813a748 169 require_once($CFG->dirroot.'/message/lib.php');
3a00a167 170 $messageid = message_mark_message_read($savemessage, time(), true);
a813a748 171 }
4d8b36aa 172 }
3b120e46 173 }
174
3a00a167 175 return $messageid;
3b120e46 176}
177
120b3758 178
179/**
180 * This code updates the message_providers table with the current set of providers
17da2e6f 181 * @param $component - examples: 'moodle', 'mod_forum', 'block_quiz_results'
120b3758 182 * @return boolean
183 */
184function message_update_providers($component='moodle') {
185 global $DB;
186
187 // load message providers from files
188 $fileproviders = message_get_providers_from_file($component);
189
190 // load message providers from the database
191 $dbproviders = message_get_providers_from_db($component);
192
193 foreach ($fileproviders as $messagename => $fileprovider) {
194
195 if (!empty($dbproviders[$messagename])) { // Already exists in the database
117bd748 196
120b3758 197 if ($dbproviders[$messagename]->capability == $fileprovider['capability']) { // Same, so ignore
198 // exact same message provider already present in db, ignore this entry
199 unset($dbproviders[$messagename]);
200 continue;
201
202 } else { // Update existing one
365a5941 203 $provider = new stdClass();
7c7d3afa
PS
204 $provider->id = $dbproviders[$messagename]->id;
205 $provider->capability = $fileprovider['capability'];
120b3758 206 $DB->update_record('message_providers', $provider);
207 unset($dbproviders[$messagename]);
208 continue;
209 }
210
211 } else { // New message provider, add it
212
365a5941 213 $provider = new stdClass();
120b3758 214 $provider->name = $messagename;
215 $provider->component = $component;
216 $provider->capability = $fileprovider['capability'];
217
218 $DB->insert_record('message_providers', $provider);
219 }
220 }
221
222 foreach ($dbproviders as $dbprovider) { // Delete old ones
223 $DB->delete_records('message_providers', array('id' => $dbprovider->id));
224 }
225
226 return true;
227}
228
229/**
230 * Returns the active providers for the current user, based on capability
231 * @return array of message providers
232 */
233function message_get_my_providers() {
234 global $DB;
235
236 $systemcontext = get_context_instance(CONTEXT_SYSTEM);
237
a813a748 238 $providers = $DB->get_records('message_providers', null, 'name');
120b3758 239
240 // Remove all the providers we aren't allowed to see now
241 foreach ($providers as $providerid => $provider) {
242 if (!empty($provider->capability)) {
243 if (!has_capability($provider->capability, $systemcontext)) {
244 unset($providers[$providerid]); // Not allowed to see this
245 }
246 }
247 }
248
249 return $providers;
250}
251
252/**
253 * Gets the message providers that are in the database for this component.
254 * @param $component - examples: 'moodle', 'mod/forum', 'block/quiz_results'
255 * @return array of message providers
256 *
257 * INTERNAL - to be used from messagelib only
258 */
259function message_get_providers_from_db($component) {
260 global $DB;
261
7c7d3afa 262 return $DB->get_records('message_providers', array('component'=>$component), '', 'name, id, component, capability'); // Name is unique per component
120b3758 263}
264
265/**
266 * Loads the messages definitions for the component (from file). If no
267 * messages are defined for the component, we simply return an empty array.
17da2e6f 268 * @param $component - examples: 'moodle', 'mod_forum', 'block_quiz_results'
120b3758 269 * @return array of message providerss or empty array if not exists
270 *
271 * INTERNAL - to be used from messagelib only
272 */
273function message_get_providers_from_file($component) {
17da2e6f 274 $defpath = get_component_directory($component).'/db/messages.php';
120b3758 275
276 $messageproviders = array();
277
278 if (file_exists($defpath)) {
279 require($defpath);
280 }
281
282 foreach ($messageproviders as $name => $messageprovider) { // Fix up missing values if required
283 if (empty($messageprovider['capability'])) {
284 $messageproviders[$name]['capability'] = NULL;
285 }
286 }
287
288 return $messageproviders;
289}
290
291/**
117bd748 292 * Remove all message providers
120b3758 293 * @param $component - examples: 'moodle', 'mod/forum', 'block/quiz_results'
294 */
295function message_uninstall($component) {
03dd0575 296 global $DB;
120b3758 297 return $DB->delete_records('message_providers', array('component' => $component));
298}
299
2044a2b2 300/**
301 * Set default message preferences.
302 * @param $user - User to set message preferences
303 */
7c7d3afa 304function message_set_default_message_preferences($user) {
2044a2b2 305 global $DB;
117bd748 306
46505ee7
AD
307 //check for the pre 2.0 disable email setting
308 $useemail = empty($user->emailstop);
69554090 309
a813a748 310 //look for the pre-2.0 preference if it exists
2ab19e2a
AD
311 $oldpreference = get_user_preferences('message_showmessagewindow', -1, $user->id);
312 //if they elected to see popups or the preference didnt exist
313 $usepopups = (intval($oldpreference)==1 || intval($oldpreference)==-1);
314
46505ee7
AD
315 $defaultonlineprocessor = 'none';
316 $defaultofflineprocessor = 'none';
317
318 if ($useemail) {
319 $defaultonlineprocessor = 'email';
320 $defaultofflineprocessor = 'email';
321 } else if ($usepopups) {
322 $defaultonlineprocessor = 'popup';
323 $defaultofflineprocessor = 'popup';
324 }
325
326 $offlineprocessortouse = $onlineprocessortouse = null;
a813a748 327
2044a2b2 328 $providers = $DB->get_records('message_providers');
329 $preferences = array();
69554090 330
7c7d3afa 331 foreach ($providers as $providerid => $provider) {
69554090 332
46505ee7
AD
333 //force some specific defaults for IMs
334 if ($provider->name=='instantmessage' && $usepopups && $useemail) {
f5b9b38c
AD
335 $onlineprocessortouse = 'popup';
336 $offlineprocessortouse = 'email,popup';
69554090
AD
337 } else {
338 $onlineprocessortouse = $defaultonlineprocessor;
339 $offlineprocessortouse = $defaultofflineprocessor;
340 }
341
342 $preferences['message_provider_'.$provider->component.'_'.$provider->name.'_loggedin'] = $onlineprocessortouse;
343 $preferences['message_provider_'.$provider->component.'_'.$provider->name.'_loggedoff'] = $offlineprocessortouse;
117bd748 344 }
7c7d3afa 345 return set_user_preferences($preferences, $user->id);
2044a2b2 346}