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