Commit | Line | Data |
---|---|---|
eb5334ff | 1 | <?php |
eb5334ff | 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/>. | |
16 | ||
17 | /** | |
18 | * Library functions for messaging | |
19 | * | |
6fbd60ef AD |
20 | * @package core_message |
21 | * @copyright 2008 Luis Rodrigues | |
22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
eb5334ff | 23 | */ |
172186b8 | 24 | |
af5765b3 | 25 | define('MESSAGE_SHORTLENGTH', 300); |
d3875524 | 26 | |
af5765b3 | 27 | define('MESSAGE_HISTORY_ALL', 1); |
c8621a02 | 28 | |
c3931654 AD |
29 | define('MESSAGE_SEARCH_MAX_RESULTS', 200); |
30 | ||
7b55aaa1 MN |
31 | define('MESSAGE_TYPE_NOTIFICATION', 'notification'); |
32 | define('MESSAGE_TYPE_MESSAGE', 'message'); | |
3274d5ca | 33 | |
7a04c476 RK |
34 | /** |
35 | * Define contants for messaging default settings population. For unambiguity of | |
36 | * plugin developer intentions we use 4-bit value (LSB numbering): | |
37 | * bit 0 - whether to send message when user is loggedin (MESSAGE_DEFAULT_LOGGEDIN) | |
38 | * bit 1 - whether to send message when user is loggedoff (MESSAGE_DEFAULT_LOGGEDOFF) | |
39 | * bit 2..3 - messaging permission (MESSAGE_DISALLOWED|MESSAGE_PERMITTED|MESSAGE_FORCED) | |
40 | * | |
41 | * MESSAGE_PERMITTED_MASK contains the mask we use to distinguish permission setting | |
42 | */ | |
43 | ||
44 | define('MESSAGE_DEFAULT_LOGGEDIN', 0x01); // 0001 | |
45 | define('MESSAGE_DEFAULT_LOGGEDOFF', 0x02); // 0010 | |
46 | ||
47 | define('MESSAGE_DISALLOWED', 0x04); // 0100 | |
48 | define('MESSAGE_PERMITTED', 0x08); // 1000 | |
49 | define('MESSAGE_FORCED', 0x0c); // 1100 | |
50 | ||
51 | define('MESSAGE_PERMITTED_MASK', 0x0c); // 1100 | |
52 | ||
1d72e9d4 RK |
53 | /** |
54 | * Set default value for default outputs permitted setting | |
55 | */ | |
814e3735 | 56 | define('MESSAGE_DEFAULT_PERMITTED', 'permitted'); |
1d72e9d4 | 57 | |
ffd7798c MN |
58 | /** |
59 | * Set default values for polling. | |
60 | */ | |
61 | define('MESSAGE_DEFAULT_MIN_POLL_IN_SECONDS', 10); | |
62 | define('MESSAGE_DEFAULT_MAX_POLL_IN_SECONDS', 2 * MINSECS); | |
63 | define('MESSAGE_DEFAULT_TIMEOUT_POLL_IN_SECONDS', 5 * MINSECS); | |
64 | ||
c8621a02 AD |
65 | /** |
66 | * Returns the count of unread messages for user. Either from a specific user or from all users. | |
ebe0c008 | 67 | * |
1bc2a3b3 MM |
68 | * @deprecated since 3.10 |
69 | * TODO: MDL-69643 | |
c8621a02 AD |
70 | * @param object $user1 the first user. Defaults to $USER |
71 | * @param object $user2 the second user. If null this function will count all of user 1's unread messages. | |
72 | * @return int the count of $user1's unread messages | |
73 | */ | |
74 | function message_count_unread_messages($user1=null, $user2=null) { | |
75 | global $USER, $DB; | |
e8e2d7f1 | 76 | |
1bc2a3b3 MM |
77 | debugging('message_count_unread_messages is deprecated and no longer used', |
78 | DEBUG_DEVELOPER); | |
79 | ||
c8621a02 AD |
80 | if (empty($user1)) { |
81 | $user1 = $USER; | |
82 | } | |
83 | ||
883ce421 MN |
84 | $sql = "SELECT COUNT(m.id) |
85 | FROM {messages} m | |
86 | INNER JOIN {message_conversations} mc | |
87 | ON mc.id = m.conversationid | |
88 | INNER JOIN {message_conversation_members} mcm | |
89 | ON mcm.conversationid = mc.id | |
90 | LEFT JOIN {message_user_actions} mua | |
91 | ON (mua.messageid = m.id AND mua.userid = ? AND (mua.action = ? OR mua.action = ?)) | |
92 | WHERE mua.id is NULL | |
93 | AND mcm.userid = ?"; | |
f88cc4cc | 94 | $params = [$user1->id, \core_message\api::MESSAGE_ACTION_DELETED, \core_message\api::MESSAGE_ACTION_READ, $user1->id]; |
883ce421 | 95 | |
c8621a02 | 96 | if (!empty($user2)) { |
883ce421 MN |
97 | $sql .= " AND m.useridfrom = ?"; |
98 | $params[] = $user2->id; | |
f88cc4cc PS |
99 | } else { |
100 | $sql .= " AND m.useridfrom <> ?"; | |
101 | $params[] = $user1->id; | |
c8621a02 | 102 | } |
883ce421 MN |
103 | |
104 | return $DB->count_records_sql($sql, $params); | |
c8621a02 AD |
105 | } |
106 | ||
4048f5b3 PS |
107 | /** |
108 | * Try to guess how to convert the message to html. | |
109 | * | |
110 | * @access private | |
111 | * | |
112 | * @param stdClass $message | |
113 | * @param bool $forcetexttohtml | |
114 | * @return string html fragment | |
115 | */ | |
116 | function message_format_message_text($message, $forcetexttohtml = false) { | |
117 | // Note: this is a very nasty hack that tries to work around the weird messaging rules and design. | |
118 | ||
119 | $options = new stdClass(); | |
120 | $options->para = false; | |
64e83119 | 121 | $options->blanktarget = true; |
3a5afbf5 | 122 | $options->trusted = isset($message->fullmessagetrust) ? $message->fullmessagetrust : false; |
4048f5b3 PS |
123 | |
124 | $format = $message->fullmessageformat; | |
125 | ||
40e50128 | 126 | if (strval($message->smallmessage) !== '') { |
883ce421 | 127 | if (!empty($message->notification)) { |
40e50128 | 128 | if (strval($message->fullmessagehtml) !== '' or strval($message->fullmessage) !== '') { |
4048f5b3 PS |
129 | $format = FORMAT_PLAIN; |
130 | } | |
131 | } | |
132 | $messagetext = $message->smallmessage; | |
133 | ||
134 | } else if ($message->fullmessageformat == FORMAT_HTML) { | |
40e50128 | 135 | if (strval($message->fullmessagehtml) !== '') { |
4048f5b3 PS |
136 | $messagetext = $message->fullmessagehtml; |
137 | } else { | |
138 | $messagetext = $message->fullmessage; | |
139 | $format = FORMAT_MOODLE; | |
140 | } | |
141 | ||
142 | } else { | |
40e50128 | 143 | if (strval($message->fullmessage) !== '') { |
4048f5b3 PS |
144 | $messagetext = $message->fullmessage; |
145 | } else { | |
146 | $messagetext = $message->fullmessagehtml; | |
147 | $format = FORMAT_HTML; | |
148 | } | |
149 | } | |
150 | ||
151 | if ($forcetexttohtml) { | |
152 | // This is a crazy hack, why not set proper format when creating the notifications? | |
153 | if ($format === FORMAT_PLAIN) { | |
154 | $format = FORMAT_MOODLE; | |
155 | } | |
156 | } | |
157 | return format_text($messagetext, $format, $options); | |
158 | } | |
159 | ||
e8e2d7f1 | 160 | /** |
a03330bd FM |
161 | * Search through course users. |
162 | * | |
163 | * If $courseids contains the site course then this function searches | |
164 | * through all undeleted and confirmed users. | |
e8e2d7f1 | 165 | * |
a03330bd FM |
166 | * @param int|array $courseids Course ID or array of course IDs. |
167 | * @param string $searchtext the text to search for. | |
168 | * @param string $sort the column name to order by. | |
2e2d1977 | 169 | * @param string|array $exceptions comma separated list or array of user IDs to exclude. |
a03330bd | 170 | * @return array An array of {@link $USER} records. |
e8e2d7f1 | 171 | */ |
a03330bd | 172 | function message_search_users($courseids, $searchtext, $sort='', $exceptions='') { |
fd1cb1e8 | 173 | global $CFG, $USER, $DB; |
e8e2d7f1 | 174 | |
a03330bd FM |
175 | // Basic validation to ensure that the parameter $courseids is not an empty array or an empty value. |
176 | if (!$courseids) { | |
177 | $courseids = array(SITEID); | |
178 | } | |
179 | ||
180 | // Allow an integer to be passed. | |
181 | if (!is_array($courseids)) { | |
182 | $courseids = array($courseids); | |
183 | } | |
184 | ||
245ac557 | 185 | $fullname = $DB->sql_fullname(); |
2e2d1977 | 186 | $ufields = user_picture::fields('u'); |
e8e2d7f1 | 187 | |
188 | if (!empty($sort)) { | |
189 | $order = ' ORDER BY '. $sort; | |
190 | } else { | |
191 | $order = ''; | |
192 | } | |
193 | ||
2e2d1977 AD |
194 | $params = array( |
195 | 'userid' => $USER->id, | |
f219eac7 | 196 | 'userid2' => $USER->id, |
2e2d1977 AD |
197 | 'query' => "%$searchtext%" |
198 | ); | |
199 | ||
200 | if (empty($exceptions)) { | |
201 | $exceptions = array(); | |
202 | } else if (!empty($exceptions) && is_string($exceptions)) { | |
203 | $exceptions = explode(',', $exceptions); | |
204 | } | |
205 | ||
206 | // Ignore self and guest account. | |
207 | $exceptions[] = $USER->id; | |
208 | $exceptions[] = $CFG->siteguest; | |
209 | ||
210 | // Exclude exceptions from the search result. | |
211 | list($except, $params_except) = $DB->get_in_or_equal($exceptions, SQL_PARAMS_NAMED, 'param', false); | |
212 | $except = ' AND u.id ' . $except; | |
213 | $params = array_merge($params_except, $params); | |
a03330bd FM |
214 | |
215 | if (in_array(SITEID, $courseids)) { | |
216 | // Search on site level. | |
f219eac7 | 217 | return $DB->get_records_sql("SELECT $ufields, mc.id as contactlistid, mub.id as userblockedid |
d9eef08a | 218 | FROM {user} u |
fd1cb1e8 | 219 | LEFT JOIN {message_contacts} mc |
2e2d1977 | 220 | ON mc.contactid = u.id AND mc.userid = :userid |
f219eac7 MN |
221 | LEFT JOIN {message_users_blocked} mub |
222 | ON mub.userid = :userid2 AND mub.blockeduserid = u.id | |
3a11c09f | 223 | WHERE u.deleted = '0' AND u.confirmed = '1' |
2e2d1977 | 224 | AND (".$DB->sql_like($fullname, ':query', false).") |
fd1cb1e8 | 225 | $except |
226 | $order", $params); | |
e8e2d7f1 | 227 | } else { |
a03330bd | 228 | // Search in courses. |
a03330bd FM |
229 | |
230 | // Getting the context IDs or each course. | |
231 | $contextids = array(); | |
232 | foreach ($courseids as $courseid) { | |
233 | $context = context_course::instance($courseid); | |
234 | $contextids = array_merge($contextids, $context->get_parent_context_ids(true)); | |
235 | } | |
2e2d1977 | 236 | list($contextwhere, $contextparams) = $DB->get_in_or_equal(array_unique($contextids), SQL_PARAMS_NAMED, 'context'); |
a03330bd FM |
237 | $params = array_merge($params, $contextparams); |
238 | ||
239 | // Everyone who has a role assignment in this course or higher. | |
240 | // TODO: add enabled enrolment join here (skodak) | |
f219eac7 | 241 | $users = $DB->get_records_sql("SELECT DISTINCT $ufields, mc.id as contactlistid, mub.id as userblockedid |
b6e52658 | 242 | FROM {user} u |
fd1cb1e8 | 243 | JOIN {role_assignments} ra ON ra.userid = u.id |
244 | LEFT JOIN {message_contacts} mc | |
2e2d1977 | 245 | ON mc.contactid = u.id AND mc.userid = :userid |
f219eac7 MN |
246 | LEFT JOIN {message_users_blocked} mub |
247 | ON mub.userid = :userid2 AND mub.blockeduserid = u.id | |
3a11c09f | 248 | WHERE u.deleted = '0' AND u.confirmed = '1' |
2e2d1977 | 249 | AND (".$DB->sql_like($fullname, ':query', false).") |
a03330bd | 250 | AND ra.contextid $contextwhere |
fd1cb1e8 | 251 | $except |
252 | $order", $params); | |
d76a5a7f | 253 | |
254 | return $users; | |
e8e2d7f1 | 255 | } |
256 | } | |
257 | ||
bcab42da | 258 | /** |
259 | * Format a message for display in the message history | |
ebe0c008 | 260 | * |
bcab42da | 261 | * @param object $message the message object |
262 | * @param string $format optional date format | |
263 | * @param string $keywords keywords to highlight | |
264 | * @param string $class CSS class to apply to the div around the message | |
265 | * @return string the formatted message | |
266 | */ | |
267 | function message_format_message($message, $format='', $keywords='', $class='other') { | |
acd21a2d | 268 | |
269 | static $dateformat; | |
270 | ||
bcab42da | 271 | //if we haven't previously set the date format or they've supplied a new one |
272 | if ( empty($dateformat) || (!empty($format) && $dateformat != $format) ) { | |
acd21a2d | 273 | if ($format) { |
274 | $dateformat = $format; | |
275 | } else { | |
bcab42da | 276 | $dateformat = get_string('strftimedatetimeshort'); |
acd21a2d | 277 | } |
b9ee0638 | 278 | } |
acd21a2d | 279 | $time = userdate($message->timecreated, $dateformat); |
6ee2611c | 280 | |
4048f5b3 | 281 | $messagetext = message_format_message_text($message, false); |
bcab42da | 282 | |
283 | if ($keywords) { | |
284 | $messagetext = highlight($keywords, $messagetext); | |
285 | } | |
286 | ||
4048f5b3 PS |
287 | $messagetext .= message_format_contexturl($message); |
288 | ||
1555e10c PS |
289 | $messagetext = clean_text($messagetext, FORMAT_HTML); |
290 | ||
a2592fec AD |
291 | return <<<TEMPLATE |
292 | <div class='message $class'> | |
f5482be6 | 293 | <a name="m{$message->id}"></a> |
a2592fec AD |
294 | <span class="message-meta"><span class="time">$time</span></span>: <span class="text">$messagetext</span> |
295 | </div> | |
296 | TEMPLATE; | |
bcab42da | 297 | } |
298 | ||
299 | /** | |
300 | * Format a the context url and context url name of a message for display | |
ebe0c008 | 301 | * |
bcab42da | 302 | * @param object $message the message object |
303 | * @return string the formatted string | |
304 | */ | |
305 | function message_format_contexturl($message) { | |
306 | $s = null; | |
307 | ||
14a0e7dd AD |
308 | if (!empty($message->contexturl)) { |
309 | $displaytext = null; | |
310 | if (!empty($message->contexturlname)) { | |
311 | $displaytext= $message->contexturlname; | |
312 | } else { | |
313 | $displaytext= $message->contexturl; | |
314 | } | |
bcab42da | 315 | $s .= html_writer::start_tag('div',array('class' => 'messagecontext')); |
316 | $s .= get_string('view').': '.html_writer::tag('a', $displaytext, array('href' => $message->contexturl)); | |
317 | $s .= html_writer::end_tag('div'); | |
38c6a928 | 318 | } |
14a0e7dd | 319 | |
bcab42da | 320 | return $s; |
b9ee0638 | 321 | } |
e8e2d7f1 | 322 | |
fd1cb1e8 | 323 | /** |
bcab42da | 324 | * Send a message from one user to another. Will be delivered according to the message recipients messaging preferences |
ebe0c008 | 325 | * |
bcab42da | 326 | * @param object $userfrom the message sender |
327 | * @param object $userto the message recipient | |
328 | * @param string $message the message | |
329 | * @param int $format message format such as FORMAT_PLAIN or FORMAT_HTML | |
3a00a167 | 330 | * @return int|false the ID of the new message or false |
405f01ee | 331 | */ |
bcab42da | 332 | function message_post_message($userfrom, $userto, $message, $format) { |
36fa0ec9 | 333 | global $PAGE; |
4ffa1463 | 334 | |
cc350fd9 AD |
335 | $eventdata = new \core\message\message(); |
336 | $eventdata->courseid = 1; | |
1560760f | 337 | $eventdata->component = 'moodle'; |
1c50df9f | 338 | $eventdata->name = 'instantmessage'; |
3b120e46 | 339 | $eventdata->userfrom = $userfrom; |
340 | $eventdata->userto = $userto; | |
4ffa1463 AD |
341 | |
342 | //using string manager directly so that strings in the message will be in the message recipients language rather than the senders | |
343 | $eventdata->subject = get_string_manager()->get_string('unreadnewmessage', 'message', fullname($userfrom), $userto->lang); | |
344 | ||
bcab42da | 345 | if ($format == FORMAT_HTML) { |
156205fc | 346 | $eventdata->fullmessagehtml = $message; |
22e23da6 AD |
347 | //some message processors may revert to sending plain text even if html is supplied |
348 | //so we keep both plain and html versions if we're intending to send html | |
349 | $eventdata->fullmessage = html_to_text($eventdata->fullmessagehtml); | |
156205fc AD |
350 | } else { |
351 | $eventdata->fullmessage = $message; | |
352 | $eventdata->fullmessagehtml = ''; | |
353 | } | |
31da70cc | 354 | |
c8621a02 | 355 | $eventdata->fullmessageformat = $format; |
7e98f60b | 356 | $eventdata->smallmessage = $message;//store the message unfiltered. Clean up on output. |
a1b53dcf | 357 | $eventdata->timecreated = time(); |
5e12b369 | 358 | $eventdata->notification = 0; |
36fa0ec9 JL |
359 | // User image. |
360 | $userpicture = new user_picture($userfrom); | |
dcadc8c5 | 361 | $userpicture->size = 1; // Use f1 size. |
36fa0ec9 JL |
362 | $userpicture->includetoken = $userto->id; // Generate an out-of-session token for the user receiving the message. |
363 | $eventdata->customdata = [ | |
364 | 'notificationiconurl' => $userpicture->get_url($PAGE)->out(false), | |
2356454f JL |
365 | 'actionbuttons' => [ |
366 | 'send' => get_string_manager()->get_string('send', 'message', null, $eventdata->userto->lang), | |
367 | ], | |
368 | 'placeholders' => [ | |
369 | 'send' => get_string_manager()->get_string('writeamessage', 'message', null, $eventdata->userto->lang), | |
370 | ], | |
36fa0ec9 | 371 | ]; |
7c7d3afa | 372 | return message_send($eventdata); |
405f01ee | 373 | } |
e8e2d7f1 | 374 | |
75c34c23 | 375 | /** |
814e3735 RK |
376 | * Get all message processors, validate corresponding plugin existance and |
377 | * system configuration | |
ebe0c008 | 378 | * |
814e3735 | 379 | * @param bool $ready only return ready-to-use processors |
ed23ad31 | 380 | * @param bool $reset Reset list of message processors (used in unit tests) |
e61a9638 | 381 | * @param bool $resetonly Just reset, then exit |
814e3735 | 382 | * @return mixed $processors array of objects containing information on message processors |
75c34c23 | 383 | */ |
e61a9638 | 384 | function get_message_processors($ready = false, $reset = false, $resetonly = false) { |
75c34c23 RK |
385 | global $DB, $CFG; |
386 | ||
1d72e9d4 | 387 | static $processors; |
ed23ad31 YB |
388 | if ($reset) { |
389 | $processors = array(); | |
e61a9638 MN |
390 | |
391 | if ($resetonly) { | |
392 | return $processors; | |
393 | } | |
ed23ad31 | 394 | } |
1d72e9d4 RK |
395 | |
396 | if (empty($processors)) { | |
814e3735 RK |
397 | // Get all processors, ensure the name column is the first so it will be the array key |
398 | $processors = $DB->get_records('message_processors', null, 'name DESC', 'name, id, enabled'); | |
1d72e9d4 | 399 | foreach ($processors as &$processor){ |
20ab51fd | 400 | $processor = \core_message\api::get_processed_processor_object($processor); |
75c34c23 RK |
401 | } |
402 | } | |
814e3735 | 403 | if ($ready) { |
72e6af03 RK |
404 | // Filter out enabled and system_configured processors |
405 | $readyprocessors = $processors; | |
406 | foreach ($readyprocessors as $readyprocessor) { | |
407 | if (!($readyprocessor->enabled && $readyprocessor->configured)) { | |
408 | unset($readyprocessors[$readyprocessor->name]); | |
409 | } | |
410 | } | |
411 | return $readyprocessors; | |
1d72e9d4 RK |
412 | } |
413 | ||
75c34c23 RK |
414 | return $processors; |
415 | } | |
814e3735 | 416 | |
7fcd3c30 RK |
417 | /** |
418 | * Get all message providers, validate their plugin existance and | |
419 | * system configuration | |
420 | * | |
421 | * @return mixed $processors array of objects containing information on message processors | |
422 | */ | |
423 | function get_message_providers() { | |
424 | global $CFG, $DB; | |
e87214bd PS |
425 | |
426 | $pluginman = core_plugin_manager::instance(); | |
7fcd3c30 RK |
427 | |
428 | $providers = $DB->get_records('message_providers', null, 'name'); | |
429 | ||
430 | // Remove all the providers whose plugins are disabled or don't exist | |
431 | foreach ($providers as $providerid => $provider) { | |
432 | $plugin = $pluginman->get_plugin_info($provider->component); | |
433 | if ($plugin) { | |
e87214bd | 434 | if ($plugin->get_status() === core_plugin_manager::PLUGIN_STATUS_MISSING) { |
7fcd3c30 RK |
435 | unset($providers[$providerid]); // Plugins does not exist |
436 | continue; | |
437 | } | |
438 | if ($plugin->is_enabled() === false) { | |
439 | unset($providers[$providerid]); // Plugin disabled | |
440 | continue; | |
441 | } | |
442 | } | |
443 | } | |
444 | return $providers; | |
445 | } | |
446 | ||
7529f9e9 TH |
447 | /** |
448 | * Get an instance of the message_output class for one of the output plugins. | |
449 | * @param string $type the message output type. E.g. 'email' or 'jabber'. | |
450 | * @return message_output message_output the requested class. | |
451 | */ | |
452 | function get_message_processor($type) { | |
453 | global $CFG; | |
454 | ||
455 | // Note, we cannot use the get_message_processors function here, becaues this | |
456 | // code is called during install after installing each messaging plugin, and | |
457 | // get_message_processors caches the list of installed plugins. | |
458 | ||
459 | $processorfile = $CFG->dirroot . "/message/output/{$type}/message_output_{$type}.php"; | |
460 | if (!is_readable($processorfile)) { | |
461 | throw new coding_exception('Unknown message processor type ' . $type); | |
462 | } | |
463 | ||
464 | include_once($processorfile); | |
465 | ||
466 | $processclass = 'message_output_' . $type; | |
467 | if (!class_exists($processclass)) { | |
468 | throw new coding_exception('Message processor ' . $type . | |
469 | ' does not define the right class'); | |
470 | } | |
471 | ||
472 | return new $processclass(); | |
473 | } | |
474 | ||
814e3735 RK |
475 | /** |
476 | * Get messaging outputs default (site) preferences | |
ebe0c008 | 477 | * |
814e3735 RK |
478 | * @return object $processors object containing information on message processors |
479 | */ | |
480 | function get_message_output_default_preferences() { | |
820a8188 | 481 | return get_config('message'); |
814e3735 | 482 | } |
7a04c476 RK |
483 | |
484 | /** | |
485 | * Translate message default settings from binary value to the array of string | |
486 | * representing the settings to be stored. Also validate the provided value and | |
487 | * use default if it is malformed. | |
ebe0c008 | 488 | * |
7a04c476 RK |
489 | * @param int $plugindefault Default setting suggested by plugin |
490 | * @param string $processorname The name of processor | |
491 | * @return array $settings array of strings in the order: $permitted, $loggedin, $loggedoff. | |
492 | */ | |
493 | function translate_message_default_setting($plugindefault, $processorname) { | |
494 | // Preset translation arrays | |
495 | $permittedvalues = array( | |
c07e039c TS |
496 | MESSAGE_DISALLOWED => 'disallowed', |
497 | MESSAGE_PERMITTED => 'permitted', | |
498 | MESSAGE_FORCED => 'forced', | |
7a04c476 RK |
499 | ); |
500 | ||
501 | $loggedinstatusvalues = array( | |
502 | 0x00 => null, // use null if loggedin/loggedoff is not defined | |
c07e039c TS |
503 | MESSAGE_DEFAULT_LOGGEDIN => 'loggedin', |
504 | MESSAGE_DEFAULT_LOGGEDOFF => 'loggedoff', | |
7a04c476 RK |
505 | ); |
506 | ||
507 | // define the default setting | |
7529f9e9 TH |
508 | $processor = get_message_processor($processorname); |
509 | $default = $processor->get_default_messaging_settings(); | |
7a04c476 RK |
510 | |
511 | // Validate the value. It should not exceed the maximum size | |
512 | if (!is_int($plugindefault) || ($plugindefault > 0x0f)) { | |
681570b4 | 513 | debugging(get_string('errortranslatingdefault', 'message')); |
7a04c476 RK |
514 | $plugindefault = $default; |
515 | } | |
516 | // Use plugin default setting of 'permitted' is 0 | |
517 | if (!($plugindefault & MESSAGE_PERMITTED_MASK)) { | |
518 | $plugindefault = $default; | |
519 | } | |
520 | ||
521 | $permitted = $permittedvalues[$plugindefault & MESSAGE_PERMITTED_MASK]; | |
83807514 | 522 | $loggedin = $loggedoff = null; |
7a04c476 RK |
523 | |
524 | if (($plugindefault & MESSAGE_PERMITTED_MASK) == MESSAGE_PERMITTED) { | |
83807514 RK |
525 | $loggedin = $loggedinstatusvalues[$plugindefault & MESSAGE_DEFAULT_LOGGEDIN]; |
526 | $loggedoff = $loggedinstatusvalues[$plugindefault & MESSAGE_DEFAULT_LOGGEDOFF]; | |
7a04c476 RK |
527 | } |
528 | ||
529 | return array($permitted, $loggedin, $loggedoff); | |
530 | } | |
46d3b9be | 531 | |
193edf7f | 532 | /** |
127ef540 | 533 | * Get messages sent or/and received by the specified users. |
4f97ea88 SA |
534 | * Please note that this function return deleted messages too. Besides, only individual conversation messages |
535 | * are returned to maintain backwards compatibility. | |
193edf7f JL |
536 | * |
537 | * @param int $useridto the user id who received the message | |
127ef540 | 538 | * @param int $useridfrom the user id who sent the message. -10 or -20 for no-reply or support user |
193edf7f | 539 | * @param int $notifications 1 for retrieving notifications, 0 for messages, -1 for both |
127ef540 | 540 | * @param bool $read true for retrieving read messages, false for unread |
193edf7f JL |
541 | * @param string $sort the column name to order by including optionally direction |
542 | * @param int $limitfrom limit from | |
543 | * @param int $limitnum limit num | |
544 | * @return external_description | |
545 | * @since 2.8 | |
546 | */ | |
547 | function message_get_messages($useridto, $useridfrom = 0, $notifications = -1, $read = true, | |
548 | $sort = 'mr.timecreated DESC', $limitfrom = 0, $limitnum = 0) { | |
549 | global $DB; | |
550 | ||
883ce421 | 551 | // If the 'useridto' value is empty then we are going to retrieve messages sent by the useridfrom to any user. |
193edf7f JL |
552 | if (empty($useridto)) { |
553 | $userfields = get_all_user_name_fields(true, 'u', '', 'userto'); | |
c57fadcc | 554 | $messageuseridtosql = 'u.id as useridto'; |
193edf7f JL |
555 | } else { |
556 | $userfields = get_all_user_name_fields(true, 'u', '', 'userfrom'); | |
c57fadcc | 557 | $messageuseridtosql = "$useridto as useridto"; |
883ce421 MN |
558 | } |
559 | ||
560 | // Create the SQL we will be using. | |
561 | $messagesql = "SELECT mr.*, $userfields, 0 as notification, '' as contexturl, '' as contexturlname, | |
562 | mua.timecreated as timeusertodeleted, mua2.timecreated as timeread, | |
c57fadcc | 563 | mua3.timecreated as timeuserfromdeleted, $messageuseridtosql |
883ce421 MN |
564 | FROM {messages} mr |
565 | INNER JOIN {message_conversations} mc | |
566 | ON mc.id = mr.conversationid | |
567 | INNER JOIN {message_conversation_members} mcm | |
568 | ON mcm.conversationid = mc.id "; | |
569 | ||
570 | $notificationsql = "SELECT mr.*, $userfields, 1 as notification | |
571 | FROM {notifications} mr "; | |
572 | ||
573 | $messagejoinsql = "LEFT JOIN {message_user_actions} mua | |
574 | ON (mua.messageid = mr.id AND mua.userid = mcm.userid AND mua.action = ?) | |
575 | LEFT JOIN {message_user_actions} mua2 | |
576 | ON (mua2.messageid = mr.id AND mua2.userid = mcm.userid AND mua2.action = ?) | |
577 | LEFT JOIN {message_user_actions} mua3 | |
578 | ON (mua3.messageid = mr.id AND mua3.userid = mr.useridfrom AND mua3.action = ?)"; | |
579 | $messagejoinparams = [\core_message\api::MESSAGE_ACTION_DELETED, \core_message\api::MESSAGE_ACTION_READ, | |
580 | \core_message\api::MESSAGE_ACTION_DELETED]; | |
581 | $notificationsparams = []; | |
582 | ||
583 | // If the 'useridto' value is empty then we are going to retrieve messages sent by the useridfrom to any user. | |
584 | if (empty($useridto)) { | |
585 | // Create the messaging query and params. | |
586 | $messagesql .= "INNER JOIN {user} u | |
587 | ON u.id = mcm.userid | |
588 | $messagejoinsql | |
589 | WHERE mr.useridfrom = ? | |
590 | AND mr.useridfrom != mcm.userid | |
4f97ea88 SA |
591 | AND u.deleted = 0 |
592 | AND mc.type = ? "; | |
593 | $messageparams = array_merge($messagejoinparams, [$useridfrom, \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL]); | |
883ce421 MN |
594 | |
595 | // Create the notifications query and params. | |
596 | $notificationsql .= "INNER JOIN {user} u | |
597 | ON u.id = mr.useridto | |
598 | WHERE mr.useridfrom = ? | |
599 | AND u.deleted = 0 "; | |
600 | $notificationsparams[] = $useridfrom; | |
601 | } else { | |
602 | // Create the messaging query and params. | |
193edf7f | 603 | // Left join because useridfrom may be -10 or -20 (no-reply and support users). |
883ce421 MN |
604 | $messagesql .= "LEFT JOIN {user} u |
605 | ON u.id = mr.useridfrom | |
606 | $messagejoinsql | |
607 | WHERE mcm.userid = ? | |
608 | AND mr.useridfrom != mcm.userid | |
4f97ea88 SA |
609 | AND u.deleted = 0 |
610 | AND mc.type = ? "; | |
611 | $messageparams = array_merge($messagejoinparams, [$useridto, \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL]); | |
612 | ||
613 | // If we're dealing with messages only and both useridto and useridfrom are set, | |
614 | // try to get a conversation between the users. Break early if we can't find one. | |
615 | if (!empty($useridfrom) && $notifications == 0) { | |
883ce421 MN |
616 | $messagesql .= " AND mr.useridfrom = ? "; |
617 | $messageparams[] = $useridfrom; | |
4f97ea88 SA |
618 | |
619 | // There should be an individual conversation between the users. If not, we can return early. | |
620 | $conversationid = \core_message\api::get_conversation_between_users([$useridto, $useridfrom]); | |
621 | if (empty($conversationid)) { | |
622 | return []; | |
623 | } | |
624 | $messagesql .= " AND mc.id = ? "; | |
625 | $messageparams[] = $conversationid; | |
883ce421 MN |
626 | } |
627 | ||
628 | // Create the notifications query and params. | |
629 | // Left join because useridfrom may be -10 or -20 (no-reply and support users). | |
630 | $notificationsql .= "LEFT JOIN {user} u | |
631 | ON (u.id = mr.useridfrom AND u.deleted = 0) | |
632 | WHERE mr.useridto = ? "; | |
633 | $notificationsparams[] = $useridto; | |
634 | if (!empty($useridfrom)) { | |
635 | $notificationsql .= " AND mr.useridfrom = ? "; | |
636 | $notificationsparams[] = $useridfrom; | |
193edf7f JL |
637 | } |
638 | } | |
883ce421 MN |
639 | if ($read) { |
640 | $notificationsql .= "AND mr.timeread IS NOT NULL "; | |
641 | } else { | |
642 | $notificationsql .= "AND mr.timeread IS NULL "; | |
643 | } | |
644 | $messagesql .= "ORDER BY $sort"; | |
645 | $notificationsql .= "ORDER BY $sort"; | |
646 | ||
647 | // Handle messages if needed. | |
648 | if ($notifications === -1 || $notifications === 0) { | |
649 | $messages = $DB->get_records_sql($messagesql, $messageparams, $limitfrom, $limitnum); | |
650 | // Get rid of the messages that have either been read or not read depending on the value of $read. | |
651 | $messages = array_filter($messages, function ($message) use ($read) { | |
652 | if ($read) { | |
653 | return !is_null($message->timeread); | |
654 | } | |
193edf7f | 655 | |
883ce421 MN |
656 | return is_null($message->timeread); |
657 | }); | |
193edf7f JL |
658 | } |
659 | ||
883ce421 MN |
660 | // All. |
661 | if ($notifications === -1) { | |
662 | return array_merge($messages, $DB->get_records_sql($notificationsql, $notificationsparams, $limitfrom, $limitnum)); | |
663 | } else if ($notifications === 1) { // Just notifications. | |
664 | return $DB->get_records_sql($notificationsql, $notificationsparams, $limitfrom, $limitnum); | |
665 | } | |
193edf7f | 666 | |
883ce421 | 667 | // Just messages. |
193edf7f JL |
668 | return $messages; |
669 | } | |
cf4a17cb | 670 | |
79f6c36c MN |
671 | /** |
672 | * Handles displaying processor settings in a fragment. | |
673 | * | |
674 | * @param array $args | |
675 | * @return bool|string | |
676 | * @throws moodle_exception | |
677 | */ | |
643b015d RW |
678 | function message_output_fragment_processor_settings($args = []) { |
679 | global $PAGE; | |
680 | ||
681 | if (!isset($args['type'])) { | |
682 | throw new moodle_exception('Must provide a processor type'); | |
683 | } | |
684 | ||
685 | if (!isset($args['userid'])) { | |
686 | throw new moodle_exception('Must provide a userid'); | |
687 | } | |
688 | ||
689 | $type = $args['type']; | |
690 | $userid = $args['userid']; | |
691 | ||
692 | $user = core_user::get_user($userid, '*', MUST_EXIST); | |
693 | $processor = get_message_processor($type); | |
694 | $providers = message_get_providers_for_user($userid); | |
a4a804b8 RW |
695 | $processorwrapper = new stdClass(); |
696 | $processorwrapper->object = $processor; | |
79f6c36c | 697 | $preferences = \core_message\api::get_all_message_preferences([$processorwrapper], $providers, $user); |
643b015d RW |
698 | |
699 | $processoroutput = new \core_message\output\preferences\processor($processor, $preferences, $user, $type); | |
700 | $renderer = $PAGE->get_renderer('core', 'message'); | |
701 | ||
702 | return $renderer->render_from_template('core_message/preferences_processor', $processoroutput->export_for_template($renderer)); | |
703 | } | |
6e65554e MG |
704 | |
705 | /** | |
706 | * Checks if current user is allowed to edit messaging preferences of another user | |
707 | * | |
708 | * @param stdClass $user user whose preferences we are updating | |
709 | * @return bool | |
710 | */ | |
711 | function core_message_can_edit_message_profile($user) { | |
712 | global $USER; | |
713 | if ($user->id == $USER->id) { | |
714 | return has_capability('moodle/user:editownmessageprofile', context_system::instance()); | |
715 | } else { | |
716 | $personalcontext = context_user::instance($user->id); | |
717 | if (!has_capability('moodle/user:editmessageprofile', $personalcontext)) { | |
718 | return false; | |
719 | } | |
720 | if (isguestuser($user)) { | |
721 | return false; | |
722 | } | |
723 | // No editing of admins by non-admins. | |
724 | if (is_siteadmin($user) and !is_siteadmin($USER)) { | |
725 | return false; | |
726 | } | |
727 | return true; | |
728 | } | |
729 | } | |
730 | ||
731 | /** | |
19fe7f27 | 732 | * Implements callback user_preferences, lists preferences that users are allowed to update directly |
6e65554e MG |
733 | * |
734 | * Used in {@see core_user::fill_preferences_cache()}, see also {@see useredit_update_user_preference()} | |
735 | * | |
736 | * @return array | |
737 | */ | |
738 | function core_message_user_preferences() { | |
6e65554e | 739 | $preferences = []; |
f7dfa9ba SA |
740 | $preferences['message_blocknoncontacts'] = array( |
741 | 'type' => PARAM_INT, | |
742 | 'null' => NULL_NOT_ALLOWED, | |
743 | 'default' => 0, | |
744 | 'choices' => array( | |
745 | \core_message\api::MESSAGE_PRIVACY_ONLYCONTACTS, | |
746 | \core_message\api::MESSAGE_PRIVACY_COURSEMEMBER, | |
747 | \core_message\api::MESSAGE_PRIVACY_SITE | |
748 | ), | |
749 | 'cleancallback' => function ($value) { | |
750 | global $CFG; | |
751 | ||
752 | // When site-wide messaging between users is disabled, MESSAGE_PRIVACY_SITE should be converted. | |
753 | if (empty($CFG->messagingallusers) && $value === \core_message\api::MESSAGE_PRIVACY_SITE) { | |
754 | return \core_message\api::MESSAGE_PRIVACY_COURSEMEMBER; | |
755 | } | |
756 | return $value; | |
757 | } | |
758 | ); | |
8c8939c1 RW |
759 | $preferences['message_entertosend'] = array( |
760 | 'type' => PARAM_BOOL, | |
761 | 'null' => NULL_NOT_ALLOWED, | |
762 | 'default' => false | |
763 | ); | |
6e65554e MG |
764 | $preferences['/^message_provider_([\w\d_]*)_logged(in|off)$/'] = array('isregex' => true, 'type' => PARAM_NOTAGS, |
765 | 'null' => NULL_NOT_ALLOWED, 'default' => 'none', | |
766 | 'permissioncallback' => function ($user, $preferencename) { | |
767 | global $CFG; | |
768 | require_once($CFG->libdir.'/messagelib.php'); | |
769 | if (core_message_can_edit_message_profile($user) && | |
770 | preg_match('/^message_provider_([\w\d_]*)_logged(in|off)$/', $preferencename, $matches)) { | |
771 | $providers = message_get_providers_for_user($user->id); | |
772 | foreach ($providers as $provider) { | |
773 | if ($matches[1] === $provider->component . '_' . $provider->name) { | |
774 | return true; | |
775 | } | |
776 | } | |
777 | } | |
778 | return false; | |
779 | }, | |
780 | 'cleancallback' => function ($value, $preferencename) { | |
781 | if ($value === 'none' || empty($value)) { | |
782 | return 'none'; | |
783 | } | |
784 | $parts = explode('/,/', $value); | |
785 | $processors = array_keys(get_message_processors()); | |
786 | array_filter($parts, function($v) use ($processors) {return in_array($v, $processors);}); | |
787 | return $parts ? join(',', $parts) : 'none'; | |
788 | }); | |
789 | return $preferences; | |
790 | } | |
5005d8cf RW |
791 | |
792 | /** | |
fd998fc6 | 793 | * Render the message drawer to be included in the top of the body of each page. |
5005d8cf RW |
794 | * |
795 | * @return string HTML | |
796 | */ | |
c75559cf | 797 | function core_message_standard_after_main_region_html() { |
fd998fc6 | 798 | return \core_message\helper::render_messaging_widget(true, null, null); |
5005d8cf | 799 | } |