ThreadItem.php 28.4 KB
Newer Older
friendica's avatar
friendica committed
1
2
<?php /** @file */

3
namespace Zotlabs\Lib;
4

Mario's avatar
Mario committed
5
6
use Zotlabs\Lib\Apps;

7
8
9
require_once('include/text.php');

/**
10
 * A thread item
11
 */
12

13
14
class ThreadItem {

15
	public  $data = array();
friendica's avatar
friendica committed
16
	private $template = 'conv_item.tpl';
17
	private $comment_box_template = 'comment_item.tpl';
18
	private $commentable = false;
19
	// list of supported reaction emojis - a site can over-ride this via config system.reactions
20
	private $reactions = ['1f60a','1f44f','1f37e','1f48b','1f61e','2665','1f606','1f62e','1f634','1f61c','1f607','1f608'];
21
22
23
24
25
26
27
28
29
30
31
	private $toplevel = false;
	private $children = array();
	private $parent = null;
	private $conversation = null;
	private $redirect_url = null;
	private $owner_url = '';
	private $owner_photo = '';
	private $owner_name = '';
	private $wall_to_wall = false;
	private $threaded = false;
	private $visiting = false;
32
	private $channel = null;
friendica's avatar
friendica committed
33
	private $display_mode = 'normal';
Zot's avatar
Zot committed
34
	private $reload = '';
35

36
37

	public function __construct($data) {
38

39
40
		$this->data = $data;
		$this->toplevel = ($this->get_id() == $this->get_data_value('parent'));
41
		$this->threaded = get_config('system','thread_allow');
42

Zot's avatar
Zot committed
43
		$observer = \App::get_observer();
Zot's avatar
Zot committed
44

45
		// Prepare the children
46
		if(isset($data['children'])) {
47
			foreach($data['children'] as $item) {
friendica's avatar
friendica committed
48

49
				/*
friendica's avatar
friendica committed
50
				 * Only add those that will be displayed
51
				 */
friendica's avatar
friendica committed
52

Zot's avatar
Zot committed
53
				if((! visible_activity($item)) || array_key_exists('blocked',$item)) {
54
55
					continue;
				}
friendica's avatar
friendica committed
56

Zot's avatar
Zot committed
57

58
				$child = new ThreadItem($item);
59
60
61
				$this->add_child($child);
			}
		}
62
63
64
65
66
67
68
69

		// allow a site to configure the order and content of the reaction emoji list
		if($this->toplevel) {
			$x = get_config('system','reactions');
			if($x && is_array($x) && count($x)) {
				$this->reactions = $x;
			}
		}
70
71
72
73
74
75
76
77
78
	}

