3 * @file mod/contacts.php
7 use Friendica\Content\ContactSelector;
8 use Friendica\Content\Nav;
9 use Friendica\Content\Text\BBCode;
10 use Friendica\Content\Widget;
11 use Friendica\Core\Addon;
12 use Friendica\Core\L10n;
13 use Friendica\Core\System;
14 use Friendica\Core\Worker;
15 use Friendica\Database\DBA;
16 use Friendica\Model\Contact;
17 use Friendica\Model\GContact;
18 use Friendica\Model\Group;
19 use Friendica\Model\Profile;
20 use Friendica\Network\Probe;
21 use Friendica\Util\DateTimeFormat;
22 use Friendica\Util\Proxy as ProxyUtils;
24 function contacts_init(App $a)
30 $nets = defaults($_GET, 'nets', '');
35 if (!x($a->page, 'aside')) {
36 $a->page['aside'] = '';
41 if ((($a->argc == 2) && intval($a->argv[1])) || (($a->argc == 3) && intval($a->argv[1]) && ($a->argv[2] == "posts"))) {
42 $contact_id = intval($a->argv[1]);
43 $contact = DBA::selectFirst('contact', [], ['id' => $contact_id, 'uid' => local_user()]);
46 if (DBA::isResult($contact)) {
47 if ($contact['self']) {
48 if (($a->argc == 3) && intval($a->argv[1]) && ($a->argv[2] == "posts")) {
49 goaway('profile/' . $contact['nick']);
51 goaway('profile/' . $contact['nick'] . '?tab=profile');
55 $a->data['contact'] = $contact;
57 if (($a->data['contact']['network'] != "") && ($a->data['contact']['network'] != NETWORK_DFRN)) {
58 $networkname = format_network_name($a->data['contact']['network'], $a->data['contact']['url']);
63 /// @TODO Add nice spaces
64 $vcard_widget = replace_macros(get_markup_template("vcard-widget.tpl"), [
65 '$name' => htmlentities($a->data['contact']['name']),
66 '$photo' => $a->data['contact']['photo'],
67 '$url' => Contact::MagicLink($a->data['contact']['url']),
68 '$addr' => (($a->data['contact']['addr'] != "") ? ($a->data['contact']['addr']) : ""),
69 '$network_name' => $networkname,
70 '$network' => L10n::t('Network:'),
71 '$account_type' => Contact::getAccountType($a->data['contact'])
74 $findpeople_widget = '';
76 $networks_widget = '';
79 $networks_widget = Widget::networks('contacts', $nets);
80 if (isset($_GET['add'])) {
81 $follow_widget = Widget::follow($_GET['add']);
83 $follow_widget = Widget::follow();
86 $findpeople_widget = Widget::findPeople();
89 $groups_widget = Group::sidebarWidget('contacts', 'group', 'full', 'everyone', $contact_id);
91 $a->page['aside'] .= replace_macros(get_markup_template("contacts-widget-sidebar.tpl"), [
92 '$vcard_widget' => $vcard_widget,
93 '$findpeople_widget' => $findpeople_widget,
94 '$follow_widget' => $follow_widget,
95 '$groups_widget' => $groups_widget,
96 '$networks_widget' => $networks_widget
99 $base = System::baseUrl();
100 $tpl = get_markup_template("contacts-head.tpl");
101 $a->page['htmlhead'] .= replace_macros($tpl, [
102 '$baseurl' => System::baseUrl(true),
106 $tpl = get_markup_template("contacts-end.tpl");
107 $a->page['end'] .= replace_macros($tpl, [
108 '$baseurl' => System::baseUrl(true),
113 function contacts_batch_actions(App $a)
115 $contacts_id = $_POST['contact_batch'];
116 if (!is_array($contacts_id)) {
120 $orig_records = q("SELECT * FROM `contact` WHERE `id` IN (%s) AND `uid` = %d AND `self` = 0",
121 implode(",", $contacts_id),
126 foreach ($orig_records as $orig_record) {
127 $contact_id = $orig_record['id'];
128 if (x($_POST, 'contacts_batch_update')) {
129 _contact_update($contact_id);
132 if (x($_POST, 'contacts_batch_block')) {
133 $r = _contact_block($contact_id, $orig_record);
138 if (x($_POST, 'contacts_batch_ignore')) {
139 $r = _contact_ignore($contact_id, $orig_record);
144 if (x($_POST, 'contacts_batch_archive')) {
145 $r = _contact_archive($contact_id, $orig_record);
150 if (x($_POST, 'contacts_batch_drop')) {
151 _contact_drop($orig_record);
155 if ($count_actions > 0) {
156 info(L10n::tt("%d contact edited.", "%d contacts edited.", $count_actions));
159 if (x($_SESSION, 'return_url')) {
160 goaway('' . $_SESSION['return_url']);
166 function contacts_post(App $a)
172 if ($a->argv[1] === "batch") {
173 contacts_batch_actions($a);
177 $contact_id = intval($a->argv[1]);
182 if (!DBA::exists('contact', ['id' => $contact_id, 'uid' => local_user()])) {
183 notice(L10n::t('Could not access contact record.') . EOL);
185 return; // NOTREACHED
188 Addon::callHooks('contact_edit_post', $_POST);
190 $profile_id = intval(defaults($_POST, 'profile-assign', 0));
192 if (!DBA::exists('profile', ['id' => $profile_id, 'uid' => local_user()])) {
193 notice(L10n::t('Could not locate selected profile.') . EOL);
198 $hidden = intval($_POST['hidden']);
200 $notify = intval($_POST['notify']);
202 $fetch_further_information = intval(defaults($_POST, 'fetch_further_information', 0));
204 $ffi_keyword_blacklist = escape_tags(trim(defaults($_POST, 'ffi_keyword_blacklist', '')));
206 $priority = intval(defaults($_POST, 'poll', 0));
207 if ($priority > 5 || $priority < 0) {
211 $info = escape_tags(trim($_POST['info']));
213 $r = q("UPDATE `contact` SET `profile-id` = %d, `priority` = %d , `info` = '%s',
214 `hidden` = %d, `notify_new_posts` = %d, `fetch_further_information` = %d,
215 `ffi_keyword_blacklist` = '%s' WHERE `id` = %d AND `uid` = %d",
221 intval($fetch_further_information),
222 DBA::escape($ffi_keyword_blacklist),
226 if (DBA::isResult($r)) {
227 info(L10n::t('Contact updated.') . EOL);
229 notice(L10n::t('Failed to update contact record.') . EOL);
232 $contact = DBA::selectFirst('contact', [], ['id' => $contact_id, 'uid' => local_user()]);
233 if (DBA::isResult($contact)) {
234 $a->data['contact'] = $contact;
240 /* contact actions */
242 function _contact_update($contact_id)
244 $contact = DBA::selectFirst('contact', ['uid', 'url', 'network'], ['id' => $contact_id, 'uid' => local_user()]);
245 if (!DBA::isResult($contact)) {
249 $uid = $contact["uid"];
251 if ($contact["network"] == NETWORK_OSTATUS) {
252 $result = Contact::createFromProbe($uid, $contact["url"], false, $contact["network"]);
254 if ($result['success']) {
255 q("UPDATE `contact` SET `subhub` = 1 WHERE `id` = %d", intval($contact_id));
258 // pull feed and consume it, which should subscribe to the hub.
259 Worker::add(PRIORITY_HIGH, "OnePoll", $contact_id, "force");
263 function _contact_update_profile($contact_id)
265 $contact = DBA::selectFirst('contact', ['uid', 'url', 'network'], ['id' => $contact_id, 'uid' => local_user()]);
266 if (!DBA::isResult($contact)) {
270 $uid = $contact["uid"];
272 $data = Probe::uri($contact["url"], "", 0, false);
274 // "Feed" or "Unknown" is mostly a sign of communication problems
275 if ((in_array($data["network"], [NETWORK_FEED, NETWORK_PHANTOM])) && ($data["network"] != $contact["network"])) {
279 $updatefields = ["name", "nick", "url", "addr", "batch", "notify", "poll", "request", "confirm",
280 "poco", "network", "alias"];
283 if ($data["network"] == NETWORK_OSTATUS) {
284 $result = Contact::createFromProbe($uid, $data["url"], false);
286 if ($result['success']) {
287 $update["subhub"] = true;
291 foreach ($updatefields AS $field) {
292 if (isset($data[$field]) && ($data[$field] != "")) {
293 $update[$field] = $data[$field];
297 $update["nurl"] = normalise_link($data["url"]);
301 if (isset($data["priority"]) && ($data["priority"] != 0)) {
302 $query = "`priority` = " . intval($data["priority"]);
305 foreach ($update AS $key => $value) {
310 $query .= "`" . $key . "` = '" . DBA::escape($value) . "'";
317 $r = q("UPDATE `contact` SET $query WHERE `id` = %d AND `uid` = %d",
322 // Update the entry in the contact table
323 Contact::updateAvatar($data['photo'], local_user(), $contact_id, true);
325 // Update the entry in the gcontact table
326 GContact::updateFromProbe($data["url"]);
329 function _contact_block($contact_id, $orig_record)
331 $blocked = (($orig_record['blocked']) ? 0 : 1);
332 $r = q("UPDATE `contact` SET `blocked` = %d WHERE `id` = %d AND `uid` = %d",
337 return DBA::isResult($r);
340 function _contact_ignore($contact_id, $orig_record)
342 $readonly = (($orig_record['readonly']) ? 0 : 1);
343 $r = q("UPDATE `contact` SET `readonly` = %d WHERE `id` = %d AND `uid` = %d",
348 return DBA::isResult($r);
351 function _contact_archive($contact_id, $orig_record)
353 $archived = (($orig_record['archive']) ? 0 : 1);
354 $r = q("UPDATE `contact` SET `archive` = %d WHERE `id` = %d AND `uid` = %d",
359 return DBA::isResult($r);
362 function _contact_drop($orig_record)
366 $r = q("SELECT `contact`.*, `user`.* FROM `contact` INNER JOIN `user` ON `contact`.`uid` = `user`.`uid`
367 WHERE `user`.`uid` = %d AND `contact`.`self` LIMIT 1",
368 intval($a->user['uid'])
370 if (!DBA::isResult($r)) {
374 Contact::terminateFriendship($r[0], $orig_record);
375 Contact::remove($orig_record['id']);
378 function contacts_content(App $a)
382 Nav::setSelected('contacts');
385 notice(L10n::t('Permission denied.') . EOL);
390 $contact_id = intval($a->argv[1]);
397 $orig_record = DBA::selectFirst('contact', [], ['id' => $contact_id, 'uid' => local_user(), 'self' => false]);
398 if (!DBA::isResult($orig_record)) {
399 notice(L10n::t('Could not access contact record.') . EOL);
401 return; // NOTREACHED
404 if ($cmd === 'update') {
405 _contact_update($contact_id);
406 goaway('contacts/' . $contact_id);
410 if ($cmd === 'updateprofile') {
411 _contact_update_profile($contact_id);
412 goaway('crepair/' . $contact_id);
416 if ($cmd === 'block') {
417 $r = _contact_block($contact_id, $orig_record);
419 $blocked = (($orig_record['blocked']) ? 0 : 1);
420 info((($blocked) ? L10n::t('Contact has been blocked') : L10n::t('Contact has been unblocked')) . EOL);
423 goaway('contacts/' . $contact_id);
424 return; // NOTREACHED
427 if ($cmd === 'ignore') {
428 $r = _contact_ignore($contact_id, $orig_record);
430 $readonly = (($orig_record['readonly']) ? 0 : 1);
431 info((($readonly) ? L10n::t('Contact has been ignored') : L10n::t('Contact has been unignored')) . EOL);
434 goaway('contacts/' . $contact_id);
435 return; // NOTREACHED
438 if ($cmd === 'archive') {
439 $r = _contact_archive($contact_id, $orig_record);
441 $archived = (($orig_record['archive']) ? 0 : 1);
442 info((($archived) ? L10n::t('Contact has been archived') : L10n::t('Contact has been unarchived')) . EOL);
445 goaway('contacts/' . $contact_id);
446 return; // NOTREACHED
449 if ($cmd === 'drop') {
450 // Check if we should do HTML-based delete confirmation
451 if (x($_REQUEST, 'confirm')) {
452 // <form> can't take arguments in its "action" parameter
453 // so add any arguments as hidden inputs
454 $query = explode_querystring($a->query_string);
456 foreach ($query['args'] as $arg) {
457 if (strpos($arg, 'confirm=') === false) {
458 $arg_parts = explode('=', $arg);
459 $inputs[] = ['name' => $arg_parts[0], 'value' => $arg_parts[1]];
463 $a->page['aside'] = '';
465 return replace_macros(get_markup_template('contact_drop_confirm.tpl'), [
466 '$header' => L10n::t('Drop contact'),
467 '$contact' => _contact_detail_for_template($orig_record),
469 '$message' => L10n::t('Do you really want to delete this contact?'),
470 '$extra_inputs' => $inputs,
471 '$confirm' => L10n::t('Yes'),
472 '$confirm_url' => $query['base'],
473 '$confirm_name' => 'confirmed',
474 '$cancel' => L10n::t('Cancel'),
477 // Now check how the user responded to the confirmation query
478 if (x($_REQUEST, 'canceled')) {
479 if (x($_SESSION, 'return_url')) {
480 goaway('' . $_SESSION['return_url']);
486 _contact_drop($orig_record);
487 info(L10n::t('Contact has been removed.') . EOL);
488 if (x($_SESSION, 'return_url')) {
489 goaway('' . $_SESSION['return_url']);
493 return; // NOTREACHED
495 if ($cmd === 'posts') {
496 return contact_posts($a, $contact_id);
500 $_SESSION['return_url'] = $a->query_string;
502 if ((x($a->data, 'contact')) && (is_array($a->data['contact']))) {
503 $contact_id = $a->data['contact']['id'];
504 $contact = $a->data['contact'];
506 $a->page['htmlhead'] .= replace_macros(get_markup_template('contact_head.tpl'), [
507 '$baseurl' => System::baseUrl(true),
509 $a->page['end'] .= replace_macros(get_markup_template('contact_end.tpl'), [
510 '$baseurl' => System::baseUrl(true),
515 switch ($contact['rel']) {
516 case Contact::FRIEND:
517 $dir_icon = 'images/lrarrow.gif';
518 $relation_text = L10n::t('You are mutual friends with %s');
521 case Contact::FOLLOWER;
522 $dir_icon = 'images/larrow.gif';
523 $relation_text = L10n::t('You are sharing with %s');
526 case Contact::SHARING;
527 $dir_icon = 'images/rarrow.gif';
528 $relation_text = L10n::t('%s is sharing with you');
535 if (!in_array($contact['network'], [NETWORK_DFRN, NETWORK_OSTATUS, NETWORK_DIASPORA])) {
539 $relation_text = sprintf($relation_text, htmlentities($contact['name']));
541 $url = Contact::magicLink($contact['url']);
542 if (strpos($url, 'redir/') === 0) {
543 $sparkle = ' class="sparkle" ';
548 $insecure = L10n::t('Private communications are not available for this contact.');
550 $last_update = (($contact['last-update'] <= NULL_DATE) ? L10n::t('Never') : DateTimeFormat::local($contact['last-update'], 'D, j M Y, g:i A'));
552 if ($contact['last-update'] > NULL_DATE) {
553 $last_update .= ' ' . (($contact['last-update'] <= $contact['success_update']) ? L10n::t("\x28Update was successful\x29") : L10n::t("\x28Update was not successful\x29"));
555 $lblsuggest = (($contact['network'] === NETWORK_DFRN) ? L10n::t('Suggest friends') : '');
557 $poll_enabled = in_array($contact['network'], [NETWORK_DFRN, NETWORK_OSTATUS, NETWORK_FEED, NETWORK_MAIL]);
559 $nettype = L10n::t('Network type: %s', ContactSelector::networkToName($contact['network'], $contact["url"]));
562 $tab_str = contacts_tab($a, $contact_id, 2);
564 $lost_contact = (($contact['archive'] && $contact['term-date'] > NULL_DATE && $contact['term-date'] < DateTimeFormat::utcNow()) ? L10n::t('Communications lost with this contact!') : '');
566 $fetch_further_information = null;
567 if ($contact['network'] == NETWORK_FEED) {
568 $fetch_further_information = [
569 'fetch_further_information',
570 L10n::t('Fetch further information for feeds'),
571 $contact['fetch_further_information'],
572 L10n::t("Fetch information like preview pictures, title and teaser from the feed item. You can activate this if the feed doesn't contain much text. Keywords are taken from the meta header in the feed item and are posted as hash tags."),
573 ['0' => L10n::t('Disabled'),
574 '1' => L10n::t('Fetch information'),
575 '3' => L10n::t('Fetch keywords'),
576 '2' => L10n::t('Fetch information and keywords')
581 $poll_interval = null;
582 if (in_array($contact['network'], [NETWORK_FEED, NETWORK_MAIL])) {
583 $poll_interval = ContactSelector::pollInterval($contact['priority'], (!$poll_enabled));
586 $profile_select = null;
587 if ($contact['network'] == NETWORK_DFRN) {
588 $profile_select = ContactSelector::profileAssign($contact['profile-id'], (($contact['network'] !== NETWORK_DFRN) ? true : false));
591 /// @todo Only show the following link with DFRN when the remote version supports it
594 if (in_array($contact['network'], [NETWORK_DIASPORA, NETWORK_OSTATUS, NETWORK_DFRN])) {
595 if ($contact['rel'] == Contact::FOLLOWER) {
596 $follow = System::baseUrl(true) . "/follow?url=" . urlencode($contact["url"]);
597 $follow_text = L10n::t("Connect/Follow");
598 } elseif ($contact['rel'] == Contact::FRIEND) {
599 $follow = System::baseUrl(true) . "/unfollow?url=" . urlencode($contact["url"]);
600 $follow_text = L10n::t("Disconnect/Unfollow");
604 // Load contactact related actions like hide, suggest, delete and others
605 $contact_actions = contact_actions($contact);
607 $tpl = get_markup_template("contact_edit.tpl");
608 $o .= replace_macros($tpl, [
609 '$header' => L10n::t("Contact"),
610 '$tab_str' => $tab_str,
611 '$submit' => L10n::t('Submit'),
612 '$lbl_vis1' => L10n::t('Profile Visibility'),
613 '$lbl_vis2' => L10n::t('Please choose the profile you would like to display to %s when viewing your profile securely.', $contact['name']),
614 '$lbl_info1' => L10n::t('Contact Information / Notes'),
615 '$lbl_info2' => L10n::t('Their personal note'),
616 '$reason' => trim(notags($contact['reason'])),
617 '$infedit' => L10n::t('Edit contact notes'),
618 '$common_link' => 'common/loc/' . local_user() . '/' . $contact['id'],
619 '$relation_text' => $relation_text,
620 '$visit' => L10n::t('Visit %s\'s profile [%s]', $contact['name'], $contact['url']),
621 '$blockunblock' => L10n::t('Block/Unblock contact'),
622 '$ignorecont' => L10n::t('Ignore contact'),
623 '$lblcrepair' => L10n::t("Repair URL settings"),
624 '$lblrecent' => L10n::t('View conversations'),
625 '$lblsuggest' => $lblsuggest,
626 '$nettype' => $nettype,
627 '$poll_interval' => $poll_interval,
628 '$poll_enabled' => $poll_enabled,
629 '$lastupdtext' => L10n::t('Last update:'),
630 '$lost_contact' => $lost_contact,
631 '$updpub' => L10n::t('Update public posts'),
632 '$last_update' => $last_update,
633 '$udnow' => L10n::t('Update now'),
634 '$follow' => $follow,
635 '$follow_text' => $follow_text,
636 '$profile_select' => $profile_select,
637 '$contact_id' => $contact['id'],
638 '$block_text' => (($contact['blocked']) ? L10n::t('Unblock') : L10n::t('Block') ),
639 '$ignore_text' => (($contact['readonly']) ? L10n::t('Unignore') : L10n::t('Ignore') ),
640 '$insecure' => (($contact['network'] !== NETWORK_DFRN && $contact['network'] !== NETWORK_MAIL && $contact['network'] !== NETWORK_FACEBOOK && $contact['network'] !== NETWORK_DIASPORA) ? $insecure : ''),
641 '$info' => $contact['info'],
642 '$cinfo' => ['info', '', $contact['info'], ''],
643 '$blocked' => (($contact['blocked']) ? L10n::t('Currently blocked') : ''),
644 '$ignored' => (($contact['readonly']) ? L10n::t('Currently ignored') : ''),
645 '$archived' => (($contact['archive']) ? L10n::t('Currently archived') : ''),
646 '$pending' => (($contact['pending']) ? L10n::t('Awaiting connection acknowledge') : ''),
647 '$hidden' => ['hidden', L10n::t('Hide this contact from others'), ($contact['hidden'] == 1), L10n::t('Replies/likes to your public posts <strong>may</strong> still be visible')],
648 '$notify' => ['notify', L10n::t('Notification for new posts'), ($contact['notify_new_posts'] == 1), L10n::t('Send a notification of every new post of this contact')],
649 '$fetch_further_information' => $fetch_further_information,
650 '$ffi_keyword_blacklist' => $contact['ffi_keyword_blacklist'],
651 '$ffi_keyword_blacklist' => ['ffi_keyword_blacklist', L10n::t('Blacklisted keywords'), $contact['ffi_keyword_blacklist'], L10n::t('Comma separated list of keywords that should not be converted to hashtags, when "Fetch information and keywords" is selected')],
652 '$photo' => $contact['photo'],
653 '$name' => htmlentities($contact['name']),
654 '$dir_icon' => $dir_icon,
655 '$sparkle' => $sparkle,
657 '$profileurllabel' => L10n::t('Profile URL'),
658 '$profileurl' => $contact['url'],
659 '$account_type' => Contact::getAccountType($contact),
660 '$location' => BBCode::convert($contact["location"]),
661 '$location_label' => L10n::t("Location:"),
662 '$xmpp' => BBCode::convert($contact["xmpp"]),
663 '$xmpp_label' => L10n::t("XMPP:"),
664 '$about' => BBCode::convert($contact["about"], false),
665 '$about_label' => L10n::t("About:"),
666 '$keywords' => $contact["keywords"],
667 '$keywords_label' => L10n::t("Tags:"),
668 '$contact_action_button' => L10n::t("Actions"),
669 '$contact_actions' => $contact_actions,
670 '$contact_status' => L10n::t("Status"),
671 '$contact_settings_label' => L10n::t('Contact Settings'),
672 '$contact_profile_label' => L10n::t("Profile"),
675 $arr = ['contact' => $contact, 'output' => $o];
677 Addon::callHooks('contact_edit', $arr);
679 return $arr['output'];
688 if (($a->argc == 2) && ($a->argv[1] === 'all')) {
691 } elseif (($a->argc == 2) && ($a->argv[1] === 'blocked')) {
692 $sql_extra = " AND `blocked` = 1 ";
694 } elseif (($a->argc == 2) && ($a->argv[1] === 'hidden')) {
695 $sql_extra = " AND `hidden` = 1 ";
697 } elseif (($a->argc == 2) && ($a->argv[1] === 'ignored')) {
698 $sql_extra = " AND `readonly` = 1 ";
700 } elseif (($a->argc == 2) && ($a->argv[1] === 'archived')) {
701 $sql_extra = " AND `archive` = 1 ";
704 $sql_extra = " AND `blocked` = 0 ";
707 $search = x($_GET, 'search') ? notags(trim($_GET['search'])) : '';
708 $nets = x($_GET, 'nets' ) ? notags(trim($_GET['nets'])) : '';
712 'label' => L10n::t('Suggestions'),
715 'title' => L10n::t('Suggest potential friends'),
716 'id' => 'suggestions-tab',
720 'label' => L10n::t('All Contacts'),
721 'url' => 'contacts/all',
722 'sel' => ($all) ? 'active' : '',
723 'title' => L10n::t('Show all contacts'),
724 'id' => 'showall-tab',
728 'label' => L10n::t('Unblocked'),
730 'sel' => ((!$all) && (!$blocked) && (!$hidden) && (!$search) && (!$nets) && (!$ignored) && (!$archived)) ? 'active' : '',
731 'title' => L10n::t('Only show unblocked contacts'),
732 'id' => 'showunblocked-tab',
736 'label' => L10n::t('Blocked'),
737 'url' => 'contacts/blocked',
738 'sel' => ($blocked) ? 'active' : '',
739 'title' => L10n::t('Only show blocked contacts'),
740 'id' => 'showblocked-tab',
744 'label' => L10n::t('Ignored'),
745 'url' => 'contacts/ignored',
746 'sel' => ($ignored) ? 'active' : '',
747 'title' => L10n::t('Only show ignored contacts'),
748 'id' => 'showignored-tab',
752 'label' => L10n::t('Archived'),
753 'url' => 'contacts/archived',
754 'sel' => ($archived) ? 'active' : '',
755 'title' => L10n::t('Only show archived contacts'),
756 'id' => 'showarchived-tab',
760 'label' => L10n::t('Hidden'),
761 'url' => 'contacts/hidden',
762 'sel' => ($hidden) ? 'active' : '',
763 'title' => L10n::t('Only show hidden contacts'),
764 'id' => 'showhidden-tab',
769 $tab_tpl = get_markup_template('common_tabs.tpl');
770 $t = replace_macros($tab_tpl, ['$tabs' => $tabs]);
777 $search_hdr = $search;
778 $search_txt = DBA::escape(protect_sprintf(preg_quote($search)));
779 $sql_extra .= " AND (name REGEXP '$search_txt' OR url REGEXP '$search_txt' OR nick REGEXP '$search_txt') ";
783 $sql_extra .= sprintf(" AND network = '%s' ", DBA::escape($nets));
786 $sql_extra2 = ((($sort_type > 0) && ($sort_type <= Contact::FRIEND)) ? sprintf(" AND `rel` = %d ", intval($sort_type)) : '');
788 $r = q("SELECT COUNT(*) AS `total` FROM `contact`
789 WHERE `uid` = %d AND `self` = 0 AND `pending` = 0 $sql_extra $sql_extra2 ",
790 intval($_SESSION['uid'])
792 if (DBA::isResult($r)) {
793 $a->set_pager_total($r[0]['total']);
794 $total = $r[0]['total'];
797 $sql_extra3 = Widget::unavailableNetworks();
801 $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` = 0 AND `pending` = 0 $sql_extra $sql_extra2 $sql_extra3 ORDER BY `name` ASC LIMIT %d , %d ",
802 intval($_SESSION['uid']),
803 intval($a->pager['start']),
804 intval($a->pager['itemspage'])
806 if (DBA::isResult($r)) {
807 foreach ($r as $rr) {
808 $contacts[] = _contact_detail_for_template($rr);
812 $tpl = get_markup_template("contacts-template.tpl");
813 $o .= replace_macros($tpl, [
814 '$baseurl' => System::baseUrl(),
815 '$header' => L10n::t('Contacts') . (($nets) ? ' - ' . ContactSelector::networkToName($nets) : ''),
818 '$search' => $search_hdr,
819 '$desc' => L10n::t('Search your contacts'),
820 '$finding' => $searching ? L10n::t('Results for: %s', $search) : "",
821 '$submit' => L10n::t('Find'),
823 '$contacts' => $contacts,
824 '$contact_drop_confirm' => L10n::t('Do you really want to delete this contact?'),
826 '$batch_actions' => [
827 'contacts_batch_update' => L10n::t('Update'),
828 'contacts_batch_block' => L10n::t('Block') . "/" . L10n::t("Unblock"),
829 "contacts_batch_ignore" => L10n::t('Ignore') . "/" . L10n::t("Unignore"),
830 "contacts_batch_archive" => L10n::t('Archive') . "/" . L10n::t("Unarchive"),
831 "contacts_batch_drop" => L10n::t('Delete'),
833 '$h_batch_actions' => L10n::t('Batch Actions'),
834 '$paginate' => paginate($a),
841 * @brief List of pages for the Contact TabBar
843 * Available Pages are 'Status', 'Profile', 'Contacts' and 'Common Friends'
846 * @param int $contact_id The ID of the contact
847 * @param int $active_tab 1 if tab should be marked as active
851 function contacts_tab($a, $contact_id, $active_tab)
856 'label' => L10n::t('Status'),
857 'url' => "contacts/" . $contact_id . "/posts",
858 'sel' => (($active_tab == 1) ? 'active' : ''),
859 'title' => L10n::t('Status Messages and Posts'),
860 'id' => 'status-tab',
864 'label' => L10n::t('Profile'),
865 'url' => "contacts/" . $contact_id,
866 'sel' => (($active_tab == 2) ? 'active' : ''),
867 'title' => L10n::t('Profile Details'),
868 'id' => 'profile-tab',
873 // Show this tab only if there is visible friend list
874 $x = GContact::countAllFriends(local_user(), $contact_id);
876 $tabs[] = ['label' => L10n::t('Contacts'),
877 'url' => "allfriends/" . $contact_id,
878 'sel' => (($active_tab == 3) ? 'active' : ''),
879 'title' => L10n::t('View all contacts'),
880 'id' => 'allfriends-tab',
884 // Show this tab only if there is visible common friend list
885 $common = GContact::countCommonFriends(local_user(), $contact_id);
887 $tabs[] = ['label' => L10n::t('Common Friends'),
888 'url' => "common/loc/" . local_user() . "/" . $contact_id,
889 'sel' => (($active_tab == 4) ? 'active' : ''),
890 'title' => L10n::t('View all common friends'),
891 'id' => 'common-loc-tab',
896 $tabs[] = ['label' => L10n::t('Advanced'),
897 'url' => 'crepair/' . $contact_id,
898 'sel' => (($active_tab == 5) ? 'active' : ''),
899 'title' => L10n::t('Advanced Contact Settings'),
900 'id' => 'advanced-tab',
904 $tab_tpl = get_markup_template('common_tabs.tpl');
905 $tab_str = replace_macros($tab_tpl, ['$tabs' => $tabs]);
910 function contact_posts(App $a, $contact_id)
912 $o = contacts_tab($a, $contact_id, 1);
914 $contact = DBA::selectFirst('contact', ['url'], ['id' => $contact_id]);
916 if (DBA::isResult($contact)) {
917 $a->page['aside'] = "";
918 Profile::load($a, "", 0, Contact::getDetailsByURL($contact["url"]));
919 $o .= Contact::getPostsFromUrl($contact["url"]);
925 function _contact_detail_for_template(array $rr)
930 switch ($rr['rel']) {
931 case Contact::FRIEND:
932 $dir_icon = 'images/lrarrow.gif';
933 $alt_text = L10n::t('Mutual Friendship');
936 case Contact::FOLLOWER;
937 $dir_icon = 'images/larrow.gif';
938 $alt_text = L10n::t('is a fan of yours');
941 case Contact::SHARING;
942 $dir_icon = 'images/rarrow.gif';
943 $alt_text = L10n::t('you are a fan of');
950 $url = Contact::magicLink($rr['url']);
952 if (strpos($url, 'redir/') === 0) {
953 $sparkle = ' class="sparkle" ';
959 $dir_icon = 'images/larrow.gif';
960 $alt_text = L10n::t('This is you');
966 'img_hover' => L10n::t('Visit %s\'s profile [%s]', $rr['name'], $rr['url']),
967 'edit_hover' => L10n::t('Edit contact'),
968 'photo_menu' => Contact::photoMenu($rr),
970 'alt_text' => $alt_text,
971 'dir_icon' => $dir_icon,
972 'thumb' => ProxyUtils::proxifyUrl($rr['thumb'], false, ProxyUtils::SIZE_THUMB),
973 'name' => htmlentities($rr['name']),
974 'username' => htmlentities($rr['name']),
975 'account_type' => Contact::getAccountType($rr),
976 'sparkle' => $sparkle,
977 'itemurl' => (($rr['addr'] != "") ? $rr['addr'] : $rr['url']),
979 'network' => ContactSelector::networkToName($rr['network'], $rr['url']),
980 'nick' => htmlentities($rr['nick']),
985 * @brief Gives a array with actions which can performed to a given contact
987 * This includes actions like e.g. 'block', 'hide', 'archive', 'delete' and others
989 * @param array $contact Data about the Contact
990 * @return array with contact related actions
992 function contact_actions($contact)
994 $poll_enabled = in_array($contact['network'], [NETWORK_DFRN, NETWORK_OSTATUS, NETWORK_FEED, NETWORK_MAIL]);
995 $contact_actions = [];
997 // Provide friend suggestion only for Friendica contacts
998 if ($contact['network'] === NETWORK_DFRN) {
999 $contact_actions['suggest'] = [
1000 'label' => L10n::t('Suggest friends'),
1001 'url' => 'fsuggest/' . $contact['id'],
1008 if ($poll_enabled) {
1009 $contact_actions['update'] = [
1010 'label' => L10n::t('Update now'),
1011 'url' => 'contacts/' . $contact['id'] . '/update',
1018 $contact_actions['block'] = [
1019 'label' => (intval($contact['blocked']) ? L10n::t('Unblock') : L10n::t('Block') ),
1020 'url' => 'contacts/' . $contact['id'] . '/block',
1021 'title' => L10n::t('Toggle Blocked status'),
1022 'sel' => (intval($contact['blocked']) ? 'active' : ''),
1023 'id' => 'toggle-block',
1026 $contact_actions['ignore'] = [
1027 'label' => (intval($contact['readonly']) ? L10n::t('Unignore') : L10n::t('Ignore') ),
1028 'url' => 'contacts/' . $contact['id'] . '/ignore',
1029 'title' => L10n::t('Toggle Ignored status'),
1030 'sel' => (intval($contact['readonly']) ? 'active' : ''),
1031 'id' => 'toggle-ignore',
1034 $contact_actions['archive'] = [
1035 'label' => (intval($contact['archive']) ? L10n::t('Unarchive') : L10n::t('Archive') ),
1036 'url' => 'contacts/' . $contact['id'] . '/archive',
1037 'title' => L10n::t('Toggle Archive status'),
1038 'sel' => (intval($contact['archive']) ? 'active' : ''),
1039 'id' => 'toggle-archive',
1042 $contact_actions['delete'] = [
1043 'label' => L10n::t('Delete'),
1044 'url' => 'contacts/' . $contact['id'] . '/drop',
1045 'title' => L10n::t('Delete contact'),
1050 return $contact_actions;