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