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