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