]> git.mxchange.org Git - friendica.git/blob - src/Module/Contact.php
Renamed System::redirect() to $a->redirect()
[friendica.git] / src / Module / Contact.php
1 <?php
2
3 namespace Friendica\Module;
4
5 use Friendica\App;
6 use Friendica\BaseModule;
7 use Friendica\Content\ContactSelector;
8 use Friendica\Content\Nav;
9 use Friendica\Content\Text\BBCode;
10 use Friendica\Content\Widget;
11 use Friendica\Core\Addon;
12 use Friendica\Core\L10n;
13 use Friendica\Core\Protocol;
14 use Friendica\Core\System;
15 use Friendica\Core\Worker;
16 use Friendica\Database\DBA;
17 use Friendica\Model;
18 use Friendica\Network\Probe;
19 use Friendica\Util\DateTimeFormat;
20 use Friendica\Util\Proxy as ProxyUtils;
21 use Friendica\Core\ACL;
22 use Friendica\Module\Login;
23
24 /**
25  *  Manages and show Contacts and their content
26  *
27  *  @brief manages contacts
28  */
29 class Contact extends BaseModule
30 {
31         public static function init()
32         {
33                 $a = self::getApp();
34
35                 if (!local_user()) {
36                         return;
37                 }
38
39                 $nets = defaults($_GET, 'nets', '');
40                 if ($nets == 'all') {
41                         $nets = '';
42                 }
43
44                 if (!x($a->page, 'aside')) {
45                         $a->page['aside'] = '';
46                 }
47
48                 $contact_id = null;
49                 $contact = null;
50                 if ($a->argc == 2 && intval($a->argv[1])
51                         || $a->argc == 3 && intval($a->argv[1]) && in_array($a->argv[2], ['posts', 'conversations'])
52                 ) {
53                         $contact_id = intval($a->argv[1]);
54                         $contact = DBA::selectFirst('contact', [], ['id' => $contact_id, 'uid' => local_user()]);
55
56                         if (!DBA::isResult($contact)) {
57                                 $contact = DBA::selectFirst('contact', [], ['id' => $contact_id, 'uid' => 0]);
58                         }
59
60                         // Don't display contacts that are about to be deleted
61                         if ($contact['network'] == Protocol::PHANTOM) {
62                                 $contact = false;
63                         }
64                 }
65
66                 if (DBA::isResult($contact)) {
67                         if ($contact['self']) {
68                                 if (($a->argc == 3) && intval($a->argv[1]) && in_array($a->argv[2], ['posts', 'conversations'])) {
69                                         $a->redirect('profile/' . $contact['nick']);
70                                 } else {
71                                         $a->redirect('profile/' . $contact['nick'] . '?tab=profile');
72                                 }
73                         }
74
75                         $a->data['contact'] = $contact;
76
77                         if (($contact['network'] != '') && ($contact['network'] != Protocol::DFRN)) {
78                                 $networkname = format_network_name($contact['network'], $contact['url']);
79                         } else {
80                                 $networkname = '';
81                         }
82
83                         /// @TODO Add nice spaces
84                         $vcard_widget = replace_macros(get_markup_template('vcard-widget.tpl'), [
85                                 '$name'         => htmlentities($contact['name']),
86                                 '$photo'        => $contact['photo'],
87                                 '$url'          => Model\Contact::MagicLink($contact['url']),
88                                 '$addr'         => defaults($contact, 'addr', ''),
89                                 '$network_name' => $networkname,
90                                 '$network'      => L10n::t('Network:'),
91                                 '$account_type' => Model\Contact::getAccountType($contact)
92                         ]);
93
94                         $findpeople_widget = '';
95                         $follow_widget = '';
96                         $networks_widget = '';
97                 } else {
98                         $vcard_widget = '';
99                         $networks_widget = Widget::networks('contact', $nets);
100                         if (isset($_GET['add'])) {
101                                 $follow_widget = Widget::follow($_GET['add']);
102                         } else {
103                                 $follow_widget = Widget::follow();
104                         }
105
106                         $findpeople_widget = Widget::findPeople();
107                 }
108
109                 if ($contact['uid'] != 0) {
110                         $groups_widget = Model\Group::sidebarWidget('contact', 'group', 'full', 'everyone', $contact_id);
111                 } else {
112                         $groups_widget = null;
113                 }
114
115                 $a->page['aside'] .= replace_macros(get_markup_template('contacts-widget-sidebar.tpl'), [
116                         '$vcard_widget'      => $vcard_widget,
117                         '$findpeople_widget' => $findpeople_widget,
118                         '$follow_widget'     => $follow_widget,
119                         '$groups_widget'     => $groups_widget,
120                         '$networks_widget'   => $networks_widget
121                 ]);
122
123                 $base = $a->getBaseURL();
124                 $tpl = get_markup_template('contacts-head.tpl');
125                 $a->page['htmlhead'] .= replace_macros($tpl, [
126                         '$baseurl' => System::baseUrl(true),
127                         '$base' => $base
128                 ]);
129         }
130
131         private static function batchActions(App $a)
132         {
133                 if (empty($_POST['contact_batch']) || !is_array($_POST['contact_batch'])) {
134                         return;
135                 }
136
137                 $contacts_id = $_POST['contact_batch'];
138
139                 $stmt = DBA::select('contact', ['id'], ['id' => $contacts_id, 'uid' => local_user(), 'self' => false]);
140                 $orig_records = DBA::toArray($stmt);
141
142                 $count_actions = 0;
143                 foreach ($orig_records as $orig_record) {
144                         $contact_id = $orig_record['id'];
145                         if (!empty($_POST['contacts_batch_update'])) {
146                                 self::updateContactFromPoll($contact_id);
147                                 $count_actions++;
148                         }
149                         if (!empty($_POST['contacts_batch_block'])) {
150                                 self::blockContact($contact_id);
151                                 $count_actions++;
152                         }
153                         if (!empty($_POST['contacts_batch_ignore'])) {
154                                 self::ignoreContact($contact_id);
155                                 $count_actions++;
156                         }
157                         if (!empty($_POST['contacts_batch_archive'])
158                                 && self::archiveContact($contact_id, $orig_record)
159                         ) {
160                                 $count_actions++;
161                         }
162                         if (!empty($_POST['contacts_batch_drop'])) {
163                                 self::dropContact($orig_record);
164                                 $count_actions++;
165                         }
166                 }
167                 if ($count_actions > 0) {
168                         info(L10n::tt('%d contact edited.', '%d contacts edited.', $count_actions));
169                 }
170
171                 $a->redirect('contact');
172         }
173
174         public static function post()
175         {
176                 $a = self::getApp();
177
178                 if (!local_user()) {
179                         return;
180                 }
181
182                 if ($a->argv[1] === 'batch') {
183                         self::batchActions($a);
184                         return;
185                 }
186
187                 $contact_id = intval($a->argv[1]);
188                 if (!$contact_id) {
189                         return;
190                 }
191
192                 if (!DBA::exists('contact', ['id' => $contact_id, 'uid' => local_user()])) {
193                         notice(L10n::t('Could not access contact record.') . EOL);
194                         $a->redirect('contact');
195                         return; // NOTREACHED
196                 }
197
198                 Addon::callHooks('contact_edit_post', $_POST);
199
200                 $profile_id = intval(defaults($_POST, 'profile-assign', 0));
201                 if ($profile_id) {
202                         if (!DBA::exists('profile', ['id' => $profile_id, 'uid' => local_user()])) {
203                                 notice(L10n::t('Could not locate selected profile.') . EOL);
204                                 return;
205                         }
206                 }
207
208                 $hidden = !empty($_POST['hidden']);
209
210                 $notify = !empty($_POST['notify']);
211
212                 $fetch_further_information = intval(defaults($_POST, 'fetch_further_information', 0));
213
214                 $ffi_keyword_blacklist = escape_tags(trim(defaults($_POST, 'ffi_keyword_blacklist', '')));
215
216                 $priority = intval(defaults($_POST, 'poll', 0));
217                 if ($priority > 5 || $priority < 0) {
218                         $priority = 0;
219                 }
220
221                 $info = escape_tags(trim($_POST['info']));
222
223                 $r = DBA::update('contact', [
224                         'profile-id' => $profile_id,
225                         'priority'   => $priority,
226                         'info'       => $info,
227                         'hidden'     => $hidden,
228                         'notify_new_posts' => $notify,
229                         'fetch_further_information' => $fetch_further_information,
230                         'ffi_keyword_blacklist'     => $ffi_keyword_blacklist],
231                         ['id' => $contact_id, 'uid' => local_user()]
232                 );
233
234                 if (DBA::isResult($r)) {
235                         info(L10n::t('Contact updated.') . EOL);
236                 } else {
237                         notice(L10n::t('Failed to update contact record.') . EOL);
238                 }
239
240                 $contact = DBA::selectFirst('contact', [], ['id' => $contact_id, 'uid' => local_user()]);
241                 if (DBA::isResult($contact)) {
242                         $a->data['contact'] = $contact;
243                 }
244
245                 return;
246         }
247
248         /* contact actions */
249
250         private static function updateContactFromPoll($contact_id)
251         {
252                 $contact = DBA::selectFirst('contact', ['uid', 'url', 'network'], ['id' => $contact_id, 'uid' => local_user()]);
253                 if (!DBA::isResult($contact)) {
254                         return;
255                 }
256
257                 $uid = $contact['uid'];
258
259                 if ($contact['network'] == Protocol::OSTATUS) {
260                         $result = Model\Contact::createFromProbe($uid, $contact['url'], false, $contact['network']);
261
262                         if ($result['success']) {
263                                 DBA::update('contact', ['subhub' => 1], ['id' => $contact_id]);
264                         }
265                 } else {
266                         // pull feed and consume it, which should subscribe to the hub.
267                         Worker::add(PRIORITY_HIGH, 'OnePoll', $contact_id, 'force');
268                 }
269         }
270
271         private static function updateContactFromProbe($contact_id)
272         {
273                 $contact = DBA::selectFirst('contact', ['uid', 'url', 'network'], ['id' => $contact_id, 'uid' => local_user()]);
274                 if (!DBA::isResult($contact)) {
275                         return;
276                 }
277
278                 $uid = $contact['uid'];
279
280                 $data = Probe::uri($contact['url'], '', 0, false);
281
282                 // 'Feed' or 'Unknown' is mostly a sign of communication problems
283                 if ((in_array($data['network'], [Protocol::FEED, Protocol::PHANTOM])) && ($data['network'] != $contact['network'])) {
284                         return;
285                 }
286
287                 $updatefields = ['name', 'nick', 'url', 'addr', 'batch', 'notify', 'poll', 'request', 'confirm', 'poco', 'network', 'alias'];
288                 $fields = [];
289
290                 if ($data['network'] == Protocol::OSTATUS) {
291                         $result = Model\Contact::createFromProbe($uid, $data['url'], false);
292
293                         if ($result['success']) {
294                                 $fields['subhub'] = true;
295                         }
296                 }
297
298                 foreach ($updatefields AS $field) {
299                         if (!empty($data[$field])) {
300                                 $fields[$field] = $data[$field];
301                         }
302                 }
303
304                 $fields['nurl'] = normalise_link($data['url']);
305
306                 if (!empty($data['priority'])) {
307                         $fields['priority'] = intval($data['priority']);
308                 }
309
310                 if (empty($fields)) {
311                         return;
312                 }
313
314                 $r = DBA::update('contact', $fields, ['id' => $contact_id, 'uid' => local_user()]);
315
316                 // Update the entry in the contact table
317                 Model\Contact::updateAvatar($data['photo'], local_user(), $contact_id, true);
318
319                 // Update the entry in the gcontact table
320                 Model\GContact::updateFromProbe($data['url']);
321         }
322
323         private static function blockContact($contact_id)
324         {
325                 $blocked = !Model\Contact::isBlockedByUser($contact_id, local_user());
326                 Model\Contact::setBlockedForUser($contact_id, local_user(), $blocked);
327         }
328
329         private static function ignoreContact($contact_id)
330         {
331                 $ignored = !Model\Contact::isIgnoredByUser($contact_id, local_user());
332                 Model\Contact::setIgnoredForUser($contact_id, local_user(), $ignored);
333         }
334
335         private static function archiveContact($contact_id, $orig_record)
336         {
337                 $archived = (($orig_record['archive']) ? 0 : 1);
338                 $r = DBA::update('contact', ['archive' => $archived], ['id' => $contact_id, 'uid' => local_user()]);
339
340                 return DBA::isResult($r);
341         }
342
343         private static function dropContact($orig_record)
344         {
345                 $owner = Model\User::getOwnerDataById(local_user());
346                 if (!DBA::isResult($owner)) {
347                         return;
348                 }
349
350                 Model\Contact::terminateFriendship($owner, $orig_record, true);
351                 Model\Contact::remove($orig_record['id']);
352         }
353
354         public static function content($update = 0)
355         {
356                 $a = self::getApp();
357                 $sort_type = 0;
358                 $o = '';
359                 Nav::setSelected('contact');
360
361                 if (!local_user()) {
362                         notice(L10n::t('Permission denied.') . EOL);
363                         return Login::form();
364                 }
365
366                 if ($a->argc == 3) {
367                         $contact_id = intval($a->argv[1]);
368                         if (!$contact_id) {
369                                 return;
370                         }
371
372                         $cmd = $a->argv[2];
373
374                         $orig_record = DBA::selectFirst('contact', [], ['id' => $contact_id, 'uid' => [0, local_user()], 'self' => false]);
375                         if (!DBA::isResult($orig_record)) {
376                                 notice(L10n::t('Could not access contact record.') . EOL);
377                                 $a->redirect('contact');
378                                 return; // NOTREACHED
379                         }
380
381                         if ($cmd === 'update' && ($orig_record['uid'] != 0)) {
382                                 self::updateContactFromPoll($contact_id);
383                                 $a->redirect('contact/' . $contact_id);
384                                 // NOTREACHED
385                         }
386
387                         if ($cmd === 'updateprofile' && ($orig_record['uid'] != 0)) {
388                                 self::updateContactFromProbe($contact_id);
389                                 $a->redirect('crepair/' . $contact_id);
390                                 // NOTREACHED
391                         }
392
393                         if ($cmd === 'block') {
394                                 self::blockContact($contact_id);
395
396                                 $blocked = Model\Contact::isBlockedByUser($contact_id, local_user());
397                                 info(($blocked ? L10n::t('Contact has been blocked') : L10n::t('Contact has been unblocked')) . EOL);
398
399                                 $a->redirect('contact/' . $contact_id);
400                                 return; // NOTREACHED
401                         }
402
403                         if ($cmd === 'ignore') {
404                                 self::ignoreContact($contact_id);
405
406                                 $ignored = Model\Contact::isIgnoredByUser($contact_id, local_user());
407                                 info(($ignored ? L10n::t('Contact has been ignored') : L10n::t('Contact has been unignored')) . EOL);
408
409                                 $a->redirect('contact/' . $contact_id);
410                                 return; // NOTREACHED
411                         }
412
413                         if ($cmd === 'archive' && ($orig_record['uid'] != 0)) {
414                                 $r = self::archiveContact($contact_id, $orig_record);
415                                 if ($r) {
416                                         $archived = (($orig_record['archive']) ? 0 : 1);
417                                         info((($archived) ? L10n::t('Contact has been archived') : L10n::t('Contact has been unarchived')) . EOL);
418                                 }
419
420                                 $a->redirect('contact/' . $contact_id);
421                                 return; // NOTREACHED
422                         }
423
424                         if ($cmd === 'drop' && ($orig_record['uid'] != 0)) {
425                                 // Check if we should do HTML-based delete confirmation
426                                 if (!empty($_REQUEST['confirm'])) {
427                                         // <form> can't take arguments in its 'action' parameter
428                                         // so add any arguments as hidden inputs
429                                         $query = explode_querystring($a->query_string);
430                                         $inputs = [];
431                                         foreach ($query['args'] as $arg) {
432                                                 if (strpos($arg, 'confirm=') === false) {
433                                                         $arg_parts = explode('=', $arg);
434                                                         $inputs[] = ['name' => $arg_parts[0], 'value' => $arg_parts[1]];
435                                                 }
436                                         }
437
438                                         $a->page['aside'] = '';
439
440                                         return replace_macros(get_markup_template('contact_drop_confirm.tpl'), [
441                                                 '$header' => L10n::t('Drop contact'),
442                                                 '$contact' => self::getContactTemplateVars($orig_record),
443                                                 '$method' => 'get',
444                                                 '$message' => L10n::t('Do you really want to delete this contact?'),
445                                                 '$extra_inputs' => $inputs,
446                                                 '$confirm' => L10n::t('Yes'),
447                                                 '$confirm_url' => $query['base'],
448                                                 '$confirm_name' => 'confirmed',
449                                                 '$cancel' => L10n::t('Cancel'),
450                                         ]);
451                                 }
452                                 // Now check how the user responded to the confirmation query
453                                 if (!empty($_REQUEST['canceled'])) {
454                                         $a->redirect('contact');
455                                 }
456
457                                 self::dropContact($orig_record);
458                                 info(L10n::t('Contact has been removed.') . EOL);
459
460                                 $a->redirect('contact');
461                                 return; // NOTREACHED
462                         }
463                         if ($cmd === 'posts') {
464                                 return self::getPostsHTML($a, $contact_id);
465                         }
466                         if ($cmd === 'conversations') {
467                                 return self::getConversationsHMTL($a, $contact_id, $update);
468                         }
469                 }
470
471                 $_SESSION['return_url'] = $a->query_string;
472
473                 if (!empty($a->data['contact']) && is_array($a->data['contact'])) {
474                         $contact_id = $a->data['contact']['id'];
475                         $contact = $a->data['contact'];
476
477                         $a->page['htmlhead'] .= replace_macros(get_markup_template('contact_head.tpl'), [
478                                 '$baseurl' => $a->getBaseURL(true),
479                         ]);
480
481                         $contact['blocked']  = Model\Contact::isBlockedByUser($contact['id'], local_user());
482                         $contact['readonly'] = Model\Contact::isIgnoredByUser($contact['id'], local_user());
483
484                         $dir_icon = '';
485                         $relation_text = '';
486                         switch ($contact['rel']) {
487                                 case Model\Contact::FRIEND:
488                                         $dir_icon = 'images/lrarrow.gif';
489                                         $relation_text = L10n::t('You are mutual friends with %s');
490                                         break;
491
492                                 case Model\Contact::FOLLOWER;
493                                         $dir_icon = 'images/larrow.gif';
494                                         $relation_text = L10n::t('You are sharing with %s');
495                                         break;
496
497                                 case Model\Contact::SHARING;
498                                         $dir_icon = 'images/rarrow.gif';
499                                         $relation_text = L10n::t('%s is sharing with you');
500                                         break;
501
502                                 default:
503                                         break;
504                         }
505
506                         if ($contact['uid'] == 0) {
507                                 $relation_text = '';
508                         }
509
510                         if (!in_array($contact['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::OSTATUS, Protocol::DIASPORA])) {
511                                 $relation_text = '';
512                         }
513
514                         $relation_text = sprintf($relation_text, htmlentities($contact['name']));
515
516                         $url = Model\Contact::magicLink($contact['url']);
517                         if (strpos($url, 'redir/') === 0) {
518                                 $sparkle = ' class="sparkle" ';
519                         } else {
520                                 $sparkle = '';
521                         }
522
523                         $insecure = L10n::t('Private communications are not available for this contact.');
524
525                         $last_update = (($contact['last-update'] <= NULL_DATE) ? L10n::t('Never') : DateTimeFormat::local($contact['last-update'], 'D, j M Y, g:i A'));
526
527                         if ($contact['last-update'] > NULL_DATE) {
528                                 $last_update .= ' ' . (($contact['last-update'] <= $contact['success_update']) ? L10n::t('(Update was successful)') : L10n::t('(Update was not successful)'));
529                         }
530                         $lblsuggest = (($contact['network'] === Protocol::DFRN) ? L10n::t('Suggest friends') : '');
531
532                         $poll_enabled = in_array($contact['network'], [Protocol::DFRN, Protocol::OSTATUS, Protocol::FEED, Protocol::MAIL]);
533
534                         $nettype = L10n::t('Network type: %s', ContactSelector::networkToName($contact['network'], $contact['url']));
535
536                         // tabs
537                         $tab_str = self::getTabsHTML($a, $contact, 3);
538
539                         $lost_contact = (($contact['archive'] && $contact['term-date'] > NULL_DATE && $contact['term-date'] < DateTimeFormat::utcNow()) ? L10n::t('Communications lost with this contact!') : '');
540
541                         $fetch_further_information = null;
542                         if ($contact['network'] == Protocol::FEED) {
543                                 $fetch_further_information = [
544                                         'fetch_further_information',
545                                         L10n::t('Fetch further information for feeds'),
546                                         $contact['fetch_further_information'],
547                                         L10n::t('Fetch information like preview pictures, title and teaser from the feed item. You can activate this if the feed doesn\'t contain much text. Keywords are taken from the meta header in the feed item and are posted as hash tags.'),
548                                         [
549                                                 '0' => L10n::t('Disabled'),
550                                                 '1' => L10n::t('Fetch information'),
551                                                 '3' => L10n::t('Fetch keywords'),
552                                                 '2' => L10n::t('Fetch information and keywords')
553                                         ]
554                                 ];
555                         }
556
557                         $poll_interval = null;
558                         if (in_array($contact['network'], [Protocol::FEED, Protocol::MAIL])) {
559                                 $poll_interval = ContactSelector::pollInterval($contact['priority'], !$poll_enabled);
560                         }
561
562                         $profile_select = null;
563                         if ($contact['network'] == Protocol::DFRN) {
564                                 $profile_select = ContactSelector::profileAssign($contact['profile-id'], $contact['network'] !== Protocol::DFRN);
565                         }
566
567                         /// @todo Only show the following link with DFRN when the remote version supports it
568                         $follow = '';
569                         $follow_text = '';
570                         if (in_array($contact['rel'], [Model\Contact::FRIEND, Model\Contact::SHARING])) {
571                                 if (in_array($contact['network'], Protocol::NATIVE_SUPPORT)) {
572                                         $follow = $a->getBaseURL(true) . '/unfollow?url=' . urlencode($contact['url']);
573                                         $follow_text = L10n::t('Disconnect/Unfollow');
574                                 }
575                         } else {
576                                 $follow = $a->getBaseURL(true) . '/follow?url=' . urlencode($contact['url']);
577                                 $follow_text = L10n::t('Connect/Follow');
578                         }
579
580                         // Load contactact related actions like hide, suggest, delete and others
581                         $contact_actions = self::getContactActions($contact);
582
583                         if ($contact['uid'] != 0) {
584                                 $lbl_vis1 = L10n::t('Profile Visibility');
585                                 $lbl_info1 = L10n::t('Contact Information / Notes');
586                                 $contact_settings_label = L10n::t('Contact Settings');
587                         } else {
588                                 $lbl_vis1 = null;
589                                 $lbl_info1 = null;
590                                 $contact_settings_label = null;
591                         }
592
593                         $tpl = get_markup_template('contact_edit.tpl');
594                         $o .= replace_macros($tpl, [
595                                 '$header'         => L10n::t('Contact'),
596                                 '$tab_str'        => $tab_str,
597                                 '$submit'         => L10n::t('Submit'),
598                                 '$lbl_vis1'       => $lbl_vis1,
599                                 '$lbl_vis2'       => L10n::t('Please choose the profile you would like to display to %s when viewing your profile securely.', $contact['name']),
600                                 '$lbl_info1'      => $lbl_info1,
601                                 '$lbl_info2'      => L10n::t('Their personal note'),
602                                 '$reason'         => trim(notags($contact['reason'])),
603                                 '$infedit'        => L10n::t('Edit contact notes'),
604                                 '$common_link'    => 'common/loc/' . local_user() . '/' . $contact['id'],
605                                 '$relation_text'  => $relation_text,
606                                 '$visit'          => L10n::t('Visit %s\'s profile [%s]', $contact['name'], $contact['url']),
607                                 '$blockunblock'   => L10n::t('Block/Unblock contact'),
608                                 '$ignorecont'     => L10n::t('Ignore contact'),
609                                 '$lblcrepair'     => L10n::t('Repair URL settings'),
610                                 '$lblrecent'      => L10n::t('View conversations'),
611                                 '$lblsuggest'     => $lblsuggest,
612                                 '$nettype'        => $nettype,
613                                 '$poll_interval'  => $poll_interval,
614                                 '$poll_enabled'   => $poll_enabled,
615                                 '$lastupdtext'    => L10n::t('Last update:'),
616                                 '$lost_contact'   => $lost_contact,
617                                 '$updpub'         => L10n::t('Update public posts'),
618                                 '$last_update'    => $last_update,
619                                 '$udnow'          => L10n::t('Update now'),
620                                 '$follow'         => $follow,
621                                 '$follow_text'    => $follow_text,
622                                 '$profile_select' => $profile_select,
623                                 '$contact_id'     => $contact['id'],
624                                 '$block_text'     => ($contact['blocked'] ? L10n::t('Unblock') : L10n::t('Block')),
625                                 '$ignore_text'    => ($contact['readonly'] ? L10n::t('Unignore') : L10n::t('Ignore')),
626                                 '$insecure'       => (in_array($contact['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::MAIL, Protocol::DIASPORA]) ? '' : $insecure),
627                                 '$info'           => $contact['info'],
628                                 '$cinfo'          => ['info', '', $contact['info'], ''],
629                                 '$blocked'        => ($contact['blocked'] ? L10n::t('Currently blocked') : ''),
630                                 '$ignored'        => ($contact['readonly'] ? L10n::t('Currently ignored') : ''),
631                                 '$archived'       => ($contact['archive'] ? L10n::t('Currently archived') : ''),
632                                 '$pending'        => ($contact['pending'] ? L10n::t('Awaiting connection acknowledge') : ''),
633                                 '$hidden'         => ['hidden', L10n::t('Hide this contact from others'), ($contact['hidden'] == 1), L10n::t('Replies/likes to your public posts <strong>may</strong> still be visible')],
634                                 '$notify'         => ['notify', L10n::t('Notification for new posts'), ($contact['notify_new_posts'] == 1), L10n::t('Send a notification of every new post of this contact')],
635                                 '$fetch_further_information' => $fetch_further_information,
636                                 '$ffi_keyword_blacklist' => $contact['ffi_keyword_blacklist'],
637                                 '$ffi_keyword_blacklist' => ['ffi_keyword_blacklist', L10n::t('Blacklisted keywords'), $contact['ffi_keyword_blacklist'], L10n::t('Comma separated list of keywords that should not be converted to hashtags, when "Fetch information and keywords" is selected')],
638                                 '$photo'          => $contact['photo'],
639                                 '$name'           => htmlentities($contact['name']),
640                                 '$dir_icon'       => $dir_icon,
641                                 '$sparkle'        => $sparkle,
642                                 '$url'            => $url,
643                                 '$profileurllabel'=> L10n::t('Profile URL'),
644                                 '$profileurl'     => $contact['url'],
645                                 '$account_type'   => Model\Contact::getAccountType($contact),
646                                 '$location'       => BBCode::convert($contact['location']),
647                                 '$location_label' => L10n::t('Location:'),
648                                 '$xmpp'           => BBCode::convert($contact['xmpp']),
649                                 '$xmpp_label'     => L10n::t('XMPP:'),
650                                 '$about'          => BBCode::convert($contact['about'], false),
651                                 '$about_label'    => L10n::t('About:'),
652                                 '$keywords'       => $contact['keywords'],
653                                 '$keywords_label' => L10n::t('Tags:'),
654                                 '$contact_action_button' => L10n::t('Actions'),
655                                 '$contact_actions'=> $contact_actions,
656                                 '$contact_status' => L10n::t('Status'),
657                                 '$contact_settings_label' => $contact_settings_label,
658                                 '$contact_profile_label' => L10n::t('Profile'),
659                         ]);
660
661                         $arr = ['contact' => $contact, 'output' => $o];
662
663                         Addon::callHooks('contact_edit', $arr);
664
665                         return $arr['output'];
666                 }
667
668                 $blocked = false;
669                 $hidden = false;
670                 $ignored = false;
671                 $archived = false;
672                 $all = false;
673
674                 if (($a->argc == 2) && ($a->argv[1] === 'all')) {
675                         $sql_extra = '';
676                         $all = true;
677                 } elseif (($a->argc == 2) && ($a->argv[1] === 'blocked')) {
678                         $sql_extra = " AND `blocked` = 1 ";
679                         $blocked = true;
680                 } elseif (($a->argc == 2) && ($a->argv[1] === 'hidden')) {
681                         $sql_extra = " AND `hidden` = 1 ";
682                         $hidden = true;
683                 } elseif (($a->argc == 2) && ($a->argv[1] === 'ignored')) {
684                         $sql_extra = " AND `readonly` = 1 ";
685                         $ignored = true;
686                 } elseif (($a->argc == 2) && ($a->argv[1] === 'archived')) {
687                         $sql_extra = " AND `archive` = 1 ";
688                         $archived = true;
689                 } else {
690                         $sql_extra = " AND `blocked` = 0 ";
691                 }
692
693                 $sql_extra .= sprintf(" AND `network` != '%s' ", Protocol::PHANTOM);
694
695                 $search = notags(trim(defaults($_GET, 'search', '')));
696                 $nets   = notags(trim(defaults($_GET, 'nets'  , '')));
697
698                 $tabs = [
699                         [
700                                 'label' => L10n::t('Suggestions'),
701                                 'url'   => 'suggest',
702                                 'sel'   => '',
703                                 'title' => L10n::t('Suggest potential friends'),
704                                 'id'    => 'suggestions-tab',
705                                 'accesskey' => 'g',
706                         ],
707                         [
708                                 'label' => L10n::t('All Contacts'),
709                                 'url'   => 'contact/all',
710                                 'sel'   => ($all) ? 'active' : '',
711                                 'title' => L10n::t('Show all contacts'),
712                                 'id'    => 'showall-tab',
713                                 'accesskey' => 'l',
714                         ],
715                         [
716                                 'label' => L10n::t('Unblocked'),
717                                 'url'   => 'contact',
718                                 'sel'   => ((!$all) && (!$blocked) && (!$hidden) && (!$search) && (!$nets) && (!$ignored) && (!$archived)) ? 'active' : '',
719                                 'title' => L10n::t('Only show unblocked contacts'),
720                                 'id'    => 'showunblocked-tab',
721                                 'accesskey' => 'o',
722                         ],
723                         [
724                                 'label' => L10n::t('Blocked'),
725                                 'url'   => 'contact/blocked',
726                                 'sel'   => ($blocked) ? 'active' : '',
727                                 'title' => L10n::t('Only show blocked contacts'),
728                                 'id'    => 'showblocked-tab',
729                                 'accesskey' => 'b',
730                         ],
731                         [
732                                 'label' => L10n::t('Ignored'),
733                                 'url'   => 'contact/ignored',
734                                 'sel'   => ($ignored) ? 'active' : '',
735                                 'title' => L10n::t('Only show ignored contacts'),
736                                 'id'    => 'showignored-tab',
737                                 'accesskey' => 'i',
738                         ],
739                         [
740                                 'label' => L10n::t('Archived'),
741                                 'url'   => 'contact/archived',
742                                 'sel'   => ($archived) ? 'active' : '',
743                                 'title' => L10n::t('Only show archived contacts'),
744                                 'id'    => 'showarchived-tab',
745                                 'accesskey' => 'y',
746                         ],
747                         [
748                                 'label' => L10n::t('Hidden'),
749                                 'url'   => 'contact/hidden',
750                                 'sel'   => ($hidden) ? 'active' : '',
751                                 'title' => L10n::t('Only show hidden contacts'),
752                                 'id'    => 'showhidden-tab',
753                                 'accesskey' => 'h',
754                         ],
755                 ];
756
757                 $tab_tpl = get_markup_template('common_tabs.tpl');
758                 $t = replace_macros($tab_tpl, ['$tabs' => $tabs]);
759
760                 $total = 0;
761                 $searching = false;
762                 $search_hdr = null;
763                 if ($search) {
764                         $searching = true;
765                         $search_hdr = $search;
766                         $search_txt = DBA::escape(protect_sprintf(preg_quote($search)));
767                         $sql_extra .= " AND (name REGEXP '$search_txt' OR url REGEXP '$search_txt'  OR nick REGEXP '$search_txt') ";
768                 }
769
770                 if ($nets) {
771                         $sql_extra .= sprintf(" AND network = '%s' ", DBA::escape($nets));
772                 }
773
774                 $sql_extra2 = ((($sort_type > 0) && ($sort_type <= Model\Contact::FRIEND)) ? sprintf(" AND `rel` = %d ", intval($sort_type)) : '');
775
776                 $r = q("SELECT COUNT(*) AS `total` FROM `contact`
777                         WHERE `uid` = %d AND `self` = 0 AND `pending` = 0 $sql_extra $sql_extra2 ",
778                         intval($_SESSION['uid'])
779                 );
780                 if (DBA::isResult($r)) {
781                         $a->setPagerTotal($r[0]['total']);
782                         $total = $r[0]['total'];
783                 }
784
785                 $sql_extra3 = Widget::unavailableNetworks();
786
787                 $contacts = [];
788
789                 $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` = 0 AND `pending` = 0 $sql_extra $sql_extra2 $sql_extra3 ORDER BY `name` ASC LIMIT %d , %d ",
790                         intval($_SESSION['uid']),
791                         intval($a->pager['start']),
792                         intval($a->pager['itemspage'])
793                 );
794                 if (DBA::isResult($r)) {
795                         foreach ($r as $rr) {
796                                 $rr['blocked'] = Model\Contact::isBlockedByUser($rr['id'], local_user());
797                                 $rr['readonly'] = Model\Contact::isIgnoredByUser($rr['id'], local_user());
798                                 $contacts[] = self::getContactTemplateVars($rr);
799                         }
800                 }
801
802                 $tpl = get_markup_template('contacts-template.tpl');
803                 $o .= replace_macros($tpl, [
804                         '$baseurl'    => System::baseUrl(),
805                         '$header'     => L10n::t('Contacts') . (($nets) ? ' - ' . ContactSelector::networkToName($nets) : ''),
806                         '$tabs'       => $t,
807                         '$total'      => $total,
808                         '$search'     => $search_hdr,
809                         '$desc'       => L10n::t('Search your contacts'),
810                         '$finding'    => $searching ? L10n::t('Results for: %s', $search) : '',
811                         '$submit'     => L10n::t('Find'),
812                         '$cmd'        => $a->cmd,
813                         '$contacts'   => $contacts,
814                         '$contact_drop_confirm' => L10n::t('Do you really want to delete this contact?'),
815                         'multiselect' => 1,
816                         '$batch_actions' => [
817                                 'contacts_batch_update'  => L10n::t('Update'),
818                                 'contacts_batch_block'   => L10n::t('Block') . '/' . L10n::t('Unblock'),
819                                 'contacts_batch_ignore'  => L10n::t('Ignore') . '/' . L10n::t('Unignore'),
820                                 'contacts_batch_archive' => L10n::t('Archive') . '/' . L10n::t('Unarchive'),
821                                 'contacts_batch_drop'    => L10n::t('Delete'),
822                         ],
823                         '$h_batch_actions' => L10n::t('Batch Actions'),
824                         '$paginate'   => paginate($a),
825                 ]);
826
827                 return $o;
828         }
829
830         /**
831          * @brief List of pages for the Contact TabBar
832          *
833          * Available Pages are 'Status', 'Profile', 'Contacts' and 'Common Friends'
834          *
835          * @param App $a
836          * @param array $contact The contact array
837          * @param int $active_tab 1 if tab should be marked as active
838          *
839          * @return string | HTML string of the contact page tabs buttons.
840
841          */
842         public static function getTabsHTML($a, $contact, $active_tab)
843         {
844                 // tabs
845                 $tabs = [
846                         [
847                                 'label' => L10n::t('Status'),
848                                 'url'   => "contact/" . $contact['id'] . "/conversations",
849                                 'sel'   => (($active_tab == 1) ? 'active' : ''),
850                                 'title' => L10n::t('Conversations started by this contact'),
851                                 'id'    => 'status-tab',
852                                 'accesskey' => 'm',
853                         ],
854                         [
855                                 'label' => L10n::t('Posts and Comments'),
856                                 'url'   => "contact/" . $contact['id'] . "/posts",
857                                 'sel'   => (($active_tab == 2) ? 'active' : ''),
858                                 'title' => L10n::t('Status Messages and Posts'),
859                                 'id'    => 'posts-tab',
860                                 'accesskey' => 'p',
861                         ],
862                         [
863                                 'label' => L10n::t('Profile'),
864                                 'url'   => "contact/" . $contact['id'],
865                                 'sel'   => (($active_tab == 3) ? 'active' : ''),
866                                 'title' => L10n::t('Profile Details'),
867                                 'id'    => 'profile-tab',
868                                 'accesskey' => 'o',
869                         ]
870                 ];
871
872                 // Show this tab only if there is visible friend list
873                 $x = Model\GContact::countAllFriends(local_user(), $contact['id']);
874                 if ($x) {
875                         $tabs[] = ['label' => L10n::t('Contacts'),
876                                 'url'   => "allfriends/" . $contact['id'],
877                                 'sel'   => (($active_tab == 4) ? 'active' : ''),
878                                 'title' => L10n::t('View all contacts'),
879                                 'id'    => 'allfriends-tab',
880                                 'accesskey' => 't'];
881                 }
882
883                 // Show this tab only if there is visible common friend list
884                 $common = Model\GContact::countCommonFriends(local_user(), $contact['id']);
885                 if ($common) {
886                         $tabs[] = ['label' => L10n::t('Common Friends'),
887                                 'url'   => "common/loc/" . local_user() . "/" . $contact['id'],
888                                 'sel'   => (($active_tab == 5) ? 'active' : ''),
889                                 'title' => L10n::t('View all common friends'),
890                                 'id'    => 'common-loc-tab',
891                                 'accesskey' => 'd'
892                         ];
893                 }
894
895                 if (!empty($contact['uid'])) {
896                         $tabs[] = ['label' => L10n::t('Advanced'),
897                                 'url'   => 'crepair/' . $contact['id'],
898                                 'sel'   => (($active_tab == 6) ? 'active' : ''),
899                                 'title' => L10n::t('Advanced Contact Settings'),
900                                 'id'    => 'advanced-tab',
901                                 'accesskey' => 'r'
902                         ];
903                 }
904
905                 $tab_tpl = get_markup_template('common_tabs.tpl');
906                 $tab_str = replace_macros($tab_tpl, ['$tabs' => $tabs]);
907
908                 return $tab_str;
909         }
910
911         private static function getConversationsHMTL($a, $contact_id, $update)
912         {
913                 $o = '';
914
915                 if (!$update) {
916                         // We need the editor here to be able to reshare an item.
917                         if (local_user()) {
918                                 $x = [
919                                         'is_owner' => true,
920                                         'allow_location' => $a->user['allow_location'],
921                                         'default_location' => $a->user['default-location'],
922                                         'nickname' => $a->user['nickname'],
923                                         'lockstate' => (is_array($a->user) && (strlen($a->user['allow_cid']) || strlen($a->user['allow_gid']) || strlen($a->user['deny_cid']) || strlen($a->user['deny_gid'])) ? 'lock' : 'unlock'),
924                                         'acl' => ACL::getFullSelectorHTML($a->user, true),
925                                         'bang' => '',
926                                         'visitor' => 'block',
927                                         'profile_uid' => local_user(),
928                                 ];
929                                 $o = status_editor($a, $x, 0, true);
930                         }
931                 }
932
933                 $contact = DBA::selectFirst('contact', ['uid', 'url', 'id'], ['id' => $contact_id]);
934
935                 if (!$update) {
936                         $o .= self::getTabsHTML($a, $contact, 1);
937                 }
938
939                 if (DBA::isResult($contact)) {
940                         $a->page['aside'] = '';
941
942                         $profiledata = Model\Contact::getDetailsByURL($contact['url']);
943
944                         if (local_user() && in_array($profiledata['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::DIASPORA, Protocol::OSTATUS])) {
945                                 $profiledata['remoteconnect'] = System::baseUrl() . '/follow?url=' . urlencode($profiledata['url']);
946                         }
947
948                         Model\Profile::load($a, '', 0, $profiledata, true);
949                         $o .= Model\Contact::getPostsFromUrl($contact['url'], true, $update);
950                 }
951
952                 return $o;
953         }
954
955         private static function getPostsHTML($a, $contact_id)
956         {
957                 $contact = DBA::selectFirst('contact', ['uid', 'url', 'id'], ['id' => $contact_id]);
958
959                 $o = self::getTabsHTML($a, $contact, 2);
960
961                 if (DBA::isResult($contact)) {
962                         $a->page['aside'] = '';
963
964                         $profiledata = Model\Contact::getDetailsByURL($contact['url']);
965
966                         if (local_user() && in_array($profiledata['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::DIASPORA, Protocol::OSTATUS])) {
967                                 $profiledata['remoteconnect'] = System::baseUrl() . '/follow?url=' . urlencode($profiledata['url']);
968                         }
969
970                         Model\Profile::load($a, '', 0, $profiledata, true);
971                         $o .= Model\Contact::getPostsFromUrl($contact['url']);
972                 }
973
974                 return $o;
975         }
976
977         public static function getContactTemplateVars(array $rr)
978         {
979                 $dir_icon = '';
980                 $alt_text = '';
981
982                 switch ($rr['rel']) {
983                         case Model\Contact::FRIEND:
984                                 $dir_icon = 'images/lrarrow.gif';
985                                 $alt_text = L10n::t('Mutual Friendship');
986                                 break;
987
988                         case Model\Contact::FOLLOWER;
989                                 $dir_icon = 'images/larrow.gif';
990                                 $alt_text = L10n::t('is a fan of yours');
991                                 break;
992
993                         case Model\Contact::SHARING;
994                                 $dir_icon = 'images/rarrow.gif';
995                                 $alt_text = L10n::t('you are a fan of');
996                                 break;
997
998                         default:
999                                 break;
1000                 }
1001
1002                 $url = Model\Contact::magicLink($rr['url']);
1003
1004                 if (strpos($url, 'redir/') === 0) {
1005                         $sparkle = ' class="sparkle" ';
1006                 } else {
1007                         $sparkle = '';
1008                 }
1009
1010                 if ($rr['self']) {
1011                         $dir_icon = 'images/larrow.gif';
1012                         $alt_text = L10n::t('This is you');
1013                         $url = $rr['url'];
1014                         $sparkle = '';
1015                 }
1016
1017                 return [
1018                         'img_hover' => L10n::t('Visit %s\'s profile [%s]', $rr['name'], $rr['url']),
1019                         'edit_hover'=> L10n::t('Edit contact'),
1020                         'photo_menu'=> Model\Contact::photoMenu($rr),
1021                         'id'        => $rr['id'],
1022                         'alt_text'  => $alt_text,
1023                         'dir_icon'  => $dir_icon,
1024                         'thumb'     => ProxyUtils::proxifyUrl($rr['thumb'], false, ProxyUtils::SIZE_THUMB),
1025                         'name'      => htmlentities($rr['name']),
1026                         'username'  => htmlentities($rr['name']),
1027                         'account_type' => Model\Contact::getAccountType($rr),
1028                         'sparkle'   => $sparkle,
1029                         'itemurl'   => defaults($rr, 'addr', $rr['url']),
1030                         'url'       => $url,
1031                         'network'   => ContactSelector::networkToName($rr['network'], $rr['url']),
1032                         'nick'      => htmlentities($rr['nick']),
1033                 ];
1034         }
1035
1036         /**
1037          * @brief Gives a array with actions which can performed to a given contact
1038          *
1039          * This includes actions like e.g. 'block', 'hide', 'archive', 'delete' and others
1040          *
1041          * @param array $contact Data about the Contact
1042          * @return array with contact related actions
1043          */
1044         private static function getContactActions($contact)
1045         {
1046                 $poll_enabled = in_array($contact['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::OSTATUS, Protocol::FEED, Protocol::MAIL]);
1047                 $contact_actions = [];
1048
1049                 // Provide friend suggestion only for Friendica contacts
1050                 if ($contact['network'] === Protocol::DFRN) {
1051                         $contact_actions['suggest'] = [
1052                                 'label' => L10n::t('Suggest friends'),
1053                                 'url'   => 'fsuggest/' . $contact['id'],
1054                                 'title' => '',
1055                                 'sel'   => '',
1056                                 'id'    => 'suggest',
1057                         ];
1058                 }
1059
1060                 if ($poll_enabled) {
1061                         $contact_actions['update'] = [
1062                                 'label' => L10n::t('Update now'),
1063                                 'url'   => 'contact/' . $contact['id'] . '/update',
1064                                 'title' => '',
1065                                 'sel'   => '',
1066                                 'id'    => 'update',
1067                         ];
1068                 }
1069
1070                 $contact_actions['block'] = [
1071                         'label' => (intval($contact['blocked']) ? L10n::t('Unblock') : L10n::t('Block')),
1072                         'url'   => 'contact/' . $contact['id'] . '/block',
1073                         'title' => L10n::t('Toggle Blocked status'),
1074                         'sel'   => (intval($contact['blocked']) ? 'active' : ''),
1075                         'id'    => 'toggle-block',
1076                 ];
1077
1078                 $contact_actions['ignore'] = [
1079                         'label' => (intval($contact['readonly']) ? L10n::t('Unignore') : L10n::t('Ignore')),
1080                         'url'   => 'contact/' . $contact['id'] . '/ignore',
1081                         'title' => L10n::t('Toggle Ignored status'),
1082                         'sel'   => (intval($contact['readonly']) ? 'active' : ''),
1083                         'id'    => 'toggle-ignore',
1084                 ];
1085
1086                 if ($contact['uid'] != 0) {
1087                         $contact_actions['archive'] = [
1088                                 'label' => (intval($contact['archive']) ? L10n::t('Unarchive') : L10n::t('Archive')),
1089                                 'url'   => 'contact/' . $contact['id'] . '/archive',
1090                                 'title' => L10n::t('Toggle Archive status'),
1091                                 'sel'   => (intval($contact['archive']) ? 'active' : ''),
1092                                 'id'    => 'toggle-archive',
1093                         ];
1094
1095                         $contact_actions['delete'] = [
1096                                 'label' => L10n::t('Delete'),
1097                                 'url'   => 'contact/' . $contact['id'] . '/drop',
1098                                 'title' => L10n::t('Delete contact'),
1099                                 'sel'   => '',
1100                                 'id'    => 'delete',
1101                         ];
1102                 }
1103
1104                 return $contact_actions;
1105         }
1106 }