From 9ac24a0f362f16ff0ec30b05aaebf06516788e8a Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 16 Feb 2022 22:56:55 +0000 Subject: [PATCH] More rework to make private communities working --- mod/settings.php | 26 +++++++----- src/Model/Item.php | 30 ++++--------- src/Module/ActivityPub/Objects.php | 1 + src/Protocol/ActivityPub/Receiver.php | 9 ++++ src/Protocol/ActivityPub/Transmitter.php | 42 ++++++++++--------- src/Worker/Notifier.php | 15 ++++--- view/templates/settings/settings.tpl | 12 +++--- .../frio/templates/settings/settings.tpl | 11 ++--- 8 files changed, 79 insertions(+), 67 deletions(-) diff --git a/mod/settings.php b/mod/settings.php index 111e52330..c18a36704 100644 --- a/mod/settings.php +++ b/mod/settings.php @@ -239,7 +239,6 @@ function settings_post(App $a) $allow_location = ((!empty($_POST['allow_location']) && (intval($_POST['allow_location']) == 1)) ? 1: 0); $publish = ((!empty($_POST['profile_in_directory']) && (intval($_POST['profile_in_directory']) == 1)) ? 1: 0); $net_publish = ((!empty($_POST['profile_in_netdirectory']) && (intval($_POST['profile_in_netdirectory']) == 1)) ? 1: 0); - $old_visibility = ((!empty($_POST['visibility']) && (intval($_POST['visibility']) == 1)) ? 1 : 0); $account_type = ((!empty($_POST['account-type']) && (intval($_POST['account-type']))) ? intval($_POST['account-type']) : 0); $page_flags = ((!empty($_POST['page-flags']) && (intval($_POST['page-flags']))) ? intval($_POST['page-flags']) : 0); $blockwall = ((!empty($_POST['blockwall']) && (intval($_POST['blockwall']) == 1)) ? 0: 1); // this setting is inverted! @@ -361,16 +360,21 @@ function settings_post(App $a) DI::pConfig()->set(local_user(), 'system', 'unlisted', $unlisted); DI::pConfig()->set(local_user(), 'system', 'accessible-photos', $accessiblephotos); + if ($account_type == User::ACCOUNT_TYPE_COMMUNITY) { + $str_group_allow = ''; + $str_contact_allow = ''; + $str_group_deny = ''; + $str_contact_deny = ''; + + DI::pConfig()->set(local_user(), 'system', 'unlisted', true); + + $blockwall = true; + $blocktags = true; + $hide_friends = true; + } + if ($page_flags == User::PAGE_FLAGS_PRVGROUP) { - $hidewall = 1; - if (!$str_contact_allow && !$str_group_allow && !$str_contact_deny && !$str_group_deny) { - if ($def_gid) { - info(DI::l10n()->t('Private forum has no privacy permissions. Using default privacy group.')); - $str_group_allow = '<' . $def_gid . '>'; - } else { - notice(DI::l10n()->t('Private forum has no privacy permissions and no default privacy group.')); - } - } + $str_group_allow = '<' . Group::FOLLOWERS . '>'; } $fields = ['username' => $username, 'email' => $email, 'timezone' => $timezone, @@ -756,7 +760,7 @@ function settings_content(App $a) '$allowloc' => ['allow_location', DI::l10n()->t('Use Browser Location:'), ($user['allow_location'] == 1), ''], '$h_prv' => DI::l10n()->t('Security and Privacy Settings'), - '$visibility' => $profile['net-publish'], + '$is_community' => ($user['account-type'] == User::ACCOUNT_TYPE_COMMUNITY), '$maxreq' => ['maxreq', DI::l10n()->t('Maximum Friend Requests/Day:'), $maxreq , DI::l10n()->t("\x28to prevent spam abuse\x29")], '$profile_in_dir' => $profile_in_dir, '$profile_in_net_dir' => ['profile_in_netdirectory', DI::l10n()->t('Allow your profile to be searchable globally?'), $profile['net-publish'], DI::l10n()->t("Activate this setting if you want others to easily find and follow you. Your profile will be searchable on remote systems. This setting also determines whether Friendica will inform search engines that your profile should be indexed or not.") . $net_pub_desc], diff --git a/src/Model/Item.php b/src/Model/Item.php index 69a967b2c..318a8ab51 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -1994,10 +1994,8 @@ class Item Logger::info('Community post will be distributed', ['uri' => $item['uri'], 'uid' => $uid, 'id' => $item_id, 'uri-id' => $item['uri-id'], 'guid' => $item['guid']]); if ($owner['page-flags'] == User::PAGE_FLAGS_PRVGROUP) { - Group::getMembersForForum($owner['id']); - - $allow_cid = '<' . $owner['id'] . '>'; - $allow_gid = '<' . Group::getIdForForum($owner['id']) . '>'; + $allow_cid = ''; + $allow_gid = '<' . Group::FOLLOWERS . '>'; $deny_cid = ''; $deny_gid = ''; self::performActivity($item['id'], 'announce', $uid, $allow_cid, $allow_gid, $deny_cid, $deny_gid); @@ -3210,30 +3208,20 @@ class Item } /** - * Is the given item array a post that is sent as starting post to a forum? + * Does the given uri-id belongs to a post that is sent as starting post to a forum? * - * @param array $item - * @param array $owner + * @param int $uri_id * * @return boolean "true" when it is a forum post */ - public static function isForumPost(array $item, array $owner = []) + public static function isForumPost(int $uri_id) { - if (empty($owner)) { - $owner = User::getOwnerDataById($item['uid']); - if (empty($owner)) { - return false; + foreach (Tag::getByURIId($uri_id, [Tag::EXCLUSIVE_MENTION]) as $tag) { + if (DBA::exists('contact', ['uid' => 0, 'nurl' => Strings::normaliseLink($tag['url']), 'contact-type' => Contact::TYPE_COMMUNITY])) { + return true; } } - - if (($item['author-id'] == $item['owner-id']) || - ($owner['id'] == $item['contact-id']) || - ($item['uri-id'] != $item['parent-uri-id']) || - $item['origin']) { - return false; - } - - return Contact::isForum($item['contact-id']); + return false; } /** diff --git a/src/Module/ActivityPub/Objects.php b/src/Module/ActivityPub/Objects.php index 0a523ea43..f3a37b7da 100644 --- a/src/Module/ActivityPub/Objects.php +++ b/src/Module/ActivityPub/Objects.php @@ -81,6 +81,7 @@ class Objects extends BaseModule $requester = HTTPSignature::getSigner('', $_SERVER); if (!empty($requester)) { $receivers = Item::enumeratePermissions($item, false); + $receivers[] = $item['contact-id']; $validated = in_array(Contact::getIdForURL($requester, $item['uid']), $receivers); if (!$validated) { diff --git a/src/Protocol/ActivityPub/Receiver.php b/src/Protocol/ActivityPub/Receiver.php index 7f08410f6..003cae0c9 100644 --- a/src/Protocol/ActivityPub/Receiver.php +++ b/src/Protocol/ActivityPub/Receiver.php @@ -667,6 +667,15 @@ class Receiver $uid = $receiver['uid']; } } + + // When we haven't found any user yet, we just chose a user who most likely could have access to the content + if (empty($uid)) { + $contact = Contact::selectFirst(['uid'], ['nurl' => Strings::normaliseLink($actor), 'rel' => [Contact::SHARING, Contact::FRIEND]]); + if (!empty($contact['uid'])) { + $uid = $contact['uid']; + } + } + return $uid; } diff --git a/src/Protocol/ActivityPub/Transmitter.php b/src/Protocol/ActivityPub/Transmitter.php index 7be55898c..884f9430f 100644 --- a/src/Protocol/ActivityPub/Transmitter.php +++ b/src/Protocol/ActivityPub/Transmitter.php @@ -509,28 +509,33 @@ class Transmitter /** * Creates an array of permissions from an item thread * - * @param array $item Item array - * @param boolean $blindcopy addressing via "bcc" or "cc"? - * @param integer $last_id Last item id for adding receivers - * @param boolean $forum_post "true" means that we are sending content to a forum + * @param array $item Item array + * @param boolean $blindcopy addressing via "bcc" or "cc"? + * @param integer $last_id Last item id for adding receivers * * @return array with permission data * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ - private static function createPermissionBlockForItem($item, $blindcopy, $last_id = 0, $forum_post = false) + private static function createPermissionBlockForItem($item, $blindcopy, $last_id = 0) { if ($last_id == 0) { $last_id = $item['id']; } $always_bcc = false; + $is_forum = false; + $follower = ''; // Check if we should always deliver our stuff via BCC if (!empty($item['uid'])) { - $profile = User::getOwnerDataById($item['uid']); - if (!empty($profile)) { - $always_bcc = $profile['hide-friends']; + $owner = User::getOwnerDataById($item['uid']); + if (!empty($owner)) { + $always_bcc = $owner['hide-friends']; + $is_forum = ($owner['account-type'] == User::ACCOUNT_TYPE_COMMUNITY) && $owner['manually-approve']; + + $profile = APContact::getByURL($owner['url'], false); + $follower = $profile['followers'] ?? ''; } } @@ -613,7 +618,9 @@ class Transmitter } } - if (!$exclusive) { + if ($is_forum && !$exclusive && !empty($follower)) { + $data['cc'][] = $follower; + } elseif (!$exclusive) { foreach ($receiver_list as $receiver) { $contact = DBA::selectFirst('contact', ['url', 'hidden', 'network', 'protocol', 'gsid'], ['id' => $receiver, 'network' => Protocol::FEDERATED]); if (!DBA::isResult($contact) || !self::isAPContact($contact, $networks)) { @@ -652,9 +659,7 @@ class Transmitter } } elseif (!$exclusive) { // Public thread parent post always are directed to the followers. - // This mustn't be done by posts that are directed to forum servers via the exclusive mention. - // But possibly in that case we could add the "followers" collection of the forum to the message. - if (($item['private'] != Item::PRIVATE) && !$forum_post) { + if ($item['private'] != Item::PRIVATE) { $data['cc'][] = $actor_profile['followers']; } } @@ -820,18 +825,17 @@ class Transmitter /** * Fetches an array of inboxes for the given item and user * - * @param array $item Item array - * @param integer $uid User ID - * @param boolean $personal fetch personal inboxes - * @param integer $last_id Last item id for adding receivers - * @param boolean $forum_post "true" means that we are sending content to a forum + * @param array $item Item array + * @param integer $uid User ID + * @param boolean $personal fetch personal inboxes + * @param integer $last_id Last item id for adding receivers * @return array with inboxes * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ - public static function fetchTargetInboxes($item, $uid, $personal = false, $last_id = 0, $forum_post = false) + public static function fetchTargetInboxes($item, $uid, $personal = false, $last_id = 0) { - $permissions = self::createPermissionBlockForItem($item, true, $last_id, $forum_post); + $permissions = self::createPermissionBlockForItem($item, true, $last_id); if (empty($permissions)) { return []; } diff --git a/src/Worker/Notifier.php b/src/Worker/Notifier.php index 3a5c7f13e..f79b46637 100644 --- a/src/Worker/Notifier.php +++ b/src/Worker/Notifier.php @@ -235,13 +235,13 @@ class Notifier } // Special treatment for forum posts - if (Item::isForumPost($target_item, $owner)) { + if (Item::isForumPost($target_item['uri-id'])) { $relay_to_owner = true; $direct_forum_delivery = true; } // Avoid that comments in a forum thread are sent to OStatus - if (Item::isForumPost($parent, $owner)) { + if (Item::isForumPost($parent['uri-id'])) { $direct_forum_delivery = true; } @@ -729,6 +729,14 @@ class Notifier $uid = $target_item['contact-uid'] ?: $target_item['uid']; + // Update the locally stored follower list when we deliver to a forum + foreach (Tag::getByURIId($target_item['uri-id'], [Tag::EXCLUSIVE_MENTION]) as $tag) { + $target_contact = Contact::getByURL(Strings::normaliseLink($tag['url']), null, [], $uid); + if (($target_contact['contact-type'] == Contact::TYPE_COMMUNITY) && $target_contact['manually-approve']) { + Group::getMembersForForum($target_contact['id']); + } + } + if ($target_item['origin']) { $inboxes = ActivityPub\Transmitter::fetchTargetInboxes($target_item, $uid); @@ -738,9 +746,6 @@ class Notifier } Logger::info('Origin item ' . $target_item['id'] . ' with URL ' . $target_item['uri'] . ' will be distributed.'); - } elseif (Item::isForumPost($target_item, $owner)) { - $inboxes = ActivityPub\Transmitter::fetchTargetInboxes($target_item, $uid, false, 0, true); - Logger::info('Forum item ' . $target_item['id'] . ' with URL ' . $target_item['uri'] . ' will be distributed.'); } elseif (!DBA::exists('conversation', ['item-uri' => $target_item['uri'], 'protocol' => Conversation::PARCEL_ACTIVITYPUB])) { Logger::info('Remote item ' . $target_item['id'] . ' with URL ' . $target_item['uri'] . ' is no AP post. It will not be distributed.'); return ['count' => 0, 'contacts' => []]; diff --git a/view/templates/settings/settings.tpl b/view/templates/settings/settings.tpl index c2ed1a942..c0e821ac7 100644 --- a/view/templates/settings/settings.tpl +++ b/view/templates/settings/settings.tpl @@ -39,28 +39,28 @@

