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

thewire_set_url()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.0625

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 4
dl 0
loc 8
ccs 3
cts 4
cp 0.75
crap 2.0625
rs 9.4285
c 0
b 0
f 0
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');
0 ignored issues
show
Deprecated Code introduced by
The function elgg_register_page_handler() has been deprecated: 3.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

41
	/** @scrutinizer ignore-deprecated */ elgg_register_page_handler('thewire', 'thewire_page_handler');

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
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'];
0 ignored issues
show
Unused Code introduced by
The assignment to $recipient is dead and can be removed.
Loading history...
163
	$language = $params['language'];
164
	$method = $params['method'];
0 ignored issues
show
Unused Code introduced by
The assignment to $method is dead and can be removed.
Loading history...
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;
1 ignored issue
show
Documentation Bug introduced by
It seems like $guid can also be of type true. However, the property $wire_thread is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
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