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