	/**
	 * Get data in a form usable by a conversation template
	 *
	 * Returns:
	 *      _ The data requested on success
	 *      _ false on failure
	 */
79

80
	public function get_template_data($conv_responses, $thread_level=1, $conv_flags = []) {
81

82
83
		$result = array();

84
		$item     = $this->get_data();
85
86
87
88
89
90

		$commentww = '';
		$sparkle = '';
		$buttons = '';
		$dropping = false;
		$star = false;
Treer's avatar
Treer committed
91
		$isstarred = "unstarred fa-star-o";
92
93
		$is_comment = false;
		$is_item = false;
94
95
		$osparkle = '';
		$total_children = $this->count_descendants();
96
		$unseen_comments = (($item['real_uid']) ? 0 : $this->count_unseen_descendants());
97

Max Kostikov's avatar
Max Kostikov committed
98
 		$conv = $this->get_conversation();
friendica's avatar
friendica committed
99
		$observer = $conv->get_observer();
100

101
		$lock = (((intval($item['item_private'])) || (($item['uid'] == local_channel()) && (strlen($item['allow_cid']) || strlen($item['allow_gid'])
102
103
104
			|| strlen($item['deny_cid']) || strlen($item['deny_gid']))))
			? t('Private Message')
			: false);
105
		$locktype = $item['item_private'];
106

friendica's avatar
friendica committed
107
		$shareable = ((($conv->get_profile_owner() == local_channel() && local_channel()) && ($item['item_private'] != 1)) ? true : false);
friendica's avatar
friendica committed
108

109
110
111
112
		// allow an exemption for sharing stuff from your private feeds
		if($item['author']['xchan_network'] === 'rss')
			$shareable = true;

113
		$privacy_warning = false;
114
		if(intval($item['item_private']) && ($item['owner']['xchan_network'] === 'activitypub')) {
115
116
			$recips = get_iconfig($item['parent'], 'activitypub', 'recips');

Mario's avatar
Mario committed
117
			if(! is_array($recips['to']) || ! in_array($observer['xchan_url'], $recips['to']))
118
119
				$privacy_warning = true;
		}
Zot's avatar
Zot committed
120

121
		if ($lock) {
122
 			if (($item['mid'] == $item['parent_mid']) && count(get_terms_oftype($item['term'],TERM_FORUM))) {
123
 				$privacy_warning = true;
124
				$conv_flags['parent_privacy_warning'] = true;
125
126
127
 			}
		}

128
		$privacy_warning = (isset($conv_flags['parent_privacy_warning'])) ? $conv_flags['parent_privacy_warning'] : $privacy_warning;
129

130
131
132
133
		if ($lock && $privacy_warning) {
			$lock = t('Privacy conflict. Discretion advised.');
		}

134
135
		$mode = $conv->get_mode();

Zot's avatar
Zot committed
136
137
138
139
140
141
142
143
144
145
146
		switch($item['item_type']) {
			case ITEM_TYPE_CARD:
				$edlink = 'card_edit';
				break;
			case ITEM_TYPE_ARTICLE:
				$edlink = 'article_edit';
				break;
			default:
				$edlink = 'editpost';
				break;
		}
Zot's avatar
Zot committed
147

friendica's avatar
friendica committed
148
		if(local_channel() && $observer['xchan_hash'] === $item['author_xchan'])
Zot's avatar
Zot committed
149
			$edpost = array(z_root() . '/' . $edlink . '/' . $item['id'], t('Edit'));
150
151
		else
			$edpost = false;
friendica's avatar
friendica committed
152

friendica's avatar
friendica committed
153

154
		if($observer && $observer['xchan_hash']
155
156
157
			&& ($observer['xchan_hash'] == $this->get_data_value('author_xchan')
			|| $observer['xchan_hash'] == $this->get_data_value('owner_xchan')
			|| $observer['xchan_hash'] == $this->get_data_value('source_xchan')
158
			|| $this->get_data_value('uid') == local_channel()))
159
160
			$dropping = true;

friendica's avatar
friendica committed
161
162
163
164
165
166
167

		if(array_key_exists('real_uid',$item)) {
			$edpost = false;
			$dropping = false;
		}


friendica's avatar
friendica committed
168
169
170
171
172
		if($dropping) {
			$drop = array(
				'dropping' => $dropping,
				'delete' => t('Delete'),
			);
173
		}
174
175
176
177
		elseif(is_site_admin()) {
			$drop = [ 'dropping' => true, 'delete' => t('Admin Delete') ];
		}

friendica's avatar
friendica committed
178
// FIXME
179
		if($observer_is_pageowner) {
friendica's avatar
friendica committed
180
			$multidrop = array(
181
				'select' => t('Select'),
friendica's avatar
friendica committed
182
183
184
			);
		}

friendica's avatar
friendica committed
185
		$filer = ((($conv->get_profile_owner() == local_channel()) && (! array_key_exists('real_uid',$item))) ? t("Save to Folder") : false);
186

friendica's avatar
friendica committed
187
		$profile_avatar = $item['author']['xchan_photo_m'];
188
		$profile_link   = chanlink_hash($item['author_xchan']);
189
		$profile_name   = $item['author']['xchan_name'];
friendica's avatar
friendica committed
190

191
		$location = format_location($item);
192
193
		$isevent = false;
		$attend = null;
friendica's avatar
friendica committed
194
		$canvote = false;
195
196
197
198
199

		// process action responses - e.g. like/dislike/attend/agree/whatever
		$response_verbs = array('like');
		if(feature_enabled($conv->get_profile_owner(),'dislike'))
			$response_verbs[] = 'dislike';
friendica's avatar
friendica committed
200
		if($item['obj_type'] === ACTIVITY_OBJ_EVENT) {
201
202
203
			$response_verbs[] = 'attendyes';
			$response_verbs[] = 'attendno';
			$response_verbs[] = 'attendmaybe';
204
			if($this->is_commentable() && $observer) {
friendica's avatar
friendica committed
205
206
207
				$isevent = true;
				$attend = array( t('I will attend'), t('I will not attend'), t('I might attend'));
			}
208
		}
friendica's avatar
friendica committed
209

Mario's avatar
Mario committed
210
211
212
213
		if($item['obj_type'] === 'Question') {
			$response_verbs[] = 'answer';
		}

214
		$consensus = (intval($item['item_consensus']) ? true : false);
215
216
217
218
		if($consensus) {
			$response_verbs[] = 'agree';
			$response_verbs[] = 'disagree';
			$response_verbs[] = 'abstain';
219
			if($this->is_commentable() && $observer) {
friendica's avatar
friendica committed
220
221
222
				$conlabels = array( t('I agree'), t('I disagree'), t('I abstain'));
				$canvote = true;
			}
223
224
		}

friendica's avatar
friendica committed
225
226
		if(! feature_enabled($conv->get_profile_owner(),'dislike'))
			unset($conv_responses['dislike']);
227

228
		$responses = get_responses($conv_responses,$response_verbs,$this,$item);
229

230
231
232
233
234
		$my_responses = [];
		foreach($response_verbs as $v) {
			$my_responses[$v] = (($conv_responses[$v][$item['mid'] . '-m']) ? 1 : 0);
		}

235
236
		$like_count = ((x($conv_responses['like'],$item['mid'])) ? $conv_responses['like'][$item['mid']] : '');
		$like_list = ((x($conv_responses['like'],$item['mid'])) ? $conv_responses['like'][$item['mid'] . '-l'] : '');
Mario's avatar
Mario committed
237
		if (($like_list) && (count($like_list) > MAX_LIKERS)) {
238
			$like_list_part = array_slice($like_list, 0, MAX_LIKERS);
239
			array_push($like_list_part, '<a class="dropdown-item" href="#" data-toggle="modal" data-target="#likeModal-' . $this->get_id() . '"><b>' . t('View all') . '</b></a>');
240
241
242
		} else {
			$like_list_part = '';
		}
243
		$like_button_label = tt('Like','Likes',$like_count,'noun');
244

Mario's avatar
Mario committed
245
		if (feature_enabled($conv->get_profile_owner(),'dislike')) {
246
247
			$dislike_count = ((x($conv_responses['dislike'],$item['mid'])) ? $conv_responses['dislike'][$item['mid']] : '');
			$dislike_list = ((x($conv_responses['dislike'],$item['mid'])) ? $conv_responses['dislike'][$item['mid'] . '-l'] : '');
248
			$dislike_button_label = tt('Dislike','Dislikes',$dislike_count,'noun');
Mario's avatar
Mario committed
249
			if (($dislike_list) && (count($dislike_list) > MAX_LIKERS)) {
250
				$dislike_list_part = array_slice($dislike_list, 0, MAX_LIKERS);
251
				array_push($dislike_list_part, '<a class="dropdown-item" href="#" data-toggle="modal" data-target="#dislikeModal-' . $this->get_id() . '"><b>' . t('View all') . '</b></a>');
252
253
254
			} else {
				$dislike_list_part = '';
			}
Mario's avatar
Mario committed
255
		}
256

257
		$showlike    = ((x($conv_responses['like'],$item['mid'])) ? format_like($conv_responses['like'][$item['mid']],$conv_responses['like'][$item['mid'] . '-l'],'like',$item['mid']) : '');
258
		$showdislike = ((x($conv_responses['dislike'],$item['mid']) && feature_enabled($conv->get_profile_owner(),'dislike'))
259
				? format_like($conv_responses['dislike'][$item['mid']],$conv_responses['dislike'][$item['mid'] . '-l'],'dislike',$item['mid']) : '');
260
261
262
263
264
265

		/*
		 * We should avoid doing this all the time, but it depends on the conversation mode
		 * And the conv mode may change when we change the conv, or it changes its mode
		 * Maybe we should establish a way to be notified about conversation changes
		 */
266

267
		$this->check_wall_to_wall();
268

269
		if($this->is_toplevel()) {
friendica's avatar
friendica committed
270
			// FIXME check this permission
friendica's avatar
friendica committed
271
			if(($conv->get_profile_owner() == local_channel()) && (! array_key_exists('real_uid',$item))) {
friendica's avatar
friendica committed
272

273
				$star = array(
274
					'toggle' => t("Toggle Star Status"),
275
					'isstarred' => ((intval($item['item_starred'])) ? true : false),
friendica's avatar
friendica committed
276
277
				);

278
			}
279
		}
280
		else {
281
			$is_comment = true;
282
283
		}

friendica's avatar
friendica committed
284

285
286
287
		$verified = (intval($item['item_verified']) ? t('Message signature validated') : '');
		$forged = ((($item['sig']) && (! intval($item['item_verified']))) ? t('Message signature incorrect') : '');
		$unverified = '' ; // (($this->is_wall_to_wall() && (! intval($item['item_verified']))) ? t('Message cannot be verified') : '');
friendica's avatar
friendica committed
288

289
		$settings = '';
friendica's avatar
friendica committed
290

291
292
		$tagger = [];

friendica's avatar
friendica committed
293
		// FIXME - check this permission
friendica's avatar
friendica committed
294
		if($conv->get_profile_owner() == local_channel()) {
295
			/* disable until we agree on how to implemnt this in zot6/activitypub
friendica's avatar
friendica committed
296
			$tagger = array(
297
				'tagit' => t("Add Tag"),
friendica's avatar
friendica committed
298
299
				'classtagger' => "",
			);
300
			*/
301

Mario's avatar
Mario committed
302
			$settings = t('Conversation Features');
friendica's avatar
friendica committed
303
304
		}

friendica's avatar
friendica committed
305
		$has_bookmarks = false;
Mario's avatar
Mario committed
306
		if(Apps::system_app_installed(local_channel(), 'Bookmarks') && is_array($item['term'])) {
friendica's avatar
friendica committed
307
			foreach($item['term'] as $t) {
M. Dent's avatar
M. Dent committed
308
				if(($t['ttype'] == TERM_BOOKMARK))
friendica's avatar
friendica committed
309
310
311
312
					$has_bookmarks = true;
			}
		}

friendica's avatar
friendica committed
313
		$has_event = false;
friendica's avatar
friendica committed
314
		if(($item['obj_type'] === ACTIVITY_OBJ_EVENT) && $conv->get_profile_owner() == local_channel())
friendica's avatar
friendica committed
315
			$has_event = true;
friendica's avatar
friendica committed
316

317
		if($this->is_commentable() && $observer) {
friendica's avatar
friendica committed
318
319
			$like = array( t("I like this \x28toggle\x29"), t("like"));
			$dislike = array( t("I don't like this \x28toggle\x29"), t("dislike"));
320
			$reply_to = array( t("Reply on this comment"), t("reply"), t("Reply to"));
321
322
		}

Zot's avatar
Zot committed
323
		if ($shareable) {
324
325
326
			// This actually turns out not to be possible in some protocol stacks without opening up hundreds of new issues.
			// Will allow it only for uri resolvable sources.
			if(strpos($item['mid'],'http') === 0) {
327
328
				$share = []; //Not yet ready for primetime
				//$share = array( t('Repeat This'), t('repeat'));
329
			}
Zot's avatar
Zot committed
330
331
			$embed = array( t('Share This'), t('share'));
		}
332

redmatrix's avatar
redmatrix committed
333
334
335
336
		$dreport = '';

		$keep_reports = intval(get_config('system','expire_delivery_reports'));
		if($keep_reports === 0)
337
			$keep_reports = 10;
redmatrix's avatar
redmatrix committed
338

Zot's avatar
Zot committed
339
		if((! get_config('system','disable_dreport')) && strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC',"now - $keep_reports days")) > 0) {
redmatrix's avatar
redmatrix committed
340
			$dreport = t('Delivery Report');
Zot's avatar
Zot committed
341
342
			$dreport_link = gen_link_id($item['mid']);
		}
redmatrix's avatar
redmatrix committed
343

344
		if(strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC','now - 12 hours')) > 0)
345
			$is_new = true;
346

friendica's avatar
friendica committed
347
		localize_item($item);
Mario's avatar
Mario committed
348

349
		$body = prepare_body($item,true);
350
351
352

		// $viewthread (below) is only valid in list mode. If this is a channel page, build the thread viewing link
		// since we can't depend on llink or plink pointing to the right local location.
353

354
355
356
		$owner_address = substr($item['owner']['xchan_addr'],0,strpos($item['owner']['xchan_addr'],'@'));
		$viewthread = $item['llink'];
		if($conv->get_mode() === 'channel')
Zot's avatar
Zot committed
357
			$viewthread = z_root() . '/channel/' . $owner_address . '?f=&mid=' . urlencode(gen_link_id($item['mid']));
358

359
		$comment_count_txt = sprintf( tt('%d comment','%d comments',$total_children),$total_children );
Zot's avatar
Zot committed
360
		$list_unseen_txt = (($unseen_comments) ? sprintf( t('%d unseen'),$unseen_comments) : '');
361

362
		$children = $this->get_children();
363

364
365
		$has_tags = (($body['tags'] || $body['categories'] || $body['mentions'] || $body['attachments'] || $body['folders']) ? true : false);

366
367
368
369
                $dropdown_extras_arr = [ 'item' => $item , 'dropdown_extras' => '' ];
                call_hooks('dropdown_extras',$dropdown_extras_arr);
                $dropdown_extras = $dropdown_extras_arr['dropdown_extras'];

370
		$midb64 = gen_link_id($item['mid']);
Max Kostikov's avatar
Max Kostikov committed
371
		$mids = [ $midb64 ];
372
373
374
375
376
377
378
379
380
381
		$response_mids = [];
		foreach($response_verbs as $v) {
			if(isset($conv_responses[$v]['mids'][$item['mid']])) {
				$response_mids = array_merge($response_mids, $conv_responses[$v]['mids'][$item['mid']]);
			}
		}

		$mids = array_merge($mids, $response_mids);
		$json_mids = json_encode($mids);

Max Kostikov's avatar
Max Kostikov committed
382
383
384
385
386
		// Pinned item processing
		$allowed_type = (in_array($item['item_type'], get_config('system', 'pin_types', [ ITEM_TYPE_POST ])) ? true : false);
		$pinned_items = ($allowed_type ? get_pconfig($item['uid'], 'pinned', $item['item_type'], []) : []);
		$pinned = ((!empty($pinned_items) && in_array($midb64, $pinned_items)) ? true : false);

387
388
		$tmp_item = array(
			'template' => $this->get_template(),
Zot's avatar
Zot committed
389
			'mode' => $mode,
390
			'item_type' => intval($item['item_type']),
Mario's avatar
Mario committed
391
			//'type' => implode("",array_slice(explode("/",$item['verb']),-1)),
392
393
394
395
396
397
398
			'body' => $body['html'],
			'tags' => $body['tags'],
			'categories' => $body['categories'],
			'mentions' => $body['mentions'],
			'attachments' => $body['attachments'],
			'folders' => $body['folders'],
			'text' => strip_tags($body['html']),
399
			'id' => $this->get_id(),
Max Kostikov's avatar
Max Kostikov committed
400
			'mid' => $midb64,
401
			'mids' => $json_mids,
402
			'parent' => $item['parent'],
403
			'author_id' => (($item['author']['xchan_addr']) ? $item['author']['xchan_addr'] : $item['author']['xchan_url']),
404
405
			'isevent' => $isevent,
			'attend' => $attend,
friendica's avatar
friendica committed
406
407
			'consensus' => $consensus,
			'conlabels' => $conlabels,
friendica's avatar
friendica committed
408
			'canvote' => $canvote,
409
410
			'linktitle' => (($item['author']['xchan_addr']) ? $item['author']['xchan_addr'] : $item['author']['xchan_url']),
			'olinktitle' => (($item['owner']['xchan_addr']) ? $item['owner']['xchan_addr'] : $item['owner']['xchan_url']),
411
			'llink' => $item['llink'],
412
			'viewthread' => $viewthread,
413
			'to' => t('to'),
414
			'via' => t('via'),
415
416
417
			'wall' => t('Wall-to-Wall'),
			'vwall' => t('via Wall-To-Wall:'),
			'profile_url' => $profile_link,
418
419
			'thread_action_menu' => thread_action_menu($item,$conv->get_mode()),
			'thread_author_menu' => thread_author_menu($item,$conv->get_mode()),
redmatrix's avatar
redmatrix committed
420
			'dreport' => $dreport,
Zot's avatar
Zot committed
421
			'dreport_link' => $dreport_link,
422
			'name' => $profile_name,
423
424
425
			'thumb' => $profile_avatar,
			'osparkle' => $osparkle,
			'sparkle' => $sparkle,
426
			'title' => $item['title'],
427
			'title_tosource' => get_pconfig($conv->get_profile_owner(),'system','title_tosource'),
Mario's avatar
Mario committed
428
			//'ago' => relative_date($item['created']),
friendica's avatar
friendica committed
429
			'app' => $item['app'],
430
			'str_app' => sprintf( t('from %s'), $item['app']),
431
			'isotime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'c'),
432
			'localtime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'r'),
433
			'editedtime' => (($item['edited'] != $item['created']) ? sprintf( t('last edited: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['edited'], 'r')) : ''),
Zot's avatar
Zot committed
434
			'expiretime' => (($item['expires'] > NULL_DATE) ? sprintf( t('Expires: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['expires'], 'r')):''),
435
			'lock' => $lock,
436
			'locktype' => $locktype,
437
			'delayed' => $item['item_delayed'],
438
			'privacy_warning' => $privacy_warning,
friendica's avatar
friendica committed
439
440
			'verified' => $verified,
			'unverified' => $unverified,
friendica's avatar
friendica committed
441
			'forged' => $forged,
442
			'location' => $location,
443
			'divider' => get_pconfig($conv->get_profile_owner(),'system','item_divider'),
444
445
446
447
			'attend_label' => t('Attend'),
			'attend_title' => t('Attendance Options'),
			'vote_label' => t('Vote'),
			'vote_title' => t('Voting Options'),
448
449
			'is_comment' => $is_comment,
			'is_new' => $is_new,
450
451
			'owner_url' => $this->get_owner_url(),
			'owner_photo' => $this->get_owner_photo(),
friendica's avatar
friendica committed
452
			'owner_name' => $this->get_owner_name(),
453
			'photo' => $body['photo'],
Mario's avatar
Mario committed
454
			'event' => $body['event'],
455
			'has_tags' => $has_tags,
456
			'reactions' => $this->reactions,
friendica's avatar
friendica committed
457
// Item toolbar buttons
458
			'emojis'	=> (($this->is_toplevel() && $this->is_commentable() && $observer && feature_enabled($conv->get_profile_owner(),'emojis')) ? '1' : ''),
friendica's avatar
friendica committed
459
460
			'like'      => $like,
			'dislike'   => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike : ''),
461
			'reply_to'	=> (((! $this->is_toplevel()) && feature_enabled($conv->get_profile_owner(),'reply_to')) ? $reply_to : ''),
462
			'top_hint'	=> t("Go to previous comment"),
friendica's avatar
friendica committed
463
			'share'     => $share,
Zot's avatar
Zot committed
464
			'embed'     => $embed,
465
			'rawmid'	=> $item['mid'],
friendica's avatar
friendica committed
466
			'plink'     => get_plink($item),
Mario's avatar
Mario committed
467
			'edpost'    => $edpost, // ((feature_enabled($conv->get_profile_owner(),'edit_posts')) ? $edpost : ''),
468
			'star'      => ((feature_enabled($conv->get_profile_owner(),'star_posts') && ($item['item_type'] == ITEM_TYPE_POST)) ? $star : ''),
friendica's avatar
friendica committed
469
			'tagger'    => ((feature_enabled($conv->get_profile_owner(),'commtag')) ? $tagger : ''),
470
			'filer'     => ((feature_enabled($conv->get_profile_owner(),'filing') && ($item['item_type'] == ITEM_TYPE_POST)) ? $filer : ''),
Max Kostikov's avatar
Max Kostikov committed
471
			'pinned'    => ($pinned ? t('Pinned post') : ''),
472
			'pinnable'  => (($this->is_toplevel() && local_channel() && $item['owner_xchan'] == $observer['xchan_hash'] && $allowed_type && $item['item_private'] == 0 && $item['item_delayed'] == 0) ? '1' : ''),
Max Kostikov's avatar
Max Kostikov committed
473
			'pinme'     => ($pinned ? t('Unpin from the top') : t('Pin to the top')),
friendica's avatar
friendica committed
474
			'bookmark'  => (($conv->get_profile_owner() == local_channel() && local_channel() && $has_bookmarks) ? t('Save Bookmarks') : ''),
friendica's avatar
friendica committed
475
			'addtocal'  => (($has_event) ? t('Add to Calendar') : ''),
friendica's avatar
friendica committed
476
477
			'drop'      => $drop,
			'multidrop' => ((feature_enabled($conv->get_profile_owner(),'multi_delete')) ? $multidrop : ''),
478
			'dropdown_extras' => $dropdown_extras,
friendica's avatar
friendica committed
479
// end toolbar buttons
480
			'unseen_comments' => $unseen_comments,
481
482
			'comment_count' => $total_children,
			'comment_count_txt' => $comment_count_txt,
483
484
			'list_unseen_txt' => $list_unseen_txt,
			'markseen' => t('Mark all seen'),
485
			'responses' => $responses,
486
			'my_responses' => $my_responses,
Mario's avatar
Mario committed
487
488
			'like_count' => $like_count,
			'like_list' => $like_list,
489
			'like_list_part' => $like_list_part,
Mario's avatar
Mario committed
490
			'like_button_label' => $like_button_label,
491
492
			'like_modal_title' => t('Likes','noun'),
			'dislike_modal_title' => t('Dislikes','noun'),
Mario's avatar
Mario committed
493
494
			'dislike_count' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike_count : ''),
			'dislike_list' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike_list : ''),
495
			'dislike_list_part' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike_list_part : ''),
Mario's avatar
Mario committed
496
			'dislike_button_label' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike_button_label : ''),
497
			'modal_dismiss' => t('Close'),
friendica's avatar
friendica committed
498
499
			'showlike' => $showlike,
			'showdislike' => $showdislike,
500
			'comment' => ($item['item_delayed'] ? '' : $this->get_comment_box($indent)),
501
			'previewing' => ($conv->is_preview() ? true : false ),
502
			'preview_lbl' => t('This is an unsaved preview'),
503
			'wait' => t('Please wait'),
504
			'thread_level' => $thread_level,
505
			'settings' => $settings,
Mario's avatar
Mario committed
506
			'thr_parent' => (($item['parent_mid'] != $item['thr_parent']) ? gen_link_id($item['thr_parent']) : '')
507
508
509
510
511
512
513
514
515
		);

		$arr = array('item' => $item, 'output' => $tmp_item);
		call_hooks('display_item', $arr);

		$result = $arr['output'];

		$result['children'] = array();
		$nb_children = count($children);
friendica's avatar
friendica committed
516

517
518
519
520
		$visible_comments = get_config('system','expanded_comments');
		if($visible_comments === false)
			$visible_comments = 3;

521
522
//		needed for scroll to comment from notification but needs more work
//		as we do not want to open all comments unless there is actually an #item_xx anchor
523
524
//		and the url fragment is not sent to the server.
//		if(in_array(\App::$module,['display','update_display']))
525
//			$visible_comments = 99999;
526

friendica's avatar
friendica committed
527
		if(($this->get_display_mode() === 'normal') && ($nb_children > 0)) {
528
			foreach($children as $child) {
529
				$result['children'][] = $child->get_template_data($conv_responses, $thread_level + 1,$conv_flags);
530
531
			}
			// Collapse
532
			if(($nb_children > $visible_comments) || ($thread_level > 1)) {
533
				$result['children'][0]['comment_firstcollapsed'] = true;
534
				$result['children'][0]['num_comments'] = $comment_count_txt;
535
				$result['children'][0]['hide_text'] = sprintf( t('%s show all'), '<i class="fa fa-chevron-down"></i>');
536
537
538
539
				if($thread_level > 1) {
					$result['children'][$nb_children - 1]['comment_lastcollapsed'] = true;
				}
				else {
540
					$result['children'][$nb_children - ($visible_comments + 1)]['comment_lastcollapsed'] = true;
541
542
543
				}
			}
		}
544

545
		$result['private'] = $item['item_private'];
546
547
548
549
550
551
552
553
554
555
556
557
558
		$result['toplevel'] = ($this->is_toplevel() ? 'toplevel_item' : '');

		if($this->is_threaded()) {
			$result['flatten'] = false;
			$result['threaded'] = true;
		}
		else {
			$result['flatten'] = true;
			$result['threaded'] = false;
		}

		return $result;
	}
559

560
561
562
563
	public function get_id() {
		return $this->get_data_value('id');
	}

friendica's avatar
friendica committed
564
565
566
567
568
569
570
571
	public function get_display_mode() {
		return $this->display_mode;
	}

	public function set_display_mode($mode) {
		$this->display_mode = $mode;
	}

572
573
574
575
	public function is_threaded() {
		return $this->threaded;
	}

Zot's avatar
Zot committed
576
577
578
579
580
581
582
583
	public function set_reload($val) {
		$this->reload = $val;
	}

	public function get_reload() {
		return $this->reload;
	}

584
585
	public function set_commentable($val) {
		$this->commentable = $val;
586
587
		foreach($this->get_children() as $child)
			$child->set_commentable($val);
588
589
590
591
592
593
	}

	public function is_commentable() {
		return $this->commentable;
	}

594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
	/**
	 * Add a child item
	 */
	public function add_child($item) {
		$item_id = $item->get_id();
		if(!$item_id) {
			logger('[ERROR] Item::add_child : Item has no ID!!', LOGGER_DEBUG);
			return false;
		}
		if($this->get_child($item->get_id())) {
			logger('[WARN] Item::add_child : Item already exists ('. $item->get_id() .').', LOGGER_DEBUG);
			return false;
		}
		/*
		 * Only add what will be displayed
		 */
610

611
		if(activity_match($item->get_data_value('verb'),ACTIVITY_LIKE) || activity_match($item->get_data_value('verb'),ACTIVITY_DISLIKE)) {
612
613
			return false;
		}
614

615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
		$item->set_parent($this);
		$this->children[] = $item;
		return end($this->children);
	}

	/**
	 * Get a child by its ID
	 */
	public function get_child($id) {
		foreach($this->get_children() as $child) {
			if($child->get_id() == $id)
				return $child;
		}
		return null;
	}

	/**
632
	 * Get all our children
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
	 */
	public function get_children() {
		return $this->children;
	}

	/**
	 * Set our parent
	 */
	protected function set_parent($item) {
		$parent = $this->get_parent();
		if($parent) {
			$parent->remove_child($this);
		}
		$this->parent = $item;
		$this->set_conversation($item->get_conversation());
	}

	/**
	 * Remove our parent
	 */
	protected function remove_parent() {
		$this->parent = null;
		$this->conversation = null;
	}

	/**
	 * Remove a child
	 */
	public function remove_child($item) {
		$id = $item->get_id();
		foreach($this->get_children() as $key => $child) {
			if($child->get_id() == $id) {
				$child->remove_parent();
				unset($this->children[$key]);
				// Reindex the array, in order to make sure there won't be any trouble on loops using count()
				$this->children = array_values($this->children);
				return true;
			}
		}
		logger('[WARN] Item::remove_child : Item is not a child ('. $id .').', LOGGER_DEBUG);
		return false;
	}

	/**
	 * Get parent item
	 */
	protected function get_parent() {
		return $this->parent;
	}

	/**
	 * set conversation
	 */
	public function set_conversation($conv) {
		$previous_mode = ($this->conversation ? $this->conversation->get_mode() : '');
688

689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
		$this->conversation = $conv;

		// Set it on our children too
		foreach($this->get_children() as $child)
			$child->set_conversation($conv);
	}

	/**
	 * get conversation
	 */
	public function get_conversation() {
		return $this->conversation;
	}

	/**
	 * Get raw data
	 *
	 * We shouldn't need this
	 */
	public function get_data() {
		return $this->data;
	}

	/**
	 * Get a data value
	 *
	 * Returns:
	 *      _ value on success
	 *      _ false on failure
	 */
	public function get_data_value($name) {
		if(!isset($this->data[$name])) {
721
//			logger('[ERROR] Item::get_data_value : Item has no value name "'. $name .'".', LOGGER_DEBUG);
722
723
724
725
726
727
728
729
730
			return false;
		}

		return $this->data[$name];
	}

	/**
	 * Get template
	 */
731
	public function get_template() {
732
733
734
		return $this->template;
	}

735

736
	public function set_template($t) {
737
738
739
		$this->template = $t;
	}

740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
	/**
	 * Check if this is a toplevel post
	 */
	private function is_toplevel() {
		return $this->toplevel;
	}

	/**
	 * Count the total of our descendants
	 */
	private function count_descendants() {
		$children = $this->get_children();
		$total = count($children);
		if($total > 0) {
			foreach($children as $child) {
				$total += $child->count_descendants();
			}
		}
		return $total;
	}

761
762
763
764
765
766
767
768
769
	private function count_unseen_descendants() {
		$children = $this->get_children();
		$total = count($children);
		if($total > 0) {
			$total = 0;
			foreach($children as $child) {
				if((! visible_activity($child->data)) || array_key_exists('author_blocked',$child->data)) {
					continue;
				}
770
				if(intval($child->data['item_unseen']))
771
772
773
774
775
776
777
					$total ++;
			}
		}
		return $total;
	}


778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
	/**
	 * Get the template for the comment box
	 */
	private function get_comment_box_template() {
		return $this->comment_box_template;
	}

	/**
	 * Get the comment box
	 *
	 * Returns:
	 *      _ The comment box string (empty if no comment box)
	 *      _ false on failure
	 */
	private function get_comment_box($indent) {
793

794
795
796
		if(!$this->is_toplevel() && !get_config('system','thread_allow')) {
			return '';
		}
797

798
799
		$comment_box = '';
		$conv = $this->get_conversation();
800

801
802
803
//		logger('Commentable conv: ' . $conv->is_commentable());

		if(! $this->is_commentable())
friendica's avatar
friendica committed
804
			return;
805

806
807
808
809
		$template = get_markup_template($this->get_comment_box_template());

		$observer = $conv->get_observer();

810
		$arr = array('comment_buttons' => '','id' => $this->get_id());
redmatrix's avatar
redmatrix committed
811
812
		call_hooks('comment_buttons',$arr);
		$comment_buttons = $arr['comment_buttons'];
813

814
815
816
		$comment_box = replace_macros($template,array(
			'$return_path' => '',
			'$threaded' => $this->is_threaded(),
Zot's avatar
Zot committed
817
			'$jsreload' => $conv->reload,
818
819
820
			'$type' => (($conv->get_mode() === 'channel') ? 'wall-comment' : 'net-comment'),
			'$id' => $this->get_id(),
			'$parent' => $this->get_id(),
redmatrix's avatar
redmatrix committed
821
			'$comment_buttons' => $comment_buttons,
822
823
824
825
826
827
828
829
830
831
832
833
			'$profile_uid' =>  $conv->get_profile_owner(),
			'$mylink' => $observer['xchan_url'],
			'$mytitle' => t('This is you'),
			'$myphoto' => $observer['xchan_photo_s'],
			'$comment' => t('Comment'),
			'$submit' => t('Submit'),
			'$edbold' => t('Bold'),
			'$editalic' => t('Italic'),
			'$eduline' => t('Underline'),
			'$edquote' => t('Quote'),
			'$edcode' => t('Code'),
			'$edimg' => t('Image'),
834
			'$edatt' => t('Attach/Upload file'),
835
			'$edurl' => t('Insert Link'),
836
			'$edvideo' => t('Video'),
Mario's avatar
Mario committed
837
			'$preview' => t('Preview'), // ((feature_enabled($conv->get_profile_owner(),'preview')) ? t('Preview') : ''),
838
			'$indent' => $indent,
839
			'$can_upload' => (perm_is_allowed($conv->get_profile_owner(),get_observer_hash(),'write_storage') && $conv->is_uploadable()),
840
841
			'$feature_encrypt' => ((feature_enabled($conv->get_profile_owner(),'content_encrypt')) ? true : false),
			'$encrypt' => t('Encrypt text'),
842
			'$cipher' => $conv->get_cipher(),
843
844
			'$sourceapp' => \App::$sourcename,
			'$observer' => get_observer_hash(),
845
			'$anoncomments' => ((in_array($conv->get_mode(), ['channel', 'display', 'cards', 'articles']) && perm_is_allowed($conv->get_profile_owner(),'','post_comments')) ? true : false),
Mario's avatar
Mario committed
846
847
			'$anonname' => [ 'anonname', t('Your full name (required)') ],
			'$anonmail' => [ 'anonmail', t('Your email address (required)') ],
848
			'$anonurl'  => [ 'anonurl',  t('Your website URL (optional)') ]
849
		));
850
851
852
853
854
855
856
857
858
859
860
861
862
863

		return $comment_box;
	}

	private function get_redirect_url() {
		return $this->redirect_url;
	}

	/**
	 * Check if we are a wall to wall item and set the relevant properties
	 */
	protected function check_wall_to_wall() {
		$conv = $this->get_conversation();
		$this->wall_to_wall = false;
864
865
866
		$this->owner_url = '';
		$this->owner_photo = '';
		$this->owner_name = '';
867

friendica's avatar
friendica committed
868
		if($conv->get_mode() === 'channel')
869
			return;
870

871
		if($this->is_toplevel() && ($this->get_data_value('author_xchan') != $this->get_data_value('owner_xchan'))) {
872
			$this->owner_url = chanlink_hash($this->data['owner']['xchan_hash']);
873
874
875
			$this->owner_photo = $this->data['owner']['xchan_photo_m'];
			$this->owner_name = $this->data['owner']['xchan_name'];
			$this->wall_to_wall = true;
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
		}
	}

	private function is_wall_to_wall() {
		return $this->wall_to_wall;
	}

	private function get_owner_url() {
		return $this->owner_url;
	}

	private function get_owner_photo() {
		return $this->owner_photo;
	}

	private function get_owner_name() {
		return $this->owner_name;
	}

	private function is_visiting() {
		return $this->visiting;
	}
898

899
}