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