diff --git a/boot.php b/boot.php index e92be51a9f..b1e98270c0 100644 --- a/boot.php +++ b/boot.php @@ -41,7 +41,7 @@ define('FRIENDICA_PLATFORM', 'Friendica'); define('FRIENDICA_CODENAME', 'The Tazmans Flax-lily'); define('FRIENDICA_VERSION', '2018.08-dev'); define('DFRN_PROTOCOL_VERSION', '2.23'); -define('DB_UPDATE_VERSION', 1282); +define('DB_UPDATE_VERSION', 1283); define('NEW_UPDATE_ROUTINE_VERSION', 1170); /** diff --git a/config/dbstructure.json b/config/dbstructure.json index 2c1ecddc56..c467ba6bc7 100644 --- a/config/dbstructure.json +++ b/config/dbstructure.json @@ -1224,6 +1224,19 @@ "username": ["username(32)"] } }, + "user-contact": { + "comment": "User specific public contact data", + "fields": { + "cid": {"type": "int unsigned", "not null": "1", "default": "0", "primary": "1", "relation": {"contact": "id"}, "comment": "Contact id of the linked public contact"}, + "uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "primary": "1", "relation": {"user": "uid"}, "comment": "User id"}, + "blocked": {"type": "boolean", "comment": "Contact is completely blocked for this user"}, + "ignored": {"type": "boolean", "comment": "Posts from this contact are ignored"}, + "collapsed": {"type": "boolean", "comment": "Posts from this contact are collapsed"} + }, + "indexes": { + "PRIMARY": ["uid", "cid"] + } + }, "user-item": { "comment": "User specific item data", "fields": { diff --git a/database.sql b/database.sql index 0dbeb80dd0..60d4b1c88e 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ -- Friendica 2018.08-dev (The Tazmans Flax-lily) --- DB_UPDATE_VERSION 1282 +-- DB_UPDATE_VERSION 1283 -- ------------------------------------------ @@ -1173,6 +1173,18 @@ CREATE TABLE IF NOT EXISTS `userd` ( INDEX `username` (`username`(32)) ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Deleted usernames'; +-- +-- TABLE user-contact +-- +CREATE TABLE IF NOT EXISTS `user-contact` ( + `cid` int unsigned NOT NULL DEFAULT 0 COMMENT 'Contact id of the linked public contact', + `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', + `blocked` boolean COMMENT 'Contact is completely blocked for this user', + `ignored` boolean COMMENT 'Posts from this contact are ignored', + `collapsed` boolean COMMENT 'Posts from this contact are collapsed', + PRIMARY KEY(`uid`,`cid`) +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='User specific public contact data'; + -- -- TABLE user-item -- diff --git a/include/conversation.php b/include/conversation.php index 5a26700fd7..ca01997f6a 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -517,6 +517,15 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o . "\r\n"; } + } elseif ($mode === 'contacts') { + $items = conversation_add_children($items, true, $order, $uid); + $profile_owner = 0; + + if (!$update) { + $live_update_div = '
' . "\r\n" + . "\r\n"; + } } elseif ($mode === 'search') { $live_update_div = '' . "\r\n"; } @@ -544,7 +553,7 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o $page_template = get_markup_template("conversation.tpl"); if (!empty($items)) { - if ($mode === 'community') { + if (in_array($mode, ['community', 'contacts'])) { $writable = true; } else { $writable = ($items[0]['uid'] == 0) && in_array($items[0]['network'], [Protocol::OSTATUS, Protocol::DIASPORA, Protocol::DFRN]); diff --git a/mod/allfriends.php b/mod/allfriends.php index 32cd23ec6b..7623a9cd06 100644 --- a/mod/allfriends.php +++ b/mod/allfriends.php @@ -35,7 +35,7 @@ function allfriends_content(App $a) $uid = $a->user['uid']; - $contact = DBA::selectFirst('contact', ['name', 'url', 'photo'], ['id' => $cid, 'uid' => local_user()]); + $contact = DBA::selectFirst('contact', ['name', 'url', 'photo', 'uid', 'id'], ['id' => $cid, 'uid' => local_user()]); if (!DBA::isResult($contact)) { return; @@ -96,7 +96,7 @@ function allfriends_content(App $a) $entries[] = $entry; } - $tab_str = contacts_tab($a, $cid, 3); + $tab_str = contacts_tab($a, $contact, 4); $tpl = get_markup_template('viewcontact_template.tpl'); diff --git a/mod/common.php b/mod/common.php index 5955b51436..afe78ce460 100644 --- a/mod/common.php +++ b/mod/common.php @@ -38,14 +38,14 @@ function common_content(App $a) } if ($cmd === 'loc' && $cid) { - $contact = DBA::selectFirst('contact', ['name', 'url', 'photo'], ['id' => $cid, 'uid' => $uid]); + $contact = DBA::selectFirst('contact', ['name', 'url', 'photo', 'uid', 'id'], ['id' => $cid, 'uid' => $uid]); if (DBA::isResult($contact)) { $a->page['aside'] = ""; Profile::load($a, "", 0, Contact::getDetailsByURL($contact["url"])); } } else { - $contact = DBA::selectFirst('contact', ['name', 'url', 'photo'], ['self' => true, 'uid' => $uid]); + $contact = DBA::selectFirst('contact', ['name', 'url', 'photo', 'uid', 'id'], ['self' => true, 'uid' => $uid]); if (DBA::isResult($contact)) { $vcard_widget = replace_macros(get_markup_template("vcard-widget.tpl"), [ @@ -137,7 +137,7 @@ function common_content(App $a) $title = ''; $tab_str = ''; if ($cmd === 'loc' && $cid && local_user() == $uid) { - $tab_str = contacts_tab($a, $cid, 4); + $tab_str = contacts_tab($a, $contact, 4); } else { $title = L10n::t('Common Friends'); } diff --git a/mod/contacts.php b/mod/contacts.php index 9800dbdc1a..4e87697172 100644 --- a/mod/contacts.php +++ b/mod/contacts.php @@ -21,6 +21,7 @@ use Friendica\Model\Profile; use Friendica\Network\Probe; use Friendica\Util\DateTimeFormat; use Friendica\Util\Proxy as ProxyUtils; +use Friendica\Core\ACL; function contacts_init(App $a) { @@ -39,14 +40,18 @@ function contacts_init(App $a) $contact_id = null; $contact = null; - if ((($a->argc == 2) && intval($a->argv[1])) || (($a->argc == 3) && intval($a->argv[1]) && ($a->argv[2] == "posts"))) { + if ((($a->argc == 2) && intval($a->argv[1])) || (($a->argc == 3) && intval($a->argv[1]) && in_array($a->argv[2], ['posts', 'conversations']))) { $contact_id = intval($a->argv[1]); $contact = DBA::selectFirst('contact', [], ['id' => $contact_id, 'uid' => local_user()]); + + if (!DBA::isResult($contact)) { + $contact = DBA::selectFirst('contact', [], ['id' => $contact_id, 'uid' => 0]); + } } if (DBA::isResult($contact)) { if ($contact['self']) { - if (($a->argc == 3) && intval($a->argv[1]) && ($a->argv[2] == "posts")) { + if (($a->argc == 3) && intval($a->argv[1]) && in_array($a->argv[2], ['posts', 'conversations'])) { goaway('profile/' . $contact['nick']); } else { goaway('profile/' . $contact['nick'] . '?tab=profile'); @@ -87,7 +92,11 @@ function contacts_init(App $a) $findpeople_widget = Widget::findPeople(); } - $groups_widget = Group::sidebarWidget('contacts', 'group', 'full', 'everyone', $contact_id); + if ($contact['uid'] != 0) { + $groups_widget = Group::sidebarWidget('contacts', 'group', 'full', 'everyone', $contact_id); + } else { + $groups_widget = null; + } $a->page['aside'] .= replace_macros(get_markup_template("contacts-widget-sidebar.tpl"), [ '$vcard_widget' => $vcard_widget, @@ -131,16 +140,12 @@ function contacts_batch_actions(App $a) $count_actions++; } if (x($_POST, 'contacts_batch_block')) { - $r = _contact_block($contact_id, $orig_record); - if ($r) { - $count_actions++; - } + _contact_block($contact_id); + $count_actions++; } if (x($_POST, 'contacts_batch_ignore')) { - $r = _contact_ignore($contact_id, $orig_record); - if ($r) { - $count_actions++; - } + _contact_ignore($contact_id); + $count_actions++; } if (x($_POST, 'contacts_batch_archive')) { $r = _contact_archive($contact_id, $orig_record); @@ -327,26 +332,16 @@ function _contact_update_profile($contact_id) GContact::updateFromProbe($data["url"]); } -function _contact_block($contact_id, $orig_record) +function _contact_block($contact_id) { - $blocked = (($orig_record['blocked']) ? 0 : 1); - $r = q("UPDATE `contact` SET `blocked` = %d WHERE `id` = %d AND `uid` = %d", - intval($blocked), - intval($contact_id), - intval(local_user()) - ); - return DBA::isResult($r); + $blocked = !Contact::isBlockedByUser($contact_id, local_user()); + Contact::setBlockedForUser($contact_id, local_user(), $blocked); } -function _contact_ignore($contact_id, $orig_record) +function _contact_ignore($contact_id) { - $readonly = (($orig_record['readonly']) ? 0 : 1); - $r = q("UPDATE `contact` SET `readonly` = %d WHERE `id` = %d AND `uid` = %d", - intval($readonly), - intval($contact_id), - intval(local_user()) - ); - return DBA::isResult($r); + $ignored = !Contact::isIgnoredByUser($contact_id, local_user()); + Contact::setIgnoredForUser($contact_id, local_user(), $ignored); } function _contact_archive($contact_id, $orig_record) @@ -376,7 +371,7 @@ function _contact_drop($orig_record) Contact::remove($orig_record['id']); } -function contacts_content(App $a) +function contacts_content(App $a, $update = 0) { $sort_type = 0; $o = ''; @@ -395,48 +390,46 @@ function contacts_content(App $a) $cmd = $a->argv[2]; - $orig_record = DBA::selectFirst('contact', [], ['id' => $contact_id, 'uid' => local_user(), 'self' => false]); + $orig_record = DBA::selectFirst('contact', [], ['id' => $contact_id, 'uid' => [0, local_user()], 'self' => false]); if (!DBA::isResult($orig_record)) { notice(L10n::t('Could not access contact record.') . EOL); goaway('contacts'); return; // NOTREACHED } - if ($cmd === 'update') { + if ($cmd === 'update' && ($orig_record['uid'] != 0)) { _contact_update($contact_id); goaway('contacts/' . $contact_id); // NOTREACHED } - if ($cmd === 'updateprofile') { + if ($cmd === 'updateprofile' && ($orig_record['uid'] != 0)) { _contact_update_profile($contact_id); goaway('crepair/' . $contact_id); // NOTREACHED } if ($cmd === 'block') { - $r = _contact_block($contact_id, $orig_record); - if ($r) { - $blocked = (($orig_record['blocked']) ? 0 : 1); - info((($blocked) ? L10n::t('Contact has been blocked') : L10n::t('Contact has been unblocked')) . EOL); - } + _contact_block($contact_id); + + $blocked = Contact::isBlockedByUser($contact_id, local_user()); + info(($blocked ? L10n::t('Contact has been blocked') : L10n::t('Contact has been unblocked')) . EOL); goaway('contacts/' . $contact_id); return; // NOTREACHED } if ($cmd === 'ignore') { - $r = _contact_ignore($contact_id, $orig_record); - if ($r) { - $readonly = (($orig_record['readonly']) ? 0 : 1); - info((($readonly) ? L10n::t('Contact has been ignored') : L10n::t('Contact has been unignored')) . EOL); - } + _contact_ignore($contact_id); + + $ignored = Contact::isIgnoredByUser($contact_id, local_user()); + info(($ignored ? L10n::t('Contact has been ignored') : L10n::t('Contact has been unignored')) . EOL); goaway('contacts/' . $contact_id); return; // NOTREACHED } - if ($cmd === 'archive') { + if ($cmd === 'archive' && ($orig_record['uid'] != 0)) { $r = _contact_archive($contact_id, $orig_record); if ($r) { $archived = (($orig_record['archive']) ? 0 : 1); @@ -447,7 +440,7 @@ function contacts_content(App $a) return; // NOTREACHED } - if ($cmd === 'drop') { + if ($cmd === 'drop' && ($orig_record['uid'] != 0)) { // Check if we should do HTML-based delete confirmation if (x($_REQUEST, 'confirm')) { //
can't take arguments in its "action" parameter @@ -496,6 +489,9 @@ function contacts_content(App $a) if ($cmd === 'posts') { return contact_posts($a, $contact_id); } + if ($cmd === 'conversations') { + return contact_conversations($a, $contact_id, $update); + } } $_SESSION['return_url'] = $a->query_string; @@ -511,6 +507,9 @@ function contacts_content(App $a) '$baseurl' => System::baseUrl(true), ]); + $contact['blocked'] = Contact::isBlockedByUser($contact['id'], local_user()); + $contact['readonly'] = Contact::isIgnoredByUser($contact['id'], local_user()); + $dir_icon = ''; $relation_text = ''; switch ($contact['rel']) { @@ -533,6 +532,10 @@ function contacts_content(App $a) break; } + if ($contact['uid'] == 0) { + $relation_text = ''; + } + if (!in_array($contact['network'], [Protocol::DFRN, Protocol::OSTATUS, Protocol::DIASPORA])) { $relation_text = ""; } @@ -560,7 +563,7 @@ function contacts_content(App $a) $nettype = L10n::t('Network type: %s', ContactSelector::networkToName($contact['network'], $contact["url"])); // tabs - $tab_str = contacts_tab($a, $contact_id, 2); + $tab_str = contacts_tab($a, $contact, 3); $lost_contact = (($contact['archive'] && $contact['term-date'] > NULL_DATE && $contact['term-date'] < DateTimeFormat::utcNow()) ? L10n::t('Communications lost with this contact!') : ''); @@ -593,26 +596,41 @@ function contacts_content(App $a) $follow = ''; $follow_text = ''; if (in_array($contact['network'], [Protocol::DIASPORA, Protocol::OSTATUS, Protocol::DFRN])) { - if ($contact['rel'] == Contact::FOLLOWER) { - $follow = System::baseUrl(true) . "/follow?url=" . urlencode($contact["url"]); - $follow_text = L10n::t("Connect/Follow"); - } elseif ($contact['rel'] == Contact::FRIEND) { + if (in_array($contact['rel'], [Contact::FRIEND, Contact::SHARING])) { $follow = System::baseUrl(true) . "/unfollow?url=" . urlencode($contact["url"]); $follow_text = L10n::t("Disconnect/Unfollow"); + } else { + $follow = System::baseUrl(true) . "/follow?url=" . urlencode($contact["url"]); + $follow_text = L10n::t("Connect/Follow"); } } + if ($contact['uid'] == 0) { + $follow = System::baseUrl(true) . "/follow?url=" . urlencode($contact["url"]); + $follow_text = L10n::t("Connect/Follow"); + } + // Load contactact related actions like hide, suggest, delete and others $contact_actions = contact_actions($contact); + if ($contact['uid'] != 0) { + $lbl_vis1 = L10n::t('Profile Visibility'); + $lbl_info1 = L10n::t('Contact Information / Notes'); + $contact_settings_label = L10n::t('Contact Settings'); + } else { + $lbl_vis1 = null; + $lbl_info1 = null; + $contact_settings_label = null; + } + $tpl = get_markup_template("contact_edit.tpl"); $o .= replace_macros($tpl, [ '$header' => L10n::t("Contact"), '$tab_str' => $tab_str, '$submit' => L10n::t('Submit'), - '$lbl_vis1' => L10n::t('Profile Visibility'), + '$lbl_vis1' => $lbl_vis1, '$lbl_vis2' => L10n::t('Please choose the profile you would like to display to %s when viewing your profile securely.', $contact['name']), - '$lbl_info1' => L10n::t('Contact Information / Notes'), + '$lbl_info1' => $lbl_info1, '$lbl_info2' => L10n::t('Their personal note'), '$reason' => trim(notags($contact['reason'])), '$infedit' => L10n::t('Edit contact notes'), @@ -669,7 +687,7 @@ function contacts_content(App $a) '$contact_action_button' => L10n::t("Actions"), '$contact_actions' => $contact_actions, '$contact_status' => L10n::t("Status"), - '$contact_settings_label' => L10n::t('Contact Settings'), + '$contact_settings_label' => $contact_settings_label, '$contact_profile_label' => L10n::t("Profile"), ]); @@ -806,6 +824,8 @@ function contacts_content(App $a) ); if (DBA::isResult($r)) { foreach ($r as $rr) { + $rr['blocked'] = Contact::isBlockedByUser($rr['id'], local_user()); + $rr['readonly'] = Contact::isIgnoredByUser($rr['id'], local_user()); $contacts[] = _contact_detail_for_template($rr); } } @@ -844,27 +864,35 @@ function contacts_content(App $a) * Available Pages are 'Status', 'Profile', 'Contacts' and 'Common Friends' * * @param App $a - * @param int $contact_id The ID of the contact + * @param array $contact The contact array * @param int $active_tab 1 if tab should be marked as active * * @return string */ -function contacts_tab($a, $contact_id, $active_tab) +function contacts_tab($a, $contact, $active_tab) { // tabs $tabs = [ [ 'label' => L10n::t('Status'), - 'url' => "contacts/" . $contact_id . "/posts", + 'url' => "contacts/" . $contact['id'] . "/conversations", 'sel' => (($active_tab == 1) ? 'active' : ''), - 'title' => L10n::t('Status Messages and Posts'), + 'title' => L10n::t('Conversations started by this contact'), 'id' => 'status-tab', 'accesskey' => 'm', ], [ - 'label' => L10n::t('Profile'), - 'url' => "contacts/" . $contact_id, + 'label' => L10n::t('Posts and Comments'), + 'url' => "contacts/" . $contact['id'] . "/posts", 'sel' => (($active_tab == 2) ? 'active' : ''), + 'title' => L10n::t('Status Messages and Posts'), + 'id' => 'posts-tab', + 'accesskey' => 'p', + ], + [ + 'label' => L10n::t('Profile'), + 'url' => "contacts/" . $contact['id'], + 'sel' => (($active_tab == 3) ? 'active' : ''), 'title' => L10n::t('Profile Details'), 'id' => 'profile-tab', 'accesskey' => 'o', @@ -872,35 +900,37 @@ function contacts_tab($a, $contact_id, $active_tab) ]; // Show this tab only if there is visible friend list - $x = GContact::countAllFriends(local_user(), $contact_id); + $x = GContact::countAllFriends(local_user(), $contact['id']); if ($x) { $tabs[] = ['label' => L10n::t('Contacts'), - 'url' => "allfriends/" . $contact_id, - 'sel' => (($active_tab == 3) ? 'active' : ''), + 'url' => "allfriends/" . $contact['id'], + 'sel' => (($active_tab == 4) ? 'active' : ''), 'title' => L10n::t('View all contacts'), 'id' => 'allfriends-tab', 'accesskey' => 't']; } // Show this tab only if there is visible common friend list - $common = GContact::countCommonFriends(local_user(), $contact_id); + $common = GContact::countCommonFriends(local_user(), $contact['id']); if ($common) { $tabs[] = ['label' => L10n::t('Common Friends'), - 'url' => "common/loc/" . local_user() . "/" . $contact_id, - 'sel' => (($active_tab == 4) ? 'active' : ''), + 'url' => "common/loc/" . local_user() . "/" . $contact['id'], + 'sel' => (($active_tab == 5) ? 'active' : ''), 'title' => L10n::t('View all common friends'), 'id' => 'common-loc-tab', 'accesskey' => 'd' ]; } - $tabs[] = ['label' => L10n::t('Advanced'), - 'url' => 'crepair/' . $contact_id, - 'sel' => (($active_tab == 5) ? 'active' : ''), - 'title' => L10n::t('Advanced Contact Settings'), - 'id' => 'advanced-tab', - 'accesskey' => 'r' - ]; + if (!empty($contact['uid'])) { + $tabs[] = ['label' => L10n::t('Advanced'), + 'url' => 'crepair/' . $contact['id'], + 'sel' => (($active_tab == 6) ? 'active' : ''), + 'title' => L10n::t('Advanced Contact Settings'), + 'id' => 'advanced-tab', + 'accesskey' => 'r' + ]; + } $tab_tpl = get_markup_template('common_tabs.tpl'); $tab_str = replace_macros($tab_tpl, ['$tabs' => $tabs]); @@ -908,15 +938,70 @@ function contacts_tab($a, $contact_id, $active_tab) return $tab_str; } -function contact_posts(App $a, $contact_id) +function contact_conversations(App $a, $contact_id, $update) { - $o = contacts_tab($a, $contact_id, 1); + $o = ''; - $contact = DBA::selectFirst('contact', ['url'], ['id' => $contact_id]); + if (!$update) { + // We need the editor here to be able to reshare an item. + if (local_user()) { + $x = [ + 'is_owner' => true, + 'allow_location' => $a->user['allow_location'], + 'default_location' => $a->user['default-location'], + 'nickname' => $a->user['nickname'], + 'lockstate' => (is_array($a->user) && (strlen($a->user['allow_cid']) || strlen($a->user['allow_gid']) || strlen($a->user['deny_cid']) || strlen($a->user['deny_gid'])) ? 'lock' : 'unlock'), + 'acl' => ACL::getFullSelectorHTML($a->user, true), + 'bang' => '', + 'visitor' => 'block', + 'profile_uid' => local_user(), + ]; + $o = status_editor($a, $x, 0, true); + } + } + + $contact = DBA::selectFirst('contact', ['uid', 'url', 'id'], ['id' => $contact_id]); + + if (!$update) { + $o .= contacts_tab($a, $contact, 1); + } if (DBA::isResult($contact)) { $a->page['aside'] = ""; - Profile::load($a, "", 0, Contact::getDetailsByURL($contact["url"])); + + $profiledata = Contact::getDetailsByURL($contact["url"]); + + if (local_user()) { + if (in_array($profiledata["network"], [Protocol::DFRN, Protocol::DIASPORA, Protocol::OSTATUS])) { + $profiledata["remoteconnect"] = System::baseUrl()."/follow?url=".urlencode($profiledata["url"]); + } + } + + Profile::load($a, "", 0, $profiledata, true); + $o .= Contact::getPostsFromUrl($contact["url"], true, $update); + } + + return $o; +} + +function contact_posts(App $a, $contact_id) +{ + $contact = DBA::selectFirst('contact', ['uid', 'url', 'id'], ['id' => $contact_id]); + + $o = contacts_tab($a, $contact, 2); + + if (DBA::isResult($contact)) { + $a->page['aside'] = ""; + + $profiledata = Contact::getDetailsByURL($contact["url"]); + + if (local_user()) { + if (in_array($profiledata["network"], [Protocol::DFRN, Protocol::DIASPORA, Protocol::OSTATUS])) { + $profiledata["remoteconnect"] = System::baseUrl()."/follow?url=".urlencode($profiledata["url"]); + } + } + + Profile::load($a, "", 0, $profiledata, true); $o .= Contact::getPostsFromUrl($contact["url"]); } @@ -1032,21 +1117,23 @@ function contact_actions($contact) 'id' => 'toggle-ignore', ]; - $contact_actions['archive'] = [ - 'label' => (intval($contact['archive']) ? L10n::t('Unarchive') : L10n::t('Archive') ), - 'url' => 'contacts/' . $contact['id'] . '/archive', - 'title' => L10n::t('Toggle Archive status'), - 'sel' => (intval($contact['archive']) ? 'active' : ''), - 'id' => 'toggle-archive', - ]; + if ($contact['uid'] != 0) { + $contact_actions['archive'] = [ + 'label' => (intval($contact['archive']) ? L10n::t('Unarchive') : L10n::t('Archive') ), + 'url' => 'contacts/' . $contact['id'] . '/archive', + 'title' => L10n::t('Toggle Archive status'), + 'sel' => (intval($contact['archive']) ? 'active' : ''), + 'id' => 'toggle-archive', + ]; - $contact_actions['delete'] = [ - 'label' => L10n::t('Delete'), - 'url' => 'contacts/' . $contact['id'] . '/drop', - 'title' => L10n::t('Delete contact'), - 'sel' => '', - 'id' => 'delete', - ]; + $contact_actions['delete'] = [ + 'label' => L10n::t('Delete'), + 'url' => 'contacts/' . $contact['id'] . '/drop', + 'title' => L10n::t('Delete contact'), + 'sel' => '', + 'id' => 'delete', + ]; + } return $contact_actions; } diff --git a/mod/crepair.php b/mod/crepair.php index 37956421f5..076c611db4 100644 --- a/mod/crepair.php +++ b/mod/crepair.php @@ -103,7 +103,7 @@ function crepair_content(App $a) $cid = (($a->argc > 1) ? intval($a->argv[1]) : 0); - $contact = null; + $contact = null; if ($cid) { $contact = DBA::selectFirst('contact', [], ['id' => $cid, 'uid' => local_user()]); } @@ -135,7 +135,7 @@ function crepair_content(App $a) $update_profile = in_array($contact['network'], [Protocol::DFRN, Protocol::DIASPORA, Protocol::OSTATUS]); - $tab_str = contacts_tab($a, $contact['id'], 5); + $tab_str = contacts_tab($a, $contact, 5); $tpl = get_markup_template('crepair.tpl'); $o = replace_macros($tpl, [ diff --git a/mod/dirfind.php b/mod/dirfind.php index fd31b2cbf1..f4ddba45d4 100644 --- a/mod/dirfind.php +++ b/mod/dirfind.php @@ -85,6 +85,7 @@ function dirfind_content(App $a, $prefix = "") { $contact = Contact::getDetailsByURL($user_data["url"], local_user()); $objresult->cid = $contact["cid"]; + $objresult->pcid = $contact["zid"]; $j->results[] = $objresult; @@ -163,6 +164,7 @@ function dirfind_content(App $a, $prefix = "") { $objresult = new stdClass(); $objresult->cid = $result["cid"]; + $objresult->pcid = $result["zid"]; $objresult->name = $result["name"]; $objresult->addr = $result["addr"]; $objresult->url = $result["url"]; @@ -217,10 +219,16 @@ function dirfind_content(App $a, $prefix = "") { } else { $connlnk = System::baseUrl().'/follow/?url='.(!empty($jj->connect) ? $jj->connect : $jj->url); $conntxt = L10n::t('Connect'); - $photo_menu = [ - 'profile' => [L10n::t("View Profile"), Contact::magicLink($jj->url)], - 'follow' => [L10n::t("Connect/Follow"), $connlnk] - ]; + + $contact = DBA::selectFirst('contact', [], ['id' => $jj->pcid]); + if (DBA::isResult($contact)) { + $photo_menu = Contact::photoMenu($contact); + } else { + $photo_menu = []; + } + + $photo_menu['profile'] = [L10n::t("View Profile"), Contact::magicLink($jj->url)]; + $photo_menu['follow'] = [L10n::t("Connect/Follow"), $connlnk]; } $jj->photo = str_replace("http:///photo/", get_server()."/photo/", $jj->photo); diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 62dc9e28b7..3e70a5cbde 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -94,6 +94,210 @@ class Contact extends BaseObject * @} */ + /** + * @brief Returns the contact id for the user and the public contact id for a given contact id + * + * @param int $cid Either public contact id or user's contact id + * @param int $uid User ID + * + * @return array with public and user's contact id + */ + private static function getPublicAndUserContacID($cid, $uid) + { + if (empty($uid) || empty($cid)) { + return []; + } + + $contact = DBA::selectFirst('contact', ['id', 'uid', 'url'], ['id' => $cid]); + if (!DBA::isResult($contact)) { + return []; + } + + // We quit when the user id don't match the user id of the provided contact + if (($contact['uid'] != $uid) && ($contact['uid'] != 0)) { + return []; + } + + if ($contact['uid'] != 0) { + $pcid = Contact::getIdForURL($contact['url'], 0, true, ['url' => $contact['url']]); + if (empty($pcid)) { + return []; + } + $ucid = $contact['id']; + } else { + $pcid = $contact['id']; + $ucid = Contact::getIdForURL($contact['url'], $uid, true); + } + + return ['public' => $pcid, 'user' => $ucid]; + } + + /** + * @brief Block contact id for user id + * + * @param int $cid Either public contact id or user's contact id + * @param int $uid User ID + * @param boolean $blocked Is the contact blocked or unblocked? + */ + public static function setBlockedForUser($cid, $uid, $blocked) + { + $cdata = self::getPublicAndUserContacID($cid, $uid); + if (empty($cdata)) { + return; + } + + if ($cdata['user'] != 0) { + DBA::update('contact', ['blocked' => $blocked], ['id' => $cdata['user'], 'pending' => false]); + } + + DBA::update('user-contact', ['blocked' => $blocked], ['cid' => $cdata['public'], 'uid' => $uid], true); + } + + /** + * @brief Returns "block" state for contact id and user id + * + * @param int $cid Either public contact id or user's contact id + * @param int $uid User ID + * + * @return boolean is the contact id blocked for the given user? + */ + public static function isBlockedByUser($cid, $uid) + { + $cdata = self::getPublicAndUserContacID($cid, $uid); + if (empty($cdata)) { + return; + } + + $public_blocked = false; + + if (!empty($cdata['public'])) { + $public_contact = DBA::selectFirst('user-contact', ['blocked'], ['cid' => $cdata['public'], 'uid' => $uid]); + if (DBA::isResult($public_contact)) { + $public_blocked = $public_contact['blocked']; + } + } + + $user_blocked = $public_blocked; + + if (!empty($cdata['user'])) { + $user_contact = DBA::selectFirst('contact', ['blocked'], ['id' => $cdata['user'], 'pending' => false]); + if (DBA::isResult($user_contact)) { + $user_blocked = $user_contact['blocked']; + } + } + + if ($user_blocked != $public_blocked) { + DBA::update('user-contact', ['blocked' => $user_blocked], ['cid' => $cdata['public'], 'uid' => $uid], true); + } + + return $user_blocked; + } + + /** + * @brief Ignore contact id for user id + * + * @param int $cid Either public contact id or user's contact id + * @param int $uid User ID + * @param boolean $ignored Is the contact ignored or unignored? + */ + public static function setIgnoredForUser($cid, $uid, $ignored) + { + $cdata = self::getPublicAndUserContacID($cid, $uid); + if (empty($cdata)) { + return; + } + + if ($cdata['user'] != 0) { + DBA::update('contact', ['readonly' => $ignored], ['id' => $cdata['user'], 'pending' => false]); + } + + DBA::update('user-contact', ['ignored' => $ignored], ['cid' => $cdata['public'], 'uid' => $uid], true); + } + + /** + * @brief Returns "ignore" state for contact id and user id + * + * @param int $cid Either public contact id or user's contact id + * @param int $uid User ID + * + * @return boolean is the contact id ignored for the given user? + */ + public static function isIgnoredByUser($cid, $uid) + { + $cdata = self::getPublicAndUserContacID($cid, $uid); + if (empty($cdata)) { + return; + } + + $public_ignored = false; + + if (!empty($cdata['public'])) { + $public_contact = DBA::selectFirst('user-contact', ['ignored'], ['cid' => $cdata['public'], 'uid' => $uid]); + if (DBA::isResult($public_contact)) { + $public_ignored = $public_contact['ignored']; + } + } + + $user_ignored = $public_ignored; + + if (!empty($cdata['user'])) { + $user_contact = DBA::selectFirst('contact', ['readonly'], ['id' => $cdata['user'], 'pending' => false]); + if (DBA::isResult($user_contact)) { + $user_ignored = $user_contact['readonly']; + } + } + + if ($user_ignored != $public_ignored) { + DBA::update('user-contact', ['ignored' => $user_ignored], ['cid' => $cdata['public'], 'uid' => $uid], true); + } + + return $user_ignored; + } + + /** + * @brief Set "collapsed" for contact id and user id + * + * @param int $cid Either public contact id or user's contact id + * @param int $uid User ID + * @param boolean $collapsed are the contact's posts collapsed or uncollapsed? + */ + public static function setCollapsedForUser($cid, $uid, $collapsed) + { + $cdata = self::getPublicAndUserContacID($cid, $uid); + if (empty($cdata)) { + return; + } + + DBA::update('user-contact', ['collapsed' => $collapsed], ['cid' => $cdata['public'], 'uid' => $uid], true); + } + + /** + * @brief Returns "collapsed" state for contact id and user id + * + * @param int $cid Either public contact id or user's contact id + * @param int $uid User ID + * + * @return boolean is the contact id blocked for the given user? + */ + public static function isCollapsedByUser($cid, $uid) + { + $cdata = self::getPublicAndUserContacID($cid, $uid); + if (empty($cdata)) { + return; + } + + $collapsed = false; + + if (!empty($cdata['public'])) { + $public_contact = DBA::selectFirst('user-contact', ['collapsed'], ['cid' => $cdata['public'], 'uid' => $uid]); + if (DBA::isResult($public_contact)) { + $collapsed = $public_contact['collapsed']; + } + } + + return $collapsed; + } + /** * @brief Returns a list of contacts belonging in a group * @@ -679,15 +883,6 @@ class Contact extends BaseObject $contact_own = DBA::selectFirst('contact', [], ['nurl' => $contact['nurl'], 'network' => $contact['network'], 'uid' => $uid]); if (DBA::isResult($contact_own)) { return self::photoMenu($contact_own, $uid); - } else { - $profile_link = self::magicLink($contact['url']); - $connlnk = 'follow/?url=' . $contact['url']; - $menu = [ - 'profile' => [L10n::t('View Profile'), $profile_link, true], - 'follow' => [L10n::t('Connect/Follow'), $connlnk, true] - ]; - - return $menu; } } @@ -719,7 +914,7 @@ class Contact extends BaseObject $contact_url = System::baseUrl() . '/contacts/' . $contact['id']; - $posts_link = System::baseUrl() . '/contacts/' . $contact['id'] . '/posts'; + $posts_link = System::baseUrl() . '/contacts/' . $contact['id'] . '/conversations'; if (!$contact['self']) { $contact_drop_link = System::baseUrl() . '/contacts/' . $contact['id'] . '/drop?confirm=1'; @@ -729,16 +924,26 @@ class Contact extends BaseObject * Menu array: * "name" => [ "Label", "link", (bool)Should the link opened in a new tab? ] */ - $menu = [ - 'status' => [L10n::t("View Status") , $status_link , true], - 'profile' => [L10n::t("View Profile") , $profile_link , true], - 'photos' => [L10n::t("View Photos") , $photos_link , true], - 'network' => [L10n::t("Network Posts"), $posts_link , false], - 'edit' => [L10n::t("View Contact") , $contact_url , false], - 'drop' => [L10n::t("Drop Contact") , $contact_drop_link, false], - 'pm' => [L10n::t("Send PM") , $pm_url , false], - 'poke' => [L10n::t("Poke") , $poke_link , false], - ]; + if (empty($contact['uid'])) { + $connlnk = 'follow/?url=' . $contact['url']; + $menu = [ + 'profile' => [L10n::t('View Profile'), $profile_link, true], + 'network' => [L10n::t('Network Posts'), $posts_link, false], + 'edit' => [L10n::t('View Contact'), $contact_url, false], + 'follow' => [L10n::t('Connect/Follow'), $connlnk, true], + ]; + } else { + $menu = [ + 'status' => [L10n::t('View Status'), $status_link, true], + 'profile' => [L10n::t('View Profile'), $profile_link, true], + 'photos' => [L10n::t('View Photos'), $photos_link, true], + 'network' => [L10n::t('Network Posts'), $posts_link, false], + 'edit' => [L10n::t('View Contact'), $contact_url, false], + 'drop' => [L10n::t('Drop Contact'), $contact_drop_link, false], + 'pm' => [L10n::t('Send PM'), $pm_url, false], + 'poke' => [L10n::t('Poke'), $poke_link, false], + ]; + } $args = ['contact' => $contact, 'menu' => &$menu]; @@ -1096,7 +1301,7 @@ class Contact extends BaseObject * * @return string posts in HTML */ - public static function getPostsFromUrl($contact_url) + public static function getPostsFromUrl($contact_url, $thread_mode = false, $update = 0) { $a = self::getApp(); @@ -1123,17 +1328,34 @@ class Contact extends BaseObject $contact = ($r[0]["contact-type"] == self::ACCOUNT_TYPE_COMMUNITY ? 'owner-id' : 'author-id'); - $condition = ["`$contact` = ? AND `gravity` IN (?, ?) AND " . $sql, - $author_id, GRAVITY_PARENT, GRAVITY_COMMENT, local_user()]; + if ($thread_mode) { + $condition = ["`$contact` = ? AND `gravity` = ? AND " . $sql, + $author_id, GRAVITY_PARENT, local_user()]; + } else { + $condition = ["`$contact` = ? AND `gravity` IN (?, ?) AND " . $sql, + $author_id, GRAVITY_PARENT, GRAVITY_COMMENT, local_user()]; + } + $params = ['order' => ['created' => true], 'limit' => [$a->pager['start'], $a->pager['itemspage']]]; - $r = Item::selectForUser(local_user(), [], $condition, $params); - $items = Item::inArray($r); + if ($thread_mode) { + $r = Item::selectThreadForUser(local_user(), ['uri'], $condition, $params); - $o = conversation($a, $items, 'contact-posts', false); + $items = Item::inArray($r); - $o .= alt_pager($a, count($items)); + $o = conversation($a, $items, 'contacts', $update); + } else { + $r = Item::selectForUser(local_user(), [], $condition, $params); + + $items = Item::inArray($r); + + $o = conversation($a, $items, 'contact-posts', false); + } + + if (!$update) { + $o .= alt_pager($a, count($items)); + } return $o; } diff --git a/src/Model/Item.php b/src/Model/Item.php index 2f4fcd21c0..d235f0a7f9 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -584,7 +584,13 @@ class Item extends BaseObject } else { $master_table = "`item`"; } - return "$master_table.`visible` AND NOT $master_table.`deleted` AND NOT $master_table.`moderated` AND (`user-item`.`hidden` IS NULL OR NOT `user-item`.`hidden`) "; + return sprintf("$master_table.`visible` AND NOT $master_table.`deleted` AND NOT $master_table.`moderated` + AND (`user-item`.`hidden` IS NULL OR NOT `user-item`.`hidden`) + AND (`user-author`.`blocked` IS NULL OR NOT `user-author`.`blocked`) + AND (`user-author`.`ignored` IS NULL OR NOT `user-author`.`ignored` OR `item`.`gravity` != %d) + AND (`user-owner`.`blocked` IS NULL OR NOT `user-owner`.`blocked`) + AND (`user-owner`.`ignored` IS NULL OR NOT `user-owner`.`ignored` OR `item`.`gravity` != %d) ", + GRAVITY_PARENT, GRAVITY_PARENT); } /** @@ -615,8 +621,10 @@ class Item extends BaseObject OR `contact`.`self` OR `item`.`gravity` != %d OR `contact`.`uid` = 0) STRAIGHT_JOIN `contact` AS `author` ON `author`.`id` = $master_table.`author-id` AND NOT `author`.`blocked` STRAIGHT_JOIN `contact` AS `owner` ON `owner`.`id` = $master_table.`owner-id` AND NOT `owner`.`blocked` - LEFT JOIN `user-item` ON `user-item`.`iid` = $master_table_key AND `user-item`.`uid` = %d", - Contact::SHARING, Contact::FRIEND, GRAVITY_PARENT, intval($uid)); + LEFT JOIN `user-item` ON `user-item`.`iid` = $master_table_key AND `user-item`.`uid` = %d + LEFT JOIN `user-contact` AS `user-author` ON `user-author`.`cid` = $master_table.`author-id` AND `user-author`.`uid` = %d + LEFT JOIN `user-contact` AS `user-owner` ON `user-owner`.`cid` = $master_table.`owner-id` AND `user-owner`.`uid` = %d", + Contact::SHARING, Contact::FRIEND, GRAVITY_PARENT, intval($uid), intval($uid), intval($uid)); } else { if (strpos($sql_commands, "`contact`.") !== false) { $joins .= "LEFT JOIN `contact` ON `contact`.`id` = $master_table.`contact-id`"; diff --git a/src/Object/Thread.php b/src/Object/Thread.php index e396d6cbc7..cd055ee5eb 100644 --- a/src/Object/Thread.php +++ b/src/Object/Thread.php @@ -71,6 +71,10 @@ class Thread extends BaseObject $this->profile_owner = 0; $this->writable = $writable; break; + case 'contacts': + $this->profile_owner = 0; + $this->writable = $writable; + break; default: logger('[ERROR] Conversation::setMode : Unhandled mode ('. $mode .').', LOGGER_DEBUG); return false; diff --git a/view/js/main.js b/view/js/main.js index 3251023332..4788d90a83 100644 --- a/view/js/main.js +++ b/view/js/main.js @@ -383,7 +383,7 @@ function NavUpdate() { $('nav').trigger('nav-update', data.result); // start live update - ['network', 'profile', 'community', 'notes', 'display'].forEach(function (src) { + ['network', 'profile', 'community', 'notes', 'display', 'contacts'].forEach(function (src) { if ($('#live-' + src).length) { liveUpdate(src); } diff --git a/view/templates/contact_edit.tpl b/view/templates/contact_edit.tpl index ab38a1c6c1..344ee296a2 100644 --- a/view/templates/contact_edit.tpl +++ b/view/templates/contact_edit.tpl @@ -20,8 +20,8 @@
  • {{$contact_actions.block.label}}
  • {{$contact_actions.ignore.label}}
  • -
  • {{$contact_actions.archive.label}}
  • -
  • {{$contact_actions.delete.label}}
  • + {{if $contact_actions.archive.url}}
  • {{$contact_actions.archive.label}}
  • {{/if}} + {{if $contact_actions.delete.url}}
  • {{$contact_actions.delete.label}}
  • {{/if}} @@ -63,6 +63,7 @@
    + {{if $contact_settings_label}}
    @@ -100,7 +101,7 @@
    - + {{/if}}
    {{* End of the form *}} diff --git a/view/theme/frio/templates/contact_edit.tpl b/view/theme/frio/templates/contact_edit.tpl index 8dc0df38fe..a72ac5c605 100644 --- a/view/theme/frio/templates/contact_edit.tpl +++ b/view/theme/frio/templates/contact_edit.tpl @@ -26,8 +26,8 @@ {{/if}}
  • {{$contact_actions.block.label}}
  • {{$contact_actions.ignore.label}}
  • -
  • {{$contact_actions.archive.label}}
  • -
  • + {{if $contact_actions.archive.url}}
  • {{$contact_actions.archive.label}}
  • {{/if}} + {{if $contact_actions.delete.url}}
  • {{/if}} @@ -119,6 +119,7 @@
    + {{if $contact_settings_label}}
    + {{/if}} + {{if $lbl_info1}}
    - + {{/if}} + {{if $lbl_vis1}}
    - + {{/if}} {{* End of the form *}} diff --git a/view/theme/frio/theme.php b/view/theme/frio/theme.php index d2737a65ac..4e01289624 100644 --- a/view/theme/frio/theme.php +++ b/view/theme/frio/theme.php @@ -150,8 +150,18 @@ function frio_item_photo_menu(App $a, &$arr) function frio_contact_photo_menu(App $a, &$args) { $cid = $args['contact']['id']; - $pokelink = $args['menu']['poke'][1]; - $pmlink = $args['menu']['pm'][1]; + + if (!empty($args['menu']['poke'])) { + $pokelink = $args['menu']['poke'][1]; + } else { + $pokelink = ''; + } + + if (!empty($args['menu']['poke'])) { + $pmlink = $args['menu']['pm'][1]; + } else { + $pmlink = ''; + } // Set the the indicator for opening the status, profile and photo pages // in a new tab to false if the contact a dfrn (friendica) contact diff --git a/view/theme/vier/templates/contact_edit.tpl b/view/theme/vier/templates/contact_edit.tpl index d5708bd568..63069a7721 100644 --- a/view/theme/vier/templates/contact_edit.tpl +++ b/view/theme/vier/templates/contact_edit.tpl @@ -21,8 +21,8 @@
  • {{$contact_actions.block.label}}
  • {{$contact_actions.ignore.label}}
  • -
  • {{$contact_actions.archive.label}}
  • -
  • {{$contact_actions.delete.label}}
  • + {{if $contact_actions.archive.url}}
  • {{$contact_actions.archive.label}}
  • {{/if}} + {{if $contact_actions.delete.url}}
  • {{$contact_actions.delete.label}}
  • {{/if}} @@ -64,6 +64,7 @@
    + {{if $contact_settings_label}}
    @@ -99,8 +100,8 @@
    {{/if}}
    - + {{/if}}