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\Database\DBM;
17 use Friendica\Model\Contact;
18 use Friendica\Model\GContact;
19 use Friendica\Model\Group;
20 use Friendica\Model\Profile;
21 use Friendica\Network\Probe;
22 use Friendica\Util\DateTimeFormat;
24 require_once 'mod/proxy.php';
26 function contacts_init(App $a)
32 $nets = defaults($_GET, 'nets', '');
37 if (!x($a->page, 'aside')) {
38 $a->page['aside'] = '';
43 if ((($a->argc == 2) && intval($a->argv[1])) || (($a->argc == 3) && intval($a->argv[1]) && ($a->argv[2] == "posts"))) {
44 $contact_id = intval($a->argv[1]);
45 $contact = DBA::selectFirst('contact', [], ['id' => $contact_id, 'uid' => local_user()]);
48 if (DBM::is_result($contact)) {
49 if ($contact['self']) {
50 if (($a->argc == 3) && intval($a->argv[1]) && ($a->argv[2] == "posts")) {
51 goaway('profile/' . $contact['nick']);
53 goaway('profile/' . $contact['nick'] . '?tab=profile');
57 $a->data['contact'] = $contact;
59 if (($a->data['contact']['network'] != "") && ($a->data['contact']['network'] != NETWORK_DFRN)) {
60 $networkname = format_network_name($a->data['contact']['network'], $a->data['contact']['url']);
65 /// @TODO Add nice spaces
66 $vcard_widget = replace_macros(get_markup_template("vcard-widget.tpl"), [
67 '$name' => htmlentities($a->data['contact']['name']),
68 '$photo' => $a->data['contact']['photo'],
69 '$url' => Contact::MagicLink($a->data['contact']['url']),
70 '$addr' => (($a->data['contact']['addr'] != "") ? ($a->data['contact']['addr']) : ""),
71 '$network_name' => $networkname,
72 '$network' => L10n::t('Network:'),
73 '$account_type' => Contact::getAccountType($a->data['contact'])
76 $findpeople_widget = '';
78 $networks_widget = '';
81 $networks_widget = Widget::networks('contacts', $nets);
82 if (isset($_GET['add'])) {
83 $follow_widget = Widget::follow($_GET['add']);
85 $follow_widget = Widget::follow();
88 $findpeople_widget = Widget::findPeople();
91 $groups_widget = Group::sidebarWidget('contacts', 'group', 'full', 'everyone', $contact_id);
93 $a->page['aside'] .= replace_macros(get_markup_template("contacts-widget-sidebar.tpl"), [
94 '$vcard_widget' => $vcard_widget,
95 '$findpeople_widget' => $findpeople_widget,
96 '$follow_widget' => $follow_widget,
97 '$groups_widget' => $groups_widget,
98 '$networks_widget' => $networks_widget
101 $base = System::baseUrl();
102 $tpl = get_markup_template("contacts-head.tpl");
103 $a->page['htmlhead'] .= replace_macros($tpl, [
104 '$baseurl' => System::baseUrl(true),
108 $tpl = get_markup_template("contacts-end.tpl");
109 $a->page['end'] .= replace_macros($tpl, [
110 '$baseurl' => System::baseUrl(true),
115 function contacts_batch_actions(App $a)
117 $contacts_id = $_POST['contact_batch'];
118 if (!is_array($contacts_id)) {
122 $orig_records = q("SELECT * FROM `contact` WHERE `id` IN (%s) AND `uid` = %d AND `self` = 0",
123 implode(",", $contacts_id),
128 foreach ($orig_records as $orig_record) {
129 $contact_id = $orig_record['id'];
130 if (x($_POST, 'contacts_batch_update')) {
131 _contact_update($contact_id);
134 if (x($_POST, 'contacts_batch_block')) {
135 $r = _contact_block($contact_id, $orig_record);
140 if (x($_POST, 'contacts_batch_ignore')) {
141 $r = _contact_ignore($contact_id, $orig_record);
146 if (x($_POST, 'contacts_batch_archive')) {
147 $r = _contact_archive($contact_id, $orig_record);
152 if (x($_POST, 'contacts_batch_drop')) {
153 _contact_drop($orig_record);
157 if ($count_actions > 0) {
158 info(L10n::tt("%d contact edited.", "%d contacts edited.", $count_actions));
161 if (x($_SESSION, 'return_url')) {
162 goaway('' . $_SESSION['return_url']);
168 function contacts_post(App $a)
174 if ($a->argv[1] === "batch") {
175 contacts_batch_actions($a);
179 $contact_id = intval($a->argv[1]);
184 if (!DBA::exists('contact', ['id' => $contact_id, 'uid' => local_user()])) {
185 notice(L10n::t('Could not access contact record.') . EOL);
187 return; // NOTREACHED
190 Addon::callHooks('contact_edit_post', $_POST);
192 $profile_id = intval($_POST['profile-assign']);
194 if (!DBA::exists('profile', ['id' => $profile_id, 'uid' => local_user()])) {
195 notice(L10n::t('Could not locate selected profile.') . EOL);
200 $hidden = intval($_POST['hidden']);
202 $notify = intval($_POST['notify']);
204 $fetch_further_information = intval($_POST['fetch_further_information']);
206 $ffi_keyword_blacklist = escape_tags(trim($_POST['ffi_keyword_blacklist']));
208 $priority = intval($_POST['poll']);
209 if ($priority > 5 || $priority < 0) {
213 $info = escape_tags(trim($_POST['info']));
215 $r = q("UPDATE `contact` SET `profile-id` = %d, `priority` = %d , `info` = '%s',
216 `hidden` = %d, `notify_new_posts` = %d, `fetch_further_information` = %d,
217 `ffi_keyword_blacklist` = '%s' WHERE `id` = %d AND `uid` = %d",
223 intval($fetch_further_information),
224 dbesc($ffi_keyword_blacklist),
228 if (DBM::is_result($r)) {
229 info(L10n::t('Contact updated.') . EOL);
231 notice(L10n::t('Failed to update contact record.') . EOL);
234 $contact = DBA::selectFirst('contact', [], ['id' => $contact_id, 'uid' => local_user()]);
235 if (DBM::is_result($contact)) {
236 $a->data['contact'] = $contact;
242 /* contact actions */
244 function _contact_update($contact_id)
246 $contact = DBA::selectFirst('contact', ['uid', 'url', 'network'], ['id' => $contact_id, 'uid' => local_user()]);
247 if (!DBM::is_result($contact)) {
251 $uid = $contact["uid"];
253 if ($contact["network"] == NETWORK_OSTATUS) {
254 $result = Contact::createFromProbe($uid, $contact["url"], false, $contact["network"]);
256 if ($result['success']) {
257 q("UPDATE `contact` SET `subhub` = 1 WHERE `id` = %d", intval($contact_id));
260 // pull feed and consume it, which should subscribe to the hub.
261 Worker::add(PRIORITY_HIGH, "OnePoll", $contact_id, "force");
265 function _contact_update_profile($contact_id)
267 $contact = DBA::selectFirst('contact', ['uid', 'url', 'network'], ['id' => $contact_id, 'uid' => local_user()]);
268 if (!DBM::is_result($contact)) {
272 $uid = $contact["uid"];
274 $data = Probe::uri($contact["url"], "", 0, false);
276 // "Feed" or "Unknown" is mostly a sign of communication problems
277 if ((in_array($data["network"], [NETWORK_FEED, NETWORK_PHANTOM])) && ($data["network"] != $contact["network"])) {
281 $updatefields = ["name", "nick", "url", "addr", "batch", "notify", "poll", "request", "confirm",
282 "poco", "network", "alias"];
285 if ($data["network"] == NETWORK_OSTATUS) {
286 $result = Contact::createFromProbe($uid, $data["url"], false);
288 if ($result['success']) {
289 $update["subhub"] = true;
293 foreach ($updatefields AS $field) {
294 if (isset($data[$field]) && ($data[$field] != "")) {
295 $update[$field] = $data[$field];
299 $update["nurl"] = normalise_link($data["url"]);
303 if (isset($data["priority"]) && ($data["priority"] != 0)) {
304 $query = "`priority` = " . intval($data["priority"]);
307 foreach ($update AS $key => $value) {
312 $query .= "`" . $key . "` = '" . dbesc($value) . "'";
319 $r = q("UPDATE `contact` SET $query WHERE `id` = %d AND `uid` = %d",
324 // Update the entry in the contact table
325 Contact::updateAvatar($data['photo'], local_user(), $contact_id, true);
327 // Update the entry in the gcontact table
328 GContact::updateFromProbe($data["url"]);
331 function _contact_block($contact_id, $orig_record)
333 $blocked = (($orig_record['blocked']) ? 0 : 1);
334 $r = q("UPDATE `contact` SET `blocked` = %d WHERE `id` = %d AND `uid` = %d",
339 return DBM::is_result($r);
342 function _contact_ignore($contact_id, $orig_record)
344 $readonly = (($orig_record['readonly']) ? 0 : 1);
345 $r = q("UPDATE `contact` SET `readonly` = %d WHERE `id` = %d AND `uid` = %d",
350 return DBM::is_result($r);
353 function _contact_archive($contact_id, $orig_record)
355 $archived = (($orig_record['archive']) ? 0 : 1);
356 $r = q("UPDATE `contact` SET `archive` = %d WHERE `id` = %d AND `uid` = %d",
361 return DBM::is_result($r);
364 function _contact_drop($orig_record)
368 $r = q("SELECT `contact`.*, `user`.* FROM `contact` INNER JOIN `user` ON `contact`.`uid` = `user`.`uid`
369 WHERE `user`.`uid` = %d AND `contact`.`self` LIMIT 1",
370 intval($a->user['uid'])
372 if (!DBM::is_result($r)) {
376 Contact::terminateFriendship($r[0], $orig_record);
377 Contact::remove($orig_record['id']);
380 function contacts_content(App $a)
384 Nav::setSelected('contacts');
387 notice(L10n::t('Permission denied.') . EOL);
392 $contact_id = intval($a->argv[1]);
399 $orig_record = DBA::selectFirst('contact', [], ['id' => $contact_id, 'uid' => local_user(), 'self' => false]);
400 if (!DBM::is_result($orig_record)) {
401 notice(L10n::t('Could not access contact record.') . EOL);
403 return; // NOTREACHED
406 if ($cmd === 'update') {
407 _contact_update($contact_id);
408 goaway('contacts/' . $contact_id);
412 if ($cmd === 'updateprofile') {
413 _contact_update_profile($contact_id);
414 goaway('crepair/' . $contact_id);
418 if ($cmd === 'block') {
419 $r = _contact_block($contact_id, $orig_record);
421 $blocked = (($orig_record['blocked']) ? 0 : 1);
422 info((($blocked) ? L10n::t('Contact has been blocked') : L10n::t('Contact has been unblocked')) . EOL);
425 goaway('contacts/' . $contact_id);
426 return; // NOTREACHED
429 if ($cmd === 'ignore') {
430 $r = _contact_ignore($contact_id, $orig_record);
432 $readonly = (($orig_record['readonly']) ? 0 : 1);
433 info((($readonly) ? L10n::t('Contact has been ignored') : L10n::t('Contact has been unignored')) . EOL);
436 goaway('contacts/' . $contact_id);
437 return; // NOTREACHED
440 if ($cmd === 'archive') {
441 $r = _contact_archive($contact_id, $orig_record);
443 $archived = (($orig_record['archive']) ? 0 : 1);
444 info((($archived) ? L10n::t('Contact has been archived') : L10n::t('Contact has been unarchived')) . EOL);
447 goaway('contacts/' . $contact_id);
448 return; // NOTREACHED
451 if ($cmd === 'drop') {
452 // Check if we should do HTML-based delete confirmation
453 if (x($_REQUEST, 'confirm')) {
454 // <form> can't take arguments in its "action" parameter
455 // so add any arguments as hidden inputs
456 $query = explode_querystring($a->query_string);
458 foreach ($query['args'] as $arg) {
459 if (strpos($arg, 'confirm=') === false) {
460 $arg_parts = explode('=', $arg);
461 $inputs[] = ['name' => $arg_parts[0], 'value' => $arg_parts[1]];
465 $a->page['aside'] = '';
467 return replace_macros(get_markup_template('contact_drop_confirm.tpl'), [
468 '$header' => L10n::t('Drop contact'),
469 '$contact' => _contact_detail_for_template($orig_record),
471 '$message' => L10n::t('Do you really want to delete this contact?'),
472 '$extra_inputs' => $inputs,
473 '$confirm' => L10n::t('Yes'),
474 '$confirm_url' => $query['base'],
475 '$confirm_name' => 'confirmed',
476 '$cancel' => L10n::t('Cancel'),
479 // Now check how the user responded to the confirmation query
480 if (x($_REQUEST, 'canceled')) {
481 if (x($_SESSION, 'return_url')) {
482 goaway('' . $_SESSION['return_url']);
488 _contact_drop($orig_record);
489 info(L10n::t('Contact has been removed.') . EOL);
490 if (x($_SESSION, 'return_url')) {
491 goaway('' . $_SESSION['return_url']);
495 return; // NOTREACHED
497 if ($cmd === 'posts') {
498 return contact_posts($a, $contact_id);
502 $_SESSION['return_url'] = $a->query_string;
504 if ((x($a->data, 'contact')) && (is_array($a->data['contact']))) {
505 $contact_id = $a->data['contact']['id'];
506 $contact = $a->data['contact'];
508 $a->page['htmlhead'] .= replace_macros(get_markup_template('contact_head.tpl'), [
509 '$baseurl' => System::baseUrl(true),
511 $a->page['end'] .= replace_macros(get_markup_template('contact_end.tpl'), [
512 '$baseurl' => System::baseUrl(true),
517 switch ($contact['rel']) {
518 case CONTACT_IS_FRIEND:
519 $dir_icon = 'images/lrarrow.gif';
520 $relation_text = L10n::t('You are mutual friends with %s');
522 case CONTACT_IS_FOLLOWER;
523 $dir_icon = 'images/larrow.gif';
524 $relation_text = L10n::t('You are sharing with %s');
526 case CONTACT_IS_SHARING;
527 $dir_icon = 'images/rarrow.gif';
528 $relation_text = L10n::t('%s is sharing with you');
534 if (!in_array($contact['network'], [NETWORK_DFRN, NETWORK_OSTATUS, NETWORK_DIASPORA])) {
538 $relation_text = sprintf($relation_text, htmlentities($contact['name']));
540 $url = Contact::magicLink($contact['url']);
541 if (strpos($url, 'redir/') === 0) {
542 $sparkle = ' class="sparkle" ';
547 $insecure = L10n::t('Private communications are not available for this contact.');
549 $last_update = (($contact['last-update'] <= NULL_DATE) ? L10n::t('Never') : DateTimeFormat::local($contact['last-update'], 'D, j M Y, g:i A'));
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"));
554 $lblsuggest = (($contact['network'] === NETWORK_DFRN) ? L10n::t('Suggest friends') : '');
556 $poll_enabled = in_array($contact['network'], [NETWORK_DFRN, NETWORK_OSTATUS, NETWORK_FEED, NETWORK_MAIL]);
558 $nettype = L10n::t('Network type: %s', ContactSelector::networkToName($contact['network'], $contact["url"]));
561 $tab_str = contacts_tab($a, $contact_id, 2);
563 $lost_contact = (($contact['archive'] && $contact['term-date'] > NULL_DATE && $contact['term-date'] < DateTimeFormat::utcNow()) ? L10n::t('Communications lost with this contact!') : '');
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')
580 $poll_interval = null;
581 if (in_array($contact['network'], [NETWORK_FEED, NETWORK_MAIL])) {
582 $poll_interval = ContactSelector::pollInterval($contact['priority'], (!$poll_enabled));
585 $profile_select = null;
586 if ($contact['network'] == NETWORK_DFRN) {
587 $profile_select = ContactSelector::profileAssign($contact['profile-id'], (($contact['network'] !== NETWORK_DFRN) ? true : false));
590 /// @todo Only show the following link with DFRN when the remote version supports it
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");
603 // Load contactact related actions like hide, suggest, delete and others
604 $contact_actions = contact_actions($contact);
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,
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"),
674 $arr = ['contact' => $contact, 'output' => $o];
676 Addon::callHooks('contact_edit', $arr);
678 return $arr['output'];
687 if (($a->argc == 2) && ($a->argv[1] === 'all')) {
690 } elseif (($a->argc == 2) && ($a->argv[1] === 'blocked')) {
691 $sql_extra = " AND `blocked` = 1 ";
693 } elseif (($a->argc == 2) && ($a->argv[1] === 'hidden')) {
694 $sql_extra = " AND `hidden` = 1 ";
696 } elseif (($a->argc == 2) && ($a->argv[1] === 'ignored')) {
697 $sql_extra = " AND `readonly` = 1 ";
699 } elseif (($a->argc == 2) && ($a->argv[1] === 'archived')) {
700 $sql_extra = " AND `archive` = 1 ";
703 $sql_extra = " AND `blocked` = 0 ";
706 $search = x($_GET, 'search') ? notags(trim($_GET['search'])) : '';
707 $nets = x($_GET, 'nets' ) ? notags(trim($_GET['nets'])) : '';
711 'label' => L10n::t('Suggestions'),
714 'title' => L10n::t('Suggest potential friends'),
715 'id' => 'suggestions-tab',
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',
727 'label' => L10n::t('Unblocked'),
729 'sel' => ((!$all) && (!$blocked) && (!$hidden) && (!$search) && (!$nets) && (!$ignored) && (!$archived)) ? 'active' : '',
730 'title' => L10n::t('Only show unblocked contacts'),
731 'id' => 'showunblocked-tab',
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',
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',
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',
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',
768 $tab_tpl = get_markup_template('common_tabs.tpl');
769 $t = replace_macros($tab_tpl, ['$tabs' => $tabs]);
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') ";
782 $sql_extra .= sprintf(" AND network = '%s' ", dbesc($nets));
785 $sql_extra2 = ((($sort_type > 0) && ($sort_type <= CONTACT_IS_FRIEND)) ? sprintf(" AND `rel` = %d ", intval($sort_type)) : '');
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'])
791 if (DBM::is_result($r)) {
792 $a->set_pager_total($r[0]['total']);
793 $total = $r[0]['total'];
796 $sql_extra3 = Widget::unavailableNetworks();
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'])
805 if (DBM::is_result($r)) {
806 foreach ($r as $rr) {
807 $contacts[] = _contact_detail_for_template($rr);
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) : ''),
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'),
822 '$contacts' => $contacts,
823 '$contact_drop_confirm' => L10n::t('Do you really want to delete this contact?'),
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'),
832 '$h_batch_actions' => L10n::t('Batch Actions'),
833 '$paginate' => paginate($a),
840 * @brief List of pages for the Contact TabBar
842 * Available Pages are 'Status', 'Profile', 'Contacts' and 'Common Friends'
845 * @param int $contact_id The ID of the contact
846 * @param int $active_tab 1 if tab should be marked as active
850 function contacts_tab($a, $contact_id, $active_tab)
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',
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',
872 // Show this tab only if there is visible friend list
873 $x = GContact::countAllFriends(local_user(), $contact_id);
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',
883 // Show this tab only if there is visible common friend list
884 $common = GContact::countCommonFriends(local_user(), $contact_id);
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',
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',
903 $tab_tpl = get_markup_template('common_tabs.tpl');
904 $tab_str = replace_macros($tab_tpl, ['$tabs' => $tabs]);
909 function contact_posts($a, $contact_id)
911 $o = contacts_tab($a, $contact_id, 1);
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"]);
923 function _contact_detail_for_template($rr)
927 switch ($rr['rel']) {
928 case CONTACT_IS_FRIEND:
929 $dir_icon = 'images/lrarrow.gif';
930 $alt_text = L10n::t('Mutual Friendship');
932 case CONTACT_IS_FOLLOWER;
933 $dir_icon = 'images/larrow.gif';
934 $alt_text = L10n::t('is a fan of yours');
936 case CONTACT_IS_SHARING;
937 $dir_icon = 'images/rarrow.gif';
938 $alt_text = L10n::t('you are a fan of');
944 $url = Contact::magicLink($rr['url']);
945 if (strpos($url, 'redir/') === 0) {
946 $sparkle = ' class="sparkle" ';
952 $dir_icon = 'images/larrow.gif';
953 $alt_text = L10n::t('This is you');
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),
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']),
972 'network' => ContactSelector::networkToName($rr['network'], $rr['url']),
973 'nick' => htmlentities($rr['nick']),
978 * @brief Gives a array with actions which can performed to a given contact
980 * This includes actions like e.g. 'block', 'hide', 'archive', 'delete' and others
982 * @param array $contact Data about the Contact
983 * @return array with contact related actions
985 function contact_actions($contact)
987 $poll_enabled = in_array($contact['network'], [NETWORK_DFRN, NETWORK_OSTATUS, NETWORK_FEED, NETWORK_MAIL]);
988 $contact_actions = [];
990 // Provide friend suggestion only for Friendica contacts
991 if ($contact['network'] === NETWORK_DFRN) {
992 $contact_actions['suggest'] = [
993 'label' => L10n::t('Suggest friends'),
994 'url' => 'fsuggest/' . $contact['id'],
1001 if ($poll_enabled) {
1002 $contact_actions['update'] = [
1003 'label' => L10n::t('Update now'),
1004 'url' => 'contacts/' . $contact['id'] . '/update',
1011 $contact_actions['block'] = [
1012 'label' => (intval($contact['blocked']) ? L10n::t('Unblock') : L10n::t('Block') ),
1013 'url' => 'contacts/' . $contact['id'] . '/block',
1014 'title' => L10n::t('Toggle Blocked status'),
1015 'sel' => (intval($contact['blocked']) ? 'active' : ''),
1016 'id' => 'toggle-block',
1019 $contact_actions['ignore'] = [
1020 'label' => (intval($contact['readonly']) ? L10n::t('Unignore') : L10n::t('Ignore') ),
1021 'url' => 'contacts/' . $contact['id'] . '/ignore',
1022 'title' => L10n::t('Toggle Ignored status'),
1023 'sel' => (intval($contact['readonly']) ? 'active' : ''),
1024 'id' => 'toggle-ignore',
1027 $contact_actions['archive'] = [
1028 'label' => (intval($contact['archive']) ? L10n::t('Unarchive') : L10n::t('Archive') ),
1029 'url' => 'contacts/' . $contact['id'] . '/archive',
1030 'title' => L10n::t('Toggle Archive status'),
1031 'sel' => (intval($contact['archive']) ? 'active' : ''),
1032 'id' => 'toggle-archive',
1035 $contact_actions['delete'] = [
1036 'label' => L10n::t('Delete'),
1037 'url' => 'contacts/' . $contact['id'] . '/drop',
1038 'title' => L10n::t('Delete contact'),
1043 return $contact_actions;