Post permissions are now copied from the thread parent

This commit is contained in:
Michael 2023-10-29 08:49:24 +00:00
parent 999c56a284
commit 8e05c282b1
10 changed files with 141 additions and 33 deletions

View file

@ -221,6 +221,10 @@ function item_insert(int $uid, array $request, bool $preview, string $return_pat
DI::contentItem()->postProcessPost($post, $recipients); DI::contentItem()->postProcessPost($post, $recipients);
if (($post['private'] == Item::PRIVATE) && ($post['thr-parent-id'] != $post['uri-id'])) {
DI::contentItem()->copyPermissions($post['thr-parent-id'], $post['uri-id']);
}
Logger::debug('post_complete'); Logger::debug('post_complete');
item_post_return(DI::baseUrl(), $return_path); item_post_return(DI::baseUrl(), $return_path);

View file

@ -48,6 +48,7 @@ use Friendica\Model\User;
use Friendica\Network\HTTPException; use Friendica\Network\HTTPException;
use Friendica\Object\EMail\ItemCCEMail; use Friendica\Object\EMail\ItemCCEMail;
use Friendica\Protocol\Activity; use Friendica\Protocol\Activity;
use Friendica\Protocol\ActivityPub;
use Friendica\Util\ACLFormatter; use Friendica\Util\ACLFormatter;
use Friendica\Util\DateTimeFormat; use Friendica\Util\DateTimeFormat;
use Friendica\Util\Emailer; use Friendica\Util\Emailer;
@ -991,12 +992,14 @@ class Item
$post['deny_gid'] = $owner['deny_gid']; $post['deny_gid'] = $owner['deny_gid'];
} }
if ($post['allow_gid'] || $post['allow_cid'] || $post['deny_gid'] || $post['deny_cid']) { if (!isset($post['private'])) {
$post['private'] = ItemModel::PRIVATE; if ($post['allow_gid'] || $post['allow_cid'] || $post['deny_gid'] || $post['deny_cid']) {
} elseif ($this->pConfig->get($post['uid'], 'system', 'unlisted')) { $post['private'] = ItemModel::PRIVATE;
$post['private'] = ItemModel::UNLISTED; } elseif ($this->pConfig->get($post['uid'], 'system', 'unlisted')) {
} else { $post['private'] = ItemModel::UNLISTED;
$post['private'] = ItemModel::PUBLIC; } else {
$post['private'] = ItemModel::PUBLIC;
}
} }
if (empty($post['contact-id'])) { if (empty($post['contact-id'])) {
@ -1046,6 +1049,8 @@ class Item
Tag::createImplicitMentions($post['uri-id'], $post['thr-parent-id']); Tag::createImplicitMentions($post['uri-id'], $post['thr-parent-id']);
} }
ActivityPub\Transmitter::storeReceiversForItem($post);
Hook::callAll('post_local_end', $post); Hook::callAll('post_local_end', $post);
$author = DBA::selectFirst('contact', ['thumb'], ['uid' => $post['uid'], 'self' => true]); $author = DBA::selectFirst('contact', ['thumb'], ['uid' => $post['uid'], 'self' => true]);
@ -1066,4 +1071,15 @@ class Item
)); ));
} }
} }
public function copyPermissions(int $fromUriId, int $toUriId)
{
$existing = array_column(Tag::getByURIId($toUriId, [Tag::TO, Tag::CC, Tag::BCC]), 'url');
foreach (Tag::getByURIId($fromUriId, [Tag::TO, Tag::CC, Tag::BCC]) as $receiver) {
if (in_array($receiver['url'], $existing)) {
continue;
}
Tag::store($toUriId, $receiver['type'], $receiver['name'], $receiver['url']);
}
}
} }

View file

