MDL-27171 messages: populate messaging defaults after the installation
[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';
e8fc7940 124 if (isset($defaultpreferences->{$defaultpreference})) {
2e075f41
RK
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
b46ca3d7 134 $userisconfigured = $processor->object->is_user_configured($eventdata->userto);
2e075f41
RK
135
136 // DEBUG: noify if we are forcing unconfigured output
b46ca3d7 137 if ($permitted == 'forced' && !$userisconfigured) {
2e075f41
RK
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
b46ca3d7 142 if ($permitted == 'forced' && $userisconfigured) {
2e075f41
RK
143 // We force messages for this processor, so use this processor unconditionally if user has configured it
144 $processorlist[] = $processor->name;
b46ca3d7 145 } else if ($permitted == 'permitted' && $userisconfigured) {
e8fc7940 146 // User settings are permitted, see if user set any, otherwise use site default ones
2e075f41
RK
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 }
e8fc7940 152 } else if (isset($defaultpreferences->{$userpreferencename})) {
2e075f41
RK
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
ebe0c008 198 *
17da2e6f 199 * @param $component - examples: 'moodle', 'mod_forum', 'block_quiz_results'
31afb0a4 200 * @return boolean
120b3758 201 */
202function message_update_providers($component='moodle') {
203 global $DB;
204
205 // load message providers from files
206 $fileproviders = message_get_providers_from_file($component);
207
208 // load message providers from the database
209 $dbproviders = message_get_providers_from_db($component);
210
211 foreach ($fileproviders as $messagename => $fileprovider) {
212
213 if (!empty($dbproviders[$messagename])) { // Already exists in the database
298925d4 214 // check if capability has changed
120b3758 215 if ($dbproviders[$messagename]->capability == $fileprovider['capability']) { // Same, so ignore
216 // exact same message provider already present in db, ignore this entry
217 unset($dbproviders[$messagename]);
218 continue;
219
220 } else { // Update existing one
365a5941 221 $provider = new stdClass();
7c7d3afa
PS
222 $provider->id = $dbproviders[$messagename]->id;
223 $provider->capability = $fileprovider['capability'];
120b3758 224 $DB->update_record('message_providers', $provider);
225 unset($dbproviders[$messagename]);
226 continue;
227 }
228
229 } else { // New message provider, add it
230
365a5941 231 $provider = new stdClass();
120b3758 232 $provider->name = $messagename;
233 $provider->component = $component;
234 $provider->capability = $fileprovider['capability'];
235
7a04c476 236 $transaction = $DB->start_delegated_transaction();
120b3758 237 $DB->insert_record('message_providers', $provider);
7a04c476
RK
238 message_set_default_message_preference($component, $messagename, $fileprovider);
239 $transaction->allow_commit();
120b3758 240 }
241 }
242
243 foreach ($dbproviders as $dbprovider) { // Delete old ones
244 $DB->delete_records('message_providers', array('id' => $dbprovider->id));
298925d4
RK
245 $DB->delete_records_select('config_plugins', "plugin = 'message' AND ".$DB->sql_like('name', '?', false), array("%_provider_{$component}_{$dbprovider->name}_%"));
246 $DB->delete_records_select('user_preferences', $DB->sql_like('name', '?', false), array("message_provider_{$component}_{$dbprovider->name}_%"));
120b3758 247 }
31afb0a4
RK
248
249 return true;
120b3758 250}
251
7a04c476
RK
252/**
253 * Setting default messaging preference for particular message provider
ebe0c008 254 *
7a04c476
RK
255 * @param string $component The name of component (e.g. moodle, mod_forum, etc.)
256 * @param string $messagename The name of message provider
257 * @param array $fileprovider The value of $messagename key in the array defined in plugin messages.php
ebe0c008 258 * @return void
7a04c476
RK
259 */
260function message_set_default_message_preference($component, $messagename, $fileprovider) {
261 global $DB;
262
263 // Fetch message processors
264 $processors = get_message_processors();
265
266 // load default messaging preferences
267 $defaultpreferences = get_message_output_default_preferences();
268
ebe0c008 269 // Setting default preference
7a04c476
RK
270 $componentproviderbase = $component.'_'.$messagename;
271 $loggedinpref = array();
272 $loggedoffpref = array();
ebe0c008 273 // set 'permitted' preference first for each messaging processor
7a04c476
RK
274 foreach ($processors as $processor) {
275 $preferencename = $processor->name.'_provider_'.$componentproviderbase.'_permitted';
ebe0c008 276 // if we do not have this setting yet, set it
e8fc7940 277 if (!isset($defaultpreferences->{$preferencename})) {
7a04c476
RK
278 // determine plugin default settings
279 $plugindefault = 0;
280 if (isset($fileprovider['defaults'][$processor->name])) {
281 $plugindefault = $fileprovider['defaults'][$processor->name];
282 }
283 // get string values of the settings
284 list($permitted, $loggedin, $loggedoff) = translate_message_default_setting($plugindefault, $processor->name);
285 // store default preferences for current processor
286 set_config($preferencename, $permitted, 'message');
287 // save loggedin/loggedoff settings
288 if ($loggedin) {
289 $loggedinpref[] = $processor->name;
290 }
291 if ($loggedoff) {
292 $loggedoffpref[] = $processor->name;
293 }
294 }
295 }
ebe0c008 296 // now set loggedin/loggedoff preferences
7a04c476
RK
297 if (!empty($loggedinpref)) {
298 $preferencename = 'message_provider_'.$componentproviderbase.'_loggedin';
299 set_config($preferencename, join(',', $loggedinpref), 'message');
300 }
301 if (!empty($loggedoffpref)) {
302 $preferencename = 'message_provider_'.$componentproviderbase.'_loggedoff';
303 set_config($preferencename, join(',', $loggedoffpref), 'message');
304 }
305}
306
120b3758 307/**
308 * Returns the active providers for the current user, based on capability
ebe0c008 309 *
120b3758 310 * @return array of message providers
311 */
312function message_get_my_providers() {
313 global $DB;
314
315 $systemcontext = get_context_instance(CONTEXT_SYSTEM);
316
a813a748 317 $providers = $DB->get_records('message_providers', null, 'name');
120b3758 318
319 // Remove all the providers we aren't allowed to see now
320 foreach ($providers as $providerid => $provider) {
321 if (!empty($provider->capability)) {
322 if (!has_capability($provider->capability, $systemcontext)) {
323 unset($providers[$providerid]); // Not allowed to see this
324 }
325 }
326 }
327
328 return $providers;
329}
330
331/**
332 * Gets the message providers that are in the database for this component.
ebe0c008 333 *
120b3758 334 * @param $component - examples: 'moodle', 'mod/forum', 'block/quiz_results'
335 * @return array of message providers
336 *
337 * INTERNAL - to be used from messagelib only
338 */
339function message_get_providers_from_db($component) {
340 global $DB;
341
7c7d3afa 342 return $DB->get_records('message_providers', array('component'=>$component), '', 'name, id, component, capability'); // Name is unique per component
120b3758 343}
344
345/**
346 * Loads the messages definitions for the component (from file). If no
347 * messages are defined for the component, we simply return an empty array.
ebe0c008 348 *
17da2e6f 349 * @param $component - examples: 'moodle', 'mod_forum', 'block_quiz_results'
120b3758 350 * @return array of message providerss or empty array if not exists
351 *
352 * INTERNAL - to be used from messagelib only
353 */
354function message_get_providers_from_file($component) {
17da2e6f 355 $defpath = get_component_directory($component).'/db/messages.php';
120b3758 356
357 $messageproviders = array();
358
359 if (file_exists($defpath)) {
360 require($defpath);
361 }
362
363 foreach ($messageproviders as $name => $messageprovider) { // Fix up missing values if required
364 if (empty($messageprovider['capability'])) {
365 $messageproviders[$name]['capability'] = NULL;
366 }
7a04c476
RK
367 if (empty($messageprovider['defaults'])) {
368 $messageproviders[$name]['defaults'] = array();
369 }
120b3758 370 }
371
372 return $messageproviders;
373}
374
375/**
117bd748 376 * Remove all message providers
ebe0c008 377 *
3bcbd80e 378 * @param $component - examples: 'moodle', 'mod_forum', 'block_quiz_results'
ebe0c008 379 * @return void
120b3758 380 */
381function message_uninstall($component) {
03dd0575 382 global $DB;
a813a748 383
7a04c476
RK
384 $transaction = $DB->start_delegated_transaction();
385 $DB->delete_records('message_providers', array('component' => $component));
386 $DB->delete_records_select('config_plugins', "plugin = 'message' AND ".$DB->sql_like('name', '?', false), array("%_provider_{$component}_%"));
387 $DB->delete_records_select('user_preferences', $DB->sql_like('name', '?', false), array("message_provider_{$component}_%"));
388 $transaction->allow_commit();
2044a2b2 389}