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