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