Passed
Push — master ( c0a3a7...3b84a4 )
by Jeroen
58:51
created

mod/thewire/start.php (3 issues)

1
<?php
2
/**
3
 * Elgg wire plugin
4
 *
5
 * Forked from Curverider's version
6
 *
7
 * JHU/APL Contributors:
8
 * Cash Costello
9
 * Clark Updike
10
 * John Norton
11
 * Max Thomas
12
 * Nathan Koterba
13
 */
14
15
/**
16
 * The Wire initialization
17
 *
18
 * @return void
19
 */
20
function thewire_init() {
21
22 31
	elgg_register_ajax_view('thewire/previous');
23
24
	// add a site navigation item
25 31
	elgg_register_menu_item('site', [
26 31
		'name' => 'thewire',
27 31
		'text' => elgg_echo('thewire'),
28 31
		'href' => 'thewire/all',
29
	]);
30
31
	// owner block menu
32 31
	elgg_register_plugin_hook_handler('register', 'menu:owner_block', 'thewire_owner_block_menu');
33
34
	// remove edit and access and add thread, reply, view previous
35 31
	elgg_register_plugin_hook_handler('register', 'menu:entity', 'thewire_setup_entity_menu_items');
36
	
37
	// Extend system CSS with our own styles, which are defined in the thewire/css view
38 31
	elgg_extend_view('elgg.css', 'thewire/css');
39
40
	// Register a page handler, so we can have nice URLs
41 31
	elgg_register_page_handler('thewire', 'thewire_page_handler');
42
43
	// Register a URL handler for thewire posts
44 31
	elgg_register_plugin_hook_handler('entity:url', 'object', 'thewire_set_url');
45
46
	// Register for notifications
47 31
	elgg_register_notification_event('object', 'thewire');
48 31
	elgg_register_plugin_hook_handler('prepare', 'notification:create:object:thewire', 'thewire_prepare_notification');
49 31
	elgg_register_plugin_hook_handler('get', 'subscriptions', 'thewire_add_original_poster');
50
51
	// allow to be liked
52 31
	elgg_register_plugin_hook_handler('likes:is_likable', 'object:thewire', 'Elgg\Values::getTrue');
53
54 31
	elgg_register_plugin_hook_handler('unit_test', 'system', 'thewire_test');
55 31
}
56
57
/**
58
 * The wire page handler
59
 *
60
 * Supports:
61
 * thewire/all                  View site wire posts
62
 * thewire/owner/<username>     View this user's wire posts
63
 * thewire/following/<username> View the posts of those this user follows
64
 * thewire/reply/<guid>         Reply to a post
65
 * thewire/view/<guid>          View a post
66
 * thewire/thread/<id>          View a conversation thread
67
 * thewire/tag/<tag>            View wire posts tagged with <tag>
68
 *
69
 * @param array $page From the page_handler function
70
 *
71
 * @return bool
72
 */
73
function thewire_page_handler($page) {
74
75
	if (!isset($page[0])) {
76
		$page = ['all'];
77
	}
78
79
	switch ($page[0]) {
80
		case "all":
81
			echo elgg_view_resource('thewire/everyone');
82
			break;
83
84
		case "friends":
85
			echo elgg_view_resource('thewire/friends');
86
			break;
87
88
		case "owner":
89
			echo elgg_view_resource('thewire/owner');
90
			break;
91
92
		case "view":
93
			echo elgg_view_resource('thewire/view', [
94
				'guid' => elgg_extract(1, $page),
95
			]);
96
			break;
97
98
		case "thread":
99
			echo elgg_view_resource('thewire/thread', [
100
				'thread_id' => elgg_extract(1, $page),
101
			]);
102
			break;
103
104
		case "reply":
105
			echo elgg_view_resource('thewire/reply', [
106
				'guid' => elgg_extract(1, $page),
107
			]);
108
			break;
109
110
		case "tag":
111
			echo elgg_view_resource('thewire/tag', [
112
				'tag' => elgg_extract(1, $page),
113
			]);
114
			break;
115
116
		case "previous":
117
			echo elgg_view_resource('thewire/previous', [
118
				'guid' => elgg_extract(1, $page),
119
			]);
120
			break;
121
122
		default:
123
			return false;
124
	}
125
	return true;
126
}
127
128
/**
129
 * Override the url for a wire post to return the thread
130
 *
131
 * @param string $hook   'entity:url'
132
 * @param string $type   'object'
133
 * @param string $url    current return value
134
 * @param array  $params supplied params
135
 *
136
 * @return void|string
137
 */
