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