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