138
function thewire_set_url($hook, $type, $url, $params) {
139
	
140 3
	$entity = elgg_extract('entity', $params);
141 3
	if (!$entity instanceof ElggWire) {
142 3
		return;
143
	}
144
	
145
	return "thewire/view/{$entity->guid}";
146
}
147
148
/**
149
 * Prepare a notification message about a new wire post
150
 *
151
 * @param string                          $hook         Hook name
152
 * @param string                          $type         Hook type
153
 * @param Elgg\Notifications\Notification $notification The notification to prepare
154
 * @param array                           $params       Hook parameters
155
 *
156
 * @return Elgg\Notifications\Notification
157
 */
158
function thewire_prepare_notification($hook, $type, $notification, $params) {
159
160
	$entity = $params['event']->getObject();
161
	$owner = $params['event']->getActor();
162
	$recipient = $params['recipient'];
163
	$language = $params['language'];
164
	$method = $params['method'];
165
	$descr = $entity->description;
166
167
	$subject = elgg_echo('thewire:notify:subject', [$owner->name], $language);
168
	if ($entity->reply) {
169
		$parent = thewire_get_parent($entity->guid);
170
		if ($parent) {
171
			$parent_owner = $parent->getOwnerEntity();
172
			$body = elgg_echo('thewire:notify:reply', [$owner->name, $parent_owner->name], $language);
173
		}
174
	} else {
175
		$body = elgg_echo('thewire:notify:post', [$owner->name], $language);
176
	}
177
	$body .= "\n\n" . $descr . "\n\n";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $body does not seem to be defined for all execution paths leading up to this point.
Loading history...
178
	$body .= elgg_echo('thewire:notify:footer', [$entity->getURL()], $language);
179
180
	$notification->subject = $subject;
181
	$notification->body = $body;
182
	$notification->summary = elgg_echo('thewire:notify:summary', [$descr], $language);
183
	$notification->url = $entity->getURL();
184
	
185
	return $notification;
186
}
187
188
/**
189
 * Get an array of hashtags from a text string
190
 *
191
 * @param string $text The text of a post
192
 *
193
 * @return array
194
 */
195
function thewire_get_hashtags($text) {
196
	// beginning of text or white space followed by hashtag
197
	// hashtag must begin with # and contain at least one character not digit, space, or punctuation
198
	$matches = [];
199
	preg_match_all('/(^|[^\w])#(\w*[^\s\d!-\/:-@]+\w*)/', $text, $matches);
200
	
201
	return $matches[2];
202
}
203
204
/**
205
 * Replace urls, hash tags, and @'s by links
206
 *
207
 * @param string $text The text of a post
208
 *
209
 * @return string
210
 */
211
function thewire_filter($text) {
212
	$text = ' ' . $text;
213
214
	// email addresses
215
	$text = preg_replace(
216
				'/(^|[^\w])([\w\-\.]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})/i',
217
				'$1<a href="mailto:$2@$3">$2@$3</a>',
218
				$text);
219
220
	// links
221
	$text = parse_urls($text);
222
223
	// usernames
224
	$text = preg_replace(
225
				'/(^|[^\w])@([\p{L}\p{Nd}._]+)/u',
226
				'$1<a href="' . elgg_get_site_url() . 'thewire/owner/$2">@$2</a>',
227
				$text);
228
229
	// hashtags
230
	$text = preg_replace(
231
				'/(^|[^\w])#(\w*[^\s\d!-\/:-@]+\w*)/',
232
				'$1<a href="' . elgg_get_site_url() . 'thewire/tag/$2">#$2</a>',
233
				$text);
234
235
	return trim($text);
236
}
237
238
/**
239
 * Create a new wire post.
240
 *
241
 * @param string $text        The post text
242
 * @param int    $userid      The user's guid
243
 * @param int    $access_id   Public/private etc
244
 * @param int    $parent_guid Parent post guid (if any)
245
 * @param string $method      The method (default: 'site')
246
 *
247
 * @return false|int
248
 */