@ -428,7 +428,7 @@ class Circle
'uid' => $uid, 'uid' => $uid,
'rel' => [Contact::FOLLOWER, Contact::FRIEND], 'rel' => [Contact::FOLLOWER, Contact::FRIEND],
'network' => $networks, 'network' => $networks,
'contact-type' => [Contact::TYPE_UNKNOWN, Contact::TYPE_PERSON], 'contact-type' => [Contact::TYPE_UNKNOWN, Contact::TYPE_PERSON, Contact::TYPE_NEWS, Contact::TYPE_ORGANISATION],
'archive' => false, 'archive' => false,
'pending' => false, 'pending' => false,
'blocked' => false, 'blocked' => false,

View file

@ -880,6 +880,10 @@ class Item
if (is_int($notify) && in_array($notify, Worker::PRIORITIES)) { if (is_int($notify) && in_array($notify, Worker::PRIORITIES)) {
$priority = $notify; $priority = $notify;
} }
// Mastodon style API visibility
$copy_permissions = ($item['visibility'] ?? 'private') == 'private';
unset($item['visibility']);
} else { } else {
$item['network'] = trim(($item['network'] ?? '') ?: Protocol::PHANTOM); $item['network'] = trim(($item['network'] ?? '') ?: Protocol::PHANTOM);
} }
@ -1359,6 +1363,9 @@ class Item
if ($notify) { if ($notify) {
DI::contentItem()->postProcessPost($posted_item); DI::contentItem()->postProcessPost($posted_item);
if ($copy_permissions && ($posted_item['thr-parent-id'] != $posted_item['uri-id']) && ($posted_item['private'] == self::PRIVATE)) {
DI::contentItem()->copyPermissions($posted_item['thr-parent-id'], $posted_item['uri-id']);
}
} else { } else {
Hook::callAll('post_remote_end', $posted_item); Hook::callAll('post_remote_end', $posted_item);
} }

View file

