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