249
function thewire_save_post($text, $userid, $access_id, $parent_guid = 0, $method = "site") {
250
	
251
	$post = new ElggWire();
252
	$post->owner_guid = $userid;
253
	$post->access_id = $access_id;
254
255
	// Character limit is now from config
256
	$limit = elgg_get_plugin_setting('limit', 'thewire');
257
	if ($limit > 0) {
258
		$text = elgg_substr($text, 0, $limit);
259
	}
260
261
	// no html tags allowed so we escape
262
	$post->description = htmlspecialchars($text, ENT_NOQUOTES | ENT_SUBSTITUTE, 'UTF-8');
263
264
	$post->method = $method; //method: site, email, api, ...
265
266
	$tags = thewire_get_hashtags($text);
267
	if ($tags) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $tags of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
268
		$post->tags = $tags;
269
	}
270
271
	// must do this before saving so notifications pick up that this is a reply
272
	if ($parent_guid) {
273
		$post->reply = true;
274
	}
275
276
	$guid = $post->save();
277
	if ($guid === false) {
278
		return false;
279
	}
280
281
	// set thread guid
282
	if ($parent_guid) {
283
		$post->addRelationship($parent_guid, 'parent');
284
		
285
		// name conversation threads by guid of first post (works even if first post deleted)
286
		$parent_post = get_entity($parent_guid);
287
		$post->wire_thread = $parent_post->wire_thread;
288
	} else {
289
		// first post in this thread
290
		$post->wire_thread = $guid;
291
	}
292
293
	elgg_create_river_item([
294
		'view' => 'river/object/thewire/create',
295
		'action_type' => 'create',
296
		'subject_guid' => $post->owner_guid,
297
		'object_guid' => $post->guid,
298
	]);
299
300
	// let other plugins know we are setting a user status
301
	$params = [
302
		'entity' => $post,
303
		'user' => $post->getOwnerEntity(),
304
		'message' => $post->description,
305
		'url' => $post->getURL(),
306
		'origin' => 'thewire',
307
	];
308
	elgg_trigger_plugin_hook('status', 'user', $params);
309
	
310
	return $guid;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $guid also could return the type true which is incompatible with the documented return type integer|false.
Loading history...
311
}
312
313
/**
314
 * Add temporary subscription for original poster if not already registered to
315
 * receive a notification of reply
316
 *
317
 * @param string $hook          Hook name
318
 * @param string $type          Hook type
319
 * @param array  $subscriptions Subscriptions for a notification event
320
 * @param array  $params        Parameters including the event
321
 *
322
 * @return void|array
323
 */
324
function thewire_add_original_poster($hook, $type, $subscriptions, $params) {
325 2
	$event = elgg_extract('event', $params);
326 2
	$entity = $event->getObject();
327 2
	if (!($entity instanceof ElggWire)) {
328 2
		return;
329
	}
330
	
331
	$parents = $entity->getEntitiesFromRelationship([
332
		'type' => 'object',
333
		'subtype' => 'thewire',
334
		'relationship' => 'parent',
335
	]);
336
	if (empty($parents)) {
337
		return;
338
	}
339
	
340
	/* @var $parent ElggWire */
341
	$parent = $parents[0];
342
	// do not add a subscription if reply was to self
343
	if ($parent->getOwnerGUID() === $entity->getOwnerGUID()) {
344
		return;
345
	}
346
	
347
	if (array_key_exists($parent->getOwnerGUID(), $subscriptions)) {
348
		// already in the list
349
		return;
350
	}
351
	
352
	/* @var $parent_owner ElggUser */
353
	$parent_owner = $parent->getOwnerEntity();
354
	$personal_methods = $parent_owner->getNotificationSettings();
355
	$methods = [];
356
	foreach ($personal_methods as $method => $state) {
357
		if ($state) {
358
			$methods[] = $method;
359
		}
360
	}
361
	
362
	if (empty($methods)) {
363
		return;
364
	}
365
	
366
	$subscriptions[$parent->getOwnerGUID()] = $methods;
367
	return $subscriptions;
368
}
369
370
/**
371
 * Get the latest wire guid - used for ajax update
372
 *
373
 * @return int
374
 */