@ -25,13 +25,11 @@ use Friendica\Content\Text\BBCode;
use Friendica\Core\Cache\Enum\Duration; use Friendica\Core\Cache\Enum\Duration;
use Friendica\Core\Logger; use Friendica\Core\Logger;
use Friendica\Core\Protocol; use Friendica\Core\Protocol;
use Friendica\Core\System;
use Friendica\Database\Database; use Friendica\Database\Database;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;
use Friendica\Protocol\ActivityPub; use Friendica\Protocol\ActivityPub;
use Friendica\Util\DateTimeFormat; use Friendica\Util\DateTimeFormat;
use Friendica\Util\Network;
use Friendica\Util\Strings; use Friendica\Util\Strings;
/** /**

View file

@ -25,7 +25,6 @@ use Friendica\Content\PageInfo;
use Friendica\Content\Text\BBCode; use Friendica\Content\Text\BBCode;
use Friendica\Content\Text\Markdown; use Friendica\Content\Text\Markdown;
use Friendica\Core\Protocol; use Friendica\Core\Protocol;
use Friendica\Core\System;
use Friendica\Core\Worker; use Friendica\Core\Worker;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;
@ -192,6 +191,7 @@ class Statuses extends BaseApi
$item['title'] = ''; $item['title'] = '';
$item['body'] = $this->formatStatus($request['status'], $uid); $item['body'] = $this->formatStatus($request['status'], $uid);
$item['app'] = $this->getApp(); $item['app'] = $this->getApp();
$item['visibility'] = $request['visibility'];
switch ($request['visibility']) { switch ($request['visibility']) {
case 'public': case 'public':
@ -209,6 +209,18 @@ class Statuses extends BaseApi
$item['private'] = Item::UNLISTED; $item['private'] = Item::UNLISTED;
break; break;
case 'private': case 'private':
if ($request['in_reply_to_id']) {
$parent_item = Post::selectFirst(Item::ITEM_FIELDLIST, ['uri-id' => $request['in_reply_to_id'], 'uid' => $uid, 'private' => Item::PRIVATE]);
if (!empty($parent_item)) {
$item['allow_cid'] = $parent_item['allow_cid'];
$item['allow_gid'] = $parent_item['allow_gid'];
$item['deny_cid'] = $parent_item['deny_cid'];
$item['deny_gid'] = $parent_item['deny_gid'];
$item['private'] = $parent_item['private'];
break;
}
}
if (!empty($owner['allow_cid'] . $owner['allow_gid'] . $owner['deny_cid'] . $owner['deny_gid'])) { if (!empty($owner['allow_cid'] . $owner['allow_gid'] . $owner['deny_cid'] . $owner['deny_gid'])) {
$item['allow_cid'] = $owner['allow_cid']; $item['allow_cid'] = $owner['allow_cid'];
$item['allow_gid'] = $owner['allow_gid']; $item['allow_gid'] = $owner['allow_gid'];
@ -287,7 +299,7 @@ class Statuses extends BaseApi
} }
$item = DI::contentItem()->expandTags($item, $request['visibility'] == 'direct'); $item = DI::contentItem()->expandTags($item, $request['visibility'] == 'direct');
if (!empty($request['media_ids'])) { if (!empty($request['media_ids'])) {
$item = $this->storeMediaIds($request['media_ids'], $item); $item = $this->storeMediaIds($request['media_ids'], $item);
} }

View file

@ -113,12 +113,26 @@ class PermissionTooltip extends \Friendica\BaseModule
exit; exit;
} }
if (!empty($model['allow_cid']) || !empty($model['allow_gid']) || !empty($model['deny_cid']) || !empty($model['deny_gid'])) {
$receivers = $this->fetchReceiversFromACL($model);
}
$this->httpExit(DI::l10n()->t('Visible to:') . '<br />' . $receivers);
}
/**
* Fetch a list of receivers based on the ACL data
*
* @param array $model
* @return string
*/
private function fetchReceiversFromACL(array $model)
{
$allowed_users = $model['allow_cid']; $allowed_users = $model['allow_cid'];
$allowed_circles = $model['allow_gid']; $allowed_circles = $model['allow_gid'];
$deny_users = $model['deny_cid']; $deny_users = $model['deny_cid'];
$deny_circles = $model['deny_gid']; $deny_circles = $model['deny_gid'];
$o = DI::l10n()->t('Visible to:') . '<br />';
$l = []; $l = [];
if (count($allowed_circles)) { if (count($allowed_circles)) {
@ -165,11 +179,7 @@ class PermissionTooltip extends \Friendica\BaseModule
$l[] = '<strike>' . $contact['name'] . '</strike>'; $l[] = '<strike>' . $contact['name'] . '</strike>';
} }
if (!empty($l)) { return implode(', ', $l);
$this->httpExit($o . implode(', ', $l));
} else {
$this->httpExit($o . $receivers);;
}
} }
/** /**

View file

@ -560,14 +560,13 @@ class Transmitter
* *
* @param array $item Item array * @param array $item Item array
* @param boolean $blindcopy addressing via "bcc" or "cc"? * @param boolean $blindcopy addressing via "bcc" or "cc"?
* @param boolean $expand_followers Expand the list of followers
* @param integer $last_id Last item id for adding receivers * @param integer $last_id Last item id for adding receivers
* *
* @return array with permission data * @return array with permission data
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException * @throws \ImagickException
*/ */
private static function createPermissionBlockForItem(array $item, bool $blindcopy, bool $expand_followers, int $last_id = 0): array private static function createPermissionBlockForItem(array $item, bool $blindcopy, int $last_id = 0): array
{ {
if ($last_id == 0) { if ($last_id == 0) {
$last_id = $item['id']; $last_id = $item['id'];
@ -704,7 +703,7 @@ class Transmitter
$data['to'][] = $actor_profile['followers']; $data['to'][] = $actor_profile['followers'];
} }
} else { } else {
$receiver_list = Item::enumeratePermissions($item, true, $expand_followers); $receiver_list = Item::enumeratePermissions($item, true, false);
foreach ($terms as $term) { foreach ($terms as $term) {
$cid = Contact::getIdForURL($term['url'], $item['uid']); $cid = Contact::getIdForURL($term['url'], $item['uid']);
@ -851,6 +850,28 @@ class Transmitter
unset($receivers['bcc']); unset($receivers['bcc']);
} }
if (!$blindcopy && count($receivers['audience']) == 1) {
$receivers['audience'] = $receivers['audience'][0];
} elseif (!$receivers['audience']) {
unset($receivers['audience']);
}
return $receivers;
}
/**
* Store the receivers for the given item
*
* @param array $item
* @return void
*/
public static function storeReceiversForItem(array $item)
{
$receivers = self::createPermissionBlockForItem($item, true);
if (empty($receivers)) {
return;
}
foreach (['to' => Tag::TO, 'cc' => Tag::CC, 'bcc' => Tag::BCC, 'audience' => Tag::AUDIENCE] as $element => $type) { foreach (['to' => Tag::TO, 'cc' => Tag::CC, 'bcc' => Tag::BCC, 'audience' => Tag::AUDIENCE] as $element => $type) {
if (!empty($receivers[$element])) { if (!empty($receivers[$element])) {
foreach ($receivers[$element] as $receiver) { foreach ($receivers[$element] as $receiver) {
@ -863,6 +884,44 @@ class Transmitter
} }
} }
} }
}
/**
* Get a list of receivers for the provided uri-id
*
* @param array $item
* @param boolean $blindcopy
* @return void
*/
public static function getReceiversForUriId(int $uri_id, bool $blindcopy)
{
$receivers = [
'to' => [],
'cc' => [],
'bcc' => [],
'audience' => [],
];
foreach (Tag::getByURIId($uri_id, [Tag::TO, Tag::CC, Tag::BCC, Tag::AUDIENCE]) as $receiver) {
switch ($receiver['type']) {
case Tag::TO:
$receivers['to'][] = $receiver['url'];
break;
case Tag::CC:
$receivers['cc'][] = $receiver['url'];
break;
case Tag::BCC:
$receivers['bcc'][] = $receiver['url'];
break;
case Tag::AUDIENCE:
$receivers['audience'][] = $receiver['url'];
break;
}
}
if (!$blindcopy) {
unset($receivers['bcc']);
}
if (!$blindcopy && count($receivers['audience']) == 1) { if (!$blindcopy && count($receivers['audience']) == 1) {
$receivers['audience'] = $receivers['audience'][0]; $receivers['audience'] = $receivers['audience'][0];
@ -932,11 +991,13 @@ class Transmitter
} }
$condition = [ $condition = [
'uid' => $uid, 'uid' => $uid,
'archive' => false, 'self' => false,
'pending' => false, 'archive' => false,
'blocked' => false, 'pending' => false,
'network' => Protocol::FEDERATED, 'blocked' => false,
'network' => Protocol::FEDERATED,
'contact-type' => [Contact::TYPE_UNKNOWN, Contact::TYPE_PERSON, Contact::TYPE_NEWS, Contact::TYPE_ORGANISATION],
]; ];
if (!empty($uid)) { if (!empty($uid)) {
@ -980,14 +1041,13 @@ class Transmitter
* @param array $item Item array * @param array $item Item array
* @param integer $uid User ID * @param integer $uid User ID
* @param boolean $personal fetch personal inboxes * @param boolean $personal fetch personal inboxes
* @param integer $last_id Last item id for adding receivers
* @return array with inboxes * @return array with inboxes
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException * @throws \ImagickException
*/ */
public static function fetchTargetInboxes(array $item, int $uid, bool $personal = false, int $last_id = 0): array public static function fetchTargetInboxes(array $item, int $uid, bool $personal = false): array
{ {
$permissions = self::createPermissionBlockForItem($item, true, true, $last_id); $permissions = self::getReceiversForUriId($item['uri-id'], true);
if (empty($permissions)) { if (empty($permissions)) {
return []; return [];
} }
@ -1019,7 +1079,7 @@ class Transmitter
} }
if ($item_profile && ($receiver == $item_profile['followers']) && ($uid == $profile_uid)) { if ($item_profile && ($receiver == $item_profile['followers']) && ($uid == $profile_uid)) {
$inboxes = array_merge_recursive($inboxes, self::fetchTargetInboxesforUser($uid, $personal, self::isAPPost($last_id))); $inboxes = array_merge_recursive($inboxes, self::fetchTargetInboxesforUser($uid, $personal, true));
} else { } else {
$profile = APContact::getByURL($receiver, false); $profile = APContact::getByURL($receiver, false);
if (!empty($profile)) { if (!empty($profile)) {
@ -1119,7 +1179,7 @@ class Transmitter
$data['actor'] = $mail['author-link']; $data['actor'] = $mail['author-link'];
$data['published'] = DateTimeFormat::utc($mail['created'] . '+00:00', DateTimeFormat::ATOM); $data['published'] = DateTimeFormat::utc($mail['created'] . '+00:00', DateTimeFormat::ATOM);
$data['instrument'] = self::getService(); $data['instrument'] = self::getService();
$data = array_merge($data, self::createPermissionBlockForItem($mail, true, false)); $data = array_merge($data, self::createPermissionBlockForItem($mail, true));
if (empty($data['to']) && !empty($data['cc'])) { if (empty($data['to']) && !empty($data['cc'])) {
$data['to'] = $data['cc']; $data['to'] = $data['cc'];
@ -1351,7 +1411,7 @@ class Transmitter
$data['instrument'] = self::getService(); $data['instrument'] = self::getService();
$data = array_merge($data, self::createPermissionBlockForItem($item, false, false)); $data = array_merge($data, self::createPermissionBlockForItem($item, false));
if (in_array($data['type'], ['Create', 'Update', 'Delete'])) { if (in_array($data['type'], ['Create', 'Update', 'Delete'])) {
$data['object'] = self::createNote($item, $api_mode); $data['object'] = self::createNote($item, $api_mode);
@ -1705,7 +1765,7 @@ class Transmitter
$data['name'] = BBCode::toPlaintext($item['title'], false); $data['name'] = BBCode::toPlaintext($item['title'], false);
} }
$permission_block = self::createPermissionBlockForItem($item, false, false); $permission_block = self::getReceiversForUriId($item['uri-id'], false);
$real_quote = false; $real_quote = false;

View file

@ -434,6 +434,7 @@ class HTTPSignature
} }
if (!$curlResult->isSuccess() || empty($curlResult->getBody())) { if (!$curlResult->isSuccess() || empty($curlResult->getBody())) {
Logger::debug('Fetching was unsuccessful', ['url' => $request, 'return-code' => $curlResult->getReturnCode(), 'error-number' => $curlResult->getErrorNumber(), 'error' => $curlResult->getError()]);
return []; return [];
} }

View file

@ -815,7 +815,7 @@ class Notifier
Logger::info('Remote item is no AP post. It will not be distributed.', ['id' => $target_item['id'], 'url' => $target_item['uri'], 'verb' => $target_item['verb']]); Logger::info('Remote item is no AP post. It will not be distributed.', ['id' => $target_item['id'], 'url' => $target_item['uri'], 'verb' => $target_item['verb']]);
return ['count' => 0, 'contacts' => []]; return ['count' => 0, 'contacts' => []];
} elseif ($parent['origin'] && (($target_item['gravity'] != Item::GRAVITY_ACTIVITY) || DI::config()->get('system', 'redistribute_activities'))) { } elseif ($parent['origin'] && (($target_item['gravity'] != Item::GRAVITY_ACTIVITY) || DI::config()->get('system', 'redistribute_activities'))) {
$inboxes = ActivityPub\Transmitter::fetchTargetInboxes($parent, $uid, false, $target_item['id']); $inboxes = ActivityPub\Transmitter::fetchTargetInboxes($parent, $uid);
if (in_array($target_item['private'], [Item::PUBLIC])) { if (in_array($target_item['private'], [Item::PUBLIC])) {
$inboxes = ActivityPub\Transmitter::addRelayServerInboxesForItem($parent['id'], $inboxes); $inboxes = ActivityPub\Transmitter::addRelayServerInboxesForItem($parent['id'], $inboxes);