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