]> git.mxchange.org Git - friendica.git/blob - mod/contacts.php
admin users, don't set account-type if page flag isn't PAGE_NORMAL
[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' => ($a->data['contact']['network'] == NETWORK_DFRN) ? "redir/" . $a->data['contact']['id'] : $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                 if (($contact['network'] === NETWORK_DFRN) && ($contact['rel'])) {
540                         $url = "redir/{$contact['id']}";
541                         $sparkle = ' class="sparkle" ';
542                 } else {
543                         $url = $contact['url'];
544                         $sparkle = '';
545                 }
546
547                 $insecure = L10n::t('Private communications are not available for this contact.');
548
549                 $last_update = (($contact['last-update'] <= NULL_DATE) ? L10n::t('Never') : DateTimeFormat::local($contact['last-update'], 'D, j M Y, g:i A'));
550
551                 if ($contact['last-update'] > NULL_DATE) {
552                         $last_update .= ' ' . (($contact['last-update'] <= $contact['success_update']) ? L10n::t("\x28Update was successful\x29") : L10n::t("\x28Update was not successful\x29"));
553                 }
554                 $lblsuggest = (($contact['network'] === NETWORK_DFRN) ? L10n::t('Suggest friends') : '');
555
556                 $poll_enabled = in_array($contact['network'], [NETWORK_DFRN, NETWORK_OSTATUS, NETWORK_FEED, NETWORK_MAIL]);
557
558                 $nettype = L10n::t('Network type: %s', ContactSelector::networkToName($contact['network'], $contact["url"]));
559
560                 // tabs
561                 $tab_str = contacts_tab($a, $contact_id, 2);
562
563                 $lost_contact = (($contact['archive'] && $contact['term-date'] > NULL_DATE && $contact['term-date'] < DateTimeFormat::utcNow()) ? L10n::t('Communications lost with this contact!') : '');
564
565                 $fetch_further_information = null;
566                 if ($contact['network'] == NETWORK_FEED) {
567                         $fetch_further_information = [
568                                 'fetch_further_information',
569                                 L10n::t('Fetch further information for feeds'),
570                                 $contact['fetch_further_information'],
571                                 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."),
572                                 ['0' => L10n::t('Disabled'),
573                                         '1' => L10n::t('Fetch information'),
574                                         '3' => L10n::t('Fetch keywords'),
575                                         '2' => L10n::t('Fetch information and keywords')
576                                 ]
577                         ];
578                 }
579
580                 $poll_interval = null;
581                 if (in_array($contact['network'], [NETWORK_FEED, NETWORK_MAIL])) {
582                         $poll_interval = ContactSelector::pollInterval($contact['priority'], (!$poll_enabled));
583                 }
584
585                 $profile_select = null;
586                 if ($contact['network'] == NETWORK_DFRN) {
587                         $profile_select = ContactSelector::profileAssign($contact['profile-id'], (($contact['network'] !== NETWORK_DFRN) ? true : false));
588                 }
589
590                 /// @todo Only show the following link with DFRN when the remote version supports it
591                 $follow = '';
592                 $follow_text = '';
593                 if (in_array($contact['network'], [NETWORK_DIASPORA, NETWORK_OSTATUS, NETWORK_DFRN])) {
594                         if ($contact['rel'] == CONTACT_IS_FOLLOWER) {
595                                 $follow = System::baseUrl(true) . "/follow?url=" . urlencode($contact["url"]);
596                                 $follow_text = L10n::t("Connect/Follow");
597                         } elseif ($contact['rel'] == CONTACT_IS_FRIEND) {
598                                 $follow = System::baseUrl(true) . "/unfollow?url=" . urlencode($contact["url"]);
599                                 $follow_text = L10n::t("Disconnect/Unfollow");
600                         }
601                 }
602
603                 // Load contactact related actions like hide, suggest, delete and others
604                 $contact_actions = contact_actions($contact);
605
606                 $tpl = get_markup_template("contact_edit.tpl");
607                 $o .= replace_macros($tpl, [
608                         '$header' => L10n::t("Contact"),
609                         '$tab_str' => $tab_str,
610                         '$submit' => L10n::t('Submit'),
611                         '$lbl_vis1' => L10n::t('Profile Visibility'),
612                         '$lbl_vis2' => L10n::t('Please choose the profile you would like to display to %s when viewing your profile securely.', $contact['name']),
613                         '$lbl_info1' => L10n::t('Contact Information / Notes'),
614                         '$lbl_info2' => L10n::t('Their personal note'),
615                         '$reason' => trim(notags($contact['reason'])),
616                         '$infedit' => L10n::t('Edit contact notes'),
617                         '$common_link' => 'common/loc/' . local_user() . '/' . $contact['id'],
618                         '$relation_text' => $relation_text,
619                         '$visit' => L10n::t('Visit %s\'s profile [%s]', $contact['name'], $contact['url']),
620                         '$blockunblock' => L10n::t('Block/Unblock contact'),
621                         '$ignorecont' => L10n::t('Ignore contact'),
622                         '$lblcrepair' => L10n::t("Repair URL settings"),
623                         '$lblrecent' => L10n::t('View conversations'),
624                         '$lblsuggest' => $lblsuggest,
625                         '$nettype' => $nettype,
626                         '$poll_interval' => $poll_interval,
627                         '$poll_enabled' => $poll_enabled,
628                         '$lastupdtext' => L10n::t('Last update:'),
629                         '$lost_contact' => $lost_contact,
630                         '$updpub' => L10n::t('Update public posts'),
631                         '$last_update' => $last_update,
632                         '$udnow' => L10n::t('Update now'),
633                         '$follow' => $follow,
634                         '$follow_text' => $follow_text,
635                         '$profile_select' => $profile_select,
636                         '$contact_id' => $contact['id'],
637                         '$block_text' => (($contact['blocked']) ? L10n::t('Unblock') : L10n::t('Block') ),
638                         '$ignore_text' => (($contact['readonly']) ? L10n::t('Unignore') : L10n::t('Ignore') ),
639                         '$insecure' => (($contact['network'] !== NETWORK_DFRN && $contact['network'] !== NETWORK_MAIL && $contact['network'] !== NETWORK_FACEBOOK && $contact['network'] !== NETWORK_DIASPORA) ? $insecure : ''),
640                         '$info' => $contact['info'],
641                         '$cinfo' => ['info', '', $contact['info'], ''],
642                         '$blocked' => (($contact['blocked']) ? L10n::t('Currently blocked') : ''),
643                         '$ignored' => (($contact['readonly']) ? L10n::t('Currently ignored') : ''),
644                         '$archived' => (($contact['archive']) ? L10n::t('Currently archived') : ''),
645                         '$pending' => (($contact['pending']) ? L10n::t('Awaiting connection acknowledge') : ''),
646                         '$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')],
647                         '$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')],
648                         '$fetch_further_information' => $fetch_further_information,
649                         '$ffi_keyword_blacklist' => $contact['ffi_keyword_blacklist'],
650                         '$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')],
651                         '$photo' => $contact['photo'],
652                         '$name' => htmlentities($contact['name']),
653                         '$dir_icon' => $dir_icon,
654                         '$sparkle' => $sparkle,
655                         '$url' => $url,
656                         '$profileurllabel' => L10n::t('Profile URL'),
657                         '$profileurl' => $contact['url'],
658                         '$account_type' => Contact::getAccountType($contact),
659                         '$location' => BBCode::convert($contact["location"]),
660                         '$location_label' => L10n::t("Location:"),
661                         '$xmpp' => BBCode::convert($contact["xmpp"]),
662                         '$xmpp_label' => L10n::t("XMPP:"),
663                         '$about' => BBCode::convert($contact["about"], false),
664                         '$about_label' => L10n::t("About:"),
665                         '$keywords' => $contact["keywords"],
666                         '$keywords_label' => L10n::t("Tags:"),
667                         '$contact_action_button' => L10n::t("Actions"),
668                         '$contact_actions' => $contact_actions,
669                         '$contact_status' => L10n::t("Status"),
670                         '$contact_settings_label' => L10n::t('Contact Settings'),
671                         '$contact_profile_label' => L10n::t("Profile"),
672                 ]);
673
674                 $arr = ['contact' => $contact, 'output' => $o];
675
676                 Addon::callHooks('contact_edit', $arr);
677
678                 return $arr['output'];
679         }
680
681         $blocked = false;
682         $hidden = false;
683         $ignored = false;
684         $archived = false;
685         $all = false;
686
687         if (($a->argc == 2) && ($a->argv[1] === 'all')) {
688                 $sql_extra = '';
689                 $all = true;
690         } elseif (($a->argc == 2) && ($a->argv[1] === 'blocked')) {
691                 $sql_extra = " AND `blocked` = 1 ";
692                 $blocked = true;
693         } elseif (($a->argc == 2) && ($a->argv[1] === 'hidden')) {
694                 $sql_extra = " AND `hidden` = 1 ";
695                 $hidden = true;
696         } elseif (($a->argc == 2) && ($a->argv[1] === 'ignored')) {
697                 $sql_extra = " AND `readonly` = 1 ";
698                 $ignored = true;
699         } elseif (($a->argc == 2) && ($a->argv[1] === 'archived')) {
700                 $sql_extra = " AND `archive` = 1 ";
701                 $archived = true;
702         } else {
703                 $sql_extra = " AND `blocked` = 0 ";
704         }
705
706         $search = x($_GET, 'search') ? notags(trim($_GET['search'])) : '';
707         $nets   = x($_GET, 'nets'  ) ? notags(trim($_GET['nets']))   : '';
708
709         $tabs = [
710                 [
711                         'label' => L10n::t('Suggestions'),
712                         'url'   => 'suggest',
713                         'sel'   => '',
714                         'title' => L10n::t('Suggest potential friends'),
715                         'id'    => 'suggestions-tab',
716                         'accesskey' => 'g',
717                 ],
718                 [
719                         'label' => L10n::t('All Contacts'),
720                         'url'   => 'contacts/all',
721                         'sel'   => ($all) ? 'active' : '',
722                         'title' => L10n::t('Show all contacts'),
723                         'id'    => 'showall-tab',
724                         'accesskey' => 'l',
725                 ],
726                 [
727                         'label' => L10n::t('Unblocked'),
728                         'url'   => 'contacts',
729                         'sel'   => ((!$all) && (!$blocked) && (!$hidden) && (!$search) && (!$nets) && (!$ignored) && (!$archived)) ? 'active' : '',
730                         'title' => L10n::t('Only show unblocked contacts'),
731                         'id'    => 'showunblocked-tab',
732                         'accesskey' => 'o',
733                 ],
734                 [
735                         'label' => L10n::t('Blocked'),
736                         'url'   => 'contacts/blocked',
737                         'sel'   => ($blocked) ? 'active' : '',
738                         'title' => L10n::t('Only show blocked contacts'),
739                         'id'    => 'showblocked-tab',
740                         'accesskey' => 'b',
741                 ],
742                 [
743                         'label' => L10n::t('Ignored'),
744                         'url'   => 'contacts/ignored',
745                         'sel'   => ($ignored) ? 'active' : '',
746                         'title' => L10n::t('Only show ignored contacts'),
747                         'id'    => 'showignored-tab',
748                         'accesskey' => 'i',
749                 ],
750                 [
751                         'label' => L10n::t('Archived'),
752                         'url'   => 'contacts/archived',
753                         'sel'   => ($archived) ? 'active' : '',
754                         'title' => L10n::t('Only show archived contacts'),
755                         'id'    => 'showarchived-tab',
756                         'accesskey' => 'y',
757                 ],
758                 [
759                         'label' => L10n::t('Hidden'),
760                         'url'   => 'contacts/hidden',
761                         'sel'   => ($hidden) ? 'active' : '',
762                         'title' => L10n::t('Only show hidden contacts'),
763                         'id'    => 'showhidden-tab',
764                         'accesskey' => 'h',
765                 ],
766         ];
767
768         $tab_tpl = get_markup_template('common_tabs.tpl');
769         $t = replace_macros($tab_tpl, ['$tabs' => $tabs]);
770
771         $total = 0;
772         $searching = false;
773         $search_hdr = null;
774         if ($search) {
775                 $searching = true;
776                 $search_hdr = $search;
777                 $search_txt = dbesc(protect_sprintf(preg_quote($search)));
778                 $sql_extra .= " AND (name REGEXP '$search_txt' OR url REGEXP '$search_txt'  OR nick REGEXP '$search_txt') ";
779         }
780
781         if ($nets) {
782                 $sql_extra .= sprintf(" AND network = '%s' ", dbesc($nets));
783         }
784
785         $sql_extra2 = ((($sort_type > 0) && ($sort_type <= CONTACT_IS_FRIEND)) ? sprintf(" AND `rel` = %d ", intval($sort_type)) : '');
786
787         $r = q("SELECT COUNT(*) AS `total` FROM `contact`
788                 WHERE `uid` = %d AND `self` = 0 AND `pending` = 0 $sql_extra $sql_extra2 ",
789                 intval($_SESSION['uid'])
790         );
791         if (DBM::is_result($r)) {
792                 $a->set_pager_total($r[0]['total']);
793                 $total = $r[0]['total'];
794         }
795
796         $sql_extra3 = Widget::unavailableNetworks();
797
798         $contacts = [];
799
800         $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 ",
801                 intval($_SESSION['uid']),
802                 intval($a->pager['start']),
803                 intval($a->pager['itemspage'])
804         );
805         if (DBM::is_result($r)) {
806                 foreach ($r as $rr) {
807                         $contacts[] = _contact_detail_for_template($rr);
808                 }
809         }
810
811         $tpl = get_markup_template("contacts-template.tpl");
812         $o .= replace_macros($tpl, [
813                 '$baseurl' => System::baseUrl(),
814                 '$header' => L10n::t('Contacts') . (($nets) ? ' - ' . ContactSelector::networkToName($nets) : ''),
815                 '$tabs' => $t,
816                 '$total' => $total,
817                 '$search' => $search_hdr,
818                 '$desc' => L10n::t('Search your contacts'),
819                 '$finding' => $searching ? L10n::t('Results for: %s', $search) : "",
820                 '$submit' => L10n::t('Find'),
821                 '$cmd' => $a->cmd,
822                 '$contacts' => $contacts,
823                 '$contact_drop_confirm' => L10n::t('Do you really want to delete this contact?'),
824                 'multiselect' => 1,
825                 '$batch_actions' => [
826                         'contacts_batch_update'  => L10n::t('Update'),
827                         'contacts_batch_block'   => L10n::t('Block') . "/" . L10n::t("Unblock"),
828                         "contacts_batch_ignore"  => L10n::t('Ignore') . "/" . L10n::t("Unignore"),
829                         "contacts_batch_archive" => L10n::t('Archive') . "/" . L10n::t("Unarchive"),
830                         "contacts_batch_drop"    => L10n::t('Delete'),
831                 ],
832                 '$h_batch_actions' => L10n::t('Batch Actions'),
833                 '$paginate' => paginate($a),
834         ]);
835
836         return $o;
837 }
838
839 /**
840  * @brief List of pages for the Contact TabBar
841  *
842  * Available Pages are 'Status', 'Profile', 'Contacts' and 'Common Friends'
843  *
844  * @param App $a
845  * @param int $contact_id The ID of the contact
846  * @param int $active_tab 1 if tab should be marked as active
847  *
848  * @return string
849  */
850 function contacts_tab($a, $contact_id, $active_tab)
851 {
852         // tabs
853         $tabs = [
854                 [
855                         'label' => L10n::t('Status'),
856                         'url'   => "contacts/" . $contact_id . "/posts",
857                         'sel'   => (($active_tab == 1) ? 'active' : ''),
858                         'title' => L10n::t('Status Messages and Posts'),
859                         'id'    => 'status-tab',
860                         'accesskey' => 'm',
861                 ],
862                 [
863                         'label' => L10n::t('Profile'),
864                         'url'   => "contacts/" . $contact_id,
865                         'sel'   => (($active_tab == 2) ? 'active' : ''),
866                         'title' => L10n::t('Profile Details'),
867                         'id'    => 'profile-tab',
868                         'accesskey' => 'o',
869                 ]
870         ];
871
872         // Show this tab only if there is visible friend list
873         $x = GContact::countAllFriends(local_user(), $contact_id);
874         if ($x) {
875                 $tabs[] = ['label' => L10n::t('Contacts'),
876                         'url'   => "allfriends/" . $contact_id,
877                         'sel'   => (($active_tab == 3) ? 'active' : ''),
878                         'title' => L10n::t('View all contacts'),
879                         'id'    => 'allfriends-tab',
880                         'accesskey' => 't'];
881         }
882
883         // Show this tab only if there is visible common friend list
884         $common = GContact::countCommonFriends(local_user(), $contact_id);
885         if ($common) {
886                 $tabs[] = ['label' => L10n::t('Common Friends'),
887                         'url'   => "common/loc/" . local_user() . "/" . $contact_id,
888                         'sel'   => (($active_tab == 4) ? 'active' : ''),
889                         'title' => L10n::t('View all common friends'),
890                         'id'    => 'common-loc-tab',
891                         'accesskey' => 'd'
892                 ];
893         }
894
895         $tabs[] = ['label' => L10n::t('Advanced'),
896                 'url'   => 'crepair/' . $contact_id,
897                 'sel'   => (($active_tab == 5) ? 'active' : ''),
898                 'title' => L10n::t('Advanced Contact Settings'),
899                 'id'    => 'advanced-tab',
900                 'accesskey' => 'r'
901         ];
902
903         $tab_tpl = get_markup_template('common_tabs.tpl');
904         $tab_str = replace_macros($tab_tpl, ['$tabs' => $tabs]);
905
906         return $tab_str;
907 }
908
909 function contact_posts($a, $contact_id)
910 {
911         $o = contacts_tab($a, $contact_id, 1);
912
913         $contact = dba::selectFirst('contact', ['url'], ['id' => $contact_id]);
914         if (DBM::is_result($contact)) {
915                 $a->page['aside'] = "";
916                 Profile::load($a, "", 0, Contact::getDetailsByURL($contact["url"]));
917                 $o .= Contact::getPostsFromUrl($contact["url"]);
918         }
919
920         return $o;
921 }
922
923 function _contact_detail_for_template($rr)
924 {
925         $dir_icon = '';
926         $alt_text = '';
927         switch ($rr['rel']) {
928                 case CONTACT_IS_FRIEND:
929                         $dir_icon = 'images/lrarrow.gif';
930                         $alt_text = L10n::t('Mutual Friendship');
931                         break;
932                 case CONTACT_IS_FOLLOWER;
933                         $dir_icon = 'images/larrow.gif';
934                         $alt_text = L10n::t('is a fan of yours');
935                         break;
936                 case CONTACT_IS_SHARING;
937                         $dir_icon = 'images/rarrow.gif';
938                         $alt_text = L10n::t('you are a fan of');
939                         break;
940                 default:
941                         break;
942         }
943         if (($rr['network'] === NETWORK_DFRN) && ($rr['rel'])) {
944                 $url = "redir/{$rr['id']}";
945                 $sparkle = ' class="sparkle" ';
946         } else {
947                 $url = $rr['url'];
948                 $sparkle = '';
949         }
950
951         if ($rr['self']) {
952                 $dir_icon = 'images/larrow.gif';
953                 $alt_text = L10n::t('This is you');
954                 $url = $rr['url'];
955                 $sparkle = '';
956         }
957
958         return [
959                 'img_hover' => L10n::t('Visit %s\'s profile [%s]', $rr['name'], $rr['url']),
960                 'edit_hover' => L10n::t('Edit contact'),
961                 'photo_menu' => Contact::photoMenu($rr),
962                 'id' => $rr['id'],
963                 'alt_text' => $alt_text,
964                 'dir_icon' => $dir_icon,
965                 'thumb' => proxy_url($rr['thumb'], false, PROXY_SIZE_THUMB),
966                 'name' => htmlentities($rr['name']),
967                 'username' => htmlentities($rr['name']),
968                 'account_type' => Contact::getAccountType($rr),
969                 'sparkle' => $sparkle,
970                 'itemurl' => (($rr['addr'] != "") ? $rr['addr'] : $rr['url']),
971                 'url' => $url,
972                 'network' => ContactSelector::networkToName($rr['network'], $rr['url']),
973         ];
974 }
975
976 /**
977  * @brief Gives a array with actions which can performed to a given contact
978  *
979  * This includes actions like e.g. 'block', 'hide', 'archive', 'delete' and others
980  *
981  * @param array $contact Data about the Contact
982  * @return array with contact related actions
983  */
984 function contact_actions($contact)
985 {
986         $poll_enabled = in_array($contact['network'], [NETWORK_DFRN, NETWORK_OSTATUS, NETWORK_FEED, NETWORK_MAIL]);
987         $contact_actions = [];
988
989         // Provide friend suggestion only for Friendica contacts
990         if ($contact['network'] === NETWORK_DFRN) {
991                 $contact_actions['suggest'] = [
992                         'label' => L10n::t('Suggest friends'),
993                         'url'   => 'fsuggest/' . $contact['id'],
994                         'title' => '',
995                         'sel'   => '',
996                         'id'    => 'suggest',
997                 ];
998         }
999
1000         if ($poll_enabled) {
1001                 $contact_actions['update'] = [
1002                         'label' => L10n::t('Update now'),
1003                         'url'   => 'contacts/' . $contact['id'] . '/update',
1004                         'title' => '',
1005                         'sel'   => '',
1006                         'id'    => 'update',
1007                 ];
1008         }
1009
1010         $contact_actions['block'] = [
1011                 'label' => (intval($contact['blocked']) ? L10n::t('Unblock') : L10n::t('Block') ),
1012                 'url'   => 'contacts/' . $contact['id'] . '/block',
1013                 'title' => L10n::t('Toggle Blocked status'),
1014                 'sel'   => (intval($contact['blocked']) ? 'active' : ''),
1015                 'id'    => 'toggle-block',
1016         ];
1017
1018         $contact_actions['ignore'] = [
1019                 'label' => (intval($contact['readonly']) ? L10n::t('Unignore') : L10n::t('Ignore') ),
1020                 'url'   => 'contacts/' . $contact['id'] . '/ignore',
1021                 'title' => L10n::t('Toggle Ignored status'),
1022                 'sel'   => (intval($contact['readonly']) ? 'active' : ''),
1023                 'id'    => 'toggle-ignore',
1024         ];
1025
1026         $contact_actions['archive'] = [
1027                 'label' => (intval($contact['archive']) ? L10n::t('Unarchive') : L10n::t('Archive') ),
1028                 'url'   => 'contacts/' . $contact['id'] . '/archive',
1029                 'title' => L10n::t('Toggle Archive status'),
1030                 'sel'   => (intval($contact['archive']) ? 'active' : ''),
1031                 'id'    => 'toggle-archive',
1032         ];
1033
1034         $contact_actions['delete'] = [
1035                 'label' => L10n::t('Delete'),
1036                 'url'   => 'contacts/' . $contact['id'] . '/drop',
1037                 'title' => L10n::t('Delete contact'),
1038                 'sel'   => '',
1039                 'id'    => 'delete',
1040         ];
1041
1042         return $contact_actions;
1043 }