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