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