375
function thewire_latest_guid() {
376
	$post = elgg_get_entities([
377
		'type' => 'object',
378
		'subtype' => 'thewire',
379
		'limit' => 1,
380
	]);
381
	if ($post) {
382
		return $post[0]->guid;
383
	}
384
	
385
	return 0;
386
}
387
388
/**
389
 * Get the parent of a wire post
390
 *
391
 * @param int $post_guid The guid of the reply
392
 *
393
 * @return void|ElggObject
394
 */
395
function thewire_get_parent($post_guid) {
396
	$parents = elgg_get_entities([
397
		'relationship' => 'parent',
398
		'relationship_guid' => $post_guid,
399
		'limit' => 1,
400
	]);
401
	if ($parents) {
402
		return $parents[0];
403
	}
404
}
405
406
/**
407
 * Sets up the entity menu for thewire
408
 *
409
 * Adds reply, thread, and view previous links. Removes edit and access.
410
 *
411
 * @param string         $hook   'register'
412
 * @param string         $type   'menu:entity'
413
 * @param ElggMenuItem[] $value  Array of menu items
414
 * @param array          $params Array with the entity
415
 *
416
 * @return void|ElggMenuItem[]
417
 */
418
function thewire_setup_entity_menu_items($hook, $type, $value, $params) {
419
	
420 1
	$entity = elgg_extract('entity', $params);
421 1
	if (!($entity instanceof \ElggWire)) {
422 1
		return;
423
	}
424
	
425
	foreach ($value as $index => $item) {
426
		if ($item->getName() == 'edit') {
427
			unset($value[$index]);
428
		}
429
	}
430
431
	if (elgg_is_logged_in()) {
432
		$value[] = ElggMenuItem::factory([
433
			'name' => 'reply',
434
			'icon' => 'reply',
435
			'text' => elgg_echo('reply'),
436
			'href' => "thewire/reply/{$entity->guid}",
437
		]);
438
	}
439
440
	if ($entity->reply) {
441
		$value[] = ElggMenuItem::factory([
442
			'name' => 'previous',
443
			'icon' => 'arrow-left',
444
			'text' => elgg_echo('previous'),
445
			'href' => "thewire/previous/{$entity->guid}",
446
			'link_class' => 'thewire-previous',
447
			'title' => elgg_echo('thewire:previous:help'),
448
		]);
449
	}
450
451
	$value[] = ElggMenuItem::factory([
452
		'name' => 'thread',
453
		'icon' => 'comments-o',
454
		'text' => elgg_echo('thewire:thread'),
455
		'href' => "thewire/thread/{$entity->wire_thread}",
456
	]);
457
458
	return $value;
459
}
460
461
/**
462
 * Add a menu item to an ownerblock
463
 *
464
 * @param string         $hook   'register'
465
 * @param string         $type   'menu:owner_block'
466
 * @param ElggMenuItem[] $return current return value
467
 * @param array          $params supplied params
468
 *
469
 * @return void|ElggMenuItem[]
470
 */
471
function thewire_owner_block_menu($hook, $type, $return, $params) {
472
	
473
	$user = elgg_extract('entity', $params);
474
	if (!$user instanceof \ElggUser) {
475
		return;
476
	}
477
478
	$return[] = \ElggMenuItem::factory([
479
		'name' => 'thewire',
480
		'text' => elgg_echo('item:object:thewire'),
481
		'href' => "thewire/owner/{$params['entity']->username}",
482
	]);
483
	
484
	return $return;
485
}
486
487
/**
488
 * Runs unit tests for the wire
489
 *
490
 * @param string $hook   'unit_test'
491
 * @param string $type   'system'
492
 * @param array  $value  current return value
493
 * @param array  $params supplied params
494
 *
495
 * @return array
496
 */
497
function thewire_test($hook, $type, $value, $params) {
498
	$value[] = elgg_get_plugins_path() . 'thewire/tests/regex.php';
499
	return $value;
500
}
501
502
return function() {
503 18
	elgg_register_event_handler('init', 'system', 'thewire_init');
504
};
505