1<?php
2/**
3* Elgg internal messages plugin
4* This plugin lets users send messages to each other.
5*/
6
7/**
8 * Messages init
9 *
10 * @return void
11 */
12function messages_init() {
13
14	// add page menu items
15	$user = elgg_get_logged_in_user_entity();
16	if (!empty($user)) {
17		elgg_register_menu_item('page', [
18			'name' => 'messages:inbox',
19			'text' => elgg_echo('messages:inbox'),
20			'href' => elgg_generate_url('collection:object:messages:owner', [
21				'username' => $user->username,
22			]),
23			'context' => 'messages',
24		]);
25
26		elgg_register_menu_item('page', [
27			'name' => 'messages:sentmessages',
28			'text' => elgg_echo('messages:sentmessages'),
29			'href' => elgg_generate_url('collection:object:messages:sent', [
30				'username' => $user->username,
31			]),
32			'context' => 'messages',
33		]);
34	}
35
36	// Extend system CSS with our own styles, which are defined in the messages/css view
37	elgg_extend_view('elgg.css', 'messages/css');
38	elgg_extend_view('elgg.js', 'messages/js');
39
40	// Extend avatar hover menu
41	elgg_register_plugin_hook_handler('register', 'menu:user_hover', 'messages_user_hover_menu');
42	elgg_register_plugin_hook_handler('register', 'menu:title', 'messages_user_hover_menu');
43
44	// delete messages sent by a user when user is deleted
45	elgg_register_event_handler('delete', 'user', 'messages_purge');
46
47	// ecml
48	elgg_register_plugin_hook_handler('get_views', 'ecml', 'messages_ecml_views_hook');
49
50	// permission overrides
51	elgg_register_plugin_hook_handler('permissions_check', 'object', 'messages_can_edit');
52	elgg_register_plugin_hook_handler('container_permissions_check', 'object', 'messages_can_edit_container');
53
54	// Topbar menu. We assume this menu will render *after* a message is rendered. If a refactor/plugin
55	// causes it to render first, the unread count notification will not update until the next page.
56	elgg_register_plugin_hook_handler('register', 'menu:topbar', 'messages_register_topbar');
57}
58
59/**
60 * Add inbox link to topbar
61 *
62 * @param \Elgg\Hook $hook "register", "menu:topbar"
63 *
64 * @return void|ElggMenuItem[]
65 */
66function messages_register_topbar(\Elgg\Hook $hook) {
67	if (!elgg_is_logged_in()) {
68		return;
69	}
70
71	$user = elgg_get_logged_in_user_entity();
72
73	$text = elgg_echo('messages');
74	$title = $text;
75
76	$num_messages = (int) messages_count_unread();
77	if ($num_messages) {
78		$title .= " (" . elgg_echo("messages:unreadcount", [$num_messages]) . ")";
79	}
80
81	$items = $hook->getValue();
82	$items[] = ElggMenuItem::factory([
83		'name' => 'messages',
84		'href' => elgg_generate_url('collection:object:messages:owner', [
85			'username' => $user->username,
86		]),
87		'text' => $text,
88		'priority' => 600,
89		'title' => $title,
90		'icon' => 'mail',
91		'badge' => $num_messages ? $num_messages : null,
92	]);
93
94	return $items;
95}
96
97/**
98 * Override the canEdit function to return true for messages within a particular context
99 *
100 * @param \Elgg\Hook $hook 'permissions_check', 'object'
101 *
102 * @return void|true
103 */
104function messages_can_edit(\Elgg\Hook $hook) {
105
106	global $messagesendflag;
107	if ($messagesendflag !== 1) {
108		return;
109	}
110
111	$entity = $hook->getEntityParam();
112	if ($entity instanceof ElggObject && $entity->getSubtype() == 'messages') {
113		return true;
114	}
115}
116
117/**
118 * Override the canEdit function to return true for messages within a particular context
119 *
120 * @param \Elgg\Hook $hook 'container_permissions_check', 'object'
121 *
122 * @return void|true
123 */
124function messages_can_edit_container(\Elgg\Hook $hook) {
125
126	global $messagesendflag;
127	if ($messagesendflag == 1) {
128		return true;
129	}
130}
131
132/**
133 * Send an internal message
134 *
135 * @param string $subject           The subject line of the message
136 * @param string $body              The body of the mesage
137 * @param int    $recipient_guid    The GUID of the user to send to
138 * @param int    $sender_guid       Optionally, the GUID of the user to send from
139 * @param int    $original_msg_guid The GUID of the message to reply from (default: none)
140 * @param bool   $notify            Send a notification (default: true)
141 * @param bool   $add_to_sent       If true (default), will add a message to the sender's 'sent' tray
142 *
143 * @return false|int
144 */
145function messages_send($subject, $body, $recipient_guid, $sender_guid = 0, $original_msg_guid = 0, $notify = true, $add_to_sent = true) {
146
147	// @todo remove globals
148	global $messagesendflag;
149	$messagesendflag = 1;
150
151	// @todo remove globals
152	global $messages_pm;
153	if ($notify) {
154		$messages_pm = 1;
155	} else {
156		$messages_pm = 0;
157	}
158
159	// If $sender_guid == 0, set to current user
160	if ($sender_guid == 0) {
161		$sender_guid = (int) elgg_get_logged_in_user_guid();
162	}
163
164	$message_to = new ElggMessage();
165	$message_sent = new ElggMessage();
166
167	$message_to->owner_guid = $recipient_guid;
168	$message_to->container_guid = $recipient_guid;
169	$message_sent->owner_guid = $sender_guid;
170	$message_sent->container_guid = $sender_guid;
171
172	$message_to->access_id = ACCESS_PUBLIC;
173	$message_sent->access_id = ACCESS_PUBLIC;
174
175	$message_to->title = $subject;
176	$message_to->description = $body;
177
178	$message_sent->title = $subject;
179	$message_sent->description = $body;
180
181	$message_to->toId = $recipient_guid; // the user receiving the message
182	$message_to->fromId = $sender_guid; // the user receiving the message
183	$message_to->readYet = 0; // this is a toggle between 0 / 1 (1 = read)
184	$message_to->hiddenFrom = 0; // this is used when a user deletes a message in their sentbox, it is a flag
185	$message_to->hiddenTo = 0; // this is used when a user deletes a message in their inbox
186
187	$message_sent->toId = $recipient_guid; // the user receiving the message
188	$message_sent->fromId = $sender_guid; // the user receiving the message
189	$message_sent->readYet = 0; // this is a toggle between 0 / 1 (1 = read)
190	$message_sent->hiddenFrom = 0; // this is used when a user deletes a message in their sentbox, it is a flag
191	$message_sent->hiddenTo = 0; // this is used when a user deletes a message in their inbox
192
193	// Save the copy of the message that goes to the recipient
194	$success = $message_to->save();
195
196	// Save the copy of the message that goes to the sender
197	if ($add_to_sent) {
198		$message_sent->save();
199	}
200
201	$message_to->access_id = ACCESS_PRIVATE;
202	$message_to->save();
203
204	if ($add_to_sent) {
205		$message_sent->access_id = ACCESS_PRIVATE;
206		$message_sent->save();
207	}
208
209	// if the new message is a reply then create a relationship link between the new message
210	// and the message it is in reply to
211	if ($original_msg_guid && $success) {
212		add_entity_relationship($message_sent->guid, "reply", $original_msg_guid);
213	}
214
215	if (($recipient_guid != elgg_get_logged_in_user_guid()) && $notify) {
216		$message_contents = $body;
217		$recipient = get_user($recipient_guid);
218		$sender = get_user($sender_guid);
219
220		$subject = elgg_echo('messages:email:subject', [], $recipient->language);
221		$body = elgg_echo('messages:email:body', [
222				$sender->getDisplayName(),
223				$message_contents,
224				elgg_generate_url('collection:object:messages:owner', [
225					'username' => $recipient->username,
226				]),
227				$sender->getDisplayName(),
228				elgg_generate_url('add:object:messages', [
229					'send_to' => $sender_guid,
230				]),
231			],
232			$recipient->language
233		);
234
235		$params = [
236			'object' => $message_to,
237			'action' => 'send',
238			'url' => $message_to->getURL(),
239		];
240		notify_user($recipient_guid, $sender_guid, $subject, $body, $params);
241	}
242
243	$messagesendflag = 0;
244	return $success;
245}
246
247/**
248 * Message URL override
249 *
250 * @param string $hook   'entity:url'
251 * @param string $type   'object'
252 * @param string $url    current return value
253 * @param array  $params supplied params
254 *
255 * @return void|string|false
256 * @deprecated 3.0 use ElggEntity::getURL()
257 */
258function messages_set_url($hook, $type, $url, $params) {
259
260	$entity = elgg_extract('entity', $params);
261	if (!$entity instanceof ElggObject || $entity->getSubtype() !== 'messages') {
262		return;
263	}
264
265	elgg_deprecated_notice(__METHOD__ . ' is deprecated please use ElggEntity::getURL()', '3.0');
266
267	return elgg_generate_entity_url($entity);
268}
269
270/**
271 * Returns the unread messages in a user's inbox
272 *
273 * @param int  $user_guid GUID of user whose inbox we're counting (0 for logged in user)
274 * @param int  $limit     Number of unread messages to return (default from settings)
275 * @param int  $offset    Start at a defined offset (for listings)
276 * @param bool $count     Switch between entities array or count mode
277 *
278 * @return ElggMessage[]|int|false
279 * @since 1.9
280 */
281function messages_get_unread($user_guid = 0, $limit = null, $offset = 0, $count = false) {
282	if (!$user_guid) {
283		$user_guid = elgg_get_logged_in_user_guid();
284	}
285
286	return elgg_get_entities([
287		'type' => 'object',
288		'subtype' => 'messages',
289		'metadata_name_value_pairs' => [
290			'toId' => (int) $user_guid,
291			'readYet' => 0,
292		],
293		'owner_guid' => (int) $user_guid,
294		'limit' => $limit ? : elgg_get_config('default_limit'),
295		'offset' => $offset,
296		'count' => $count,
297		'distinct' => false,
298	]);
299}
300
301/**
302 * Count the unread messages in a user's inbox
303 *
304 * @param int $user_guid GUID of user whose inbox we're counting (0 for logged in user)
305 *
306 * @return int
307 */
308function messages_count_unread($user_guid = 0) {
309	return (int) messages_get_unread($user_guid, 10, 0, true);
310}
311
312/**
313 * Prepare the compose form variables
314 *
315 * @param int $recipient_guid new message recipient
316 *
317 * @return array
318 */
319function messages_prepare_form_vars($recipient_guid = 0) {
320
321	$recipients = [];
322	$recipient = get_user($recipient_guid);
323	if (!empty($recipient)) {
324		$recipients[] = $recipient->getGUID();
325	}
326
327	// input names => defaults
328	$values = [
329		'subject' => elgg_get_sticky_value('messages', 'subject', ''),
330		'body' => elgg_get_sticky_value('messages', 'body', ''),
331		'recipients' => elgg_get_sticky_value('messages', 'recipients', $recipients),
332	];
333
334	elgg_clear_sticky_form('messages');
335
336	return $values;
337}
338
339/**
340 * Add to the user hover menu
341 *
342 * @param \Elgg\Hook $hook 'register', 'menu:user_hover' or 'menu:title'
343 *
344 * @return void|ElggMenuItem[]
345 */
346function messages_user_hover_menu(\Elgg\Hook $hook) {
347
348	$user = $hook->getEntityParam();
349	if (!elgg_is_logged_in() || !$user instanceof ElggUser) {
350		return;
351	}
352
353	if (elgg_get_logged_in_user_guid() === $user->guid) {
354		return;
355	}
356
357	$menu_options = [
358		'name' => 'send',
359		'text' => elgg_echo('messages:sendmessage'),
360		'icon' => 'mail',
361		'href' => elgg_generate_url('add:object:messages', [
362			'send_to' => $user->guid,
363		]),
364	];
365
366	if ($hook->getType() == 'menu:user_hover') {
367		$menu_options['section'] = 'action';
368	}
369
370	if ($hook->getType() == 'menu:title') {
371		$menu_options['class'] = ['elgg-button', 'elgg-button-action'];
372	}
373
374	$return = $hook->getValue();
375	$return[] = ElggMenuItem::factory($menu_options);
376
377	return $return;
378}
379
380/**
381 * Delete messages from a user who is being deleted
382 *
383 * @param \Elgg\Event $event 'delete', 'user'
384 *
385 * @return void
386 */
387function messages_purge(\Elgg\Event $event) {
388	$user = $event->getObject();
389	if (!$user->guid) {
390		return;
391	}
392
393	// make sure we delete them all
394	elgg_call(ELGG_IGNORE_ACCESS | ELGG_SHOW_DISABLED_ENTITIES, function() use ($user) {
395		$batch = new ElggBatch('elgg_get_entities', [
396			'type' => 'object',
397			'subtype' => 'messages',
398			'metadata_name_value_pairs' => [
399				'fromId' => $user->guid,
400			],
401			'limit' => false,
402		]);
403		$batch->setIncrementOffset(false);
404		foreach ($batch as $e) {
405			$e->delete();
406		}
407	});
408}
409
410/**
411 * Register messages with ECML.
412 *
413 * @param \Elgg\Hook $hook 'get_views', 'ecml'
414 *
415 * @return array
416 */
417function messages_ecml_views_hook(\Elgg\Hook $hook) {
418	$return_value = $hook->getValue();
419	$return_value['messages/messages'] = elgg_echo('messages');
420	return $return_value;
421}
422
423return function() {
424	elgg_register_event_handler('init', 'system', 'messages_init');
425};
426