{{$h_prv}}

- - - {{include file="field_input.tpl" field=$maxreq}} {{$profile_in_dir nofilter}} {{include file="field_checkbox.tpl" field=$profile_in_net_dir}} - {{include file="field_checkbox.tpl" field=$hide_friends}} + {{if not $is_community}}{{include file="field_checkbox.tpl" field=$hide_friends}}{{/if}} {{include file="field_checkbox.tpl" field=$hide_wall}} - {{include file="field_checkbox.tpl" field=$unlisted}} + {{if not $is_community}}{{include file="field_checkbox.tpl" field=$unlisted}}{{/if}} {{include file="field_checkbox.tpl" field=$accessiblephotos}} + {{if not $is_community}} {{include file="field_checkbox.tpl" field=$blockwall}} {{include file="field_checkbox.tpl" field=$blocktags}} + {{/if}} {{include file="field_checkbox.tpl" field=$unkmail}} {{include file="field_input.tpl" field=$cntunkmail}} {{$group_select nofilter}} - + {{if not $is_community}}

{{$permissions}}

{{$aclselect nofilter}} + {{/if}}
diff --git a/view/theme/frio/templates/settings/settings.tpl b/view/theme/frio/templates/settings/settings.tpl index bf129cc12..b9e105e88 100644 --- a/view/theme/frio/templates/settings/settings.tpl +++ b/view/theme/frio/templates/settings/settings.tpl @@ -70,28 +70,29 @@
- - - {{include file="field_input.tpl" field=$maxreq}} {{$profile_in_dir nofilter}} {{include file="field_checkbox.tpl" field=$profile_in_net_dir}} - {{include file="field_checkbox.tpl" field=$hide_friends}} + {{if not $is_community}}{{include file="field_checkbox.tpl" field=$hide_friends}}{{/if}} {{include file="field_checkbox.tpl" field=$hide_wall}} - {{include file="field_checkbox.tpl" field=$unlisted}} + {{if not $is_community}}{{include file="field_checkbox.tpl" field=$unlisted}}{{/if}} {{include file="field_checkbox.tpl" field=$accessiblephotos}} + {{if not $is_community}} {{include file="field_checkbox.tpl" field=$blockwall}} {{include file="field_checkbox.tpl" field=$blocktags}} + {{/if}} {{include file="field_checkbox.tpl" field=$unkmail}} {{include file="field_input.tpl" field=$cntunkmail}} {{$group_select nofilter}} + {{if not $is_community}}

{{$permissions}}

{{$aclselect nofilter}} + {{/if}}