1
0
Fork 0

Merge pull request #13226 from annando/lemmy

Better support for "audience" / simplified Lemmy processing
This commit is contained in:
Hypolite Petovan 2023-06-23 17:12:38 -04:00 committed by GitHub
commit b731f8a0ad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 1123 additions and 802 deletions

View file

@ -1,6 +1,6 @@
-- ------------------------------------------ -- ------------------------------------------
-- Friendica 2023.09-dev (Giant Rhubarb) -- Friendica 2023.09-dev (Giant Rhubarb)
-- DB_UPDATE_VERSION 1520 -- DB_UPDATE_VERSION 1521
-- ------------------------------------------ -- ------------------------------------------
@ -1968,6 +1968,7 @@ CREATE VIEW `post-user-view` AS SELECT
`author`.`addr` AS `author-addr`, `author`.`addr` AS `author-addr`,
IF (`contact`.`url` = `author`.`url` AND `contact`.`name` != '', `contact`.`name`, `author`.`name`) AS `author-name`, IF (`contact`.`url` = `author`.`url` AND `contact`.`name` != '', `contact`.`name`, `author`.`name`) AS `author-name`,
`author`.`nick` AS `author-nick`, `author`.`nick` AS `author-nick`,
`author`.`alias` AS `author-alias`,
IF (`contact`.`url` = `author`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `author`.`thumb`) AS `author-avatar`, IF (`contact`.`url` = `author`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `author`.`thumb`) AS `author-avatar`,
`author`.`network` AS `author-network`, `author`.`network` AS `author-network`,
`author`.`blocked` AS `author-blocked`, `author`.`blocked` AS `author-blocked`,
@ -1980,6 +1981,7 @@ CREATE VIEW `post-user-view` AS SELECT
`owner`.`addr` AS `owner-addr`, `owner`.`addr` AS `owner-addr`,
IF (`contact`.`url` = `owner`.`url` AND `contact`.`name` != '', `contact`.`name`, `owner`.`name`) AS `owner-name`, IF (`contact`.`url` = `owner`.`url` AND `contact`.`name` != '', `contact`.`name`, `owner`.`name`) AS `owner-name`,
`owner`.`nick` AS `owner-nick`, `owner`.`nick` AS `owner-nick`,
`owner`.`alias` AS `owner-alias`,
IF (`contact`.`url` = `owner`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `owner`.`thumb`) AS `owner-avatar`, IF (`contact`.`url` = `owner`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `owner`.`thumb`) AS `owner-avatar`,
`owner`.`network` AS `owner-network`, `owner`.`network` AS `owner-network`,
`owner`.`blocked` AS `owner-blocked`, `owner`.`blocked` AS `owner-blocked`,
@ -2145,6 +2147,7 @@ CREATE VIEW `post-thread-user-view` AS SELECT
`author`.`addr` AS `author-addr`, `author`.`addr` AS `author-addr`,
IF (`contact`.`url` = `author`.`url` AND `contact`.`name` != '', `contact`.`name`, `author`.`name`) AS `author-name`, IF (`contact`.`url` = `author`.`url` AND `contact`.`name` != '', `contact`.`name`, `author`.`name`) AS `author-name`,
`author`.`nick` AS `author-nick`, `author`.`nick` AS `author-nick`,
`author`.`alias` AS `author-alias`,
IF (`contact`.`url` = `author`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `author`.`thumb`) AS `author-avatar`, IF (`contact`.`url` = `author`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `author`.`thumb`) AS `author-avatar`,
`author`.`network` AS `author-network`, `author`.`network` AS `author-network`,
`author`.`blocked` AS `author-blocked`, `author`.`blocked` AS `author-blocked`,
@ -2157,6 +2160,7 @@ CREATE VIEW `post-thread-user-view` AS SELECT
`owner`.`addr` AS `owner-addr`, `owner`.`addr` AS `owner-addr`,
IF (`contact`.`url` = `owner`.`url` AND `contact`.`name` != '', `contact`.`name`, `owner`.`name`) AS `owner-name`, IF (`contact`.`url` = `owner`.`url` AND `contact`.`name` != '', `contact`.`name`, `owner`.`name`) AS `owner-name`,
`owner`.`nick` AS `owner-nick`, `owner`.`nick` AS `owner-nick`,
`owner`.`alias` AS `owner-alias`,
IF (`contact`.`url` = `owner`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `owner`.`thumb`) AS `owner-avatar`, IF (`contact`.`url` = `owner`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `owner`.`thumb`) AS `owner-avatar`,
`owner`.`network` AS `owner-network`, `owner`.`network` AS `owner-network`,
`owner`.`blocked` AS `owner-blocked`, `owner`.`blocked` AS `owner-blocked`,
@ -2308,6 +2312,7 @@ CREATE VIEW `post-view` AS SELECT
`author`.`addr` AS `author-addr`, `author`.`addr` AS `author-addr`,
`author`.`name` AS `author-name`, `author`.`name` AS `author-name`,
`author`.`nick` AS `author-nick`, `author`.`nick` AS `author-nick`,
`author`.`alias` AS `author-alias`,
`author`.`thumb` AS `author-avatar`, `author`.`thumb` AS `author-avatar`,
`author`.`network` AS `author-network`, `author`.`network` AS `author-network`,
`author`.`blocked` AS `author-blocked`, `author`.`blocked` AS `author-blocked`,
@ -2320,6 +2325,7 @@ CREATE VIEW `post-view` AS SELECT
`owner`.`addr` AS `owner-addr`, `owner`.`addr` AS `owner-addr`,
`owner`.`name` AS `owner-name`, `owner`.`name` AS `owner-name`,
`owner`.`nick` AS `owner-nick`, `owner`.`nick` AS `owner-nick`,
`owner`.`alias` AS `owner-alias`,
`owner`.`thumb` AS `owner-avatar`, `owner`.`thumb` AS `owner-avatar`,
`owner`.`network` AS `owner-network`, `owner`.`network` AS `owner-network`,
`owner`.`blocked` AS `owner-blocked`, `owner`.`blocked` AS `owner-blocked`,
@ -2448,6 +2454,7 @@ CREATE VIEW `post-thread-view` AS SELECT
`author`.`addr` AS `author-addr`, `author`.`addr` AS `author-addr`,
`author`.`name` AS `author-name`, `author`.`name` AS `author-name`,
`author`.`nick` AS `author-nick`, `author`.`nick` AS `author-nick`,
`author`.`alias` AS `author-alias`,
`author`.`thumb` AS `author-avatar`, `author`.`thumb` AS `author-avatar`,
`author`.`network` AS `author-network`, `author`.`network` AS `author-network`,
`author`.`blocked` AS `author-blocked`, `author`.`blocked` AS `author-blocked`,
@ -2460,6 +2467,7 @@ CREATE VIEW `post-thread-view` AS SELECT
`owner`.`addr` AS `owner-addr`, `owner`.`addr` AS `owner-addr`,
`owner`.`name` AS `owner-name`, `owner`.`name` AS `owner-name`,
`owner`.`nick` AS `owner-nick`, `owner`.`nick` AS `owner-nick`,
`owner`.`alias` AS `owner-alias`,
`owner`.`thumb` AS `owner-avatar`, `owner`.`thumb` AS `owner-avatar`,
`owner`.`network` AS `owner-network`, `owner`.`network` AS `owner-network`,
`owner`.`blocked` AS `owner-blocked`, `owner`.`blocked` AS `owner-blocked`,

View file

@ -117,7 +117,7 @@ function photos_init(App $a)
$tpl = Renderer::getMarkupTemplate("photos_head.tpl"); $tpl = Renderer::getMarkupTemplate("photos_head.tpl");
DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl,[ DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl, [
'$ispublic' => DI::l10n()->t('everybody') '$ispublic' => DI::l10n()->t('everybody')
]); ]);
} }
@ -214,13 +214,15 @@ function photos_post(App $a)
// get the list of photos we are about to delete // get the list of photos we are about to delete
if ($visitor) { if ($visitor) {
$r = DBA::toArray(DBA::p("SELECT distinct(`resource-id`) as `rid` FROM `photo` WHERE `contact-id` = ? AND `uid` = ? AND `album` = ?", $r = DBA::toArray(DBA::p(
"SELECT distinct(`resource-id`) as `rid` FROM `photo` WHERE `contact-id` = ? AND `uid` = ? AND `album` = ?",
$visitor, $visitor,
$page_owner_uid, $page_owner_uid,
$album $album
)); ));
} else { } else {
$r = DBA::toArray(DBA::p("SELECT distinct(`resource-id`) as `rid` FROM `photo` WHERE `uid` = ? AND `album` = ?", $r = DBA::toArray(DBA::p(
"SELECT distinct(`resource-id`) as `rid` FROM `photo` WHERE `uid` = ? AND `album` = ?",
DI::userSession()->getLocalUserId(), DI::userSession()->getLocalUserId(),
$album $album
)); ));
@ -258,7 +260,6 @@ function photos_post(App $a)
// same as above but remove single photo // same as above but remove single photo
if ($visitor) { if ($visitor) {
$condition = ['contact-id' => $visitor, 'uid' => $page_owner_uid, 'resource-id' => DI::args()->getArgv()[3]]; $condition = ['contact-id' => $visitor, 'uid' => $page_owner_uid, 'resource-id' => DI::args()->getArgv()[3]];
} else { } else {
$condition = ['uid' => DI::userSession()->getLocalUserId(), 'resource-id' => DI::args()->getArgv()[3]]; $condition = ['uid' => DI::userSession()->getLocalUserId(), 'resource-id' => DI::args()->getArgv()[3]];
} }
@ -405,7 +406,7 @@ function photos_post(App $a)
if (strpos($tag, '@') === 0) { if (strpos($tag, '@') === 0) {
$profile = ''; $profile = '';
$contact = null; $contact = null;
$name = substr($tag,1); $name = substr($tag, 1);
if ((strpos($name, '@')) || (strpos($name, 'http://'))) { if ((strpos($name, '@')) || (strpos($name, 'http://'))) {
$newname = $name; $newname = $name;
@ -441,13 +442,15 @@ function photos_post(App $a)
if ($tagcid) { if ($tagcid) {
$contact = DBA::selectFirst('contact', [], ['id' => $tagcid, 'uid' => $page_owner_uid]); $contact = DBA::selectFirst('contact', [], ['id' => $tagcid, 'uid' => $page_owner_uid]);
} else { } else {
$newname = str_replace('_',' ',$name); $newname = str_replace('_', ' ', $name);
//select someone from this user's contacts by name //select someone from this user's contacts by name
$contact = DBA::selectFirst('contact', [], ['name' => $newname, 'uid' => $page_owner_uid]); $contact = DBA::selectFirst('contact', [], ['name' => $newname, 'uid' => $page_owner_uid]);
if (!DBA::isResult($contact)) { if (!DBA::isResult($contact)) {
//select someone by attag or nick and the name passed in //select someone by attag or nick and the name passed in
$contact = DBA::selectFirst('contact', [], $contact = DBA::selectFirst(
'contact',
[],
['(`attag` = ? OR `nick` = ?) AND `uid` = ?', $name, $name, $page_owner_uid], ['(`attag` = ? OR `nick` = ?) AND `uid` = ?', $name, $name, $page_owner_uid],
['order' => ['attag' => true]] ['order' => ['attag' => true]]
); );
@ -689,11 +692,13 @@ function photos_content(App $a)
$uploader = ''; $uploader = '';
$ret = ['post_url' => 'profile/' . $user['nickname'] . '/photos', $ret = [
'addon_text' => $uploader, 'post_url' => 'profile/' . $user['nickname'] . '/photos',
'default_upload' => true]; 'addon_text' => $uploader,
'default_upload' => true
];
Hook::callAll('photo_upload_form',$ret); Hook::callAll('photo_upload_form', $ret);
$default_upload_box = Renderer::replaceMacros(Renderer::getMarkupTemplate('photos_default_uploader_box.tpl'), []); $default_upload_box = Renderer::replaceMacros(Renderer::getMarkupTemplate('photos_default_uploader_box.tpl'), []);
$default_upload_submit = Renderer::replaceMacros(Renderer::getMarkupTemplate('photos_default_uploader_submit.tpl'), [ $default_upload_submit = Renderer::replaceMacros(Renderer::getMarkupTemplate('photos_default_uploader_submit.tpl'), [
@ -705,7 +710,7 @@ function photos_content(App $a)
$umf_bytes = Strings::getBytesFromShorthand(ini_get('upload_max_filesize')); $umf_bytes = Strings::getBytesFromShorthand(ini_get('upload_max_filesize'));
// Per Friendica definition a value of '0' means unlimited: // Per Friendica definition a value of '0' means unlimited:
If ($mis_bytes == 0) { if ($mis_bytes == 0) {
$mis_bytes = INF; $mis_bytes = INF;
} }
@ -719,7 +724,7 @@ function photos_content(App $a)
$aclselect_e = ($visitor ? '' : ACL::getFullSelectorHTML(DI::page(), $a->getLoggedInUserId())); $aclselect_e = ($visitor ? '' : ACL::getFullSelectorHTML(DI::page(), $a->getLoggedInUserId()));
$o .= Renderer::replaceMacros($tpl,[ $o .= Renderer::replaceMacros($tpl, [
'$pagename' => DI::l10n()->t('Upload Photos'), '$pagename' => DI::l10n()->t('Upload Photos'),
'$sessid' => session_id(), '$sessid' => session_id(),
'$usage' => $usage_message, '$usage' => $usage_message,
@ -747,7 +752,7 @@ function photos_content(App $a)
if ($datatype === 'album') { if ($datatype === 'album') {
// if $datum is not a valid hex, redirect to the default page // if $datum is not a valid hex, redirect to the default page
if (is_null($datum) || !Strings::isHex($datum)) { if (is_null($datum) || !Strings::isHex($datum)) {
DI::baseUrl()->redirect('photos/' . $user['nickname']. '/album'); DI::baseUrl()->redirect('photos/' . $user['nickname'] . '/album');
} }
$album = hex2bin($datum); $album = hex2bin($datum);
@ -756,7 +761,8 @@ function photos_content(App $a)
} }
$total = 0; $total = 0;
$r = DBA::toArray(DBA::p("SELECT `resource-id`, max(`scale`) AS `scale` FROM `photo` WHERE `uid` = ? AND `album` = ? $r = DBA::toArray(DBA::p(
"SELECT `resource-id`, max(`scale`) AS `scale` FROM `photo` WHERE `uid` = ? AND `album` = ?
AND `scale` <= 4 $sql_extra GROUP BY `resource-id`", AND `scale` <= 4 $sql_extra GROUP BY `resource-id`",
$owner_uid, $owner_uid,
$album $album
@ -775,7 +781,8 @@ function photos_content(App $a)
$order = 'DESC'; $order = 'DESC';
} }
$r = DBA::toArray(DBA::p("SELECT `resource-id`, ANY_VALUE(`id`) AS `id`, ANY_VALUE(`filename`) AS `filename`, $r = DBA::toArray(DBA::p(
"SELECT `resource-id`, ANY_VALUE(`id`) AS `id`, ANY_VALUE(`filename`) AS `filename`,
ANY_VALUE(`type`) AS `type`, max(`scale`) AS `scale`, ANY_VALUE(`desc`) as `desc`, ANY_VALUE(`type`) AS `type`, max(`scale`) AS `scale`, ANY_VALUE(`desc`) as `desc`,
ANY_VALUE(`created`) as `created` ANY_VALUE(`created`) as `created`
FROM `photo` WHERE `uid` = ? AND `album` = ? FROM `photo` WHERE `uid` = ? AND `album` = ?
@ -809,7 +816,7 @@ function photos_content(App $a)
$album_e = $album; $album_e = $album;
$o .= Renderer::replaceMacros($edit_tpl,[ $o .= Renderer::replaceMacros($edit_tpl, [
'$nametext' => DI::l10n()->t('New album name: '), '$nametext' => DI::l10n()->t('New album name: '),
'$nickname' => $user['nickname'], '$nickname' => $user['nickname'],
'$album' => $album_e, '$album' => $album_e,
@ -843,16 +850,16 @@ function photos_content(App $a)
$desc_e = $rr['desc']; $desc_e = $rr['desc'];
$photos[] = [ $photos[] = [
'id' => $rr['id'], 'id' => $rr['id'],
'twist' => ' ' . ($twist ? 'rotleft' : 'rotright') . rand(2,4), 'twist' => ' ' . ($twist ? 'rotleft' : 'rotright') . rand(2, 4),
'link' => 'photos/' . $user['nickname'] . '/image/' . $rr['resource-id'] 'link' => 'photos/' . $user['nickname'] . '/image/' . $rr['resource-id']
. ($order_field === 'created' ? '?order=created' : ''), . ($order_field === 'created' ? '?order=created' : ''),
'title' => DI::l10n()->t('View Photo'), 'title' => DI::l10n()->t('View Photo'),
'src' => 'photo/' . $rr['resource-id'] . '-' . $rr['scale'] . '.' .$ext, 'src' => 'photo/' . $rr['resource-id'] . '-' . $rr['scale'] . '.' . $ext,
'alt' => $imgalt_e, 'alt' => $imgalt_e,
'desc'=> $desc_e, 'desc' => $desc_e,
'ext' => $ext, 'ext' => $ext,
'hash'=> $rr['resource-id'], 'hash' => $rr['resource-id'],
]; ];
} }
} }
@ -870,7 +877,6 @@ function photos_content(App $a)
]); ]);
return $o; return $o;
} }
// Display one photo // Display one photo
@ -955,7 +961,7 @@ function photos_content(App $a)
} }
$tpl = Renderer::getMarkupTemplate('photo_edit_head.tpl'); $tpl = Renderer::getMarkupTemplate('photo_edit_head.tpl');
DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl,[ DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl, [
'$prevlink' => $prevlink, '$prevlink' => $prevlink,
'$nextlink' => $nextlink '$nextlink' => $nextlink
]); ]);
@ -967,7 +973,7 @@ function photos_content(App $a)
if ($nextlink) { if ($nextlink) {
$nextlink = [$nextlink, '<div class="icon next"></div>']; $nextlink = [$nextlink, '<div class="icon next"></div>'];
} }
} }
} }
if (count($ph) == 1) { if (count($ph) == 1) {
@ -1007,12 +1013,12 @@ function photos_content(App $a)
} }
$photo = [ $photo = [
'href' => 'photo/' . $hires['resource-id'] . '-' . $hires['scale'] . '.' . $phototypes[$hires['type']], 'href' => 'photo/' . $hires['resource-id'] . '-' . $hires['scale'] . '.' . $phototypes[$hires['type']],
'title'=> DI::l10n()->t('View Full Size'), 'title' => DI::l10n()->t('View Full Size'),
'src' => 'photo/' . $lores['resource-id'] . '-' . $lores['scale'] . '.' . $phototypes[$lores['type']] . '?_u=' . DateTimeFormat::utcNow('ymdhis'), 'src' => 'photo/' . $lores['resource-id'] . '-' . $lores['scale'] . '.' . $phototypes[$lores['type']] . '?_u=' . DateTimeFormat::utcNow('ymdhis'),
'height' => $hires['height'], 'height' => $hires['height'],
'width' => $hires['width'], 'width' => $hires['width'],
'album' => $hires['album'], 'album' => $hires['album'],
'filename' => $hires['filename'], 'filename' => $hires['filename'],
]; ];
@ -1079,12 +1085,12 @@ function photos_content(App $a)
$edit = Renderer::replaceMacros($edit_tpl, [ $edit = Renderer::replaceMacros($edit_tpl, [
'$id' => $ph[0]['id'], '$id' => $ph[0]['id'],
'$album' => ['albname', DI::l10n()->t('New album name'), $album_e,''], '$album' => ['albname', DI::l10n()->t('New album name'), $album_e, ''],
'$caption' => ['desc', DI::l10n()->t('Caption'), $caption_e, ''], '$caption' => ['desc', DI::l10n()->t('Caption'), $caption_e, ''],
'$tags' => ['newtag', DI::l10n()->t('Add a Tag'), "", DI::l10n()->t('Example: @bob, @Barbara_Jensen, @jim@example.com, #California, #camping')], '$tags' => ['newtag', DI::l10n()->t('Add a Tag'), "", DI::l10n()->t('Example: @bob, @Barbara_Jensen, @jim@example.com, #California, #camping')],
'$rotate_none' => ['rotate', DI::l10n()->t('Do not rotate'),0,'', true], '$rotate_none' => ['rotate', DI::l10n()->t('Do not rotate'), 0, '', true],
'$rotate_cw' => ['rotate', DI::l10n()->t("Rotate CW \x28right\x29"),1,''], '$rotate_cw' => ['rotate', DI::l10n()->t("Rotate CW \x28right\x29"), 1, ''],
'$rotate_ccw' => ['rotate', DI::l10n()->t("Rotate CCW \x28left\x29"),2,''], '$rotate_ccw' => ['rotate', DI::l10n()->t("Rotate CCW \x28left\x29"), 2, ''],
'$nickname' => $user['nickname'], '$nickname' => $user['nickname'],
'$resource_id' => $ph[0]['resource-id'], '$resource_id' => $ph[0]['resource-id'],
@ -1179,7 +1185,7 @@ function photos_content(App $a)
$qcomment = $words ? explode("\n", $words) : []; $qcomment = $words ? explode("\n", $words) : [];
} }
$comments .= Renderer::replaceMacros($cmnt_tpl,[ $comments .= Renderer::replaceMacros($cmnt_tpl, [
'$return_path' => '', '$return_path' => '',
'$jsreload' => $return_path, '$jsreload' => $return_path,
'$id' => $link_item['id'], '$id' => $link_item['id'],
@ -1203,13 +1209,19 @@ function photos_content(App $a)
$activity = DI::activity(); $activity = DI::activity();
if (($activity->match($item['verb'], Activity::LIKE) || if (($activity->match($item['verb'], Activity::LIKE) ||
$activity->match($item['verb'], Activity::DISLIKE)) && $activity->match($item['verb'], Activity::DISLIKE)) &&
($item['gravity'] != Item::GRAVITY_PARENT)) { ($item['gravity'] != Item::GRAVITY_PARENT)
) {
continue; continue;
} }
$author = ['uid' => 0, 'id' => $item['author-id'], $author = [
'network' => $item['author-network'], 'url' => $item['author-link']]; 'uid' => 0,
'id' => $item['author-id'],
'network' => $item['author-network'],
'url' => $item['author-link'],
'alias' => $item['author-alias']
];
$profile_url = Contact::magicLinkByContact($author); $profile_url = Contact::magicLinkByContact($author);
if (strpos($profile_url, 'contact/redir/') === 0) { if (strpos($profile_url, 'contact/redir/') === 0) {
$sparkle = ' sparkle'; $sparkle = ' sparkle';
@ -1228,7 +1240,7 @@ function photos_content(App $a)
$title_e = $item['title']; $title_e = $item['title'];
$body_e = BBCode::convertForUriId($item['uri-id'], $item['body']); $body_e = BBCode::convertForUriId($item['uri-id'], $item['body']);
$comments .= Renderer::replaceMacros($template,[ $comments .= Renderer::replaceMacros($template, [
'$id' => $item['id'], '$id' => $item['id'],
'$profile_url' => $profile_url, '$profile_url' => $profile_url,
'$name' => $item['author-name'], '$name' => $item['author-name'],

View file

@ -154,7 +154,8 @@ class Conversation
'uid' => 0, 'uid' => 0,
'id' => $activity['author-id'], 'id' => $activity['author-id'],
'network' => $activity['author-network'], 'network' => $activity['author-network'],
'url' => $activity['author-link'] 'url' => $activity['author-link'],
'alias' => $activity['author-alias'],
]; ];
$url = Contact::magicLinkByContact($author); $url = Contact::magicLinkByContact($author);
if (strpos($url, 'contact/redir/') === 0) { if (strpos($url, 'contact/redir/') === 0) {
@ -272,7 +273,7 @@ class Conversation
$phrase = $this->l10n->tt('<button type="button" %2$s>%1$d person</button> attends', '<button type="button" %2$s>%1$d people</button> attend', $total, $spanatts); $phrase = $this->l10n->tt('<button type="button" %2$s>%1$d person</button> attends', '<button type="button" %2$s>%1$d people</button> attend', $total, $spanatts);
break; break;
case 'attendno': case 'attendno':
$phrase = $this->l10n->tt('<button type="button" %2$s>%1$d person</button> doesn\'t attend','<button type="button" %2$s>%1$d people</button> don\'t attend', $total, $spanatts); $phrase = $this->l10n->tt('<button type="button" %2$s>%1$d person</button> doesn\'t attend', '<button type="button" %2$s>%1$d people</button> don\'t attend', $total, $spanatts);
break; break;
case 'attendmaybe': case 'attendmaybe':
$phrase = $this->l10n->tt('<button type="button" %2$s>%1$d person</button> attends maybe', '<button type="button" %2$s>%1$d people</button> attend maybe', $total, $spanatts); $phrase = $this->l10n->tt('<button type="button" %2$s>%1$d person</button> attends maybe', '<button type="button" %2$s>%1$d people</button> attend maybe', $total, $spanatts);
@ -538,7 +539,7 @@ class Conversation
if (!$update) { if (!$update) {
$live_update_div = '<div id="live-contact"></div>' . "\r\n" $live_update_div = '<div id="live-contact"></div>' . "\r\n"
. "<script> var profile_uid = -1; var netargs = '" . substr($this->args->getCommand(), 8) . "<script> var profile_uid = -1; var netargs = '" . substr($this->args->getCommand(), 8)
."?f='; </script>\r\n"; . "?f='; </script>\r\n";
} }
} elseif ($mode === self::MODE_SEARCH) { } elseif ($mode === self::MODE_SEARCH) {
$live_update_div = '<div id="live-search"></div>' . "\r\n"; $live_update_div = '<div id="live-search"></div>' . "\r\n";
@ -626,7 +627,13 @@ class Conversation
$tags = Tag::populateFromItem($item); $tags = Tag::populateFromItem($item);
$author = ['uid' => 0, 'id' => $item['author-id'], 'network' => $item['author-network'], 'url' => $item['author-link']]; $author = [
'uid' => 0,
'id' => $item['author-id'],
'network' => $item['author-network'],
'url' => $item['author-link'],
'alias' => $item['author-alias'],
];
$profile_link = Contact::magicLinkByContact($author); $profile_link = Contact::magicLinkByContact($author);
$sparkle = ''; $sparkle = '';
@ -857,7 +864,8 @@ class Conversation
$row['causer-avatar'] = $contact['thumb']; $row['causer-avatar'] = $contact['thumb'];
$row['causer-name'] = $contact['name']; $row['causer-name'] = $contact['name'];
} elseif (($row['gravity'] == ItemModel::GRAVITY_ACTIVITY) && ($row['verb'] == Activity::ANNOUNCE) && } elseif (($row['gravity'] == ItemModel::GRAVITY_ACTIVITY) && ($row['verb'] == Activity::ANNOUNCE) &&
($row['author-id'] == $activity['causer-id'])) { ($row['author-id'] == $activity['causer-id'])
) {
return $row; return $row;
} }
} }
@ -893,9 +901,15 @@ class Conversation
} }
if (in_array($row['gravity'], [ItemModel::GRAVITY_PARENT, ItemModel::GRAVITY_COMMENT]) && !empty($row['causer-id'])) { if (in_array($row['gravity'], [ItemModel::GRAVITY_PARENT, ItemModel::GRAVITY_COMMENT]) && !empty($row['causer-id'])) {
$causer = ['uid' => 0, 'id' => $row['causer-id'], 'network' => $row['causer-network'], 'url' => $row['causer-link']]; $causer = [
'uid' => 0,
'id' => $row['causer-id'],
'network' => $row['causer-network'],
'url' => $row['causer-link'],
'alias' => $row['causer-alias'],
];
$row['reshared'] = $this->l10n->t('%s reshared this.', '<a href="'. htmlentities(Contact::magicLinkByContact($causer)) .'">' . htmlentities($row['causer-name']) . '</a>'); $row['reshared'] = $this->l10n->t('%s reshared this.', '<a href="' . htmlentities(Contact::magicLinkByContact($causer)) . '">' . htmlentities($row['causer-name']) . '</a>');
} }
$row['direction'] = ['direction' => 3, 'title' => (empty($row['causer-id']) ? $this->l10n->t('Reshared') : $this->l10n->t('Reshared by %s <%s>', $row['causer-name'], $row['causer-link']))]; $row['direction'] = ['direction' => 3, 'title' => (empty($row['causer-id']) ? $this->l10n->t('Reshared') : $this->l10n->t('Reshared by %s <%s>', $row['causer-name'], $row['causer-link']))];
break; break;
@ -995,15 +1009,21 @@ class Conversation
$condition = DBA::mergeConditions($condition, ["(`gravity` != ? OR `origin`)", ItemModel::GRAVITY_ACTIVITY]); $condition = DBA::mergeConditions($condition, ["(`gravity` != ? OR `origin`)", ItemModel::GRAVITY_ACTIVITY]);
} }
$condition = DBA::mergeConditions($condition, $condition = DBA::mergeConditions(
["`uid` IN (0, ?) AND (NOT `vid` IN (?, ?, ?) OR `vid` IS NULL)", $uid, Verb::getID(Activity::FOLLOW), Verb::getID(Activity::VIEW), Verb::getID(Activity::READ)]); $condition,
["`uid` IN (0, ?) AND (NOT `vid` IN (?, ?, ?) OR `vid` IS NULL)", $uid, Verb::getID(Activity::FOLLOW), Verb::getID(Activity::VIEW), Verb::getID(Activity::READ)]
);
$condition = DBA::mergeConditions($condition, ["(`uid` != ? OR `private` != ?)", 0, ItemModel::PRIVATE]); $condition = DBA::mergeConditions($condition, ["(`uid` != ? OR `private` != ?)", 0, ItemModel::PRIVATE]);
$condition = DBA::mergeConditions($condition, $condition = DBA::mergeConditions(
["`visible` AND NOT `deleted` AND NOT `author-blocked` AND NOT `owner-blocked` $condition,
[
"`visible` AND NOT `deleted` AND NOT `author-blocked` AND NOT `owner-blocked`
AND ((NOT `contact-pending` AND (`contact-rel` IN (?, ?))) OR `self` OR `contact-uid` = ?)", AND ((NOT `contact-pending` AND (`contact-rel` IN (?, ?))) OR `self` OR `contact-uid` = ?)",
Contact::SHARING, Contact::FRIEND, 0]); Contact::SHARING, Contact::FRIEND, 0
]
);
$thread_parents = Post::select(['uri-id', 'causer-id'], $condition, ['order' => ['uri-id' => false, 'uid']]); $thread_parents = Post::select(['uri-id', 'causer-id'], $condition, ['order' => ['uri-id' => false, 'uid']]);
@ -1110,8 +1130,10 @@ class Conversation
$items[$key]['user-collapsed-author'] = !$always_display && in_array($row['author-id'], $collapses); $items[$key]['user-collapsed-author'] = !$always_display && in_array($row['author-id'], $collapses);
$items[$key]['user-collapsed-owner'] = !$always_display && in_array($row['owner-id'], $collapses); $items[$key]['user-collapsed-owner'] = !$always_display && in_array($row['owner-id'], $collapses);
if (in_array($mode, [self::MODE_COMMUNITY, self::MODE_NETWORK]) && if (
(in_array($row['author-id'], $blocks) || in_array($row['owner-id'], $blocks) || in_array($row['author-id'], $ignores) || in_array($row['owner-id'], $ignores))) { in_array($mode, [self::MODE_COMMUNITY, self::MODE_NETWORK]) &&
(in_array($row['author-id'], $blocks) || in_array($row['owner-id'], $blocks) || in_array($row['author-id'], $ignores) || in_array($row['owner-id'], $ignores))
) {
unset($items[$key]); unset($items[$key]);
} }
} }
@ -1146,7 +1168,7 @@ class Conversation
$condition = DBA::mergeConditions(['parent-uri-id' => $uriids, 'gravity' => ItemModel::GRAVITY_ACTIVITY, 'verb' => $verbs], ["NOT `deleted`"]); $condition = DBA::mergeConditions(['parent-uri-id' => $uriids, 'gravity' => ItemModel::GRAVITY_ACTIVITY, 'verb' => $verbs], ["NOT `deleted`"]);
$separator = chr(255) . chr(255) . chr(255); $separator = chr(255) . chr(255) . chr(255);
$sql = "SELECT `thr-parent-id`, `body`, `verb`, COUNT(*) AS `total`, GROUP_CONCAT(REPLACE(`author-name`, '" . $separator . "', ' ') SEPARATOR '". $separator ."' LIMIT 50) AS `title` FROM `post-view` WHERE " . array_shift($condition) . " GROUP BY `thr-parent-id`, `verb`, `body`"; $sql = "SELECT `thr-parent-id`, `body`, `verb`, COUNT(*) AS `total`, GROUP_CONCAT(REPLACE(`author-name`, '" . $separator . "', ' ') SEPARATOR '" . $separator . "' LIMIT 50) AS `title` FROM `post-view` WHERE " . array_shift($condition) . " GROUP BY `thr-parent-id`, `verb`, `body`";
$emojis = []; $emojis = [];
@ -1287,7 +1309,7 @@ class Conversation
// Searches the post item in the children // Searches the post item in the children
$j = 0; $j = 0;
while ($child['children'][$j]['verb'] !== Activity::POST && $j < count($child['children'])) { while ($child['children'][$j]['verb'] !== Activity::POST && $j < count($child['children'])) {
$j ++; $j++;
} }
$moved_item = $child['children'][$j]; $moved_item = $child['children'][$j];
@ -1361,8 +1383,10 @@ class Conversation
* items and add them as children of their top-level post. * items and add them as children of their top-level post.
*/ */
foreach ($parents as $i => $parent) { foreach ($parents as $i => $parent) {
$parents[$i]['children'] = array_merge($this->getItemChildren($item_array, $parent, true), $parents[$i]['children'] = array_merge(
$this->getItemChildren($item_array, $parent, false)); $this->getItemChildren($item_array, $parent, true),
$this->getItemChildren($item_array, $parent, false)
);
} }
foreach ($parents as $i => $parent) { foreach ($parents as $i => $parent) {

View file

@ -78,7 +78,7 @@ class GroupManager
$groupList = []; $groupList = [];
$fields = ['id', 'url', 'name', 'micro', 'thumb', 'avatar', 'network', 'uid']; $fields = ['id', 'url', 'alias', 'name', 'micro', 'thumb', 'avatar', 'network', 'uid'];
$contacts = DBA::select('account-user-view', $fields, $condition, $params); $contacts = DBA::select('account-user-view', $fields, $condition, $params);
if (!$contacts) { if (!$contacts) {
return $groupList; return $groupList;
@ -87,6 +87,7 @@ class GroupManager
while ($contact = DBA::fetch($contacts)) { while ($contact = DBA::fetch($contacts)) {
$groupList[] = [ $groupList[] = [
'url' => $contact['url'], 'url' => $contact['url'],
'alias' => $contact['alias'],
'name' => $contact['name'], 'name' => $contact['name'],
'id' => $contact['id'], 'id' => $contact['id'],
'micro' => $contact['micro'], 'micro' => $contact['micro'],

View file

@ -305,18 +305,20 @@ class Item
} }
$author_arr = [ $author_arr = [
'uid' => 0, 'uid' => 0,
'id' => $item['author-id'], 'id' => $item['author-id'],
'network' => $item['author-network'], 'network' => $item['author-network'],
'url' => $item['author-link'], 'url' => $item['author-link'],
'alias' => $item['author-lias'],
]; ];
$author = '[url=' . Contact::magicLinkByContact($author_arr) . ']' . $item['author-name'] . '[/url]'; $author = '[url=' . Contact::magicLinkByContact($author_arr) . ']' . $item['author-name'] . '[/url]';
$author_arr = [ $author_arr = [
'uid' => 0, 'uid' => 0,
'id' => $obj['author-id'], 'id' => $obj['author-id'],
'network' => $obj['author-network'], 'network' => $obj['author-network'],
'url' => $obj['author-link'], 'url' => $obj['author-link'],
'alias' => $obj['author-alias'],
]; ];
$objauthor = '[url=' . Contact::magicLinkByContact($author_arr) . ']' . $obj['author-name'] . '[/url]'; $objauthor = '[url=' . Contact::magicLinkByContact($author_arr) . ']' . $obj['author-name'] . '[/url]';
@ -372,10 +374,11 @@ class Item
} }
$author = [ $author = [
'uid' => 0, 'uid' => 0,
'id' => $item['author-id'], 'id' => $item['author-id'],
'network' => $item['author-network'], 'network' => $item['author-network'],
'url' => $item['author-link'], 'url' => $item['author-link'],
'alias' => $item['author-alias'],
]; ];
$profile_link = Contact::magicLinkByContact($author, $item['author-link']); $profile_link = Contact::magicLinkByContact($author, $item['author-link']);
if (strpos($profile_link, 'contact/redir/') === 0) { if (strpos($profile_link, 'contact/redir/') === 0) {

View file

@ -422,7 +422,8 @@ class HTML
{ {
$URLSearchString = "^\[\]"; $URLSearchString = "^\[\]";
$matches = ["/\[url\=([$URLSearchString]*)\].*?\[\/url\]/ism", $matches = [
"/\[url\=([$URLSearchString]*)\].*?\[\/url\]/ism",
"/\[url\]([$URLSearchString]*)\[\/url\]/ism", "/\[url\]([$URLSearchString]*)\[\/url\]/ism",
"/\[img\=[0-9]*x[0-9]*\](.*?)\[\/img\]/ism", "/\[img\=[0-9]*x[0-9]*\](.*?)\[\/img\]/ism",
"/\[img\](.*?)\[\/img\]/ism", "/\[img\](.*?)\[\/img\]/ism",
@ -531,8 +532,10 @@ class HTML
$ignore = false; $ignore = false;
// A list of some links that should be ignored // A list of some links that should be ignored
$list = ["/user/", "/tag/", "/group/", "/circle/", "/profile/", "/search?search=", "/search?tag=", "mailto:", "/u/", "/node/", $list = [
"//plus.google.com/", "//twitter.com/"]; "/user/", "/tag/", "/group/", "/circle/", "/profile/", "/search?search=", "/search?tag=", "mailto:", "/u/", "/node/",
"//plus.google.com/", "//twitter.com/"
];
foreach ($list as $listitem) { foreach ($list as $listitem) {
if (strpos($treffer[1], $listitem) !== false) { if (strpos($treffer[1], $listitem) !== false) {
$ignore = true; $ignore = true;
@ -941,7 +944,8 @@ class HTML
$domain = '(?:(?!-)[A-Za-z0-9-]{1,63}(?<!-)\.)*' . preg_quote(trim($domain, '/'), '%'); $domain = '(?:(?!-)[A-Za-z0-9-]{1,63}(?<!-)\.)*' . preg_quote(trim($domain, '/'), '%');
}); });
$config->set('URI.SafeIframeRegexp', $config->set(
'URI.SafeIframeRegexp',
'%^https://(?: '%^https://(?:
' . implode('|', $allowedIframeDomains) . ' ' . implode('|', $allowedIframeDomains) . '
) )
@ -1050,7 +1054,8 @@ class HTML
if (isset($mediaType->parameters['charset'])) { if (isset($mediaType->parameters['charset'])) {
return strtolower($mediaType->parameters['charset']); return strtolower($mediaType->parameters['charset']);
} }
} catch(\InvalidArgumentException $e) {} } catch (\InvalidArgumentException $e) {
}
return null; return null;
} }

View file

@ -476,7 +476,6 @@ class Widget
$nextday = substr($nextday, 4); $nextday = substr($nextday, 4);
$dnow = substr($dnow, 0, 8) . '01'; $dnow = substr($dnow, 0, 8) . '01';
$dthen = substr($dthen, 0, 8) . '01'; $dthen = substr($dthen, 0, 8) . '01';
/* /*
* Starting with the current month, get the first and last days of every * Starting with the current month, get the first and last days of every
@ -496,7 +495,6 @@ class Widget
$ret[$dyear][] = [$str, $end_month, $start_month]; $ret[$dyear][] = [$str, $end_month, $start_month];
$dnow = DateTimeFormat::utc($dnow . ' -1 month', 'Y-m-d'); $dnow = DateTimeFormat::utc($dnow . ' -1 month', 'Y-m-d');
} }
} }
@ -508,7 +506,7 @@ class Widget
$cutoff_year = intval(DateTimeFormat::localNow('Y')) - $visible_years; $cutoff_year = intval(DateTimeFormat::localNow('Y')) - $visible_years;
$cutoff = array_key_exists($cutoff_year, $ret); $cutoff = array_key_exists($cutoff_year, $ret);
$o = Renderer::replaceMacros(Renderer::getMarkupTemplate('widget/posted_date.tpl'),[ $o = Renderer::replaceMacros(Renderer::getMarkupTemplate('widget/posted_date.tpl'), [
'$title' => DI::l10n()->t('Archives'), '$title' => DI::l10n()->t('Archives'),
'$size' => $visible_years, '$size' => $visible_years,
'$cutoff_year' => $cutoff_year, '$cutoff_year' => $cutoff_year,
@ -543,7 +541,14 @@ class Widget
['ref' => 'community', 'name' => DI::l10n()->t('Groups')], ['ref' => 'community', 'name' => DI::l10n()->t('Groups')],
]; ];
return self::filter('accounttype', DI::l10n()->t('Account Types'), '', return self::filter(
DI::l10n()->t('All'), $base, $accounts, $accounttype); 'accounttype',
DI::l10n()->t('Account Types'),
'',
DI::l10n()->t('All'),
$base,
$accounts,
$accounttype
);
} }
} }

View file

@ -104,7 +104,7 @@ class ContactBlock
$contact_uriids = array_column($personal_contacts, 'uri-id'); $contact_uriids = array_column($personal_contacts, 'uri-id');
if (!empty($contact_uriids)) { if (!empty($contact_uriids)) {
$contacts_stmt = DBA::select('contact', ['id', 'uid', 'addr', 'url', 'name', 'thumb', 'avatar', 'network'], ['uri-id' => $contact_uriids, 'uid' => $contact_uid]); $contacts_stmt = DBA::select('contact', ['id', 'uid', 'addr', 'url', 'alias', 'name', 'thumb', 'avatar', 'network'], ['uri-id' => $contact_uriids, 'uid' => $contact_uid]);
if (DBA::isResult($contacts_stmt)) { if (DBA::isResult($contacts_stmt)) {
$contacts_title = DI::l10n()->tt('%d Contact', '%d Contacts', $total); $contacts_title = DI::l10n()->tt('%d Contact', '%d Contacts', $total);

View file

@ -29,6 +29,7 @@ use Friendica\Core\Renderer;
use Friendica\Core\System; use Friendica\Core\System;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Contact; use Friendica\Model\Contact;
use Friendica\Util\Network;
use Friendica\Util\Strings; use Friendica\Util\Strings;
/** /**
@ -50,9 +51,15 @@ class VCard
Logger::warning('Incomplete contact', ['contact' => $contact ?? [], 'callstack' => System::callstack(20)]); Logger::warning('Incomplete contact', ['contact' => $contact ?? [], 'callstack' => System::callstack(20)]);
} }
if (!Network::isValidHttpUrl($contact['url']) && Network::isValidHttpUrl($contact['alias'])) {
$contact_url = $contact['alias'];
} else {
$contact_url = $contact['url'];
}
if ($contact['network'] != '') { if ($contact['network'] != '') {
$network_link = Strings::formatNetworkName($contact['network'], $contact['url']); $network_link = Strings::formatNetworkName($contact['network'], $contact_url);
$network_avatar = ContactSelector::networkToIcon($contact['network'], $contact['url']); $network_avatar = ContactSelector::networkToIcon($contact['network'], $contact_url);
} else { } else {
$network_link = ''; $network_link = '';
$network_avatar = ''; $network_avatar = '';
@ -83,9 +90,9 @@ class VCard
if (empty($contact['self']) && Protocol::supportsFollow($contact['network'])) { if (empty($contact['self']) && Protocol::supportsFollow($contact['network'])) {
if (in_array($rel, [Contact::SHARING, Contact::FRIEND])) { if (in_array($rel, [Contact::SHARING, Contact::FRIEND])) {
$unfollow_link = 'contact/unfollow?url=' . urlencode($contact['url']) . '&auto=1'; $unfollow_link = 'contact/unfollow?url=' . urlencode($contact_url) . '&auto=1';
} elseif (!$pending) { } elseif (!$pending) {
$follow_link = 'contact/follow?url=' . urlencode($contact['url']) . '&auto=1'; $follow_link = 'contact/follow?url=' . urlencode($contact_url) . '&auto=1';
} }
} }
@ -97,7 +104,7 @@ class VCard
return Renderer::replaceMacros(Renderer::getMarkupTemplate('widget/vcard.tpl'), [ return Renderer::replaceMacros(Renderer::getMarkupTemplate('widget/vcard.tpl'), [
'$contact' => $contact, '$contact' => $contact,
'$photo' => $photo, '$photo' => $photo,
'$url' => Contact::magicLinkByContact($contact, $contact['url']), '$url' => Contact::magicLinkByContact($contact, $contact_url),
'$about' => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['about'] ?? ''), '$about' => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['about'] ?? ''),
'$xmpp' => DI::l10n()->t('XMPP:'), '$xmpp' => DI::l10n()->t('XMPP:'),
'$matrix' => DI::l10n()->t('Matrix:'), '$matrix' => DI::l10n()->t('Matrix:'),

View file

@ -119,6 +119,11 @@ class APContact
return []; return [];
} }
if (!Network::isValidHttpUrl($url) && !filter_var($url, FILTER_VALIDATE_EMAIL)) {
Logger::info('Invalid URL', ['url' => $url]);
return [];
}
$fetched_contact = []; $fetched_contact = [];
if (empty($update)) { if (empty($update)) {

View file

@ -23,7 +23,7 @@ namespace Friendica\Model;
use Friendica\Contact\Avatar; use Friendica\Contact\Avatar;
use Friendica\Contact\Introduction\Exception\IntroductionNotFoundException; use Friendica\Contact\Introduction\Exception\IntroductionNotFoundException;
use Friendica\Content\Conversation As ConversationContent; use Friendica\Content\Conversation as ConversationContent;
use Friendica\Content\Pager; use Friendica\Content\Pager;
use Friendica\Content\Text\HTML; use Friendica\Content\Text\HTML;
use Friendica\Core\Hook; use Friendica\Core\Hook;
@ -111,12 +111,12 @@ class Contact
* @} * @}
*/ */
const MIRROR_DEACTIVATED = 0; const MIRROR_DEACTIVATED = 0;
const MIRROR_FORWARDED = 1; // Deprecated, now does the same like MIRROR_OWN_POST const MIRROR_FORWARDED = 1; // Deprecated, now does the same like MIRROR_OWN_POST
const MIRROR_OWN_POST = 2; const MIRROR_OWN_POST = 2;
const MIRROR_NATIVE_RESHARE = 3; const MIRROR_NATIVE_RESHARE = 3;
/** /**
* @param array $fields Array of selected fields, empty for all * @param array $fields Array of selected fields, empty for all
* @param array $condition Array of fields for condition * @param array $condition Array of fields for condition
* @param array $params Array of several parameters * @param array $params Array of several parameters
@ -725,8 +725,11 @@ class Contact
*/ */
public static function createSelfFromUserId(int $uid): bool public static function createSelfFromUserId(int $uid): bool
{ {
$user = DBA::selectFirst('user', ['uid', 'username', 'nickname', 'pubkey', 'prvkey'], $user = DBA::selectFirst(
['uid' => $uid, 'account_expired' => false]); 'user',
['uid', 'username', 'nickname', 'pubkey', 'prvkey'],
['uid' => $uid, 'account_expired' => false]
);
if (!DBA::isResult($user)) { if (!DBA::isResult($user)) {
return false; return false;
} }
@ -786,9 +789,11 @@ class Contact
*/ */
public static function updateSelfFromUserID(int $uid, bool $update_avatar = false): bool public static function updateSelfFromUserID(int $uid, bool $update_avatar = false): bool
{ {
$fields = ['id', 'uri-id', 'name', 'nick', 'location', 'about', 'keywords', 'avatar', 'prvkey', 'pubkey', 'manually-approve', $fields = [
'id', 'uri-id', 'name', 'nick', 'location', 'about', 'keywords', 'avatar', 'prvkey', 'pubkey', 'manually-approve',
'xmpp', 'matrix', 'contact-type', 'forum', 'prv', 'avatar-date', 'url', 'nurl', 'unsearchable', 'xmpp', 'matrix', 'contact-type', 'forum', 'prv', 'avatar-date', 'url', 'nurl', 'unsearchable',
'photo', 'thumb', 'micro', 'header', 'addr', 'request', 'notify', 'poll', 'confirm', 'poco', 'network']; 'photo', 'thumb', 'micro', 'header', 'addr', 'request', 'notify', 'poll', 'confirm', 'poco', 'network'
];
$self = DBA::selectFirst('contact', $fields, ['uid' => $uid, 'self' => true]); $self = DBA::selectFirst('contact', $fields, ['uid' => $uid, 'self' => true]);
if (!DBA::isResult($self)) { if (!DBA::isResult($self)) {
return false; return false;
@ -800,8 +805,10 @@ class Contact
return false; return false;
} }
$fields = ['name', 'photo', 'thumb', 'about', 'address', 'locality', 'region', $fields = [
'country-name', 'pub_keywords', 'xmpp', 'matrix', 'net-publish']; 'name', 'photo', 'thumb', 'about', 'address', 'locality', 'region',
'country-name', 'pub_keywords', 'xmpp', 'matrix', 'net-publish'
];
$profile = DBA::selectFirst('profile', $fields, ['uid' => $uid]); $profile = DBA::selectFirst('profile', $fields, ['uid' => $uid]);
if (!DBA::isResult($profile)) { if (!DBA::isResult($profile)) {
return false; return false;
@ -830,7 +837,7 @@ class Contact
'addr' => $user['nickname'] . '@' . substr(DI::baseUrl(), strpos(DI::baseUrl(), '://') + 3), 'addr' => $user['nickname'] . '@' . substr(DI::baseUrl(), strpos(DI::baseUrl(), '://') + 3),
'request' => DI::baseUrl() . '/dfrn_request/' . $user['nickname'], 'request' => DI::baseUrl() . '/dfrn_request/' . $user['nickname'],
'notify' => DI::baseUrl() . '/dfrn_notify/' . $user['nickname'], 'notify' => DI::baseUrl() . '/dfrn_notify/' . $user['nickname'],
'poll' => DI::baseUrl() . '/dfrn_poll/'. $user['nickname'], 'poll' => DI::baseUrl() . '/dfrn_poll/' . $user['nickname'],
'confirm' => DI::baseUrl() . '/dfrn_confirm/' . $user['nickname'], 'confirm' => DI::baseUrl() . '/dfrn_confirm/' . $user['nickname'],
]; ];
@ -850,7 +857,7 @@ class Contact
// We are adding a timestamp value so that other systems won't use cached content // We are adding a timestamp value so that other systems won't use cached content
$timestamp = strtotime($fields['avatar-date']); $timestamp = strtotime($fields['avatar-date']);
$prefix = DI::baseUrl() . '/photo/' .$avatar['resource-id'] . '-'; $prefix = DI::baseUrl() . '/photo/' . $avatar['resource-id'] . '-';
$suffix = '.' . $file_suffix . '?ts=' . $timestamp; $suffix = '.' . $file_suffix . '?ts=' . $timestamp;
$fields['photo'] = $prefix . '4' . $suffix; $fields['photo'] = $prefix . '4' . $suffix;
@ -1188,22 +1195,22 @@ class Contact
*/ */
if (empty($contact['uid'])) { if (empty($contact['uid'])) {
$menu = [ $menu = [
'profile' => [DI::l10n()->t('View Profile') , $profile_link , true ], 'profile' => [DI::l10n()->t('View Profile'), $profile_link, true],
'network' => [DI::l10n()->t('Network Posts') , $posts_link , false], 'network' => [DI::l10n()->t('Network Posts'), $posts_link, false],
'edit' => [DI::l10n()->t('View Contact') , $contact_url , false], 'edit' => [DI::l10n()->t('View Contact'), $contact_url, false],
'follow' => [DI::l10n()->t('Connect/Follow'), $follow_link , true ], 'follow' => [DI::l10n()->t('Connect/Follow'), $follow_link, true],
'unfollow' => [DI::l10n()->t('Unfollow') , $unfollow_link, true ], 'unfollow' => [DI::l10n()->t('Unfollow'), $unfollow_link, true],
]; ];
} else { } else {
$menu = [ $menu = [
'status' => [DI::l10n()->t('View Status') , $status_link , true ], 'status' => [DI::l10n()->t('View Status'), $status_link, true],
'profile' => [DI::l10n()->t('View Profile') , $profile_link , true ], 'profile' => [DI::l10n()->t('View Profile'), $profile_link, true],
'photos' => [DI::l10n()->t('View Photos') , $photos_link , true ], 'photos' => [DI::l10n()->t('View Photos'), $photos_link, true],
'network' => [DI::l10n()->t('Network Posts') , $posts_link , false], 'network' => [DI::l10n()->t('Network Posts'), $posts_link, false],
'edit' => [DI::l10n()->t('View Contact') , $contact_url , false], 'edit' => [DI::l10n()->t('View Contact'), $contact_url, false],
'pm' => [DI::l10n()->t('Send PM') , $pm_url , false], 'pm' => [DI::l10n()->t('Send PM'), $pm_url, false],
'follow' => [DI::l10n()->t('Connect/Follow'), $follow_link , true ], 'follow' => [DI::l10n()->t('Connect/Follow'), $follow_link, true],
'unfollow' => [DI::l10n()->t('Unfollow') , $unfollow_link, true ], 'unfollow' => [DI::l10n()->t('Unfollow'), $unfollow_link, true],
]; ];
if (!empty($contact['pending'])) { if (!empty($contact['pending'])) {
@ -1310,9 +1317,11 @@ class Contact
if (($uid == 0) && (empty($data['network']) || ($data['network'] == Protocol::PHANTOM))) { if (($uid == 0) && (empty($data['network']) || ($data['network'] == Protocol::PHANTOM))) {
// Fetch data for the public contact via the first found personal contact // Fetch data for the public contact via the first found personal contact
/// @todo Check if this case can happen at all (possibly with mail accounts?) /// @todo Check if this case can happen at all (possibly with mail accounts?)
$fields = ['name', 'nick', 'url', 'addr', 'alias', 'avatar', 'header', 'contact-type', $fields = [
'name', 'nick', 'url', 'addr', 'alias', 'avatar', 'header', 'contact-type',
'keywords', 'location', 'about', 'unsearchable', 'batch', 'notify', 'poll', 'keywords', 'location', 'about', 'unsearchable', 'batch', 'notify', 'poll',
'request', 'confirm', 'poco', 'subscribe', 'network', 'baseurl', 'gsid']; 'request', 'confirm', 'poco', 'subscribe', 'network', 'baseurl', 'gsid'
];
$personal_contact = DBA::selectFirst('contact', $fields, ["`addr` = ? AND `uid` != 0", $url]); $personal_contact = DBA::selectFirst('contact', $fields, ["`addr` = ? AND `uid` != 0", $url]);
if (!DBA::isResult($personal_contact)) { if (!DBA::isResult($personal_contact)) {
@ -1458,7 +1467,7 @@ class Contact
if (!empty($contact['batch'])) { if (!empty($contact['batch'])) {
$condition = ['archive' => true, 'uid' => 0, 'network' => Protocol::FEDERATED, 'batch' => $contact['batch'], 'contact-type' => self::TYPE_RELAY]; $condition = ['archive' => true, 'uid' => 0, 'network' => Protocol::FEDERATED, 'batch' => $contact['batch'], 'contact-type' => self::TYPE_RELAY];
return DBA::exists('contact', $condition); return DBA::exists('contact', $condition);
} }
return false; return false;
} }
@ -1551,11 +1560,15 @@ class Contact
$contact_field = ((($contact["contact-type"] == self::TYPE_COMMUNITY) || ($contact['network'] == Protocol::MAIL)) ? 'owner-id' : 'author-id'); $contact_field = ((($contact["contact-type"] == self::TYPE_COMMUNITY) || ($contact['network'] == Protocol::MAIL)) ? 'owner-id' : 'author-id');
if ($thread_mode) { if ($thread_mode) {
$condition = ["((`$contact_field` = ? AND `gravity` = ?) OR (`author-id` = ? AND `gravity` = ? AND `vid` = ? AND `protocol` != ? AND `thr-parent-id` = `parent-uri-id`)) AND " . $sql, $condition = [
$cid, Item::GRAVITY_PARENT, $cid, Item::GRAVITY_ACTIVITY, Verb::getID(Activity::ANNOUNCE), Conversation::PARCEL_DIASPORA, DI::userSession()->getLocalUserId()]; "((`$contact_field` = ? AND `gravity` = ?) OR (`author-id` = ? AND `gravity` = ? AND `vid` = ? AND `protocol` != ? AND `thr-parent-id` = `parent-uri-id`)) AND " . $sql,
$cid, Item::GRAVITY_PARENT, $cid, Item::GRAVITY_ACTIVITY, Verb::getID(Activity::ANNOUNCE), Conversation::PARCEL_DIASPORA, DI::userSession()->getLocalUserId()
];
} else { } else {
$condition = ["`$contact_field` = ? AND `gravity` IN (?, ?) AND " . $sql, $condition = [
$cid, Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT, DI::userSession()->getLocalUserId()]; "`$contact_field` = ? AND `gravity` IN (?, ?) AND " . $sql,
$cid, Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT, DI::userSession()->getLocalUserId()
];
} }
if (!empty($parent)) { if (!empty($parent)) {
@ -1568,16 +1581,26 @@ class Contact
} }
if ($only_media) { if ($only_media) {
$condition = DBA::mergeConditions($condition, ["`uri-id` IN (SELECT `uri-id` FROM `post-media` WHERE `type` IN (?, ?, ?))", $condition = DBA::mergeConditions($condition, [
Post\Media::AUDIO, Post\Media::IMAGE, Post\Media::VIDEO]); "`uri-id` IN (SELECT `uri-id` FROM `post-media` WHERE `type` IN (?, ?, ?))",
Post\Media::AUDIO, Post\Media::IMAGE, Post\Media::VIDEO
]);
} }
if (DI::mode()->isMobile()) { if (DI::mode()->isMobile()) {
$itemsPerPage = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'itemspage_mobile_network', $itemsPerPage = DI::pConfig()->get(
DI::config()->get('system', 'itemspage_network_mobile')); DI::userSession()->getLocalUserId(),
'system',
'itemspage_mobile_network',
DI::config()->get('system', 'itemspage_network_mobile')
);
} else { } else {
$itemsPerPage = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'itemspage_network', $itemsPerPage = DI::pConfig()->get(
DI::config()->get('system', 'itemspage_network')); DI::userSession()->getLocalUserId(),
'system',
'itemspage_network',
DI::config()->get('system', 'itemspage_network')
);
} }
$pager = new Pager(DI::l10n(), DI::args()->getQueryString(), $itemsPerPage); $pager = new Pager(DI::l10n(), DI::args()->getQueryString(), $itemsPerPage);
@ -1611,8 +1634,10 @@ class Contact
if ($pager->getStart() == 0) { if ($pager->getStart() == 0) {
$cdata = self::getPublicAndUserContactID($cid, DI::userSession()->getLocalUserId()); $cdata = self::getPublicAndUserContactID($cid, DI::userSession()->getLocalUserId());
if (!empty($cdata['public'])) { if (!empty($cdata['public'])) {
$condition = ["`uri-id` IN (SELECT `uri-id` FROM `collection-view` WHERE `cid` = ? AND `type` = ?)", $condition = [
$cdata['public'], Post\Collection::FEATURED]; "`uri-id` IN (SELECT `uri-id` FROM `collection-view` WHERE `cid` = ? AND `type` = ?)",
$cdata['public'], Post\Collection::FEATURED
];
$pinned = Post::toArray(Post::selectForUser(DI::userSession()->getLocalUserId(), $fields, $condition, $params)); $pinned = Post::toArray(Post::selectForUser(DI::userSession()->getLocalUserId(), $fields, $condition, $params));
$items = array_merge($pinned, $items); $items = array_merge($pinned, $items);
} }
@ -1719,7 +1744,7 @@ class Contact
} elseif (DI::config()->get('system', 'avatar_cache') && (empty($contact['photo']) || empty($contact['thumb']) || empty($contact['micro']))) { } elseif (DI::config()->get('system', 'avatar_cache') && (empty($contact['photo']) || empty($contact['thumb']) || empty($contact['micro']))) {
Logger::info('Adding avatar cache file', ['id' => $cid, 'contact' => $contact]); Logger::info('Adding avatar cache file', ['id' => $cid, 'contact' => $contact]);
self::updateAvatar($cid, $contact['avatar'], true); self::updateAvatar($cid, $contact['avatar'], true);
return; return;
} }
} }
@ -2121,8 +2146,10 @@ class Contact
*/ */
public static function getAvatarUrlForUrl(string $url, int $uid, string $size = ''): string public static function getAvatarUrlForUrl(string $url, int $uid, string $size = ''): string
{ {
$condition = ["`nurl` = ? AND ((`uid` = ? AND `network` IN (?, ?)) OR `uid` = ?)", $condition = [
Strings::normaliseLink($url), $uid, Protocol::FEED, Protocol::MAIL, 0]; "`nurl` = ? AND ((`uid` = ? AND `network` IN (?, ?)) OR `uid` = ?)",
Strings::normaliseLink($url), $uid, Protocol::FEED, Protocol::MAIL, 0
];
$contact = self::selectFirst(['id', 'updated'], $condition, ['order' => ['uid' => true]]); $contact = self::selectFirst(['id', 'updated'], $condition, ['order' => ['uid' => true]]);
return self::getAvatarUrlForId($contact['id'] ?? 0, $size, $contact['updated'] ?? ''); return self::getAvatarUrlForId($contact['id'] ?? 0, $size, $contact['updated'] ?? '');
} }
@ -2193,8 +2220,11 @@ class Contact
*/ */
public static function updateAvatar(int $cid, string $avatar, bool $force = false, bool $create_cache = false) public static function updateAvatar(int $cid, string $avatar, bool $force = false, bool $create_cache = false)
{ {
$contact = DBA::selectFirst('contact', ['uid', 'avatar', 'photo', 'thumb', 'micro', 'blurhash', 'xmpp', 'addr', 'nurl', 'url', 'network', 'uri-id'], $contact = DBA::selectFirst(
['id' => $cid, 'self' => false]); 'contact',
['uid', 'avatar', 'photo', 'thumb', 'micro', 'blurhash', 'xmpp', 'addr', 'nurl', 'url', 'network', 'uri-id'],
['id' => $cid, 'self' => false]
);
if (!DBA::isResult($contact)) { if (!DBA::isResult($contact)) {
return; return;
} }
@ -2268,10 +2298,12 @@ class Contact
} }
if ($default_avatar && Proxy::isLocalImage($avatar)) { if ($default_avatar && Proxy::isLocalImage($avatar)) {
$fields = ['avatar' => $avatar, 'avatar-date' => DateTimeFormat::utcNow(), $fields = [
'avatar' => $avatar, 'avatar-date' => DateTimeFormat::utcNow(),
'photo' => $avatar, 'photo' => $avatar,
'thumb' => self::getDefaultAvatar($contact, Proxy::SIZE_THUMB), 'thumb' => self::getDefaultAvatar($contact, Proxy::SIZE_THUMB),
'micro' => self::getDefaultAvatar($contact, Proxy::SIZE_MICRO)]; 'micro' => self::getDefaultAvatar($contact, Proxy::SIZE_MICRO)
];
Logger::debug('Use default avatar', ['id' => $cid, 'uid' => $uid]); Logger::debug('Use default avatar', ['id' => $cid, 'uid' => $uid]);
} }
@ -2330,8 +2362,11 @@ class Contact
$uids = []; $uids = [];
if (($uid == 0) && !in_array($contact['network'], [Protocol::FEED, Protocol::MAIL])) { if (($uid == 0) && !in_array($contact['network'], [Protocol::FEED, Protocol::MAIL])) {
// Collect all user contacts of the given public contact // Collect all user contacts of the given public contact
$personal_contacts = DBA::select('contact', ['id', 'uid'], $personal_contacts = DBA::select(
["`nurl` = ? AND `id` != ? AND NOT `self`", $contact['nurl'], $cid]); 'contact',
['id', 'uid'],
["`nurl` = ? AND `id` != ? AND NOT `self`", $contact['nurl'], $cid]
);
while ($personal_contact = DBA::fetch($personal_contacts)) { while ($personal_contact = DBA::fetch($personal_contacts)) {
$cids[] = $personal_contact['id']; $cids[] = $personal_contact['id'];
$uids[] = $personal_contact['uid']; $uids[] = $personal_contact['uid'];
@ -2650,10 +2685,12 @@ class Contact
// These fields aren't updated by this routine: // These fields aren't updated by this routine:
// 'sensitive' // 'sensitive'
$fields = ['uid', 'uri-id', 'avatar', 'header', 'name', 'nick', 'location', 'keywords', 'about', 'subscribe', $fields = [
'uid', 'uri-id', 'avatar', 'header', 'name', 'nick', 'location', 'keywords', 'about', 'subscribe',
'manually-approve', 'unsearchable', 'url', 'addr', 'batch', 'notify', 'poll', 'request', 'confirm', 'poco', 'manually-approve', 'unsearchable', 'url', 'addr', 'batch', 'notify', 'poll', 'request', 'confirm', 'poco',
'network', 'alias', 'baseurl', 'gsid', 'forum', 'prv', 'contact-type', 'pubkey', 'last-item', 'xmpp', 'matrix', 'network', 'alias', 'baseurl', 'gsid', 'forum', 'prv', 'contact-type', 'pubkey', 'last-item', 'xmpp', 'matrix',
'created', 'last-update']; 'created', 'last-update'
];
$contact = DBA::selectFirst('contact', $fields, ['id' => $id]); $contact = DBA::selectFirst('contact', $fields, ['id' => $id]);
if (!DBA::isResult($contact)) { if (!DBA::isResult($contact)) {
return false; return false;
@ -2677,7 +2714,7 @@ class Contact
return true; return true;
} }
$has_local_data = self::hasLocalData($id, $contact); $has_local_data = self::hasLocalData($id, $contact);
$uid = $contact['uid']; $uid = $contact['uid'];
unset($contact['uid']); unset($contact['uid']);
@ -2724,7 +2761,8 @@ class Contact
// We must not try to update relay contacts via probe. They are no real contacts. // We must not try to update relay contacts via probe. They are no real contacts.
// We check after the probing to be able to correct falsely detected contact types. // We check after the probing to be able to correct falsely detected contact types.
if (($contact['contact-type'] == self::TYPE_RELAY) && if (($contact['contact-type'] == self::TYPE_RELAY) &&
(!Strings::compareLink($ret['url'], $contact['url']) || in_array($ret['network'], [Protocol::FEED, Protocol::PHANTOM]))) { (!Strings::compareLink($ret['url'], $contact['url']) || in_array($ret['network'], [Protocol::FEED, Protocol::PHANTOM]))
) {
self::updateContact($id, $uid, $uriid, $contact['url'], ['failed' => false, 'local-data' => $has_local_data, 'last-update' => $updated, 'next-update' => $success_next_update, 'success_update' => $updated]); self::updateContact($id, $uid, $uriid, $contact['url'], ['failed' => false, 'local-data' => $has_local_data, 'last-update' => $updated, 'next-update' => $success_next_update, 'success_update' => $updated]);
Logger::info('Not updating relais', ['id' => $id, 'url' => $contact['url']]); Logger::info('Not updating relais', ['id' => $id, 'url' => $contact['url']]);
return true; return true;
@ -2773,7 +2811,7 @@ class Contact
} }
$update = false; $update = false;
$guid = ($ret['guid'] ?? '') ?: Item::guidFromUri($ret['url'], $ret['baseurl'] ?: $ret['alias']); $guid = ($ret['guid'] ?? '') ?: Item::guidFromUri($ret['url'], $ret['baseurl'] ?? $ret['alias']);
// make sure to not overwrite existing values with blank entries except some technical fields // make sure to not overwrite existing values with blank entries except some technical fields
$keep = ['batch', 'notify', 'poll', 'request', 'confirm', 'poco', 'baseurl']; $keep = ['batch', 'notify', 'poll', 'request', 'confirm', 'poco', 'baseurl'];
@ -3078,11 +3116,11 @@ class Contact
'protocol' => $protocol, 'protocol' => $protocol,
'pubkey' => $ret['pubkey'], 'pubkey' => $ret['pubkey'],
'rel' => $new_relation, 'rel' => $new_relation,
'priority'=> $ret['priority'], 'priority' => $ret['priority'],
'writable'=> $writeable, 'writable' => $writeable,
'hidden' => $hidden, 'hidden' => $hidden,
'blocked' => 0, 'blocked' => 0,
'readonly'=> 0, 'readonly' => 0,
'pending' => $pending, 'pending' => $pending,
'subhub' => $subhub 'subhub' => $subhub
]); ]);
@ -3172,8 +3210,10 @@ class Contact
} }
// Contact is blocked at user-level // Contact is blocked at user-level
if (!empty($contact['id']) && !empty($importer['id']) && if (
Contact\User::isBlocked($contact['id'], $importer['id'])) { !empty($contact['id']) && !empty($importer['id']) &&
Contact\User::isBlocked($contact['id'], $importer['id'])
) {
return false; return false;
} }
@ -3181,9 +3221,12 @@ class Contact
self::unmarkForArchival($contact); self::unmarkForArchival($contact);
if (($contact['rel'] == self::SHARING) if (($contact['rel'] == self::SHARING)
|| ($sharing && $contact['rel'] == self::FOLLOWER)) { || ($sharing && $contact['rel'] == self::FOLLOWER)
self::update(['rel' => self::FRIEND, 'writable' => true, 'pending' => false], ) {
['id' => $contact['id'], 'uid' => $importer['uid']]); self::update(
['rel' => self::FRIEND, 'writable' => true, 'pending' => false],
['id' => $contact['id'], 'uid' => $importer['uid']]
);
} }
// Ensure to always have the correct network type, independent from the connection request method // Ensure to always have the correct network type, independent from the connection request method
@ -3422,7 +3465,7 @@ class Contact
*/ */
public static function magicLinkById(int $cid, string $url = ''): string public static function magicLinkById(int $cid, string $url = ''): string
{ {
$contact = DBA::selectFirst('contact', ['id', 'network', 'url', 'uid'], ['id' => $cid]); $contact = DBA::selectFirst('contact', ['id', 'network', 'url', 'alias', 'uid'], ['id' => $cid]);
return self::magicLinkByContact($contact, $url); return self::magicLinkByContact($contact, $url);
} }
@ -3439,7 +3482,7 @@ class Contact
*/ */
public static function magicLinkByContact(array $contact, string $url = ''): string public static function magicLinkByContact(array $contact, string $url = ''): string
{ {
$destination = $url ?: $contact['url']; $destination = $url ?: (!Network::isValidHttpUrl($contact['url']) && !empty($contact['alias']) && Network::isValidHttpUrl($contact['alias']) ? $contact['alias'] : $contact['url']);
if (!DI::userSession()->isAuthenticated()) { if (!DI::userSession()->isAuthenticated()) {
return $destination; return $destination;
@ -3568,8 +3611,10 @@ class Contact
$params['limit'] = $limit; $params['limit'] = $limit;
} }
$condition = DBA::mergeConditions($condition, $condition = DBA::mergeConditions(
["(`addr` LIKE ? OR `name` LIKE ? OR `nick` LIKE ?)", $search, $search, $search]); $condition,
["(`addr` LIKE ? OR `name` LIKE ? OR `nick` LIKE ?)", $search, $search, $search]
);
return DBA::selectToArray('account-user-view', [], $condition, $params); return DBA::selectToArray('account-user-view', [], $condition, $params);
} }
@ -3619,7 +3664,7 @@ class Contact
*/ */
public static function getRandomContact(): array public static function getRandomContact(): array
{ {
$contact = DBA::selectFirst('contact', ['id', 'network', 'url', 'uid'], [ $contact = DBA::selectFirst('contact', ['id', 'network', 'url', 'alias', 'uid'], [
"`uid` = ? AND `network` = ? AND NOT `failed` AND `last-item` > ?", "`uid` = ? AND `network` = ? AND NOT `failed` AND `last-item` > ?",
0, Protocol::DFRN, DateTimeFormat::utc('now - 1 month'), 0, Protocol::DFRN, DateTimeFormat::utc('now - 1 month'),
], ['order' => ['RAND()']]); ], ['order' => ['RAND()']]);

View file

@ -553,7 +553,8 @@ class Event
WHERE `event`.`id` = ? WHERE `event`.`id` = ?
AND `event`.`uid` = ? AND `event`.`uid` = ?
$sql_perms", $sql_perms",
$event_id, $owner_uid $event_id,
$owner_uid
)); ));
if (empty($events)) { if (empty($events)) {
throw new HTTPException\NotFoundException(DI::l10n()->t('Event not found.')); throw new HTTPException\NotFoundException(DI::l10n()->t('Event not found.'));
@ -616,7 +617,8 @@ class Event
AND `start` <= ? AND `start` <= ?
$sql_perms", $sql_perms",
$owner_uid, $owner_uid,
$start, $start, $start,
$start,
$finish $finish
)); ));
@ -661,9 +663,9 @@ class Event
$copy = null; $copy = null;
$drop = null; $drop = null;
if (DI::userSession()->getLocalUserId() && DI::userSession()->getLocalUserId() == $event['uid'] && $event['type'] == 'event') { if (DI::userSession()->getLocalUserId() && DI::userSession()->getLocalUserId() == $event['uid'] && $event['type'] == 'event') {
$edit = !$event['cid'] ? ['calendar/event/edit/' . $event['id'], DI::l10n()->t('Edit event') , '', ''] : null; $edit = !$event['cid'] ? ['calendar/event/edit/' . $event['id'], DI::l10n()->t('Edit event'), '', ''] : null;
$copy = !$event['cid'] ? ['calendar/event/copy/' . $event['id'] , DI::l10n()->t('Duplicate event'), '', ''] : null; $copy = !$event['cid'] ? ['calendar/event/copy/' . $event['id'], DI::l10n()->t('Duplicate event'), '', ''] : null;
$drop = ['calendar/api/delete/' . $event['id'] , DI::l10n()->t('Delete event') , '', '']; $drop = ['calendar/api/delete/' . $event['id'], DI::l10n()->t('Delete event'), '', ''];
} }
$title = BBCode::convertForUriId($event['uri-id'], Strings::escapeHtml($event['summary'])); $title = BBCode::convertForUriId($event['uri-id'], Strings::escapeHtml($event['summary']));
@ -708,7 +710,7 @@ class Event
} }
switch ($format) { switch ($format) {
// Format the exported data as a CSV file. // Format the exported data as a CSV file.
case "csv": case "csv":
$o .= '"Subject", "Start Date", "Start Time", "Description", "End Date", "End Time", "Location"' . PHP_EOL; $o .= '"Subject", "Start Date", "Start Time", "Description", "End Date", "End Time", "Location"' . PHP_EOL;
@ -728,7 +730,7 @@ class Event
} }
break; break;
// Format the exported data as a ics file. // Format the exported data as a ics file.
case "ical": case "ical":
$o = 'BEGIN:VCALENDAR' . PHP_EOL $o = 'BEGIN:VCALENDAR' . PHP_EOL
. 'VERSION:2.0' . PHP_EOL . 'VERSION:2.0' . PHP_EOL
@ -929,8 +931,13 @@ class Event
$location = self::locationToArray($item['event-location']); $location = self::locationToArray($item['event-location']);
// Construct the profile link (magic-auth). // Construct the profile link (magic-auth).
$author = ['uid' => 0, 'id' => $item['author-id'], $author = [
'network' => $item['author-network'], 'url' => $item['author-link']]; 'uid' => 0,
'id' => $item['author-id'],
'network' => $item['author-network'],
'url' => $item['author-link'],
'alias' => $item['author-alias']
];
$profile_link = Contact::magicLinkByContact($author); $profile_link = Contact::magicLinkByContact($author);
$tpl = Renderer::getMarkupTemplate('event_stream_item.tpl'); $tpl = Renderer::getMarkupTemplate('event_stream_item.tpl');

View file

@ -94,7 +94,7 @@ class Item
'wall', 'private', 'starred', 'origin', 'parent-origin', 'title', 'body', 'language', 'wall', 'private', 'starred', 'origin', 'parent-origin', 'title', 'body', 'language',
'content-warning', 'location', 'coord', 'app', 'rendered-hash', 'rendered-html', 'object', 'content-warning', 'location', 'coord', 'app', 'rendered-hash', 'rendered-html', 'object',
'quote-uri', 'quote-uri-id', 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'mention', 'global', 'quote-uri', 'quote-uri-id', 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'mention', 'global',
'author-id', 'author-link', 'author-name', 'author-avatar', 'author-network', 'author-updated', 'author-gsid', 'author-addr', 'author-uri-id', 'author-id', 'author-link', 'author-alias', 'author-name', 'author-avatar', 'author-network', 'author-updated', 'author-gsid', 'author-addr', 'author-uri-id',
'owner-id', 'owner-link', 'owner-name', 'owner-avatar', 'owner-network', 'owner-contact-type', 'owner-updated', 'owner-id', 'owner-link', 'owner-name', 'owner-avatar', 'owner-network', 'owner-contact-type', 'owner-updated',
'causer-id', 'causer-link', 'causer-name', 'causer-avatar', 'causer-contact-type', 'causer-network', 'causer-id', 'causer-link', 'causer-name', 'causer-avatar', 'causer-contact-type', 'causer-network',
'contact-id', 'contact-uid', 'contact-link', 'contact-name', 'contact-avatar', 'contact-id', 'contact-uid', 'contact-link', 'contact-name', 'contact-avatar',
@ -108,31 +108,35 @@ class Item
]; ];
// Field list that is used to deliver items via the protocols // Field list that is used to deliver items via the protocols
const DELIVER_FIELDLIST = ['uid', 'id', 'parent', 'uri-id', 'uri', 'thr-parent', 'parent-uri', 'guid', const DELIVER_FIELDLIST = [
'parent-guid', 'conversation', 'received', 'created', 'edited', 'verb', 'object-type', 'object', 'target', 'uid', 'id', 'parent', 'uri-id', 'uri', 'thr-parent', 'parent-uri', 'guid',
'private', 'title', 'body', 'raw-body', 'location', 'coord', 'app', 'parent-guid', 'conversation', 'received', 'created', 'edited', 'verb', 'object-type', 'object', 'target',
'inform', 'deleted', 'extid', 'post-type', 'post-reason', 'gravity', 'private', 'title', 'body', 'raw-body', 'location', 'coord', 'app',
'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'inform', 'deleted', 'extid', 'post-type', 'post-reason', 'gravity',
'author-id', 'author-addr', 'author-link', 'author-name', 'author-avatar', 'owner-id', 'owner-link', 'contact-uid', 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid',
'signed_text', 'network', 'wall', 'contact-id', 'plink', 'origin', 'author-id', 'author-addr', 'author-link', 'author-name', 'author-avatar', 'owner-id', 'owner-link', 'contact-uid',
'thr-parent-id', 'parent-uri-id', 'quote-uri', 'quote-uri-id', 'postopts', 'pubmail', 'signed_text', 'network', 'wall', 'contact-id', 'plink', 'origin',
'event-created', 'event-edited', 'event-start', 'event-finish', 'thr-parent-id', 'parent-uri-id', 'quote-uri', 'quote-uri-id', 'postopts', 'pubmail',
'event-summary', 'event-desc', 'event-location', 'event-type', 'event-created', 'event-edited', 'event-start', 'event-finish',
'event-nofinish', 'event-ignore', 'event-id']; 'event-summary', 'event-desc', 'event-location', 'event-type',
'event-nofinish', 'event-ignore', 'event-id'
];
// All fields in the item table // All fields in the item table
const ITEM_FIELDLIST = ['id', 'uid', 'parent', 'uri', 'parent-uri', 'thr-parent', const ITEM_FIELDLIST = [
'guid', 'uri-id', 'parent-uri-id', 'thr-parent-id', 'conversation', 'vid', 'id', 'uid', 'parent', 'uri', 'parent-uri', 'thr-parent',
'quote-uri', 'quote-uri-id', 'contact-id', 'wall', 'gravity', 'extid', 'psid', 'guid', 'uri-id', 'parent-uri-id', 'thr-parent-id', 'conversation', 'vid',
'created', 'edited', 'commented', 'received', 'changed', 'verb', 'quote-uri', 'quote-uri-id', 'contact-id', 'wall', 'gravity', 'extid', 'psid',
'postopts', 'plink', 'resource-id', 'event-id', 'inform', 'created', 'edited', 'commented', 'received', 'changed', 'verb',
'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'post-type', 'post-reason', 'postopts', 'plink', 'resource-id', 'event-id', 'inform',
'private', 'pubmail', 'visible', 'starred', 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'post-type', 'post-reason',
'unseen', 'deleted', 'origin', 'mention', 'global', 'network', 'private', 'pubmail', 'visible', 'starred',
'title', 'content-warning', 'body', 'location', 'coord', 'app', 'unseen', 'deleted', 'origin', 'mention', 'global', 'network',
'rendered-hash', 'rendered-html', 'object-type', 'object', 'target-type', 'target', 'title', 'content-warning', 'body', 'location', 'coord', 'app',
'author-id', 'author-link', 'author-name', 'author-avatar', 'author-network', 'rendered-hash', 'rendered-html', 'object-type', 'object', 'target-type', 'target',
'owner-id', 'owner-link', 'owner-name', 'owner-avatar', 'causer-id']; 'author-id', 'author-link', 'author-name', 'author-avatar', 'author-network',
'owner-id', 'owner-link', 'owner-name', 'owner-avatar', 'causer-id'
];
// List of all verbs that don't need additional content data. // List of all verbs that don't need additional content data.
// Never reorder or remove entries from this list. Just add new ones at the end, if needed. // Never reorder or remove entries from this list. Just add new ones at the end, if needed.
@ -140,7 +144,8 @@ class Item
Activity::LIKE, Activity::DISLIKE, Activity::LIKE, Activity::DISLIKE,
Activity::ATTEND, Activity::ATTENDNO, Activity::ATTENDMAYBE, Activity::ATTEND, Activity::ATTENDNO, Activity::ATTENDMAYBE,
Activity::FOLLOW, Activity::FOLLOW,
Activity::ANNOUNCE]; Activity::ANNOUNCE
];
// Privacy levels // Privacy levels
const PUBLIC = 0; const PUBLIC = 0;
@ -191,8 +196,10 @@ class Item
} }
// We only need to call the line by line update for specific fields // We only need to call the line by line update for specific fields
if (empty($fields['body']) && empty($fields['file']) && if (
empty($fields['attach']) && empty($fields['edited'])) { empty($fields['body']) && empty($fields['file']) &&
empty($fields['attach']) && empty($fields['edited'])
) {
return $rows; return $rows;
} }
@ -319,9 +326,11 @@ class Item
{ {
Logger::info('Mark item for deletion by id', ['id' => $item_id, 'callstack' => System::callstack()]); Logger::info('Mark item for deletion by id', ['id' => $item_id, 'callstack' => System::callstack()]);
// locate item to be deleted // locate item to be deleted
$fields = ['id', 'uri', 'uri-id', 'uid', 'parent', 'parent-uri-id', 'origin', $fields = [
'id', 'uri', 'uri-id', 'uid', 'parent', 'parent-uri-id', 'origin',
'deleted', 'resource-id', 'event-id', 'deleted', 'resource-id', 'event-id',
'verb', 'object-type', 'object', 'target', 'contact-id', 'psid', 'gravity']; 'verb', 'object-type', 'object', 'target', 'contact-id', 'psid', 'gravity'
];
$item = Post::selectFirst($fields, ['id' => $item_id]); $item = Post::selectFirst($fields, ['id' => $item_id]);
if (!DBA::isResult($item)) { if (!DBA::isResult($item)) {
Logger::info('Item not found.', ['id' => $item_id]); Logger::info('Item not found.', ['id' => $item_id]);
@ -359,7 +368,7 @@ class Item
// If item has attachments, drop them // If item has attachments, drop them
$attachments = Post\Media::getByURIId($item['uri-id'], [Post\Media::DOCUMENT]); $attachments = Post\Media::getByURIId($item['uri-id'], [Post\Media::DOCUMENT]);
foreach($attachments as $attachment) { foreach ($attachments as $attachment) {
if (preg_match('|attach/(\d+)|', $attachment['url'], $matches)) { if (preg_match('|attach/(\d+)|', $attachment['url'], $matches)) {
Attach::delete(['id' => $matches[1], 'uid' => $item['uid']]); Attach::delete(['id' => $matches[1], 'uid' => $item['uid']]);
} }
@ -473,7 +482,7 @@ class Item
private static function contactId(array $item): int private static function contactId(array $item): int
{ {
if ($item['uid'] == 0) { if ($item['uid'] == 0) {
return $item['author-id']; return $item['owner-id'];
} }
if ($item['origin']) { if ($item['origin']) {
@ -481,6 +490,23 @@ class Item
return $owner['id']; return $owner['id'];
} }
$contact_id = 0;
$user_contact_id = 0;
foreach (['group-link', 'causer-link', 'owner-link', 'author-link'] as $field) {
if (empty($item[$field])) {
continue;
}
if (!$user_contact_id && Contact::isSharingByURL($item[$field], $item['uid'], true)) {
$user_contact_id = Contact::getIdForURL($item[$field], $item['uid']);
} elseif (!$contact_id) {
$contact_id = Contact::getIdForURL($item[$field]);
}
}
if ($user_contact_id) {
return $user_contact_id;
}
if (!empty($item['causer-id']) && Contact::isSharing($item['causer-id'], $item['uid'], true)) { if (!empty($item['causer-id']) && Contact::isSharing($item['causer-id'], $item['uid'], true)) {
$cdata = Contact::getPublicAndUserContactID($item['causer-id'], $item['uid']); $cdata = Contact::getPublicAndUserContactID($item['causer-id'], $item['uid']);
if (!empty($cdata['user'])) { if (!empty($cdata['user'])) {
@ -488,24 +514,7 @@ class Item
} }
} }
if ($item['gravity'] == self::GRAVITY_PARENT) { if ($contact_id) {
if (Contact::isSharingByURL($item['owner-link'], $item['uid'], true)) {
$contact_id = Contact::getIdForURL($item['owner-link'], $item['uid']);
} else {
$contact_id = Contact::getIdForURL($item['owner-link']);
}
if (!empty($contact_id)) {
return $contact_id;
}
}
if (Contact::isSharingByURL($item['author-link'], $item['uid'], true)) {
$contact_id = Contact::getIdForURL($item['author-link'], $item['uid']);
} else {
$contact_id = Contact::getIdForURL($item['author-link']);
}
if (!empty($contact_id)) {
return $contact_id; return $contact_id;
} }
@ -551,8 +560,10 @@ class Item
return true; return true;
} }
$condition = ['uri-id' => $item['uri-id'], 'uid' => $item['uid'], $condition = [
'network' => [$item['network'], Protocol::DFRN]]; 'uri-id' => $item['uri-id'], 'uid' => $item['uid'],
'network' => [$item['network'], Protocol::DFRN]
];
if (Post::exists($condition)) { if (Post::exists($condition)) {
Logger::notice('duplicated item with the same uri found.', $condition); Logger::notice('duplicated item with the same uri found.', $condition);
return true; return true;
@ -567,8 +578,10 @@ class Item
} }
} elseif ($item['network'] == Protocol::OSTATUS) { } elseif ($item['network'] == Protocol::OSTATUS) {
// Check for an existing post with the same content. There seems to be a problem with OStatus. // Check for an existing post with the same content. There seems to be a problem with OStatus.
$condition = ["`body` = ? AND `network` = ? AND `created` = ? AND `contact-id` = ? AND `uid` = ?", $condition = [
$item['body'], $item['network'], $item['created'], $item['contact-id'], $item['uid']]; "`body` = ? AND `network` = ? AND `created` = ? AND `contact-id` = ? AND `uid` = ?",
$item['body'], $item['network'], $item['created'], $item['contact-id'], $item['uid']
];
if (Post::exists($condition)) { if (Post::exists($condition)) {
Logger::notice('duplicated item with the same body found.', $item); Logger::notice('duplicated item with the same body found.', $item);
return true; return true;
@ -642,8 +655,10 @@ class Item
return false; return false;
} }
$condition = ['verb' => Activity::FOLLOW, 'uid' => $item['uid'], $condition = [
'parent-uri' => $item['parent-uri'], 'author-id' => $item['author-id']]; 'verb' => Activity::FOLLOW, 'uid' => $item['uid'],
'parent-uri' => $item['parent-uri'], 'author-id' => $item['author-id']
];
if (Post::exists($condition)) { if (Post::exists($condition)) {
// It happens that we receive multiple follow requests by the same author - we only store one. // It happens that we receive multiple follow requests by the same author - we only store one.
Logger::info('Follow: Found existing follow request from author', ['author-id' => $item['author-id'], 'parent-uri' => $item['parent-uri']]); Logger::info('Follow: Found existing follow request from author', ['author-id' => $item['author-id'], 'parent-uri' => $item['parent-uri']]);
@ -695,7 +710,8 @@ class Item
private static function getDuplicateID(array $item): int private static function getDuplicateID(array $item): int
{ {
if (empty($item['network']) || in_array($item['network'], Protocol::FEDERATED)) { if (empty($item['network']) || in_array($item['network'], Protocol::FEDERATED)) {
$condition = ['`uri-id` = ? AND `uid` = ? AND `network` IN (?, ?, ?, ?)', $condition = [
'`uri-id` = ? AND `uid` = ? AND `network` IN (?, ?, ?, ?)',
$item['uri-id'], $item['uri-id'],
$item['uid'], $item['uid'],
Protocol::ACTIVITYPUB, Protocol::ACTIVITYPUB,
@ -751,10 +767,12 @@ class Item
*/ */
private static function getTopLevelParent(array $item): array private static function getTopLevelParent(array $item): array
{ {
$fields = ['uid', 'uri', 'parent-uri', 'id', 'deleted', $fields = [
'uid', 'uri', 'parent-uri', 'id', 'deleted',
'uri-id', 'parent-uri-id', 'uri-id', 'parent-uri-id',
'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid',
'wall', 'private', 'origin', 'author-id']; 'wall', 'private', 'origin', 'author-id'
];
$condition = ['uri-id' => [$item['thr-parent-id'], $item['parent-uri-id']], 'uid' => $item['uid']]; $condition = ['uri-id' => [$item['thr-parent-id'], $item['parent-uri-id']], 'uid' => $item['uid']];
$params = ['order' => ['id' => false]]; $params = ['order' => ['id' => false]];
$parent = Post::selectFirst($fields, $condition, $params); $parent = Post::selectFirst($fields, $condition, $params);
@ -779,9 +797,11 @@ class Item
return $parent; return $parent;
} }
$condition = ['uri-id' => $parent['parent-uri-id'], $condition = [
'uri-id' => $parent['parent-uri-id'],
'parent-uri-id' => $parent['parent-uri-id'], 'parent-uri-id' => $parent['parent-uri-id'],
'uid' => $parent['uid']]; 'uid' => $parent['uid']
];
$params = ['order' => ['id' => false]]; $params = ['order' => ['id' => false]];
$toplevel_parent = Post::selectFirst($fields, $condition, $params); $toplevel_parent = Post::selectFirst($fields, $condition, $params);
@ -946,7 +966,7 @@ class Item
// Communities aren't working with the Diaspora protocol // Communities aren't working with the Diaspora protocol
if (($uid != 0) && ($item['network'] == Protocol::DIASPORA)) { if (($uid != 0) && ($item['network'] == Protocol::DIASPORA)) {
$user = User::getById($uid, ['account-type']); $user = User::getById($uid, ['account-type']);
if ($user['account-type'] == Contact::TYPE_COMMUNITY) { if ($user['account-type'] == Contact::TYPE_COMMUNITY) {
Logger::info('Community posts are not supported via Diaspora'); Logger::info('Community posts are not supported via Diaspora');
return 0; return 0;
} }
@ -966,12 +986,16 @@ class Item
$item['gravity'] = self::getGravity($item); $item['gravity'] = self::getGravity($item);
$default = ['url' => $item['author-link'], 'name' => $item['author-name'], $default = [
'photo' => $item['author-avatar'], 'network' => $item['network']]; 'url' => $item['author-link'], 'name' => $item['author-name'],
'photo' => $item['author-avatar'], 'network' => $item['network']
];
$item['author-id'] = ($item['author-id'] ?? 0) ?: Contact::getIdForURL($item['author-link'], 0, null, $default); $item['author-id'] = ($item['author-id'] ?? 0) ?: Contact::getIdForURL($item['author-link'], 0, null, $default);
$default = ['url' => $item['owner-link'], 'name' => $item['owner-name'], $default = [
'photo' => $item['owner-avatar'], 'network' => $item['network']]; 'url' => $item['owner-link'], 'name' => $item['owner-name'],
'photo' => $item['owner-avatar'], 'network' => $item['network']
];
$item['owner-id'] = ($item['owner-id'] ?? 0) ?: Contact::getIdForURL($item['owner-link'], 0, null, $default); $item['owner-id'] = ($item['owner-id'] ?? 0) ?: Contact::getIdForURL($item['owner-link'], 0, null, $default);
$item['post-reason'] = self::getPostReason($item); $item['post-reason'] = self::getPostReason($item);
@ -982,8 +1006,10 @@ class Item
$item['contact-id'] = self::contactId($item); $item['contact-id'] = self::contactId($item);
if (!empty($item['direction']) && in_array($item['direction'], [Conversation::PUSH, Conversation::RELAY]) && if (
empty($item['origin']) && self::isTooOld($item)) { !empty($item['direction']) && in_array($item['direction'], [Conversation::PUSH, Conversation::RELAY]) &&
empty($item['origin']) && self::isTooOld($item)
) {
Logger::info('Item is too old', ['item' => $item]); Logger::info('Item is too old', ['item' => $item]);
return 0; return 0;
} }
@ -1124,7 +1150,8 @@ class Item
$item['allow_gid'], $item['allow_gid'],
$item['deny_cid'], $item['deny_cid'],
$item['deny_gid'] $item['deny_gid']
))->id; )
)->id;
if (!empty($item['extid'])) { if (!empty($item['extid'])) {
$item['external-id'] = ItemURI::getIdByURI($item['extid']); $item['external-id'] = ItemURI::getIdByURI($item['extid']);
@ -1356,7 +1383,8 @@ class Item
// Don't relay participation messages // Don't relay participation messages
if (($posted_item['verb'] == Activity::FOLLOW) && if (($posted_item['verb'] == Activity::FOLLOW) &&
(!$posted_item['origin'] || ($posted_item['author-id'] != Contact::getPublicIdByUserId($uid)))) { (!$posted_item['origin'] || ($posted_item['author-id'] != Contact::getPublicIdByUserId($uid)))
) {
Logger::info('Participation messages will not be relayed', ['item' => $posted_item['id'], 'uri' => $posted_item['uri'], 'verb' => $posted_item['verb']]); Logger::info('Participation messages will not be relayed', ['item' => $posted_item['id'], 'uri' => $posted_item['uri'], 'verb' => $posted_item['verb']]);
$transmit = false; $transmit = false;
} }
@ -1429,8 +1457,10 @@ class Item
*/ */
private static function setOwnerforResharedItem(array $item) private static function setOwnerforResharedItem(array $item)
{ {
$parent = Post::selectFirst(['id', 'causer-id', 'owner-id', 'author-id', 'author-link', 'origin', 'post-reason'], $parent = Post::selectFirst(
['uri-id' => $item['thr-parent-id'], 'uid' => $item['uid']]); ['id', 'causer-id', 'owner-id', 'author-id', 'author-link', 'origin', 'post-reason'],
['uri-id' => $item['thr-parent-id'], 'uid' => $item['uid']]
);
if (!DBA::isResult($parent)) { if (!DBA::isResult($parent)) {
Logger::error('Parent not found', ['uri-id' => $item['thr-parent-id'], 'uid' => $item['uid']]); Logger::error('Parent not found', ['uri-id' => $item['thr-parent-id'], 'uid' => $item['uid']]);
return; return;
@ -1502,9 +1532,11 @@ class Item
} }
// Only distribute public items from native networks // Only distribute public items from native networks
$condition = ['id' => $itemid, 'uid' => 0, $condition = [
'network' => array_merge(Protocol::FEDERATED ,['']), 'id' => $itemid, 'uid' => 0,
'visible' => true, 'deleted' => false, 'private' => [self::PUBLIC, self::UNLISTED]]; 'network' => array_merge(Protocol::FEDERATED, ['']),
'visible' => true, 'deleted' => false, 'private' => [self::PUBLIC, self::UNLISTED]
];
$item = Post::selectFirst(array_merge(self::ITEM_FIELDLIST, ['protocol']), $condition); $item = Post::selectFirst(array_merge(self::ITEM_FIELDLIST, ['protocol']), $condition);
if (!DBA::isResult($item)) { if (!DBA::isResult($item)) {
Logger::warning('Item not found', ['condition' => $condition]); Logger::warning('Item not found', ['condition' => $condition]);
@ -1625,7 +1657,8 @@ class Item
if (($uid != 0) && (($item['gravity'] == self::GRAVITY_PARENT) || $is_reshare) && if (($uid != 0) && (($item['gravity'] == self::GRAVITY_PARENT) || $is_reshare) &&
DI::pConfig()->get($uid, 'system', 'accept_only_sharer') == self::COMPLETION_NONE && DI::pConfig()->get($uid, 'system', 'accept_only_sharer') == self::COMPLETION_NONE &&
!in_array($item['post-reason'], [self::PR_FOLLOWER, self::PR_TAG, self::PR_TO, self::PR_CC, self::PR_ACTIVITY, self::PR_AUDIENCE])) { !in_array($item['post-reason'], [self::PR_FOLLOWER, self::PR_TAG, self::PR_TO, self::PR_CC, self::PR_ACTIVITY, self::PR_AUDIENCE])
) {
Logger::info('Contact is not a follower, thread will not be stored', ['author' => $item['author-link'], 'uid' => $uid, 'uri-id' => $uri_id, 'post-reason' => $item['post-reason']]); Logger::info('Contact is not a follower, thread will not be stored', ['author' => $item['author-link'], 'uid' => $uid, 'uri-id' => $uri_id, 'post-reason' => $item['post-reason']]);
return 0; return 0;
} }
@ -1821,7 +1854,7 @@ class Item
} }
// is it an entry from a connector? Only add an entry for natively connected networks // is it an entry from a connector? Only add an entry for natively connected networks
if (!in_array($item["network"], array_merge(Protocol::FEDERATED ,['']))) { if (!in_array($item["network"], array_merge(Protocol::FEDERATED, ['']))) {
return; return;
} }
@ -2124,12 +2157,16 @@ class Item
} }
// Now do the same for the system wide contacts with uid=0 // Now do the same for the system wide contacts with uid=0
if ($arr['private'] != self::PRIVATE) { if ($arr['private'] != self::PRIVATE) {
Contact::update(['failed' => false, 'local-data' => true, 'success_update' => $arr['received'], 'last-item' => $arr['received']], Contact::update(
['id' => $arr['owner-id']]); ['failed' => false, 'local-data' => true, 'success_update' => $arr['received'], 'last-item' => $arr['received']],
['id' => $arr['owner-id']]
);
if ($arr['owner-id'] != $arr['author-id']) { if ($arr['owner-id'] != $arr['author-id']) {
Contact::update(['failed' => false, 'local-data' => true, 'success_update' => $arr['received'], 'last-item' => $arr['received']], Contact::update(
['id' => $arr['author-id']]); ['failed' => false, 'local-data' => true, 'success_update' => $arr['received'], 'last-item' => $arr['received']],
['id' => $arr['author-id']]
);
} }
} }
} }
@ -2155,29 +2192,44 @@ class Item
// All hashtags should point to the home server if "local_tags" is activated // All hashtags should point to the home server if "local_tags" is activated
if (DI::config()->get('system', 'local_tags')) { if (DI::config()->get('system', 'local_tags')) {
$body = preg_replace("/#\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", $body = preg_replace(
"#[url=" . DI::baseUrl() . "/search?tag=$2]$2[/url]", $body); "/#\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism",
"#[url=" . DI::baseUrl() . "/search?tag=$2]$2[/url]",
$body
);
} }
// mask hashtags inside of url, bookmarks and attachments to avoid urls in urls // mask hashtags inside of url, bookmarks and attachments to avoid urls in urls
$body = preg_replace_callback("/\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", $body = preg_replace_callback(
"/\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism",
function ($match) { function ($match) {
return ("[url=" . str_replace("#", "&num;", $match[1]) . "]" . str_replace("#", "&num;", $match[2]) . "[/url]"); return ("[url=" . str_replace("#", "&num;", $match[1]) . "]" . str_replace("#", "&num;", $match[2]) . "[/url]");
}, $body); },
$body
);
$body = preg_replace_callback("/\[bookmark\=([$URLSearchString]*)\](.*?)\[\/bookmark\]/ism", $body = preg_replace_callback(
"/\[bookmark\=([$URLSearchString]*)\](.*?)\[\/bookmark\]/ism",
function ($match) { function ($match) {
return ("[bookmark=" . str_replace("#", "&num;", $match[1]) . "]" . str_replace("#", "&num;", $match[2]) . "[/bookmark]"); return ("[bookmark=" . str_replace("#", "&num;", $match[1]) . "]" . str_replace("#", "&num;", $match[2]) . "[/bookmark]");
}, $body); },
$body
);
$body = preg_replace_callback("/\[attachment (.*?)\](.*?)\[\/attachment\]/ism", $body = preg_replace_callback(
"/\[attachment (.*?)\](.*?)\[\/attachment\]/ism",
function ($match) { function ($match) {
return ("[attachment " . str_replace("#", "&num;", $match[1]) . "]" . $match[2] . "[/attachment]"); return ("[attachment " . str_replace("#", "&num;", $match[1]) . "]" . $match[2] . "[/attachment]");
}, $body); },
$body
);
// Repair recursive urls // Repair recursive urls
$body = preg_replace("/&num;\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", $body = preg_replace(
"&num;$2", $body); "/&num;\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism",
"&num;$2",
$body
);
foreach ($tags as $tag) { foreach ($tags as $tag) {
if ((strpos($tag, '#') !== 0) || strpos($tag, '[url=') || strlen($tag) < 2 || $tag[1] == '#') { if ((strpos($tag, '#') !== 0) || strpos($tag, '[url=') || strlen($tag) < 2 || $tag[1] == '#') {
@ -2210,8 +2262,6 @@ class Item
*/ */
private static function tagDeliver(int $uid, int $item_id): bool private static function tagDeliver(int $uid, int $item_id): bool
{ {
$mention = false;
$owner = User::getOwnerDataById($uid); $owner = User::getOwnerDataById($uid);
if (!DBA::isResult($owner)) { if (!DBA::isResult($owner)) {
Logger::warning('User not found, quitting here.', ['uid' => $uid]); Logger::warning('User not found, quitting here.', ['uid' => $uid]);
@ -2331,10 +2381,13 @@ class Item
} }
$datarray2 = $datarray; $datarray2 = $datarray;
Logger::info('remote-self start', ['contact' => $contact['url'], 'remote_self'=> $contact['remote_self'], 'item' => $datarray]); Logger::info('remote-self start', ['contact' => $contact['url'], 'remote_self' => $contact['remote_self'], 'item' => $datarray]);
$self = DBA::selectFirst('contact', ['id', 'name', 'url', 'thumb'], $self = DBA::selectFirst(
['uid' => $contact['uid'], 'self' => true]); 'contact',
['id', 'name', 'url', 'thumb'],
['uid' => $contact['uid'], 'self' => true]
);
if (!DBA::isResult($self)) { if (!DBA::isResult($self)) {
Logger::error('Self contact not found', ['uid' => $contact['uid']]); Logger::error('Self contact not found', ['uid' => $contact['uid']]);
return false; return false;
@ -2370,7 +2423,7 @@ class Item
// Store the original post // Store the original post
$result = self::insert($datarray2); $result = self::insert($datarray2);
Logger::info('remote-self post original item', ['contact' => $contact['url'], 'result'=> $result, 'item' => $datarray2]); Logger::info('remote-self post original item', ['contact' => $contact['url'], 'result' => $result, 'item' => $datarray2]);
} else { } else {
$datarray['private'] = self::PUBLIC; $datarray['private'] = self::PUBLIC;
$datarray['app'] = 'Feed'; $datarray['app'] = 'Feed';
@ -2497,7 +2550,8 @@ class Item
if (($obj1['allow_cid'] == $obj2['allow_cid']) if (($obj1['allow_cid'] == $obj2['allow_cid'])
&& ($obj1['allow_gid'] == $obj2['allow_gid']) && ($obj1['allow_gid'] == $obj2['allow_gid'])
&& ($obj1['deny_cid'] == $obj2['deny_cid']) && ($obj1['deny_cid'] == $obj2['deny_cid'])
&& ($obj1['deny_gid'] == $obj2['deny_gid'])) { && ($obj1['deny_gid'] == $obj2['deny_gid'])
) {
return true; return true;
} }
@ -2544,8 +2598,10 @@ class Item
return; return;
} }
$condition = ["`uid` = ? AND NOT `deleted` AND `gravity` = ?", $condition = [
$uid, self::GRAVITY_PARENT]; "`uid` = ? AND NOT `deleted` AND `gravity` = ?",
$uid, self::GRAVITY_PARENT
];
/* /*
* $expire_network_only = save your own wall posts * $expire_network_only = save your own wall posts
@ -2619,8 +2675,10 @@ class Item
return false; return false;
} }
$condition = ["`uid` = ? AND `wall` = ? AND NOT `deleted` AND `visible` AND `received` >= ?", $condition = [
$uid, $wall, $user['register_date']]; "`uid` = ? AND `wall` = ? AND NOT `deleted` AND `visible` AND `received` >= ?",
$uid, $wall, $user['register_date']
];
$params = ['order' => ['received' => false]]; $params = ['order' => ['received' => false]];
$thread = Post::selectFirstThread(['received'], $condition, $params); $thread = Post::selectFirstThread(['received'], $condition, $params);
if (DBA::isResult($thread)) { if (DBA::isResult($thread)) {
@ -2752,8 +2810,10 @@ class Item
$vids = Verb::getID($activity); $vids = Verb::getID($activity);
} }
$condition = ['vid' => $vids, 'deleted' => false, 'gravity' => self::GRAVITY_ACTIVITY, $condition = [
'author-id' => $author_id, 'uid' => $uid, 'thr-parent-id' => $uri_id]; 'vid' => $vids, 'deleted' => false, 'gravity' => self::GRAVITY_ACTIVITY,
'author-id' => $author_id, 'uid' => $uid, 'thr-parent-id' => $uri_id
];
$like_item = Post::selectFirst(['id', 'guid', 'verb'], $condition); $like_item = Post::selectFirst(['id', 'guid', 'verb'], $condition);
if (DBA::isResult($like_item)) { if (DBA::isResult($like_item)) {
@ -2857,12 +2917,14 @@ class Item
// Profile owner - everything is visible // Profile owner - everything is visible
$condition = []; $condition = [];
} elseif ($remote_user) { } elseif ($remote_user) {
// Authenticated visitor - fetch the matching permissionsets // Authenticated visitor - fetch the matching permissionsets
$permissionSets = DI::permissionSet()->selectByContactId($remote_user, $owner_id); $permissionSets = DI::permissionSet()->selectByContactId($remote_user, $owner_id);
if (!empty($set)) { if (!empty($set)) {
$condition = ["(`private` != ? OR (`private` = ? AND `wall` $condition = [
"(`private` != ? OR (`private` = ? AND `wall`
AND `psid` IN (" . implode(', ', array_fill(0, count($set), '?')) . ")))", AND `psid` IN (" . implode(', ', array_fill(0, count($set), '?')) . ")))",
self::PRIVATE, self::PRIVATE]; self::PRIVATE, self::PRIVATE
];
$condition = array_merge($condition, $permissionSets->column('id')); $condition = array_merge($condition, $permissionSets->column('id'));
} }
} }
@ -2958,7 +3020,8 @@ class Item
$rendered_hash = $item['rendered-hash'] ?? ''; $rendered_hash = $item['rendered-hash'] ?? '';
$rendered_html = $item['rendered-html'] ?? ''; $rendered_html = $item['rendered-html'] ?? '';
if ($rendered_hash == '' if (
$rendered_hash == ''
|| $rendered_html == '' || $rendered_html == ''
|| $rendered_hash != hash('md5', BBCode::VERSION . '::' . $body) || $rendered_hash != hash('md5', BBCode::VERSION . '::' . $body)
|| DI::config()->get('system', 'ignore_cache') || DI::config()->get('system', 'ignore_cache')
@ -3263,8 +3326,10 @@ class Item
} }
foreach ([0, 1, 2] as $size) { foreach ([0, 1, 2] as $size) {
if (preg_match('#/photo/.*-' . $size . '\.#ism', $url) && if (
strpos(preg_replace('#(/photo/.*)-[012]\.#ism', '$1-' . $size . '.', $body), $url)) { preg_match('#/photo/.*-' . $size . '\.#ism', $url) &&
strpos(preg_replace('#(/photo/.*)-[012]\.#ism', '$1-' . $size . '.', $body), $url)
) {
return true; return true;
} }
} }
@ -3461,7 +3526,8 @@ class Item
'text' => '', 'text' => '',
'title' => $attachment['name'] ?? '', 'title' => $attachment['name'] ?? '',
'type' => 'link', 'type' => 'link',
'url' => $attachment['url']]; 'url' => $attachment['url']
];
if ($preview && !empty($attachment['preview'])) { if ($preview && !empty($attachment['preview'])) {
if ($attachment['preview-width'] >= 500) { if ($attachment['preview-width'] >= 500) {
@ -3551,8 +3617,13 @@ class Item
continue; continue;
} }
$author = ['uid' => 0, 'id' => $item['author-id'], $author = [
'network' => $item['author-network'], 'url' => $item['author-link']]; 'uid' => 0,
'id' => $item['author-id'],
'network' => $item['author-network'],
'url' => $item['author-link'],
'alias' => $item['author-alias']
];
$the_url = Contact::magicLinkByContact($author, $attachment['url']); $the_url = Contact::magicLinkByContact($author, $attachment['url']);
$title = Strings::escapeHtml(trim(($attachment['description'] ?? '') ?: $attachment['url'])); $title = Strings::escapeHtml(trim(($attachment['description'] ?? '') ?: $attachment['url']));
@ -3601,7 +3672,7 @@ class Item
$summary = DI::l10n()->tt('%d voter.', '%d voters.', $question['voters']); $summary = DI::l10n()->tt('%d voter.', '%d voters.', $question['voters']);
} elseif (!empty($question['endtime'])) { } elseif (!empty($question['endtime'])) {
$summary = DI::l10n()->t('Poll end: %s', Temporal::getRelativeDate($question['endtime'])); $summary = DI::l10n()->t('Poll end: %s', Temporal::getRelativeDate($question['endtime']));
} else { } else {
$summary = ''; $summary = '';
} }
@ -3610,7 +3681,7 @@ class Item
'$options' => $options, '$options' => $options,
'$summary' => $summary, '$summary' => $summary,
]); ]);
} }
DI::profiler()->stopRecording(); DI::profiler()->stopRecording();
return $content; return $content;
} }
@ -3639,8 +3710,13 @@ class Item
]; ];
if (!empty($plink) && ($item['private'] == self::PRIVATE)) { if (!empty($plink) && ($item['private'] == self::PRIVATE)) {
$author = ['uid' => 0, 'id' => $item['author-id'], $author = [
'network' => $item['author-network'], 'url' => $item['author-link']]; 'uid' => 0,
'id' => $item['author-id'],
'network' => $item['author-network'],
'url' => $item['author-link'],
'alias' => $item['author-alias'],
];
$plink = Contact::magicLinkByContact($author, $plink); $plink = Contact::magicLinkByContact($author, $plink);
} }
@ -3664,6 +3740,7 @@ class Item
/** /**
* Does the given uri-id belongs to a post that is sent as starting post to a group? * Does the given uri-id belongs to a post that is sent as starting post to a group?
* This does apply to posts that are sent via ! and not in parallel to a group via @
* *
* @param int $uri_id * @param int $uri_id
* *
@ -3671,7 +3748,13 @@ class Item
*/ */
public static function isGroupPost(int $uri_id): bool public static function isGroupPost(int $uri_id): bool
{ {
foreach (Tag::getByURIId($uri_id, [Tag::EXCLUSIVE_MENTION]) as $tag) { if (Post::exists(['private' => Item::PUBLIC, 'uri-id' => $uri_id])) {
return false;
}
foreach (Tag::getByURIId($uri_id, [Tag::EXCLUSIVE_MENTION, Tag::AUDIENCE]) as $tag) {
// @todo Possibly check for a public audience in the future, see https://socialhub.activitypub.rocks/t/fep-1b12-group-federation/2724
// and https://codeberg.org/fediverse/fep/src/branch/main/feps/fep-1b12.md
if (DBA::exists('contact', ['uid' => 0, 'nurl' => Strings::normaliseLink($tag['url']), 'contact-type' => Contact::TYPE_COMMUNITY])) { if (DBA::exists('contact', ['uid' => 0, 'nurl' => Strings::normaliseLink($tag['url']), 'contact-type' => Contact::TYPE_COMMUNITY])) {
return true; return true;
} }

View file

@ -487,7 +487,7 @@ class Tag
* *
* @return boolean * @return boolean
*/ */
public static function isMentioned(int $uriId, string $url, array $type = [self::MENTION, self::EXCLUSIVE_MENTION]): bool public static function isMentioned(int $uriId, string $url, array $type = [self::MENTION, self::EXCLUSIVE_MENTION, self::AUDIENCE]): bool
{ {
$tags = self::getByURIId($uriId, $type); $tags = self::getByURIId($uriId, $type);
foreach ($tags as $tag) { foreach ($tags as $tag) {

View file

@ -219,8 +219,7 @@ class Contact extends BaseModule
DI::page()['aside'] .= $vcard_widget . $findpeople_widget . $follow_widget . $rel_widget . $circles_widget . $networks_widget . $account_widget; DI::page()['aside'] .= $vcard_widget . $findpeople_widget . $follow_widget . $rel_widget . $circles_widget . $networks_widget . $account_widget;
$tpl = Renderer::getMarkupTemplate('contacts-head.tpl'); $tpl = Renderer::getMarkupTemplate('contacts-head.tpl');
DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl, [ DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl, []);
]);
$o = ''; $o = '';
Nav::setSelected('contact'); Nav::setSelected('contact');
@ -412,20 +411,41 @@ class Contact extends BaseModule
$tabs_html = Renderer::replaceMacros($tabs_tpl, ['$tabs' => $tabs]); $tabs_html = Renderer::replaceMacros($tabs_tpl, ['$tabs' => $tabs]);
switch ($rel) { switch ($rel) {
case 'followers': $header = DI::l10n()->t('Followers'); break; case 'followers':
case 'following': $header = DI::l10n()->t('Following'); break; $header = DI::l10n()->t('Followers');
case 'mutuals': $header = DI::l10n()->t('Mutual friends'); break; break;
case 'nothing': $header = DI::l10n()->t('No relationship'); break; case 'following':
default: $header = DI::l10n()->t('Contacts'); $header = DI::l10n()->t('Following');
break;
case 'mutuals':
$header = DI::l10n()->t('Mutual friends');
break;
case 'nothing':
$header = DI::l10n()->t('No relationship');
break;
default:
$header = DI::l10n()->t('Contacts');
} }
switch ($type) { switch ($type) {
case 'pending': $header .= ' - ' . DI::l10n()->t('Pending'); break; case 'pending':
case 'blocked': $header .= ' - ' . DI::l10n()->t('Blocked'); break; $header .= ' - ' . DI::l10n()->t('Pending');
case 'hidden': $header .= ' - ' . DI::l10n()->t('Hidden'); break; break;
case 'ignored': $header .= ' - ' . DI::l10n()->t('Ignored'); break; case 'blocked':
case 'collapsed': $header .= ' - ' . DI::l10n()->t('Collapsed'); break; $header .= ' - ' . DI::l10n()->t('Blocked');
case 'archived': $header .= ' - ' . DI::l10n()->t('Archived'); break; break;
case 'hidden':
$header .= ' - ' . DI::l10n()->t('Hidden');
break;
case 'ignored':
$header .= ' - ' . DI::l10n()->t('Ignored');
break;
case 'collapsed':
$header .= ' - ' . DI::l10n()->t('Collapsed');
break;
case 'archived':
$header .= ' - ' . DI::l10n()->t('Archived');
break;
} }
$header .= $nets ? ' - ' . ContactSelector::networkToName($nets) : ''; $header .= $nets ? ' - ' . ContactSelector::networkToName($nets) : '';
@ -512,7 +532,8 @@ class Contact extends BaseModule
'id' => 'media-tab', 'id' => 'media-tab',
'accesskey' => 'd', 'accesskey' => 'd',
], ],
['label' => DI::l10n()->t('Contacts'), [
'label' => DI::l10n()->t('Contacts'),
'url' => 'contact/' . $pcid . '/contacts', 'url' => 'contact/' . $pcid . '/contacts',
'sel' => (($active_tab == self::TAB_CONTACTS) ? 'active' : ''), 'sel' => (($active_tab == self::TAB_CONTACTS) ? 'active' : ''),
'title' => DI::l10n()->t('View all known contacts'), 'title' => DI::l10n()->t('View all known contacts'),
@ -522,7 +543,8 @@ class Contact extends BaseModule
]; ];
if (!empty($contact['network']) && in_array($contact['network'], [Protocol::FEED, Protocol::MAIL]) && ($cid != $pcid)) { if (!empty($contact['network']) && in_array($contact['network'], [Protocol::FEED, Protocol::MAIL]) && ($cid != $pcid)) {
$tabs[] = ['label' => DI::l10n()->t('Advanced'), $tabs[] = [
'label' => DI::l10n()->t('Advanced'),
'url' => 'contact/' . $cid . '/advanced/', 'url' => 'contact/' . $cid . '/advanced/',
'sel' => (($active_tab == self::TAB_ADVANCED) ? 'active' : ''), 'sel' => (($active_tab == self::TAB_ADVANCED) ? 'active' : ''),
'title' => DI::l10n()->t('Advanced Contact Settings'), 'title' => DI::l10n()->t('Advanced Contact Settings'),

View file

@ -93,7 +93,7 @@ class Unfollow extends \Friendica\BaseModule
Strings::normaliseLink($url), Strings::normaliseLink($url), $url, Strings::normaliseLink($url), Strings::normaliseLink($url), $url,
]; ];
$contact = $this->database->selectFirst('contact', ['url', 'id', 'uid', 'network', 'addr', 'name'], $condition); $contact = $this->database->selectFirst('contact', ['url', 'alias', 'id', 'uid', 'network', 'addr', 'name'], $condition);
if (!$this->database->isResult($contact)) { if (!$this->database->isResult($contact)) {
$this->systemMessages->addNotice($this->t("You aren't following this contact.")); $this->systemMessages->addNotice($this->t("You aren't following this contact."));
$this->baseUrl->redirect($base_return_path); $this->baseUrl->redirect($base_return_path);

View file

@ -29,12 +29,14 @@ use Friendica\Core\Session\Capability\IHandleUserSessions;
use Friendica\Core\System; use Friendica\Core\System;
use Friendica\Database\Database; use Friendica\Database\Database;
use Friendica\Model\Contact; use Friendica\Model\Contact;
use Friendica\Model\GServer;
use Friendica\Model\User; use Friendica\Model\User;
use Friendica\Network\HTTPClient\Capability\ICanSendHttpRequests; use Friendica\Network\HTTPClient\Capability\ICanSendHttpRequests;
use Friendica\Network\HTTPClient\Client\HttpClientOptions; use Friendica\Network\HTTPClient\Client\HttpClientOptions;
use Friendica\Util\HTTPSignature; use Friendica\Util\HTTPSignature;
use Friendica\Util\Profiler; use Friendica\Util\Profiler;
use Friendica\Util\Strings; use Friendica\Util\Strings;
use GuzzleHttp\Psr7\Uri;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
/** /**
@ -83,6 +85,8 @@ class Magic extends BaseModule
$this->logger->debug('bdest detected', ['dest' => $dest]); $this->logger->debug('bdest detected', ['dest' => $dest]);
} }
$target = $dest ?: $addr;
if ($addr ?: $dest) { if ($addr ?: $dest) {
$contact = Contact::getByURL($addr ?: $dest); $contact = Contact::getByURL($addr ?: $dest);
} }
@ -110,14 +114,21 @@ class Magic extends BaseModule
// OpenWebAuth // OpenWebAuth
$owner = User::getOwnerDataById($this->userSession->getLocalUserId()); $owner = User::getOwnerDataById($this->userSession->getLocalUserId());
$gserver = $this->dba->selectFirst('gserver', ['url'], ['id' => $contact['gsid']]); if (!empty($contact['gsid'])) {
if (empty($gserver)) { $gserver = $this->dba->selectFirst('gserver', ['url'], ['id' => $contact['gsid']]);
$this->logger->notice('Server not found, redirecting to destination.', ['gsid' => $contact['gsid'], 'dest' => $dest]); if (empty($gserver)) {
$this->logger->notice('Server not found, redirecting to destination.', ['gsid' => $contact['gsid'], 'dest' => $dest]);
System::externalRedirect($dest);
}
$basepath = $gserver['url'];
} elseif (GServer::check($target)) {
$basepath = (string)GServer::cleanUri(new Uri($target));
} else {
$this->logger->notice('The target is not a server path, redirecting to destination.', ['target' => $target]);
System::externalRedirect($dest); System::externalRedirect($dest);
} }
$basepath = $gserver['url'];
$header = [ $header = [
'Accept' => 'application/x-dfrn+json, application/x-zot+json', 'Accept' => 'application/x-dfrn+json, application/x-zot+json',
'X-Open-Web-Auth' => Strings::getRandomHex() 'X-Open-Web-Auth' => Strings::getRandomHex()

View file

@ -91,9 +91,13 @@ class Post
} }
$this->writable = $this->getDataValue('writable') || $this->getDataValue('self'); $this->writable = $this->getDataValue('writable') || $this->getDataValue('self');
$author = ['uid' => 0, 'id' => $this->getDataValue('author-id'), $author = [
'uid' => 0,
'id' => $this->getDataValue('author-id'),
'network' => $this->getDataValue('author-network'), 'network' => $this->getDataValue('author-network'),
'url' => $this->getDataValue('author-link')]; 'url' => $this->getDataValue('author-link'),
'alias' => $this->getDataValue('author-alias')
];
$this->redirect_url = Contact::magicLinkByContact($author); $this->redirect_url = Contact::magicLinkByContact($author);
if (!$this->isToplevel()) { if (!$this->isToplevel()) {
$this->threaded = true; $this->threaded = true;
@ -286,8 +290,13 @@ class Post
} }
if (DI::userSession()->isAuthenticated()) { if (DI::userSession()->isAuthenticated()) {
$author = ['uid' => 0, 'id' => $item['author-id'], $author = [
'network' => $item['author-network'], 'url' => $item['author-link']]; 'uid' => 0,
'id' => $item['author-id'],
'network' => $item['author-network'],
'url' => $item['author-link'],
'alias' => $item['author-alias'],
];
$profile_link = Contact::magicLinkByContact($author); $profile_link = Contact::magicLinkByContact($author);
} else { } else {
$profile_link = $item['author-link']; $profile_link = $item['author-link'];
@ -388,7 +397,7 @@ class Post
} }
if ($conv->isWritable()) { if ($conv->isWritable()) {
$buttons['like'] = [DI::l10n()->t("I like this \x28toggle\x29") , DI::l10n()->t('Like')]; $buttons['like'] = [DI::l10n()->t("I like this \x28toggle\x29"), DI::l10n()->t('Like')];
$buttons['dislike'] = [DI::l10n()->t("I don't like this \x28toggle\x29"), DI::l10n()->t('Dislike')]; $buttons['dislike'] = [DI::l10n()->t("I don't like this \x28toggle\x29"), DI::l10n()->t('Dislike')];
if ($shareable) { if ($shareable) {
$buttons['share'] = [DI::l10n()->t('Quote share this'), DI::l10n()->t('Quote Share')]; $buttons['share'] = [DI::l10n()->t('Quote share this'), DI::l10n()->t('Quote Share')];
@ -451,8 +460,10 @@ class Post
// Fetching of Diaspora posts doesn't always work. There are issues with reshares and possibly comments // Fetching of Diaspora posts doesn't always work. There are issues with reshares and possibly comments
if (!DI::userSession()->getLocalUserId() && ($item['network'] != Protocol::DIASPORA) && !empty(DI::session()->get('remote_comment'))) { if (!DI::userSession()->getLocalUserId() && ($item['network'] != Protocol::DIASPORA) && !empty(DI::session()->get('remote_comment'))) {
$remote_comment = [DI::l10n()->t('Comment this item on your system'), DI::l10n()->t('Remote comment'), $remote_comment = [
str_replace('{uri}', urlencode($item['uri']), DI::session()->get('remote_comment'))]; DI::l10n()->t('Comment this item on your system'), DI::l10n()->t('Remote comment'),
str_replace('{uri}', urlencode($item['uri']), DI::session()->get('remote_comment'))
];
// Ensure to either display the remote comment or the local activities // Ensure to either display the remote comment or the local activities
$buttons = []; $buttons = [];
@ -670,7 +681,6 @@ class Post
$title = DI::l10n()->t('Reacted with %s by: %s', $element['emoji'], $actors); $title = DI::l10n()->t('Reacted with %s by: %s', $element['emoji'], $actors);
$icon = []; $icon = [];
break; break;
break;
} }
$emojis[$index] = ['emoji' => $element['emoji'], 'total' => $element['total'], 'title' => $title, 'icon' => $icon]; $emojis[$index] = ['emoji' => $element['emoji'], 'total' => $element['total'], 'title' => $title, 'icon' => $icon];
} }
@ -719,8 +729,10 @@ class Post
if ($item->getDataValue('network') === Protocol::MAIL && DI::userSession()->getLocalUserId() != $item->getDataValue('uid')) { if ($item->getDataValue('network') === Protocol::MAIL && DI::userSession()->getLocalUserId() != $item->getDataValue('uid')) {
Logger::warning('Post object does not belong to local user', ['post' => $item, 'local_user' => DI::userSession()->getLocalUserId()]); Logger::warning('Post object does not belong to local user', ['post' => $item, 'local_user' => DI::userSession()->getLocalUserId()]);
return false; return false;
} elseif (DI::activity()->match($item->getDataValue('verb'), Activity::LIKE) || } elseif (
DI::activity()->match($item->getDataValue('verb'), Activity::DISLIKE)) { DI::activity()->match($item->getDataValue('verb'), Activity::LIKE) ||
DI::activity()->match($item->getDataValue('verb'), Activity::DISLIKE)
) {
Logger::warning('Post objects is a like/dislike', ['post' => $item]); Logger::warning('Post objects is a like/dislike', ['post' => $item]);
return false; return false;
} }
@ -1004,8 +1016,10 @@ class Post
} }
$profile = Contact::getByURL($term['url'], false, ['addr', 'contact-type']); $profile = Contact::getByURL($term['url'], false, ['addr', 'contact-type']);
if (!empty($profile['addr']) && (($profile['contact-type'] ?? Contact::TYPE_UNKNOWN) != Contact::TYPE_COMMUNITY) && if (
($profile['addr'] != $owner['addr']) && !strstr($text, $profile['addr'])) { !empty($profile['addr']) && (($profile['contact-type'] ?? Contact::TYPE_UNKNOWN) != Contact::TYPE_COMMUNITY) &&
($profile['addr'] != $owner['addr']) && !strstr($text, $profile['addr'])
) {
$text .= '@' . $profile['addr'] . ' '; $text .= '@' . $profile['addr'] . ' ';
} }
} }
@ -1134,6 +1148,7 @@ class Post
'id' => $this->getDataValue('owner-id'), 'id' => $this->getDataValue('owner-id'),
'network' => $this->getDataValue('owner-network'), 'network' => $this->getDataValue('owner-network'),
'url' => $this->getDataValue('owner-link'), 'url' => $this->getDataValue('owner-link'),
'alias' => $this->getDataValue('owner-alias'),
]; ];
$this->owner_url = Contact::magicLinkByContact($owner); $this->owner_url = Contact::magicLinkByContact($owner);
} }

View file

@ -160,7 +160,7 @@ class Delivery
if (!empty($actor)) { if (!empty($actor)) {
$drop = !ActivityPub\Transmitter::sendRelayFollow($actor); $drop = !ActivityPub\Transmitter::sendRelayFollow($actor);
Logger::notice('Resubscribed to relay', ['url' => $actor, 'success' => !$drop]); Logger::notice('Resubscribed to relay', ['url' => $actor, 'success' => !$drop]);
} elseif ($cmd = ProtocolDelivery::DELETION) { } elseif ($cmd == ProtocolDelivery::DELETION) {
// Remote systems not always accept our deletion requests, so we drop them if rejected. // Remote systems not always accept our deletion requests, so we drop them if rejected.
// Situation is: In Friendica we allow the thread owner to delete foreign comments to their thread. // Situation is: In Friendica we allow the thread owner to delete foreign comments to their thread.
// Most AP systems don't allow this, so they will reject the deletion request. // Most AP systems don't allow this, so they will reject the deletion request.

View file

@ -432,9 +432,38 @@ class Processor
$item['owner-link'] = $item['author-link']; $item['owner-link'] = $item['author-link'];
$item['owner-id'] = $item['author-id']; $item['owner-id'] = $item['author-id'];
}
if (!$item['isGroup'] && !empty($activity['receiver_urls']['as:audience'])) {
foreach ($activity['receiver_urls']['as:audience'] as $audience) {
$actor = APContact::getByURL($audience, false);
if (($actor['type'] ?? 'Person') == 'Group') {
Logger::debug('Group post detected via audience.', ['audience' => $audience, 'actor' => $activity['actor'], 'author' => $activity['author']]);
$item['isGroup'] = true;
$item['group-link'] = $audience;
}
}
} else { } else {
$actor = APContact::getByURL($item['owner-link'], false); $owner = APContact::getByURL($item['owner-link'], false);
$item['isGroup'] = ($actor['type'] ?? 'Person') == 'Group'; }
if (!$item['isGroup'] && (($owner['type'] ?? 'Person') == 'Group')) {
Logger::debug('Group post detected via owner.', ['actor' => $activity['actor'], 'author' => $activity['author']]);
$item['isGroup'] = true;
$item['group-link'] = $item['owner-link'];
} elseif (!empty($item['causer-link'])) {
$causer = APContact::getByURL($item['causer-link'], false);
}
if (!$item['isGroup'] && (($causer['type'] ?? 'Person') == 'Group')) {
Logger::debug('Group post detected via causer.', ['actor' => $activity['actor'], 'author' => $activity['author'], 'causer' => $item['causer-link']]);
$item['isGroup'] = true;
$item['group-link'] = $item['causer-link'];
}
if (!empty($item['group-link']) && empty($item['causer-link'])) {
$item['causer-link'] = $item['group-link'];
$item['causer-id'] = Contact::getIdForURL($item['causer-link']);
} }
$item['uri'] = $activity['id']; $item['uri'] = $activity['id'];
@ -1059,10 +1088,10 @@ class Processor
$item['causer-id'] = ($item['gravity'] == Item::GRAVITY_PARENT) ? $item['owner-id'] : $item['author-id']; $item['causer-id'] = ($item['gravity'] == Item::GRAVITY_PARENT) ? $item['owner-id'] : $item['author-id'];
} }
if ($item['isGroup'] ?? false) { if ($item['isGroup']) {
$item['contact-id'] = Contact::getIdForURL($activity['actor'], $receiver); $item['contact-id'] = Contact::getIdForURL($item['group-link'], $receiver);
} else { } else {
$item['contact-id'] = Contact::getIdForURL($activity['author'], $receiver); $item['contact-id'] = Contact::getIdForURL($item['author-link'], $receiver);
} }
if (($receiver != 0) && empty($item['contact-id'])) { if (($receiver != 0) && empty($item['contact-id'])) {
@ -1075,7 +1104,7 @@ class Processor
} }
if (($receiver != 0) && ($item['gravity'] == Item::GRAVITY_PARENT) && !in_array($item['post-reason'], [Item::PR_FOLLOWER, Item::PR_TAG, item::PR_TO, Item::PR_CC, Item::PR_AUDIENCE])) { if (($receiver != 0) && ($item['gravity'] == Item::GRAVITY_PARENT) && !in_array($item['post-reason'], [Item::PR_FOLLOWER, Item::PR_TAG, item::PR_TO, Item::PR_CC, Item::PR_AUDIENCE])) {
if (!($item['isGroup'] ?? false)) { if (!$item['isGroup']) {
if ($item['post-reason'] == Item::PR_BCC) { if ($item['post-reason'] == Item::PR_BCC) {
Logger::info('Top level post via BCC from a non sharer, ignoring', ['uid' => $receiver, 'contact' => $item['contact-id'], 'url' => $item['uri']]); Logger::info('Top level post via BCC from a non sharer, ignoring', ['uid' => $receiver, 'contact' => $item['contact-id'], 'url' => $item['uri']]);
continue; continue;
@ -1096,7 +1125,7 @@ class Processor
} }
if ((DI::pConfig()->get($receiver, 'system', 'accept_only_sharer') == Item::COMPLETION_NONE) if ((DI::pConfig()->get($receiver, 'system', 'accept_only_sharer') == Item::COMPLETION_NONE)
&& ((!$isGroup && !($item['isGroup'] ?? false) && ($activity['type'] != 'as:Announce')) && ((!$isGroup && !$item['isGroup'] && ($activity['type'] != 'as:Announce'))
|| !Contact::isSharingByURL($activity['actor'], $receiver))) { || !Contact::isSharingByURL($activity['actor'], $receiver))) {
Logger::info('Actor is a non sharer, is no group or it is no announce', ['uid' => $receiver, 'actor' => $activity['actor'], 'url' => $item['uri'], 'type' => $activity['type']]); Logger::info('Actor is a non sharer, is no group or it is no announce', ['uid' => $receiver, 'actor' => $activity['actor'], 'url' => $item['uri'], 'type' => $activity['type']]);
continue; continue;
@ -1533,6 +1562,7 @@ class Processor
$activity['id'] = $object['id']; $activity['id'] = $object['id'];
$activity['to'] = $object['to'] ?? []; $activity['to'] = $object['to'] ?? [];
$activity['cc'] = $object['cc'] ?? []; $activity['cc'] = $object['cc'] ?? [];
$activity['audience'] = $object['audience'] ?? [];
$activity['actor'] = $actor; $activity['actor'] = $actor;
$activity['object'] = $object; $activity['object'] = $object;
$activity['published'] = $published; $activity['published'] = $published;

View file

@ -291,16 +291,17 @@ class Receiver
/** /**
* Prepare the object array * Prepare the object array
* *
* @param array $activity Array with activity data * @param array $activity Array with activity data
* @param integer $uid User ID * @param integer $uid User ID
* @param boolean $push Message had been pushed to our system * @param boolean $push Message had been pushed to our system
* @param boolean $trust_source Do we trust the source? * @param boolean $trust_source Do we trust the source?
* @param string $original_actor Actor of the original activity. Used for receiver detection. (Optional)
* *
* @return array with object data * @return array with object data
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException * @throws \ImagickException
*/ */
public static function prepareObjectData(array $activity, int $uid, bool $push, bool &$trust_source): array public static function prepareObjectData(array $activity, int $uid, bool $push, bool &$trust_source, string $original_actor = ''): array
{ {
$id = JsonLD::fetchElement($activity, '@id'); $id = JsonLD::fetchElement($activity, '@id');
$type = JsonLD::fetchElement($activity, '@type'); $type = JsonLD::fetchElement($activity, '@type');
@ -319,7 +320,7 @@ class Receiver
$fetched = false; $fetched = false;
if (!empty($id) && !$trust_source) { if (!empty($id) && !$trust_source) {
$fetch_uid = $uid ?: self::getBestUserForActivity($activity); $fetch_uid = $uid ?: self::getBestUserForActivity($activity, $original_actor);
$fetched_activity = Processor::fetchCachedActivity($fetch_id, $fetch_uid); $fetched_activity = Processor::fetchCachedActivity($fetch_id, $fetch_uid);
if (!empty($fetched_activity)) { if (!empty($fetched_activity)) {
@ -355,7 +356,7 @@ class Receiver
$type = JsonLD::fetchElement($activity, '@type'); $type = JsonLD::fetchElement($activity, '@type');
// Fetch all receivers from to, cc, bto and bcc // Fetch all receivers from to, cc, bto and bcc
$receiverdata = self::getReceivers($activity, $actor, [], false, $push || $fetched); $receiverdata = self::getReceivers($activity, $original_actor ?: $actor, [], false, $push || $fetched);
$receivers = $reception_types = []; $receivers = $reception_types = [];
foreach ($receiverdata as $key => $data) { foreach ($receiverdata as $key => $data) {
$receivers[$key] = $data['uid']; $receivers[$key] = $data['uid'];
@ -379,7 +380,7 @@ class Receiver
// We possibly need some user to fetch private content, // We possibly need some user to fetch private content,
// so we fetch one out of the receivers if no uid is provided. // so we fetch one out of the receivers if no uid is provided.
$fetch_uid = $uid ?: self::getBestUserForActivity($activity); $fetch_uid = $uid ?: self::getBestUserForActivity($activity, $original_actor);
$object_id = JsonLD::fetchElement($activity, 'as:object', '@id'); $object_id = JsonLD::fetchElement($activity, 'as:object', '@id');
if (empty($object_id)) { if (empty($object_id)) {
@ -394,28 +395,6 @@ class Receiver
$object_type = self::fetchObjectType($activity, $object_id, $fetch_uid); $object_type = self::fetchObjectType($activity, $object_id, $fetch_uid);
// Fetch the activity on Lemmy "Announce" messages (announces of activities)
if (($type == 'as:Announce') && in_array($object_type, array_merge(self::ACTIVITY_TYPES, ['as:Delete', 'as:Undo', 'as:Update']))) {
Logger::debug('Fetch announced activity', ['object' => $object_id, 'uid' => $fetch_uid]);
$data = Processor::fetchCachedActivity($object_id, $fetch_uid);
if (!empty($data)) {
$type = $object_type;
$announced_activity = JsonLD::compact($data);
// Some variables need to be refetched since the activity changed
$actor = JsonLD::fetchElement($announced_activity, 'as:actor', '@id');
$announced_id = JsonLD::fetchElement($announced_activity, 'as:object', '@id');
if (empty($announced_id)) {
Logger::warning('No object id in announced activity', ['id' => $object_id, 'activity' => $activity, 'announced' => $announced_activity]);
return [];
} else {
$activity = $announced_activity;
$object_id = $announced_id;
}
$object_type = self::fetchObjectType($activity, $object_id, $fetch_uid);
}
}
// Any activities on account types must not be altered // Any activities on account types must not be altered
if (in_array($type, ['as:Flag'])) { if (in_array($type, ['as:Flag'])) {
$object_data = []; $object_data = [];
@ -454,7 +433,7 @@ class Receiver
} elseif (in_array($type, array_merge(self::ACTIVITY_TYPES, ['as:Announce', 'as:Follow'])) && in_array($object_type, self::CONTENT_TYPES)) { } elseif (in_array($type, array_merge(self::ACTIVITY_TYPES, ['as:Announce', 'as:Follow'])) && in_array($object_type, self::CONTENT_TYPES)) {
// Create a mostly empty array out of the activity data (instead of the object). // Create a mostly empty array out of the activity data (instead of the object).
// This way we later don't have to check for the existence of each individual array element. // This way we later don't have to check for the existence of each individual array element.
$object_data = self::processObject($activity); $object_data = self::processObject($activity, $original_actor);
$object_data['name'] = $type; $object_data['name'] = $type;
$object_data['author'] = JsonLD::fetchElement($activity, 'as:actor', '@id'); $object_data['author'] = JsonLD::fetchElement($activity, 'as:actor', '@id');
$object_data['object_id'] = $object_id; $object_data['object_id'] = $object_id;
@ -598,18 +577,32 @@ class Receiver
} }
} }
// $trust_source is called by reference and is set to true if the content was retrieved successfully // Lemmy announces activities.
$object_data = self::prepareObjectData($activity, $uid, $push, $trust_source); // To simplify the further processing, we modify the received object.
if (empty($object_data)) { // For announced "create" activities we remove the middle layer.
Logger::info('No object data found', ['activity' => $activity]); // For the rest (like, dislike, update, ...) we just process the activity directly.
return true; $original_actor = '';
$object_type = JsonLD::fetchElement($activity['as:object'] ?? [], '@type');
if (($type == 'as:Announce') && !empty($object_type) && !in_array($object_type, self::CONTENT_TYPES) && self::isGroup($actor)) {
$object_object_type = JsonLD::fetchElement($activity['as:object']['as:object'] ?? [], '@type');
if (in_array($object_type, ['as:Create']) && in_array($object_object_type, self::CONTENT_TYPES)) {
Logger::debug('Replace "create" activity with inner object', ['type' => $object_type, 'object_type' => $object_object_type]);
$activity['as:object'] = $activity['as:object']['as:object'];
} elseif (in_array($object_type, array_merge(self::ACTIVITY_TYPES, ['as:Delete', 'as:Undo', 'as:Update']))) {
Logger::debug('Change announced activity to activity', ['type' => $object_type]);
$original_actor = $actor;
$type = $object_type;
$activity = $activity['as:object'];
} else {
Logger::info('Unhandled announced activity', ['type' => $object_type, 'object_type' => $object_object_type]);
}
} }
// Lemmy is announcing activities. // $trust_source is called by reference and is set to true if the content was retrieved successfully
// We are changing the announces into regular activities. $object_data = self::prepareObjectData($activity, $uid, $push, $trust_source, $original_actor);
if (($type == 'as:Announce') && in_array($object_data['type'] ?? '', array_merge(self::ACTIVITY_TYPES, ['as:Delete', 'as:Undo', 'as:Update']))) { if (empty($object_data)) {
Logger::debug('Change type of announce to activity', ['type' => $object_data['type']]); Logger::info('No object data found', ['activity' => $activity, 'callstack' => System::callstack(20)]);
$type = $object_data['type']; return true;
} }
if (!empty($body) && empty($object_data['raw'])) { if (!empty($body) && empty($object_data['raw'])) {
@ -688,6 +681,18 @@ class Receiver
return true; return true;
} }
/**
* Checks if the provided actor is a group account
*
* @param string $actor
* @return boolean
*/
private static function isGroup(string $actor): bool
{
$profile = APContact::getByURL($actor);
return ($profile['type'] ?? '') == 'Group';
}
/** /**
* Route activities * Route activities
* *
@ -1009,10 +1014,10 @@ class Receiver
* *
* @return int user id * @return int user id
*/ */
public static function getBestUserForActivity(array $activity): int public static function getBestUserForActivity(array $activity, string $actor = ''): int
{ {
$uid = 0; $uid = 0;
$actor = JsonLD::fetchElement($activity, 'as:actor', '@id') ?? ''; $actor = $actor ?: JsonLD::fetchElement($activity, 'as:actor', '@id') ?? '';
$receivers = self::getReceivers($activity, $actor, [], false, false); $receivers = self::getReceivers($activity, $actor, [], false, false);
foreach ($receivers as $receiver) { foreach ($receivers as $receiver) {
@ -1129,7 +1134,7 @@ class Receiver
} }
// Fetch the receivers for the public and the followers collection // Fetch the receivers for the public and the followers collection
if ((($receiver == $followers) || (($receiver == self::PUBLIC_COLLECTION) && !$isGroup)) && !empty($actor)) { if ((($receiver == $followers) || (($receiver == self::PUBLIC_COLLECTION) && !$isGroup) || ($isGroup && ($element == 'as:audience'))) && !empty($actor)) {
$receivers = self::getReceiverForActor($actor, $tags, $receivers, $follower_target, $profile); $receivers = self::getReceiverForActor($actor, $tags, $receivers, $follower_target, $profile);
continue; continue;
} }
@ -1196,12 +1201,16 @@ class Receiver
// "birdsitelive" is a service that mirrors tweets into the fediverse // "birdsitelive" is a service that mirrors tweets into the fediverse
// These posts can be fetched without authentication, but are not marked as public // These posts can be fetched without authentication, but are not marked as public
// We treat them as unlisted posts to be able to handle them. // We treat them as unlisted posts to be able to handle them.
// We always process deletion activities.
$activity_type = JsonLD::fetchElement($activity, '@type');
if (empty($receivers) && $fetch_unlisted && Contact::isPlatform($actor, 'birdsitelive')) { if (empty($receivers) && $fetch_unlisted && Contact::isPlatform($actor, 'birdsitelive')) {
$receivers[0] = ['uid' => 0, 'type' => self::TARGET_GLOBAL]; $receivers[0] = ['uid' => 0, 'type' => self::TARGET_GLOBAL];
$receivers[-1] = ['uid' => -1, 'type' => self::TARGET_GLOBAL]; $receivers[-1] = ['uid' => -1, 'type' => self::TARGET_GLOBAL];
Logger::notice('Post from "birdsitelive" is set to "unlisted"', ['id' => JsonLD::fetchElement($activity, '@id')]); Logger::notice('Post from "birdsitelive" is set to "unlisted"', ['id' => JsonLD::fetchElement($activity, '@id')]);
} elseif (empty($receivers) && in_array($activity_type, ['as:Delete', 'as:Undo'])) {
$receivers[0] = ['uid' => 0, 'type' => self::TARGET_GLOBAL];
} elseif (empty($receivers)) { } elseif (empty($receivers)) {
Logger::notice('Post has got no receivers', ['fetch_unlisted' => $fetch_unlisted, 'actor' => $actor, 'id' => JsonLD::fetchElement($activity, '@id'), 'type' => JsonLD::fetchElement($activity, '@type')]); Logger::notice('Post has got no receivers', ['fetch_unlisted' => $fetch_unlisted, 'actor' => $actor, 'id' => JsonLD::fetchElement($activity, '@id'), 'type' => $activity_type, 'callstack' => System::callstack(20)]);
} }
return $receivers; return $receivers;
@ -1437,21 +1446,9 @@ class Receiver
return false; return false;
} }
// Lemmy is resharing "create" activities instead of content
// We fetch the content from the activity.
if (in_array($type, ['as:Create'])) {
$object = $object['as:object'];
$type = JsonLD::fetchElement($object, '@type');
if (empty($type)) {
Logger::info('Empty type');
return false;
}
$object_data = self::processObject($object);
}
// We currently don't handle 'pt:CacheFile', but with this step we avoid logging // We currently don't handle 'pt:CacheFile', but with this step we avoid logging
if (in_array($type, self::CONTENT_TYPES) || ($type == 'pt:CacheFile')) { if (in_array($type, self::CONTENT_TYPES) || ($type == 'pt:CacheFile')) {
$object_data = self::processObject($object); $object_data = self::processObject($object, '');
if (!empty($data)) { if (!empty($data)) {
$object_data['raw-object'] = json_encode($data); $object_data['raw-object'] = json_encode($data);
@ -1855,12 +1852,13 @@ class Receiver
/** /**
* Fetches data from the object part of an activity * Fetches data from the object part of an activity
* *
* @param array $object * @param array $object
* @param string $actor
* *
* @return array|bool Object data or FALSE if $object does not contain @id element * @return array|bool Object data or FALSE if $object does not contain @id element
* @throws \Exception * @throws \Exception
*/ */
private static function processObject(array $object) private static function processObject(array $object, string $actor)
{ {
if (!JsonLD::fetchElement($object, '@id')) { if (!JsonLD::fetchElement($object, '@id')) {
return false; return false;
@ -1868,7 +1866,7 @@ class Receiver
$object_data = self::getObjectDataFromActivity($object); $object_data = self::getObjectDataFromActivity($object);
$receiverdata = self::getReceivers($object, $object_data['actor'] ?? '', $object_data['tags'], true, false); $receiverdata = self::getReceivers($object, $actor ?: $object_data['actor'] ?? '', $object_data['tags'], true, false);
$receivers = $reception_types = []; $receivers = $reception_types = [];
foreach ($receiverdata as $key => $data) { foreach ($receiverdata as $key => $data) {
$receivers[$key] = $data['uid']; $receivers[$key] = $data['uid'];

View file

@ -492,7 +492,6 @@ class Transmitter
* Returns an array with permissions of the thread parent of the given item array * Returns an array with permissions of the thread parent of the given item array
* *
* @param array $item * @param array $item
* @param bool $is_group_thread
* *
* @return array with permissions * @return array with permissions
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
@ -514,6 +513,7 @@ class Transmitter
'cc' => [], 'cc' => [],
'bto' => [], 'bto' => [],
'bcc' => [], 'bcc' => [],
'audience' => [],
]; ];
$parent_profile = APContact::getByURL($parent['author-link']); $parent_profile = APContact::getByURL($parent['author-link']);
@ -525,8 +525,8 @@ class Transmitter
$exclude[] = $item['owner-link']; $exclude[] = $item['owner-link'];
} }
$type = [Tag::TO => 'to', Tag::CC => 'cc', Tag::BTO => 'bto', Tag::BCC => 'bcc']; $type = [Tag::TO => 'to', Tag::CC => 'cc', Tag::BTO => 'bto', Tag::BCC => 'bcc', Tag::AUDIENCE => 'audience'];
foreach (Tag::getByURIId($item['thr-parent-id'], [Tag::TO, Tag::CC, Tag::BTO, Tag::BCC]) as $receiver) { foreach (Tag::getByURIId($item['thr-parent-id'], [Tag::TO, Tag::CC, Tag::BTO, Tag::BCC, Tag::AUDIENCE]) as $receiver) {
if (!empty($parent_profile['followers']) && $receiver['url'] == $parent_profile['followers'] && !empty($item_profile['followers'])) { if (!empty($parent_profile['followers']) && $receiver['url'] == $parent_profile['followers'] && !empty($item_profile['followers'])) {
if (!$is_group_thread) { if (!$is_group_thread) {
$permissions[$type[$receiver['type']]][] = $item_profile['followers']; $permissions[$type[$receiver['type']]][] = $item_profile['followers'];
@ -600,6 +600,45 @@ class Transmitter
$is_group_thread = false; $is_group_thread = false;
} }
$exclusive = false;
$mention = false;
$audience = [];
$parent_tags = Tag::getByURIId($item['parent-uri-id'], [Tag::AUDIENCE, Tag::MENTION]);
if (!empty($parent_tags)) {
$is_group_thread = false;
foreach ($parent_tags as $tag) {
if ($tag['type'] != Tag::AUDIENCE) {
continue;
}
$profile = APContact::getByURL($tag['url'], false);
if (!empty($profile) && ($profile['type'] == 'Group')) {
$audience[] = $tag['url'];
$is_group_thread = true;
}
}
if ($is_group_thread) {
foreach ($parent_tags as $tag) {
if (($tag['type'] == Tag::MENTION) && in_array($tag['url'], $audience)) {
$mention = true;
}
}
$exclusive = !$mention;
}
} elseif ($is_group_thread) {
foreach (Tag::getByURIId($item['parent-uri-id'], [Tag::MENTION, Tag::EXCLUSIVE_MENTION]) as $term) {
$profile = APContact::getByURL($term['url'], false);
if (!empty($profile) && ($profile['type'] == 'Group')) {
if ($term['type'] == Tag::EXCLUSIVE_MENTION) {
$audience[] = $term['url'];
$exclusive = true;
} elseif ($term['type'] == Tag::MENTION) {
$mention = true;
}
}
}
}
if (self::isAnnounce($item) || self::isAPPost($last_id)) { if (self::isAnnounce($item) || self::isAPPost($last_id)) {
// Will be activated in a later step // Will be activated in a later step
$networks = Protocol::FEDERATED; $networks = Protocol::FEDERATED;
@ -608,7 +647,7 @@ class Transmitter
$networks = [Protocol::ACTIVITYPUB, Protocol::OSTATUS]; $networks = [Protocol::ACTIVITYPUB, Protocol::OSTATUS];
} }
$data = ['to' => [], 'cc' => [], 'bcc' => [] , 'audience' => []]; $data = ['to' => [], 'cc' => [], 'bcc' => [] , 'audience' => $audience];
if ($item['gravity'] == Item::GRAVITY_PARENT) { if ($item['gravity'] == Item::GRAVITY_PARENT) {
$actor_profile = APContact::getByURL($item['owner-link']); $actor_profile = APContact::getByURL($item['owner-link']);
@ -616,23 +655,7 @@ class Transmitter
$actor_profile = APContact::getByURL($item['author-link']); $actor_profile = APContact::getByURL($item['author-link']);
} }
$exclusive = false; $terms = Tag::getByURIId($item['uri-id'], [Tag::MENTION, Tag::IMPLICIT_MENTION, Tag::EXCLUSIVE_MENTION, Tag::AUDIENCE]);
$mention = false;
if ($is_group_thread) {
foreach (Tag::getByURIId($item['parent-uri-id'], [Tag::MENTION, Tag::EXCLUSIVE_MENTION]) as $term) {
$profile = APContact::getByURL($term['url'], false);
if (!empty($profile) && ($profile['type'] == 'Group')) {
if ($term['type'] == Tag::EXCLUSIVE_MENTION) {
$exclusive = true;
} elseif ($term['type'] == Tag::MENTION) {
$mention = true;
}
}
}
}
$terms = Tag::getByURIId($item['uri-id'], [Tag::MENTION, Tag::IMPLICIT_MENTION, Tag::EXCLUSIVE_MENTION]);
if ($item['private'] != Item::PRIVATE) { if ($item['private'] != Item::PRIVATE) {
// Directly mention the original author upon a quoted reshare. // Directly mention the original author upon a quoted reshare.
@ -644,7 +667,9 @@ class Transmitter
$data['cc'][] = $announce['actor']['url']; $data['cc'][] = $announce['actor']['url'];
} }
$data = array_merge($data, self::fetchPermissionBlockFromThreadParent($item, $is_group_thread)); if (!$exclusive) {
$data = array_merge($data, self::fetchPermissionBlockFromThreadParent($item, $is_group_thread));
}
// Check if the item is completely public or unlisted // Check if the item is completely public or unlisted
if ($item['private'] == Item::PUBLIC) { if ($item['private'] == Item::PUBLIC) {
@ -656,6 +681,9 @@ class Transmitter
foreach ($terms as $term) { foreach ($terms as $term) {
$profile = APContact::getByURL($term['url'], false); $profile = APContact::getByURL($term['url'], false);
if (!empty($profile)) { if (!empty($profile)) {
if (($term['type'] == Tag::AUDIENCE) && ($profile['type'] == 'Group')) {
$data['audience'][] = $profile['url'];
}
if ($term['type'] == Tag::EXCLUSIVE_MENTION) { if ($term['type'] == Tag::EXCLUSIVE_MENTION) {
$exclusive = true; $exclusive = true;
if (!empty($profile['followers']) && ($profile['type'] == 'Group')) { if (!empty($profile['followers']) && ($profile['type'] == 'Group')) {
@ -684,6 +712,9 @@ class Transmitter
$profile = APContact::getByURL($term['url'], false); $profile = APContact::getByURL($term['url'], false);
if (!empty($profile)) { if (!empty($profile)) {
if (($term['type'] == Tag::AUDIENCE) && ($profile['type'] == 'Group')) {
$data['audience'][] = $profile['url'];
}
if ($term['type'] == Tag::EXCLUSIVE_MENTION) { if ($term['type'] == Tag::EXCLUSIVE_MENTION) {
$exclusive = true; $exclusive = true;
if (!empty($profile['followers']) && ($profile['type'] == 'Group')) { if (!empty($profile['followers']) && ($profile['type'] == 'Group')) {
@ -727,7 +758,7 @@ class Transmitter
} }
} }
if (!empty($item['parent'])) { if (!empty($item['parent']) && (!$exclusive || ($item['private'] == Item::PRIVATE))) {
if ($item['private'] == Item::PRIVATE) { if ($item['private'] == Item::PRIVATE) {
$condition = ['parent' => $item['parent'], 'uri-id' => $item['thr-parent-id']]; $condition = ['parent' => $item['parent'], 'uri-id' => $item['thr-parent-id']];
} else { } else {
@ -814,20 +845,13 @@ class Transmitter
} }
} }
$receivers = ['to' => array_values($data['to']), 'cc' => array_values($data['cc']), 'bcc' => array_values($data['bcc'])]; $receivers = ['to' => array_values($data['to']), 'cc' => array_values($data['cc']), 'bcc' => array_values($data['bcc']), 'audience' => array_values($data['audience'])];
if (!empty($data['audience'])) {
$receivers['audience'] = array_values($data['audience']);
if (count($receivers['audience']) == 1) {
$receivers['audience'] = $receivers['audience'][0];
}
}
if (!$blindcopy) { if (!$blindcopy) {
unset($receivers['bcc']); unset($receivers['bcc']);
} }
foreach (['to' => Tag::TO, 'cc' => Tag::CC, 'bcc' => Tag::BCC] 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) {
if ($receiver == ActivityPub::PUBLIC_COLLECTION) { if ($receiver == ActivityPub::PUBLIC_COLLECTION) {
@ -840,6 +864,12 @@ class Transmitter
} }
} }
if (!$blindcopy && count($receivers['audience']) == 1) {
$receivers['audience'] = $receivers['audience'][0];
} elseif (!$receivers['audience']) {
unset($receivers['audience']);
}
return $receivers; return $receivers;
} }
@ -976,7 +1006,7 @@ class Transmitter
$profile_uid = User::getIdForURL($item_profile['url']); $profile_uid = User::getIdForURL($item_profile['url']);
foreach (['to', 'cc', 'bto', 'bcc'] as $element) { foreach (['to', 'cc', 'bto', 'bcc', 'audience'] as $element) {
if (empty($permissions[$element])) { if (empty($permissions[$element])) {
continue; continue;
} }
@ -1000,7 +1030,7 @@ class Transmitter
} else { } else {
$target = $profile['sharedinbox']; $target = $profile['sharedinbox'];
} }
if (!self::archivedInbox($target)) { if (!self::archivedInbox($target) && !in_array($contact['id'], $inboxes[$target] ?? [])) {
$inboxes[$target][] = $contact['id'] ?? 0; $inboxes[$target][] = $contact['id'] ?? 0;
} }
} }
@ -1101,12 +1131,14 @@ class Transmitter
unset($data['cc']); unset($data['cc']);
unset($data['bcc']); unset($data['bcc']);
unset($data['audience']);
$object['to'] = $data['to']; $object['to'] = $data['to'];
$object['tag'] = [['type' => 'Mention', 'href' => $object['to'][0], 'name' => '']]; $object['tag'] = [['type' => 'Mention', 'href' => $object['to'][0], 'name' => '']];
unset($object['cc']); unset($object['cc']);
unset($object['bcc']); unset($object['bcc']);
unset($object['audience']);
$data['directMessage'] = true; $data['directMessage'] = true;

View file

@ -56,7 +56,7 @@ use Friendica\Database\DBA;
// This file is required several times during the test in DbaDefinition which justifies this condition // This file is required several times during the test in DbaDefinition which justifies this condition
if (!defined('DB_UPDATE_VERSION')) { if (!defined('DB_UPDATE_VERSION')) {
define('DB_UPDATE_VERSION', 1520); define('DB_UPDATE_VERSION', 1521);
} }
return [ return [

View file

@ -149,6 +149,7 @@
"author-addr" => ["author", "addr"], "author-addr" => ["author", "addr"],
"author-name" => "IF (`contact`.`url` = `author`.`url` AND `contact`.`name` != '', `contact`.`name`, `author`.`name`)", "author-name" => "IF (`contact`.`url` = `author`.`url` AND `contact`.`name` != '', `contact`.`name`, `author`.`name`)",
"author-nick" => ["author", "nick"], "author-nick" => ["author", "nick"],
"author-alias" => ["author", "alias"],
"author-avatar" => "IF (`contact`.`url` = `author`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `author`.`thumb`)", "author-avatar" => "IF (`contact`.`url` = `author`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `author`.`thumb`)",
"author-network" => ["author", "network"], "author-network" => ["author", "network"],
"author-blocked" => ["author", "blocked"], "author-blocked" => ["author", "blocked"],
@ -161,6 +162,7 @@
"owner-addr" => ["owner", "addr"], "owner-addr" => ["owner", "addr"],
"owner-name" => "IF (`contact`.`url` = `owner`.`url` AND `contact`.`name` != '', `contact`.`name`, `owner`.`name`)", "owner-name" => "IF (`contact`.`url` = `owner`.`url` AND `contact`.`name` != '', `contact`.`name`, `owner`.`name`)",
"owner-nick" => ["owner", "nick"], "owner-nick" => ["owner", "nick"],
"owner-alias" => ["owner", "alias"],
"owner-avatar" => "IF (`contact`.`url` = `owner`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `owner`.`thumb`)", "owner-avatar" => "IF (`contact`.`url` = `owner`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `owner`.`thumb`)",
"owner-network" => ["owner", "network"], "owner-network" => ["owner", "network"],
"owner-blocked" => ["owner", "blocked"], "owner-blocked" => ["owner", "blocked"],
@ -324,6 +326,7 @@
"author-addr" => ["author", "addr"], "author-addr" => ["author", "addr"],
"author-name" => "IF (`contact`.`url` = `author`.`url` AND `contact`.`name` != '', `contact`.`name`, `author`.`name`)", "author-name" => "IF (`contact`.`url` = `author`.`url` AND `contact`.`name` != '', `contact`.`name`, `author`.`name`)",
"author-nick" => ["author", "nick"], "author-nick" => ["author", "nick"],
"author-alias" => ["author", "alias"],
"author-avatar" => "IF (`contact`.`url` = `author`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `author`.`thumb`)", "author-avatar" => "IF (`contact`.`url` = `author`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `author`.`thumb`)",
"author-network" => ["author", "network"], "author-network" => ["author", "network"],
"author-blocked" => ["author", "blocked"], "author-blocked" => ["author", "blocked"],
@ -336,6 +339,7 @@
"owner-addr" => ["owner", "addr"], "owner-addr" => ["owner", "addr"],
"owner-name" => "IF (`contact`.`url` = `owner`.`url` AND `contact`.`name` != '', `contact`.`name`, `owner`.`name`)", "owner-name" => "IF (`contact`.`url` = `owner`.`url` AND `contact`.`name` != '', `contact`.`name`, `owner`.`name`)",
"owner-nick" => ["owner", "nick"], "owner-nick" => ["owner", "nick"],
"owner-alias" => ["owner", "alias"],
"owner-avatar" => "IF (`contact`.`url` = `owner`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `owner`.`thumb`)", "owner-avatar" => "IF (`contact`.`url` = `owner`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `owner`.`thumb`)",
"owner-network" => ["owner", "network"], "owner-network" => ["owner", "network"],
"owner-blocked" => ["owner", "blocked"], "owner-blocked" => ["owner", "blocked"],
@ -485,6 +489,7 @@
"author-addr" => ["author", "addr"], "author-addr" => ["author", "addr"],
"author-name" => ["author", "name"], "author-name" => ["author", "name"],
"author-nick" => ["author", "nick"], "author-nick" => ["author", "nick"],
"author-alias" => ["author", "alias"],
"author-avatar" => ["author", "thumb"], "author-avatar" => ["author", "thumb"],
"author-network" => ["author", "network"], "author-network" => ["author", "network"],
"author-blocked" => ["author", "blocked"], "author-blocked" => ["author", "blocked"],
@ -497,6 +502,7 @@
"owner-addr" => ["owner", "addr"], "owner-addr" => ["owner", "addr"],
"owner-name" => ["owner", "name"], "owner-name" => ["owner", "name"],
"owner-nick" => ["owner", "nick"], "owner-nick" => ["owner", "nick"],
"owner-alias" => ["owner", "alias"],
"owner-avatar" => ["owner", "thumb"], "owner-avatar" => ["owner", "thumb"],
"owner-network" => ["owner", "network"], "owner-network" => ["owner", "network"],
"owner-blocked" => ["owner", "blocked"], "owner-blocked" => ["owner", "blocked"],
@ -623,6 +629,7 @@
"author-addr" => ["author", "addr"], "author-addr" => ["author", "addr"],
"author-name" => ["author", "name"], "author-name" => ["author", "name"],
"author-nick" => ["author", "nick"], "author-nick" => ["author", "nick"],
"author-alias" => ["author", "alias"],
"author-avatar" => ["author", "thumb"], "author-avatar" => ["author", "thumb"],
"author-network" => ["author", "network"], "author-network" => ["author", "network"],
"author-blocked" => ["author", "blocked"], "author-blocked" => ["author", "blocked"],
@ -635,6 +642,7 @@
"owner-addr" => ["owner", "addr"], "owner-addr" => ["owner", "addr"],
"owner-name" => ["owner", "name"], "owner-name" => ["owner", "name"],
"owner-nick" => ["owner", "nick"], "owner-nick" => ["owner", "nick"],
"owner-alias" => ["owner", "alias"],
"owner-avatar" => ["owner", "thumb"], "owner-avatar" => ["owner", "thumb"],
"owner-network" => ["owner", "network"], "owner-network" => ["owner", "network"],
"owner-blocked" => ["owner", "blocked"], "owner-blocked" => ["owner", "blocked"],

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: 2023.09-dev\n" "Project-Id-Version: 2023.09-dev\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-06-15 12:51-0400\n" "POT-Creation-Date: 2023-06-18 17:38+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -45,7 +45,7 @@ msgid "Item not found."
msgstr "" msgstr ""
#: mod/item.php:451 mod/message.php:67 mod/message.php:113 mod/notes.php:45 #: mod/item.php:451 mod/message.php:67 mod/message.php:113 mod/notes.php:45
#: mod/photos.php:152 mod/photos.php:667 src/Model/Event.php:522 #: mod/photos.php:152 mod/photos.php:670 src/Model/Event.php:522
#: src/Module/Attach.php:55 src/Module/BaseApi.php:99 #: src/Module/Attach.php:55 src/Module/BaseApi.php:99
#: src/Module/BaseNotifications.php:98 src/Module/BaseSettings.php:52 #: src/Module/BaseNotifications.php:98 src/Module/BaseSettings.php:52
#: src/Module/Calendar/Event/API.php:88 src/Module/Calendar/Event/Form.php:84 #: src/Module/Calendar/Event/API.php:88 src/Module/Calendar/Event/Form.php:84
@ -281,7 +281,7 @@ msgstr ""
msgid "Your message:" msgid "Your message:"
msgstr "" msgstr ""
#: mod/message.php:199 mod/message.php:354 src/Content/Conversation.php:360 #: mod/message.php:199 mod/message.php:354 src/Content/Conversation.php:361
#: src/Module/Post/Edit.php:131 #: src/Module/Post/Edit.php:131
msgid "Upload photo" msgid "Upload photo"
msgstr "" msgstr ""
@ -291,16 +291,16 @@ msgstr ""
msgid "Insert web link" msgid "Insert web link"
msgstr "" msgstr ""
#: mod/message.php:201 mod/message.php:357 mod/photos.php:1289 #: mod/message.php:201 mod/message.php:357 mod/photos.php:1301
#: src/Content/Conversation.php:391 src/Content/Conversation.php:735 #: src/Content/Conversation.php:392 src/Content/Conversation.php:742
#: src/Module/Item/Compose.php:206 src/Module/Post/Edit.php:145 #: src/Module/Item/Compose.php:206 src/Module/Post/Edit.php:145
#: src/Module/Profile/UnkMail.php:154 src/Object/Post.php:557 #: src/Module/Profile/UnkMail.php:154 src/Object/Post.php:568
msgid "Please wait" msgid "Please wait"
msgstr "" msgstr ""
#: mod/message.php:202 mod/message.php:356 mod/photos.php:700 #: mod/message.php:202 mod/message.php:356 mod/photos.php:705
#: mod/photos.php:817 mod/photos.php:1095 mod/photos.php:1136 #: mod/photos.php:824 mod/photos.php:1101 mod/photos.php:1142
#: mod/photos.php:1192 mod/photos.php:1266 #: mod/photos.php:1198 mod/photos.php:1278
#: src/Module/Calendar/Event/Form.php:250 src/Module/Contact/Advanced.php:132 #: src/Module/Calendar/Event/Form.php:250 src/Module/Contact/Advanced.php:132
#: src/Module/Contact/Profile.php:339 #: src/Module/Contact/Profile.php:339
#: src/Module/Debug/ActivityPubConversion.php:140 #: src/Module/Debug/ActivityPubConversion.php:140
@ -311,7 +311,7 @@ msgstr ""
#: src/Module/Install.php:309 src/Module/Invite.php:178 #: src/Module/Install.php:309 src/Module/Invite.php:178
#: src/Module/Item/Compose.php:189 src/Module/Moderation/Item/Source.php:79 #: src/Module/Item/Compose.php:189 src/Module/Moderation/Item/Source.php:79
#: src/Module/Profile/Profile.php:274 src/Module/Profile/UnkMail.php:155 #: src/Module/Profile/Profile.php:274 src/Module/Profile/UnkMail.php:155
#: src/Module/Settings/Profile/Index.php:230 src/Object/Post.php:1070 #: src/Module/Settings/Profile/Index.php:230 src/Object/Post.php:1084
#: view/theme/duepuntozero/config.php:85 view/theme/frio/config.php:171 #: view/theme/duepuntozero/config.php:85 view/theme/frio/config.php:171
#: view/theme/quattro/config.php:87 view/theme/vier/config.php:135 #: view/theme/quattro/config.php:87 view/theme/vier/config.php:135
msgid "Submit" msgid "Submit"
@ -377,13 +377,13 @@ msgstr ""
msgid "Personal notes are visible only by yourself." msgid "Personal notes are visible only by yourself."
msgstr "" msgstr ""
#: mod/notes.php:57 src/Content/Text/HTML.php:856 #: mod/notes.php:57 src/Content/Text/HTML.php:859
#: src/Module/Admin/Storage.php:142 src/Module/Filer/SaveTag.php:74 #: src/Module/Admin/Storage.php:142 src/Module/Filer/SaveTag.php:74
#: src/Module/Post/Edit.php:129 #: src/Module/Post/Edit.php:129
msgid "Save" msgid "Save"
msgstr "" msgstr ""
#: mod/photos.php:67 mod/photos.php:132 mod/photos.php:575 #: mod/photos.php:67 mod/photos.php:132 mod/photos.php:578
#: src/Model/Event.php:514 src/Model/Profile.php:234 #: src/Model/Event.php:514 src/Model/Profile.php:234
#: src/Module/Calendar/Export.php:74 src/Module/Calendar/Show.php:74 #: src/Module/Calendar/Export.php:74 src/Module/Calendar/Show.php:74
#: src/Module/DFRN/Poll.php:43 src/Module/Feed.php:65 src/Module/HCard.php:51 #: src/Module/DFRN/Poll.php:43 src/Module/Feed.php:65 src/Module/HCard.php:51
@ -405,7 +405,7 @@ msgstr ""
msgid "Recent Photos" msgid "Recent Photos"
msgstr "" msgstr ""
#: mod/photos.php:109 mod/photos.php:865 src/Module/Profile/Photos.php:382 #: mod/photos.php:109 mod/photos.php:872 src/Module/Profile/Photos.php:382
#: src/Module/Profile/Photos.php:402 #: src/Module/Profile/Photos.php:402
msgid "Upload New Photos" msgid "Upload New Photos"
msgstr "" msgstr ""
@ -423,72 +423,72 @@ msgstr ""
msgid "Album not found." msgid "Album not found."
msgstr "" msgstr ""
#: mod/photos.php:242 #: mod/photos.php:244
msgid "Album successfully deleted" msgid "Album successfully deleted"
msgstr "" msgstr ""
#: mod/photos.php:244 #: mod/photos.php:246
msgid "Album was empty." msgid "Album was empty."
msgstr "" msgstr ""
#: mod/photos.php:276 #: mod/photos.php:277
msgid "Failed to delete the photo." msgid "Failed to delete the photo."
msgstr "" msgstr ""
#: mod/photos.php:542 #: mod/photos.php:545
msgid "a photo" msgid "a photo"
msgstr "" msgstr ""
#: mod/photos.php:542 #: mod/photos.php:545
#, php-format #, php-format
msgid "%1$s was tagged in %2$s by %3$s" msgid "%1$s was tagged in %2$s by %3$s"
msgstr "" msgstr ""
#: mod/photos.php:579 src/Module/Conversation/Community.php:188 #: mod/photos.php:582 src/Module/Conversation/Community.php:188
#: src/Module/Directory.php:48 src/Module/Profile/Photos.php:295 #: src/Module/Directory.php:48 src/Module/Profile/Photos.php:295
#: src/Module/Search/Index.php:65 #: src/Module/Search/Index.php:65
msgid "Public access denied." msgid "Public access denied."
msgstr "" msgstr ""
#: mod/photos.php:584 #: mod/photos.php:587
msgid "No photos selected" msgid "No photos selected"
msgstr "" msgstr ""
#: mod/photos.php:716 #: mod/photos.php:721
#, php-format #, php-format
msgid "The maximum accepted image size is %s" msgid "The maximum accepted image size is %s"
msgstr "" msgstr ""
#: mod/photos.php:723 #: mod/photos.php:728
msgid "Upload Photos" msgid "Upload Photos"
msgstr "" msgstr ""
#: mod/photos.php:727 mod/photos.php:813 #: mod/photos.php:732 mod/photos.php:820
msgid "New album name: " msgid "New album name: "
msgstr "" msgstr ""
#: mod/photos.php:728 #: mod/photos.php:733
msgid "or select existing album:" msgid "or select existing album:"
msgstr "" msgstr ""
#: mod/photos.php:729 #: mod/photos.php:734
msgid "Do not show a status post for this upload" msgid "Do not show a status post for this upload"
msgstr "" msgstr ""
#: mod/photos.php:731 mod/photos.php:1091 src/Content/Conversation.php:393 #: mod/photos.php:736 mod/photos.php:1097 src/Content/Conversation.php:394
#: src/Module/Calendar/Event/Form.php:253 src/Module/Post/Edit.php:183 #: src/Module/Calendar/Event/Form.php:253 src/Module/Post/Edit.php:183
msgid "Permissions" msgid "Permissions"
msgstr "" msgstr ""
#: mod/photos.php:794 #: mod/photos.php:801
msgid "Do you really want to delete this photo album and all its photos?" msgid "Do you really want to delete this photo album and all its photos?"
msgstr "" msgstr ""
#: mod/photos.php:795 mod/photos.php:818 #: mod/photos.php:802 mod/photos.php:825
msgid "Delete Album" msgid "Delete Album"
msgstr "" msgstr ""
#: mod/photos.php:796 mod/photos.php:897 src/Content/Conversation.php:409 #: mod/photos.php:803 mod/photos.php:903 src/Content/Conversation.php:410
#: src/Module/Contact/Follow.php:173 src/Module/Contact/Revoke.php:109 #: src/Module/Contact/Follow.php:173 src/Module/Contact/Revoke.php:109
#: src/Module/Contact/Unfollow.php:126 #: src/Module/Contact/Unfollow.php:126
#: src/Module/Media/Attachment/Browser.php:77 #: src/Module/Media/Attachment/Browser.php:77
@ -498,130 +498,130 @@ msgstr ""
msgid "Cancel" msgid "Cancel"
msgstr "" msgstr ""
#: mod/photos.php:822 #: mod/photos.php:829
msgid "Edit Album" msgid "Edit Album"
msgstr "" msgstr ""
#: mod/photos.php:823 #: mod/photos.php:830
msgid "Drop Album" msgid "Drop Album"
msgstr "" msgstr ""
#: mod/photos.php:827 #: mod/photos.php:834
msgid "Show Newest First" msgid "Show Newest First"
msgstr "" msgstr ""
#: mod/photos.php:829 #: mod/photos.php:836
msgid "Show Oldest First" msgid "Show Oldest First"
msgstr "" msgstr ""
#: mod/photos.php:850 src/Module/Profile/Photos.php:350 #: mod/photos.php:857 src/Module/Profile/Photos.php:350
msgid "View Photo" msgid "View Photo"
msgstr "" msgstr ""
#: mod/photos.php:883 #: mod/photos.php:889
msgid "Permission denied. Access to this item may be restricted." msgid "Permission denied. Access to this item may be restricted."
msgstr "" msgstr ""
#: mod/photos.php:885 #: mod/photos.php:891
msgid "Photo not available" msgid "Photo not available"
msgstr "" msgstr ""
#: mod/photos.php:895 #: mod/photos.php:901
msgid "Do you really want to delete this photo?" msgid "Do you really want to delete this photo?"
msgstr "" msgstr ""
#: mod/photos.php:896 mod/photos.php:1096 #: mod/photos.php:902 mod/photos.php:1102
msgid "Delete Photo" msgid "Delete Photo"
msgstr "" msgstr ""
#: mod/photos.php:994 #: mod/photos.php:1000
msgid "View photo" msgid "View photo"
msgstr "" msgstr ""
#: mod/photos.php:996 #: mod/photos.php:1002
msgid "Edit photo" msgid "Edit photo"
msgstr "" msgstr ""
#: mod/photos.php:997 #: mod/photos.php:1003
msgid "Delete photo" msgid "Delete photo"
msgstr "" msgstr ""
#: mod/photos.php:998 #: mod/photos.php:1004
msgid "Use as profile photo" msgid "Use as profile photo"
msgstr "" msgstr ""
#: mod/photos.php:1005 #: mod/photos.php:1011
msgid "Private Photo" msgid "Private Photo"
msgstr "" msgstr ""
#: mod/photos.php:1011 #: mod/photos.php:1017
msgid "View Full Size" msgid "View Full Size"
msgstr "" msgstr ""
#: mod/photos.php:1064 #: mod/photos.php:1070
msgid "Tags: " msgid "Tags: "
msgstr "" msgstr ""
#: mod/photos.php:1067 #: mod/photos.php:1073
msgid "[Select tags to remove]" msgid "[Select tags to remove]"
msgstr "" msgstr ""
#: mod/photos.php:1082 #: mod/photos.php:1088
msgid "New album name" msgid "New album name"
msgstr "" msgstr ""
#: mod/photos.php:1083 #: mod/photos.php:1089
msgid "Caption" msgid "Caption"
msgstr "" msgstr ""
#: mod/photos.php:1084 #: mod/photos.php:1090
msgid "Add a Tag" msgid "Add a Tag"
msgstr "" msgstr ""
#: mod/photos.php:1084 #: mod/photos.php:1090
msgid "Example: @bob, @Barbara_Jensen, @jim@example.com, #California, #camping" msgid "Example: @bob, @Barbara_Jensen, @jim@example.com, #California, #camping"
msgstr "" msgstr ""
#: mod/photos.php:1085 #: mod/photos.php:1091
msgid "Do not rotate" msgid "Do not rotate"
msgstr "" msgstr ""
#: mod/photos.php:1086 #: mod/photos.php:1092
msgid "Rotate CW (right)" msgid "Rotate CW (right)"
msgstr "" msgstr ""
#: mod/photos.php:1087 #: mod/photos.php:1093
msgid "Rotate CCW (left)" msgid "Rotate CCW (left)"
msgstr "" msgstr ""
#: mod/photos.php:1133 mod/photos.php:1189 mod/photos.php:1263 #: mod/photos.php:1139 mod/photos.php:1195 mod/photos.php:1275
#: src/Module/Contact.php:597 src/Module/Item/Compose.php:188 #: src/Module/Contact.php:619 src/Module/Item/Compose.php:188
#: src/Object/Post.php:1067 #: src/Object/Post.php:1081
msgid "This is you" msgid "This is you"
msgstr "" msgstr ""
#: mod/photos.php:1135 mod/photos.php:1191 mod/photos.php:1265 #: mod/photos.php:1141 mod/photos.php:1197 mod/photos.php:1277
#: src/Object/Post.php:551 src/Object/Post.php:1069 #: src/Object/Post.php:562 src/Object/Post.php:1083
msgid "Comment" msgid "Comment"
msgstr "" msgstr ""
#: mod/photos.php:1137 mod/photos.php:1193 mod/photos.php:1267 #: mod/photos.php:1143 mod/photos.php:1199 mod/photos.php:1279
#: src/Content/Conversation.php:406 src/Module/Calendar/Event/Form.php:248 #: src/Content/Conversation.php:407 src/Module/Calendar/Event/Form.php:248
#: src/Module/Item/Compose.php:201 src/Module/Post/Edit.php:165 #: src/Module/Item/Compose.php:201 src/Module/Post/Edit.php:165
#: src/Object/Post.php:1083 #: src/Object/Post.php:1097
msgid "Preview" msgid "Preview"
msgstr "" msgstr ""
#: mod/photos.php:1138 src/Content/Conversation.php:359 #: mod/photos.php:1144 src/Content/Conversation.php:360
#: src/Module/Post/Edit.php:130 src/Object/Post.php:1071 #: src/Module/Post/Edit.php:130 src/Object/Post.php:1085
msgid "Loading..." msgid "Loading..."
msgstr "" msgstr ""
#: mod/photos.php:1224 src/Content/Conversation.php:651 src/Object/Post.php:258 #: mod/photos.php:1236 src/Content/Conversation.php:658 src/Object/Post.php:262
msgid "Select" msgid "Select"
msgstr "" msgstr ""
#: mod/photos.php:1225 src/Content/Conversation.php:652 #: mod/photos.php:1237 src/Content/Conversation.php:659
#: src/Module/Moderation/Users/Active.php:136 #: src/Module/Moderation/Users/Active.php:136
#: src/Module/Moderation/Users/Blocked.php:136 #: src/Module/Moderation/Users/Blocked.php:136
#: src/Module/Moderation/Users/Index.php:151 #: src/Module/Moderation/Users/Index.php:151
@ -629,23 +629,23 @@ msgstr ""
msgid "Delete" msgid "Delete"
msgstr "" msgstr ""
#: mod/photos.php:1286 src/Object/Post.php:391 #: mod/photos.php:1298 src/Object/Post.php:400
msgid "Like" msgid "Like"
msgstr "" msgstr ""
#: mod/photos.php:1287 src/Object/Post.php:391 #: mod/photos.php:1299 src/Object/Post.php:400
msgid "I like this (toggle)" msgid "I like this (toggle)"
msgstr "" msgstr ""
#: mod/photos.php:1288 src/Object/Post.php:392 #: mod/photos.php:1300 src/Object/Post.php:401
msgid "Dislike" msgid "Dislike"
msgstr "" msgstr ""
#: mod/photos.php:1290 src/Object/Post.php:392 #: mod/photos.php:1302 src/Object/Post.php:401
msgid "I don't like this (toggle)" msgid "I don't like this (toggle)"
msgstr "" msgstr ""
#: mod/photos.php:1312 #: mod/photos.php:1324
msgid "Map" msgid "Map"
msgstr "" msgstr ""
@ -780,11 +780,11 @@ msgstr ""
msgid "Followers" msgid "Followers"
msgstr "" msgstr ""
#: src/BaseModule.php:437 src/Content/Widget.php:244 src/Module/Contact.php:416 #: src/BaseModule.php:437 src/Content/Widget.php:244 src/Module/Contact.php:418
msgid "Following" msgid "Following"
msgstr "" msgstr ""
#: src/BaseModule.php:442 src/Content/Widget.php:245 src/Module/Contact.php:417 #: src/BaseModule.php:442 src/Content/Widget.php:245 src/Module/Contact.php:421
msgid "Mutual friends" msgid "Mutual friends"
msgstr "" msgstr ""
@ -1122,65 +1122,65 @@ msgstr ""
msgid "%s (via %s)" msgid "%s (via %s)"
msgstr "" msgstr ""
#: src/Content/Conversation.php:218 #: src/Content/Conversation.php:219
msgid "and" msgid "and"
msgstr "" msgstr ""
#: src/Content/Conversation.php:221 #: src/Content/Conversation.php:222
#, php-format #, php-format
msgid "and %d other people" msgid "and %d other people"
msgstr "" msgstr ""
#: src/Content/Conversation.php:227 #: src/Content/Conversation.php:228
#, php-format #, php-format
msgid "%2$s likes this." msgid "%2$s likes this."
msgid_plural "%2$s like this." msgid_plural "%2$s like this."
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: src/Content/Conversation.php:229 #: src/Content/Conversation.php:230
#, php-format #, php-format
msgid "%2$s doesn't like this." msgid "%2$s doesn't like this."
msgid_plural "%2$s don't like this." msgid_plural "%2$s don't like this."
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: src/Content/Conversation.php:231 #: src/Content/Conversation.php:232
#, php-format #, php-format
msgid "%2$s attends." msgid "%2$s attends."
msgid_plural "%2$s attend." msgid_plural "%2$s attend."
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: src/Content/Conversation.php:233 #: src/Content/Conversation.php:234
#, php-format #, php-format
msgid "%2$s doesn't attend." msgid "%2$s doesn't attend."
msgid_plural "%2$s don't attend." msgid_plural "%2$s don't attend."
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: src/Content/Conversation.php:235 #: src/Content/Conversation.php:236
#, php-format #, php-format
msgid "%2$s attends maybe." msgid "%2$s attends maybe."
msgid_plural "%2$s attend maybe." msgid_plural "%2$s attend maybe."
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: src/Content/Conversation.php:237 #: src/Content/Conversation.php:238
#, php-format #, php-format
msgid "%2$s reshared this." msgid "%2$s reshared this."
msgid_plural "%2$s reshared this." msgid_plural "%2$s reshared this."
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: src/Content/Conversation.php:266 #: src/Content/Conversation.php:267
#, php-format #, php-format
msgid "<button type=\"button\" %2$s>%1$d person</button> likes this" msgid "<button type=\"button\" %2$s>%1$d person</button> likes this"
msgid_plural "<button type=\"button\" %2$s>%1$d people</button> like this" msgid_plural "<button type=\"button\" %2$s>%1$d people</button> like this"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: src/Content/Conversation.php:269 #: src/Content/Conversation.php:270
#, php-format #, php-format
msgid "<button type=\"button\" %2$s>%1$d person</button> doesn't like this" msgid "<button type=\"button\" %2$s>%1$d person</button> doesn't like this"
msgid_plural "" msgid_plural ""
@ -1188,304 +1188,304 @@ msgid_plural ""
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: src/Content/Conversation.php:272 #: src/Content/Conversation.php:273
#, php-format #, php-format
msgid "<button type=\"button\" %2$s>%1$d person</button> attends" msgid "<button type=\"button\" %2$s>%1$d person</button> attends"
msgid_plural "<button type=\"button\" %2$s>%1$d people</button> attend" msgid_plural "<button type=\"button\" %2$s>%1$d people</button> attend"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: src/Content/Conversation.php:275 #: src/Content/Conversation.php:276
#, php-format #, php-format
msgid "<button type=\"button\" %2$s>%1$d person</button> doesn't attend" msgid "<button type=\"button\" %2$s>%1$d person</button> doesn't attend"
msgid_plural "<button type=\"button\" %2$s>%1$d people</button> don't attend" msgid_plural "<button type=\"button\" %2$s>%1$d people</button> don't attend"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: src/Content/Conversation.php:278 #: src/Content/Conversation.php:279
#, php-format #, php-format
msgid "<button type=\"button\" %2$s>%1$d person</button> attends maybe" msgid "<button type=\"button\" %2$s>%1$d person</button> attends maybe"
msgid_plural "<button type=\"button\" %2$s>%1$d people</button> attend maybe" msgid_plural "<button type=\"button\" %2$s>%1$d people</button> attend maybe"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: src/Content/Conversation.php:281 #: src/Content/Conversation.php:282
#, php-format #, php-format
msgid "<button type=\"button\" %2$s>%1$d person</button> reshared this" msgid "<button type=\"button\" %2$s>%1$d person</button> reshared this"
msgid_plural "<button type=\"button\" %2$s>%1$d people</button> reshared this" msgid_plural "<button type=\"button\" %2$s>%1$d people</button> reshared this"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: src/Content/Conversation.php:328 #: src/Content/Conversation.php:329
msgid "Visible to <strong>everybody</strong>" msgid "Visible to <strong>everybody</strong>"
msgstr "" msgstr ""
#: src/Content/Conversation.php:329 src/Module/Item/Compose.php:200 #: src/Content/Conversation.php:330 src/Module/Item/Compose.php:200
#: src/Object/Post.php:1082 #: src/Object/Post.php:1096
msgid "Please enter a image/video/audio/webpage URL:" msgid "Please enter a image/video/audio/webpage URL:"
msgstr "" msgstr ""
#: src/Content/Conversation.php:330 #: src/Content/Conversation.php:331
msgid "Tag term:" msgid "Tag term:"
msgstr "" msgstr ""
#: src/Content/Conversation.php:331 src/Module/Filer/SaveTag.php:73 #: src/Content/Conversation.php:332 src/Module/Filer/SaveTag.php:73
msgid "Save to Folder:" msgid "Save to Folder:"
msgstr "" msgstr ""
#: src/Content/Conversation.php:332 #: src/Content/Conversation.php:333
msgid "Where are you right now?" msgid "Where are you right now?"
msgstr "" msgstr ""
#: src/Content/Conversation.php:333 #: src/Content/Conversation.php:334
msgid "Delete item(s)?" msgid "Delete item(s)?"
msgstr "" msgstr ""
#: src/Content/Conversation.php:345 src/Module/Item/Compose.php:175 #: src/Content/Conversation.php:346 src/Module/Item/Compose.php:175
msgid "Created at" msgid "Created at"
msgstr "" msgstr ""
#: src/Content/Conversation.php:355 #: src/Content/Conversation.php:356
msgid "New Post" msgid "New Post"
msgstr "" msgstr ""
#: src/Content/Conversation.php:358 #: src/Content/Conversation.php:359
msgid "Share" msgid "Share"
msgstr "" msgstr ""
#: src/Content/Conversation.php:361 src/Module/Post/Edit.php:132 #: src/Content/Conversation.php:362 src/Module/Post/Edit.php:132
msgid "upload photo" msgid "upload photo"
msgstr "" msgstr ""
#: src/Content/Conversation.php:362 src/Module/Post/Edit.php:133 #: src/Content/Conversation.php:363 src/Module/Post/Edit.php:133
msgid "Attach file" msgid "Attach file"
msgstr "" msgstr ""
#: src/Content/Conversation.php:363 src/Module/Post/Edit.php:134 #: src/Content/Conversation.php:364 src/Module/Post/Edit.php:134
msgid "attach file" msgid "attach file"
msgstr "" msgstr ""
#: src/Content/Conversation.php:364 src/Module/Item/Compose.php:190 #: src/Content/Conversation.php:365 src/Module/Item/Compose.php:190
#: src/Module/Post/Edit.php:171 src/Object/Post.php:1072 #: src/Module/Post/Edit.php:171 src/Object/Post.php:1086
msgid "Bold" msgid "Bold"
msgstr "" msgstr ""
#: src/Content/Conversation.php:365 src/Module/Item/Compose.php:191 #: src/Content/Conversation.php:366 src/Module/Item/Compose.php:191
#: src/Module/Post/Edit.php:172 src/Object/Post.php:1073 #: src/Module/Post/Edit.php:172 src/Object/Post.php:1087
msgid "Italic" msgid "Italic"
msgstr "" msgstr ""
#: src/Content/Conversation.php:366 src/Module/Item/Compose.php:192 #: src/Content/Conversation.php:367 src/Module/Item/Compose.php:192
#: src/Module/Post/Edit.php:173 src/Object/Post.php:1074 #: src/Module/Post/Edit.php:173 src/Object/Post.php:1088
msgid "Underline" msgid "Underline"
msgstr "" msgstr ""
#: src/Content/Conversation.php:367 src/Module/Item/Compose.php:193 #: src/Content/Conversation.php:368 src/Module/Item/Compose.php:193
#: src/Module/Post/Edit.php:174 src/Object/Post.php:1076 #: src/Module/Post/Edit.php:174 src/Object/Post.php:1090
msgid "Quote" msgid "Quote"
msgstr "" msgstr ""
#: src/Content/Conversation.php:368 src/Module/Item/Compose.php:194 #: src/Content/Conversation.php:369 src/Module/Item/Compose.php:194
#: src/Module/Post/Edit.php:175 src/Object/Post.php:1077 #: src/Module/Post/Edit.php:175 src/Object/Post.php:1091
msgid "Add emojis" msgid "Add emojis"
msgstr "" msgstr ""
#: src/Content/Conversation.php:369 src/Module/Item/Compose.php:195 #: src/Content/Conversation.php:370 src/Module/Item/Compose.php:195
#: src/Object/Post.php:1075 #: src/Object/Post.php:1089
msgid "Content Warning" msgid "Content Warning"
msgstr "" msgstr ""
#: src/Content/Conversation.php:370 src/Module/Item/Compose.php:196 #: src/Content/Conversation.php:371 src/Module/Item/Compose.php:196
#: src/Module/Post/Edit.php:176 src/Object/Post.php:1078 #: src/Module/Post/Edit.php:176 src/Object/Post.php:1092
msgid "Code" msgid "Code"
msgstr "" msgstr ""
#: src/Content/Conversation.php:371 src/Module/Item/Compose.php:197 #: src/Content/Conversation.php:372 src/Module/Item/Compose.php:197
#: src/Object/Post.php:1079 #: src/Object/Post.php:1093
msgid "Image" msgid "Image"
msgstr "" msgstr ""
#: src/Content/Conversation.php:372 src/Module/Item/Compose.php:198 #: src/Content/Conversation.php:373 src/Module/Item/Compose.php:198
#: src/Module/Post/Edit.php:177 src/Object/Post.php:1080 #: src/Module/Post/Edit.php:177 src/Object/Post.php:1094
msgid "Link" msgid "Link"
msgstr "" msgstr ""
#: src/Content/Conversation.php:373 src/Module/Item/Compose.php:199 #: src/Content/Conversation.php:374 src/Module/Item/Compose.php:199
#: src/Module/Post/Edit.php:178 src/Object/Post.php:1081 #: src/Module/Post/Edit.php:178 src/Object/Post.php:1095
msgid "Link or Media" msgid "Link or Media"
msgstr "" msgstr ""
#: src/Content/Conversation.php:374 #: src/Content/Conversation.php:375
msgid "Video" msgid "Video"
msgstr "" msgstr ""
#: src/Content/Conversation.php:375 src/Module/Item/Compose.php:202 #: src/Content/Conversation.php:376 src/Module/Item/Compose.php:202
#: src/Module/Post/Edit.php:141 #: src/Module/Post/Edit.php:141
msgid "Set your location" msgid "Set your location"
msgstr "" msgstr ""
#: src/Content/Conversation.php:376 src/Module/Post/Edit.php:142 #: src/Content/Conversation.php:377 src/Module/Post/Edit.php:142
msgid "set location" msgid "set location"
msgstr "" msgstr ""
#: src/Content/Conversation.php:377 src/Module/Post/Edit.php:143 #: src/Content/Conversation.php:378 src/Module/Post/Edit.php:143
msgid "Clear browser location" msgid "Clear browser location"
msgstr "" msgstr ""
#: src/Content/Conversation.php:378 src/Module/Post/Edit.php:144 #: src/Content/Conversation.php:379 src/Module/Post/Edit.php:144
msgid "clear location" msgid "clear location"
msgstr "" msgstr ""
#: src/Content/Conversation.php:380 src/Module/Item/Compose.php:207 #: src/Content/Conversation.php:381 src/Module/Item/Compose.php:207
#: src/Module/Post/Edit.php:157 #: src/Module/Post/Edit.php:157
msgid "Set title" msgid "Set title"
msgstr "" msgstr ""
#: src/Content/Conversation.php:382 src/Module/Item/Compose.php:208 #: src/Content/Conversation.php:383 src/Module/Item/Compose.php:208
#: src/Module/Post/Edit.php:159 #: src/Module/Post/Edit.php:159
msgid "Categories (comma-separated list)" msgid "Categories (comma-separated list)"
msgstr "" msgstr ""
#: src/Content/Conversation.php:387 src/Module/Item/Compose.php:224 #: src/Content/Conversation.php:388 src/Module/Item/Compose.php:224
msgid "Scheduled at" msgid "Scheduled at"
msgstr "" msgstr ""
#: src/Content/Conversation.php:392 src/Module/Post/Edit.php:146 #: src/Content/Conversation.php:393 src/Module/Post/Edit.php:146
msgid "Permission settings" msgid "Permission settings"
msgstr "" msgstr ""
#: src/Content/Conversation.php:402 src/Module/Post/Edit.php:155 #: src/Content/Conversation.php:403 src/Module/Post/Edit.php:155
msgid "Public post" msgid "Public post"
msgstr "" msgstr ""
#: src/Content/Conversation.php:416 src/Content/Widget/VCard.php:113 #: src/Content/Conversation.php:417 src/Content/Widget/VCard.php:120
#: src/Model/Profile.php:469 src/Module/Admin/Logs/View.php:92 #: src/Model/Profile.php:469 src/Module/Admin/Logs/View.php:92
#: src/Module/Post/Edit.php:181 #: src/Module/Post/Edit.php:181
msgid "Message" msgid "Message"
msgstr "" msgstr ""
#: src/Content/Conversation.php:417 src/Module/Post/Edit.php:182 #: src/Content/Conversation.php:418 src/Module/Post/Edit.php:182
#: src/Module/Settings/TwoFactor/Trusted.php:140 #: src/Module/Settings/TwoFactor/Trusted.php:140
msgid "Browser" msgid "Browser"
msgstr "" msgstr ""
#: src/Content/Conversation.php:419 src/Module/Post/Edit.php:185 #: src/Content/Conversation.php:420 src/Module/Post/Edit.php:185
msgid "Open Compose page" msgid "Open Compose page"
msgstr "" msgstr ""
#: src/Content/Conversation.php:679 src/Object/Post.php:244 #: src/Content/Conversation.php:686 src/Object/Post.php:248
msgid "Pinned item" msgid "Pinned item"
msgstr "" msgstr ""
#: src/Content/Conversation.php:695 src/Object/Post.php:502 #: src/Content/Conversation.php:702 src/Object/Post.php:513
#: src/Object/Post.php:503 #: src/Object/Post.php:514
#, php-format #, php-format
msgid "View %s's profile @ %s" msgid "View %s's profile @ %s"
msgstr "" msgstr ""
#: src/Content/Conversation.php:708 src/Object/Post.php:490 #: src/Content/Conversation.php:715 src/Object/Post.php:501
msgid "Categories:" msgid "Categories:"
msgstr "" msgstr ""
#: src/Content/Conversation.php:709 src/Object/Post.php:491 #: src/Content/Conversation.php:716 src/Object/Post.php:502
msgid "Filed under:" msgid "Filed under:"
msgstr "" msgstr ""
#: src/Content/Conversation.php:717 src/Object/Post.php:516 #: src/Content/Conversation.php:724 src/Object/Post.php:527
#, php-format #, php-format
msgid "%s from %s" msgid "%s from %s"
msgstr "" msgstr ""
#: src/Content/Conversation.php:733 #: src/Content/Conversation.php:740
msgid "View in context" msgid "View in context"
msgstr "" msgstr ""
#: src/Content/Conversation.php:798 #: src/Content/Conversation.php:805
msgid "remove" msgid "remove"
msgstr "" msgstr ""
#: src/Content/Conversation.php:802 #: src/Content/Conversation.php:809
msgid "Delete Selected Items" msgid "Delete Selected Items"
msgstr "" msgstr ""
#: src/Content/Conversation.php:867 src/Content/Conversation.php:870 #: src/Content/Conversation.php:875 src/Content/Conversation.php:878
#: src/Content/Conversation.php:873 src/Content/Conversation.php:876 #: src/Content/Conversation.php:881 src/Content/Conversation.php:884
#: src/Content/Conversation.php:879 #: src/Content/Conversation.php:887
#, php-format #, php-format
msgid "You had been addressed (%s)." msgid "You had been addressed (%s)."
msgstr "" msgstr ""
#: src/Content/Conversation.php:882 #: src/Content/Conversation.php:890
#, php-format #, php-format
msgid "You are following %s." msgid "You are following %s."
msgstr "" msgstr ""
#: src/Content/Conversation.php:885 #: src/Content/Conversation.php:893
msgid "You subscribed to one or more tags in this post." msgid "You subscribed to one or more tags in this post."
msgstr "" msgstr ""
#: src/Content/Conversation.php:898 #: src/Content/Conversation.php:912
#, php-format #, php-format
msgid "%s reshared this." msgid "%s reshared this."
msgstr "" msgstr ""
#: src/Content/Conversation.php:900 #: src/Content/Conversation.php:914
msgid "Reshared" msgid "Reshared"
msgstr "" msgstr ""
#: src/Content/Conversation.php:900 #: src/Content/Conversation.php:914
#, php-format #, php-format
msgid "Reshared by %s <%s>" msgid "Reshared by %s <%s>"
msgstr "" msgstr ""
#: src/Content/Conversation.php:903 #: src/Content/Conversation.php:917
#, php-format #, php-format
msgid "%s is participating in this thread." msgid "%s is participating in this thread."
msgstr "" msgstr ""
#: src/Content/Conversation.php:906 #: src/Content/Conversation.php:920
msgid "Stored for general reasons" msgid "Stored for general reasons"
msgstr "" msgstr ""
#: src/Content/Conversation.php:909 #: src/Content/Conversation.php:923
msgid "Global post" msgid "Global post"
msgstr "" msgstr ""
#: src/Content/Conversation.php:912 #: src/Content/Conversation.php:926
msgid "Sent via an relay server" msgid "Sent via an relay server"
msgstr "" msgstr ""
#: src/Content/Conversation.php:912 #: src/Content/Conversation.php:926
#, php-format #, php-format
msgid "Sent via the relay server %s <%s>" msgid "Sent via the relay server %s <%s>"
msgstr "" msgstr ""
#: src/Content/Conversation.php:915 #: src/Content/Conversation.php:929
msgid "Fetched" msgid "Fetched"
msgstr "" msgstr ""
#: src/Content/Conversation.php:915 #: src/Content/Conversation.php:929
#, php-format #, php-format
msgid "Fetched because of %s <%s>" msgid "Fetched because of %s <%s>"
msgstr "" msgstr ""
#: src/Content/Conversation.php:918 #: src/Content/Conversation.php:932
msgid "Stored because of a child post to complete this thread." msgid "Stored because of a child post to complete this thread."
msgstr "" msgstr ""
#: src/Content/Conversation.php:921 #: src/Content/Conversation.php:935
msgid "Local delivery" msgid "Local delivery"
msgstr "" msgstr ""
#: src/Content/Conversation.php:924 #: src/Content/Conversation.php:938
msgid "Stored because of your activity (like, comment, star, ...)" msgid "Stored because of your activity (like, comment, star, ...)"
msgstr "" msgstr ""
#: src/Content/Conversation.php:927 #: src/Content/Conversation.php:941
msgid "Distributed" msgid "Distributed"
msgstr "" msgstr ""
#: src/Content/Conversation.php:930 #: src/Content/Conversation.php:944
msgid "Pushed to us" msgid "Pushed to us"
msgstr "" msgstr ""
@ -1601,79 +1601,79 @@ msgid ""
"Contact birthday events are private to you." "Contact birthday events are private to you."
msgstr "" msgstr ""
#: src/Content/GroupManager.php:151 src/Content/Nav.php:276 #: src/Content/GroupManager.php:152 src/Content/Nav.php:276
#: src/Content/Text/HTML.php:877 src/Content/Widget.php:543 #: src/Content/Text/HTML.php:880 src/Content/Widget.php:541
msgid "Groups" msgid "Groups"
msgstr "" msgstr ""
#: src/Content/GroupManager.php:153 #: src/Content/GroupManager.php:154
msgid "External link to group" msgid "External link to group"
msgstr "" msgstr ""
#: src/Content/GroupManager.php:157 src/Content/Widget.php:518 #: src/Content/GroupManager.php:158 src/Content/Widget.php:516
msgid "show less" msgid "show less"
msgstr "" msgstr ""
#: src/Content/GroupManager.php:158 src/Content/Widget.php:414 #: src/Content/GroupManager.php:159 src/Content/Widget.php:414
#: src/Content/Widget.php:519 #: src/Content/Widget.php:517
msgid "show more" msgid "show more"
msgstr "" msgstr ""
#: src/Content/GroupManager.php:159 #: src/Content/GroupManager.php:160
msgid "Create new group" msgid "Create new group"
msgstr "" msgstr ""
#: src/Content/Item.php:327 src/Model/Item.php:2931 #: src/Content/Item.php:329 src/Model/Item.php:2986
msgid "event" msgid "event"
msgstr "" msgstr ""
#: src/Content/Item.php:330 src/Content/Item.php:340 #: src/Content/Item.php:332 src/Content/Item.php:342
msgid "status" msgid "status"
msgstr "" msgstr ""
#: src/Content/Item.php:336 src/Model/Item.php:2933 #: src/Content/Item.php:338 src/Model/Item.php:2988
#: src/Module/Post/Tag/Add.php:123 #: src/Module/Post/Tag/Add.php:123
msgid "photo" msgid "photo"
msgstr "" msgstr ""
#: src/Content/Item.php:350 src/Module/Post/Tag/Add.php:141 #: src/Content/Item.php:352 src/Module/Post/Tag/Add.php:141
#, php-format #, php-format
msgid "%1$s tagged %2$s's %3$s with %4$s" msgid "%1$s tagged %2$s's %3$s with %4$s"
msgstr "" msgstr ""
#: src/Content/Item.php:418 view/theme/frio/theme.php:262 #: src/Content/Item.php:421 view/theme/frio/theme.php:262
msgid "Follow Thread" msgid "Follow Thread"
msgstr "" msgstr ""
#: src/Content/Item.php:419 src/Model/Contact.php:1199 #: src/Content/Item.php:422 src/Model/Contact.php:1206
msgid "View Status" msgid "View Status"
msgstr "" msgstr ""
#: src/Content/Item.php:420 src/Content/Item.php:440 src/Model/Contact.php:1148 #: src/Content/Item.php:423 src/Content/Item.php:443 src/Model/Contact.php:1155
#: src/Model/Contact.php:1191 src/Model/Contact.php:1200 #: src/Model/Contact.php:1198 src/Model/Contact.php:1207
#: src/Module/Directory.php:157 src/Module/Settings/Profile/Index.php:233 #: src/Module/Directory.php:157 src/Module/Settings/Profile/Index.php:233
msgid "View Profile" msgid "View Profile"
msgstr "" msgstr ""
#: src/Content/Item.php:421 src/Model/Contact.php:1201 #: src/Content/Item.php:424 src/Model/Contact.php:1208
msgid "View Photos" msgid "View Photos"
msgstr "" msgstr ""
#: src/Content/Item.php:422 src/Model/Contact.php:1192 #: src/Content/Item.php:425 src/Model/Contact.php:1199
#: src/Model/Contact.php:1202 #: src/Model/Contact.php:1209
msgid "Network Posts" msgid "Network Posts"
msgstr "" msgstr ""
#: src/Content/Item.php:423 src/Model/Contact.php:1193 #: src/Content/Item.php:426 src/Model/Contact.php:1200
#: src/Model/Contact.php:1203 #: src/Model/Contact.php:1210
msgid "View Contact" msgid "View Contact"
msgstr "" msgstr ""
#: src/Content/Item.php:424 src/Model/Contact.php:1204 #: src/Content/Item.php:427 src/Model/Contact.php:1211
msgid "Send PM" msgid "Send PM"
msgstr "" msgstr ""
#: src/Content/Item.php:425 src/Module/Contact.php:448 #: src/Content/Item.php:428 src/Module/Contact.php:468
#: src/Module/Contact/Profile.php:477 #: src/Module/Contact/Profile.php:477
#: src/Module/Moderation/Blocklist/Contact.php:116 #: src/Module/Moderation/Blocklist/Contact.php:116
#: src/Module/Moderation/Users/Active.php:137 #: src/Module/Moderation/Users/Active.php:137
@ -1681,7 +1681,7 @@ msgstr ""
msgid "Block" msgid "Block"
msgstr "" msgstr ""
#: src/Content/Item.php:426 src/Module/Contact.php:449 #: src/Content/Item.php:429 src/Module/Contact.php:469
#: src/Module/Contact/Profile.php:485 #: src/Module/Contact/Profile.php:485
#: src/Module/Notifications/Introductions.php:134 #: src/Module/Notifications/Introductions.php:134
#: src/Module/Notifications/Introductions.php:206 #: src/Module/Notifications/Introductions.php:206
@ -1689,22 +1689,22 @@ msgstr ""
msgid "Ignore" msgid "Ignore"
msgstr "" msgstr ""
#: src/Content/Item.php:427 src/Module/Contact.php:450 #: src/Content/Item.php:430 src/Module/Contact.php:470
#: src/Module/Contact/Profile.php:493 #: src/Module/Contact/Profile.php:493
msgid "Collapse" msgid "Collapse"
msgstr "" msgstr ""
#: src/Content/Item.php:431 src/Object/Post.php:471 #: src/Content/Item.php:434 src/Object/Post.php:482
msgid "Languages" msgid "Languages"
msgstr "" msgstr ""
#: src/Content/Item.php:437 src/Content/Widget.php:80 #: src/Content/Item.php:440 src/Content/Widget.php:80
#: src/Model/Contact.php:1194 src/Model/Contact.php:1205 #: src/Model/Contact.php:1201 src/Model/Contact.php:1212
#: src/Module/Contact/Follow.php:167 view/theme/vier/theme.php:195 #: src/Module/Contact/Follow.php:167 view/theme/vier/theme.php:195
msgid "Connect/Follow" msgid "Connect/Follow"
msgstr "" msgstr ""
#: src/Content/Item.php:862 #: src/Content/Item.php:865
msgid "Unable to fetch user." msgid "Unable to fetch user."
msgstr "" msgstr ""
@ -1720,7 +1720,7 @@ msgstr ""
msgid "Clear notifications" msgid "Clear notifications"
msgstr "" msgstr ""
#: src/Content/Nav.php:126 src/Content/Text/HTML.php:864 #: src/Content/Nav.php:126 src/Content/Text/HTML.php:867
msgid "@name, !group, #tags, content" msgid "@name, !group, #tags, content"
msgstr "" msgstr ""
@ -1742,7 +1742,7 @@ msgid "Sign in"
msgstr "" msgstr ""
#: src/Content/Nav.php:227 src/Module/BaseProfile.php:57 #: src/Content/Nav.php:227 src/Module/BaseProfile.php:57
#: src/Module/Contact.php:492 #: src/Module/Contact.php:512
msgid "Conversations" msgid "Conversations"
msgstr "" msgstr ""
@ -1751,7 +1751,7 @@ msgid "Conversations you started"
msgstr "" msgstr ""
#: src/Content/Nav.php:228 src/Module/BaseProfile.php:49 #: src/Content/Nav.php:228 src/Module/BaseProfile.php:49
#: src/Module/BaseSettings.php:100 src/Module/Contact.php:484 #: src/Module/BaseSettings.php:100 src/Module/Contact.php:504
#: src/Module/Contact/Profile.php:392 src/Module/Profile/Profile.php:268 #: src/Module/Contact/Profile.php:392 src/Module/Profile/Profile.php:268
#: src/Module/Welcome.php:57 view/theme/frio/theme.php:230 #: src/Module/Welcome.php:57 view/theme/frio/theme.php:230
msgid "Profile" msgid "Profile"
@ -1771,7 +1771,7 @@ msgid "Your photos"
msgstr "" msgstr ""
#: src/Content/Nav.php:230 src/Module/BaseProfile.php:73 #: src/Content/Nav.php:230 src/Module/BaseProfile.php:73
#: src/Module/BaseProfile.php:76 src/Module/Contact.php:508 #: src/Module/BaseProfile.php:76 src/Module/Contact.php:528
#: view/theme/frio/theme.php:235 #: view/theme/frio/theme.php:235
msgid "Media" msgid "Media"
msgstr "" msgstr ""
@ -1837,7 +1837,7 @@ msgstr ""
msgid "Addon applications, utilities, games" msgid "Addon applications, utilities, games"
msgstr "" msgstr ""
#: src/Content/Nav.php:267 src/Content/Text/HTML.php:862 #: src/Content/Nav.php:267 src/Content/Text/HTML.php:865
#: src/Module/Admin/Logs/View.php:86 src/Module/Search/Index.php:112 #: src/Module/Admin/Logs/View.php:86 src/Module/Search/Index.php:112
msgid "Search" msgid "Search"
msgstr "" msgstr ""
@ -1846,19 +1846,19 @@ msgstr ""
msgid "Search site content" msgid "Search site content"
msgstr "" msgstr ""
#: src/Content/Nav.php:270 src/Content/Text/HTML.php:871 #: src/Content/Nav.php:270 src/Content/Text/HTML.php:874
msgid "Full Text" msgid "Full Text"
msgstr "" msgstr ""
#: src/Content/Nav.php:271 src/Content/Text/HTML.php:872 #: src/Content/Nav.php:271 src/Content/Text/HTML.php:875
#: src/Content/Widget/TagCloud.php:68 #: src/Content/Widget/TagCloud.php:68
msgid "Tags" msgid "Tags"
msgstr "" msgstr ""
#: src/Content/Nav.php:272 src/Content/Nav.php:327 #: src/Content/Nav.php:272 src/Content/Nav.php:327
#: src/Content/Text/HTML.php:873 src/Module/BaseProfile.php:127 #: src/Content/Text/HTML.php:876 src/Module/BaseProfile.php:127
#: src/Module/BaseProfile.php:130 src/Module/Contact.php:419 #: src/Module/BaseProfile.php:130 src/Module/Contact.php:427
#: src/Module/Contact.php:515 view/theme/frio/theme.php:243 #: src/Module/Contact.php:536 view/theme/frio/theme.php:243
msgid "Contacts" msgid "Contacts"
msgstr "" msgstr ""
@ -2037,12 +2037,12 @@ msgid ""
"<a href=\"%1$s\" target=\"_blank\" rel=\"noopener noreferrer\">%2$s</a> %3$s" "<a href=\"%1$s\" target=\"_blank\" rel=\"noopener noreferrer\">%2$s</a> %3$s"
msgstr "" msgstr ""
#: src/Content/Text/BBCode.php:938 src/Model/Item.php:3649 #: src/Content/Text/BBCode.php:938 src/Model/Item.php:3718
#: src/Model/Item.php:3655 src/Model/Item.php:3656 #: src/Model/Item.php:3724 src/Model/Item.php:3725
msgid "Link to source" msgid "Link to source"
msgstr "" msgstr ""
#: src/Content/Text/BBCode.php:1508 src/Content/Text/HTML.php:901 #: src/Content/Text/BBCode.php:1508 src/Content/Text/HTML.php:904
msgid "Click to open/close" msgid "Click to open/close"
msgstr "" msgstr ""
@ -2062,15 +2062,15 @@ msgstr ""
msgid "Invalid link protocol" msgid "Invalid link protocol"
msgstr "" msgstr ""
#: src/Content/Text/HTML.php:779 #: src/Content/Text/HTML.php:782
msgid "Loading more entries..." msgid "Loading more entries..."
msgstr "" msgstr ""
#: src/Content/Text/HTML.php:780 #: src/Content/Text/HTML.php:783
msgid "The end" msgid "The end"
msgstr "" msgstr ""
#: src/Content/Text/HTML.php:856 src/Content/Widget/VCard.php:109 #: src/Content/Text/HTML.php:859 src/Content/Widget/VCard.php:116
#: src/Model/Profile.php:463 src/Module/Contact/Profile.php:437 #: src/Model/Profile.php:463 src/Module/Contact/Profile.php:437
msgid "Follow" msgid "Follow"
msgstr "" msgstr ""
@ -2110,7 +2110,7 @@ msgstr ""
msgid "Examples: Robert Morgenstein, Fishing" msgid "Examples: Robert Morgenstein, Fishing"
msgstr "" msgstr ""
#: src/Content/Widget.php:82 src/Module/Contact.php:441 #: src/Content/Widget.php:82 src/Module/Contact.php:461
#: src/Module/Directory.php:96 view/theme/vier/theme.php:197 #: src/Module/Directory.php:96 view/theme/vier/theme.php:197
msgid "Find" msgid "Find"
msgstr "" msgstr ""
@ -2142,7 +2142,7 @@ msgid "Local Directory"
msgstr "" msgstr ""
#: src/Content/Widget.php:219 src/Model/Circle.php:596 #: src/Content/Widget.php:219 src/Model/Circle.php:596
#: src/Module/Contact.php:402 src/Module/Welcome.php:76 #: src/Module/Contact.php:401 src/Module/Welcome.php:76
msgid "Circles" msgid "Circles"
msgstr "" msgstr ""
@ -2150,7 +2150,7 @@ msgstr ""
msgid "Everyone" msgid "Everyone"
msgstr "" msgstr ""
#: src/Content/Widget.php:246 src/Module/Contact.php:418 #: src/Content/Widget.php:246 src/Module/Contact.php:424
msgid "No relationship" msgid "No relationship"
msgstr "" msgstr ""
@ -2159,7 +2159,7 @@ msgid "Relationships"
msgstr "" msgstr ""
#: src/Content/Widget.php:253 src/Module/Circle.php:293 #: src/Content/Widget.php:253 src/Module/Circle.php:293
#: src/Module/Contact.php:346 #: src/Module/Contact.php:345
msgid "All Contacts" msgid "All Contacts"
msgstr "" msgstr ""
@ -2190,23 +2190,23 @@ msgid_plural "%d contacts in common"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: src/Content/Widget.php:512 #: src/Content/Widget.php:510
msgid "Archives" msgid "Archives"
msgstr "" msgstr ""
#: src/Content/Widget.php:520 #: src/Content/Widget.php:518
msgid "On this date" msgid "On this date"
msgstr "" msgstr ""
#: src/Content/Widget.php:540 #: src/Content/Widget.php:538
msgid "Persons" msgid "Persons"
msgstr "" msgstr ""
#: src/Content/Widget.php:541 #: src/Content/Widget.php:539
msgid "Organisations" msgid "Organisations"
msgstr "" msgstr ""
#: src/Content/Widget.php:542 src/Model/Contact.php:1651 #: src/Content/Widget.php:540 src/Model/Contact.php:1676
msgid "News" msgid "News"
msgstr "" msgstr ""
@ -2214,7 +2214,7 @@ msgstr ""
msgid "Account Types" msgid "Account Types"
msgstr "" msgstr ""
#: src/Content/Widget.php:547 src/Module/Moderation/BaseUsers.php:69 #: src/Content/Widget.php:548 src/Module/Moderation/BaseUsers.php:69
msgid "All" msgid "All"
msgstr "" msgstr ""
@ -2264,31 +2264,31 @@ msgstr[1] ""
msgid "More Trending Tags" msgid "More Trending Tags"
msgstr "" msgstr ""
#: src/Content/Widget/VCard.php:102 src/Model/Profile.php:378 #: src/Content/Widget/VCard.php:109 src/Model/Profile.php:378
#: src/Module/Contact/Profile.php:381 src/Module/Profile/Profile.php:199 #: src/Module/Contact/Profile.php:381 src/Module/Profile/Profile.php:199
msgid "XMPP:" msgid "XMPP:"
msgstr "" msgstr ""
#: src/Content/Widget/VCard.php:103 src/Model/Profile.php:379 #: src/Content/Widget/VCard.php:110 src/Model/Profile.php:379
#: src/Module/Contact/Profile.php:383 src/Module/Profile/Profile.php:203 #: src/Module/Contact/Profile.php:383 src/Module/Profile/Profile.php:203
msgid "Matrix:" msgid "Matrix:"
msgstr "" msgstr ""
#: src/Content/Widget/VCard.php:104 src/Model/Event.php:82 #: src/Content/Widget/VCard.php:111 src/Model/Event.php:82
#: src/Model/Event.php:109 src/Model/Event.php:473 src/Model/Event.php:958 #: src/Model/Event.php:109 src/Model/Event.php:473 src/Model/Event.php:965
#: src/Model/Profile.php:373 src/Module/Contact/Profile.php:379 #: src/Model/Profile.php:373 src/Module/Contact/Profile.php:379
#: src/Module/Directory.php:147 src/Module/Notifications/Introductions.php:187 #: src/Module/Directory.php:147 src/Module/Notifications/Introductions.php:187
#: src/Module/Profile/Profile.php:221 #: src/Module/Profile/Profile.php:221
msgid "Location:" msgid "Location:"
msgstr "" msgstr ""
#: src/Content/Widget/VCard.php:107 src/Model/Profile.php:476 #: src/Content/Widget/VCard.php:114 src/Model/Profile.php:476
#: src/Module/Notifications/Introductions.php:201 #: src/Module/Notifications/Introductions.php:201
msgid "Network:" msgid "Network:"
msgstr "" msgstr ""
#: src/Content/Widget/VCard.php:111 src/Model/Contact.php:1195 #: src/Content/Widget/VCard.php:118 src/Model/Contact.php:1202
#: src/Model/Contact.php:1206 src/Model/Profile.php:465 #: src/Model/Contact.php:1213 src/Model/Profile.php:465
#: src/Module/Contact/Profile.php:429 #: src/Module/Contact/Profile.php:429
msgid "Unfollow" msgid "Unfollow"
msgstr "" msgstr ""
@ -2553,8 +2553,8 @@ msgstr ""
#: src/Core/Installer.php:511 #: src/Core/Installer.php:511
msgid "" msgid ""
"The web installer needs to be able to create a file called \"local.config." "The web installer needs to be able to create a file called \"local.config.php"
"php\" in the \"config\" folder of your web server and it is unable to do so." "\" in the \"config\" folder of your web server and it is unable to do so."
msgstr "" msgstr ""
#: src/Core/Installer.php:512 #: src/Core/Installer.php:512
@ -3035,82 +3035,82 @@ msgstr ""
msgid "Edit circles" msgid "Edit circles"
msgstr "" msgstr ""
#: src/Model/Contact.php:1212 src/Module/Moderation/Users/Pending.php:102 #: src/Model/Contact.php:1219 src/Module/Moderation/Users/Pending.php:102
#: src/Module/Notifications/Introductions.php:132 #: src/Module/Notifications/Introductions.php:132
#: src/Module/Notifications/Introductions.php:204 #: src/Module/Notifications/Introductions.php:204
msgid "Approve" msgid "Approve"
msgstr "" msgstr ""
#: src/Model/Contact.php:1647 #: src/Model/Contact.php:1672
msgid "Organisation" msgid "Organisation"
msgstr "" msgstr ""
#: src/Model/Contact.php:1655 #: src/Model/Contact.php:1680
msgid "Group" msgid "Group"
msgstr "" msgstr ""
#: src/Model/Contact.php:2951 #: src/Model/Contact.php:2989
msgid "Disallowed profile URL." msgid "Disallowed profile URL."
msgstr "" msgstr ""
#: src/Model/Contact.php:2956 src/Module/Friendica.php:83 #: src/Model/Contact.php:2994 src/Module/Friendica.php:83
msgid "Blocked domain" msgid "Blocked domain"
msgstr "" msgstr ""
#: src/Model/Contact.php:2961 #: src/Model/Contact.php:2999
msgid "Connect URL missing." msgid "Connect URL missing."
msgstr "" msgstr ""
#: src/Model/Contact.php:2970 #: src/Model/Contact.php:3008
msgid "" msgid ""
"The contact could not be added. Please check the relevant network " "The contact could not be added. Please check the relevant network "
"credentials in your Settings -> Social Networks page." "credentials in your Settings -> Social Networks page."
msgstr "" msgstr ""
#: src/Model/Contact.php:2988 #: src/Model/Contact.php:3026
#, php-format #, php-format
msgid "Expected network %s does not match actual network %s" msgid "Expected network %s does not match actual network %s"
msgstr "" msgstr ""
#: src/Model/Contact.php:3005 #: src/Model/Contact.php:3043
msgid "The profile address specified does not provide adequate information." msgid "The profile address specified does not provide adequate information."
msgstr "" msgstr ""
#: src/Model/Contact.php:3007 #: src/Model/Contact.php:3045
msgid "No compatible communication protocols or feeds were discovered." msgid "No compatible communication protocols or feeds were discovered."
msgstr "" msgstr ""
#: src/Model/Contact.php:3010 #: src/Model/Contact.php:3048
msgid "An author or name was not found." msgid "An author or name was not found."
msgstr "" msgstr ""
#: src/Model/Contact.php:3013 #: src/Model/Contact.php:3051
msgid "No browser URL could be matched to this address." msgid "No browser URL could be matched to this address."
msgstr "" msgstr ""
#: src/Model/Contact.php:3016 #: src/Model/Contact.php:3054
msgid "" msgid ""
"Unable to match @-style Identity Address with a known protocol or email " "Unable to match @-style Identity Address with a known protocol or email "
"contact." "contact."
msgstr "" msgstr ""
#: src/Model/Contact.php:3017 #: src/Model/Contact.php:3055
msgid "Use mailto: in front of address to force email check." msgid "Use mailto: in front of address to force email check."
msgstr "" msgstr ""
#: src/Model/Contact.php:3023 #: src/Model/Contact.php:3061
msgid "" msgid ""
"The profile address specified belongs to a network which has been disabled " "The profile address specified belongs to a network which has been disabled "
"on this site." "on this site."
msgstr "" msgstr ""
#: src/Model/Contact.php:3028 #: src/Model/Contact.php:3066
msgid "" msgid ""
"Limited profile. This person will be unable to receive direct/personal " "Limited profile. This person will be unable to receive direct/personal "
"notifications from you." "notifications from you."
msgstr "" msgstr ""
#: src/Model/Contact.php:3093 #: src/Model/Contact.php:3131
msgid "Unable to retrieve contact information." msgid "Unable to retrieve contact information."
msgstr "" msgstr ""
@ -3119,12 +3119,12 @@ msgid "l F d, Y \\@ g:i A \\G\\M\\TP (e)"
msgstr "" msgstr ""
#: src/Model/Event.php:75 src/Model/Event.php:92 src/Model/Event.php:471 #: src/Model/Event.php:75 src/Model/Event.php:92 src/Model/Event.php:471
#: src/Model/Event.php:940 #: src/Model/Event.php:947
msgid "Starts:" msgid "Starts:"
msgstr "" msgstr ""
#: src/Model/Event.php:78 src/Model/Event.php:98 src/Model/Event.php:472 #: src/Model/Event.php:78 src/Model/Event.php:98 src/Model/Event.php:472
#: src/Model/Event.php:944 #: src/Model/Event.php:951
msgid "Finishes:" msgid "Finishes:"
msgstr "" msgstr ""
@ -3165,131 +3165,131 @@ msgstr ""
msgid "Access to this profile has been restricted." msgid "Access to this profile has been restricted."
msgstr "" msgstr ""
#: src/Model/Event.php:559 src/Module/Calendar/Event/Show.php:67 #: src/Model/Event.php:560 src/Module/Calendar/Event/Show.php:67
msgid "Event not found." msgid "Event not found."
msgstr "" msgstr ""
#: src/Model/Event.php:637 #: src/Model/Event.php:639
msgid "l, F j" msgid "l, F j"
msgstr "" msgstr ""
#: src/Model/Event.php:664 #: src/Model/Event.php:666
msgid "Edit event" msgid "Edit event"
msgstr "" msgstr ""
#: src/Model/Event.php:665 #: src/Model/Event.php:667
msgid "Duplicate event" msgid "Duplicate event"
msgstr "" msgstr ""
#: src/Model/Event.php:666 #: src/Model/Event.php:668
msgid "Delete event" msgid "Delete event"
msgstr "" msgstr ""
#: src/Model/Event.php:896 src/Module/Debug/Localtime.php:38 #: src/Model/Event.php:898 src/Module/Debug/Localtime.php:38
msgid "l F d, Y \\@ g:i A" msgid "l F d, Y \\@ g:i A"
msgstr "" msgstr ""
#: src/Model/Event.php:897 #: src/Model/Event.php:899
msgid "D g:i A" msgid "D g:i A"
msgstr "" msgstr ""
#: src/Model/Event.php:898 #: src/Model/Event.php:900
msgid "g:i A" msgid "g:i A"
msgstr "" msgstr ""
#: src/Model/Event.php:959 src/Model/Event.php:961 #: src/Model/Event.php:966 src/Model/Event.php:968
msgid "Show map" msgid "Show map"
msgstr "" msgstr ""
#: src/Model/Event.php:960 #: src/Model/Event.php:967
msgid "Hide map" msgid "Hide map"
msgstr "" msgstr ""
#: src/Model/Event.php:1053 #: src/Model/Event.php:1060
#, php-format #, php-format
msgid "%s's birthday" msgid "%s's birthday"
msgstr "" msgstr ""
#: src/Model/Event.php:1054 #: src/Model/Event.php:1061
#, php-format #, php-format
msgid "Happy Birthday %s" msgid "Happy Birthday %s"
msgstr "" msgstr ""
#: src/Model/Item.php:2023 #: src/Model/Item.php:2049
#, php-format #, php-format
msgid "Detected languages in this post:\\n%s" msgid "Detected languages in this post:\\n%s"
msgstr "" msgstr ""
#: src/Model/Item.php:2935 #: src/Model/Item.php:2990
msgid "activity" msgid "activity"
msgstr "" msgstr ""
#: src/Model/Item.php:2937 #: src/Model/Item.php:2992
msgid "comment" msgid "comment"
msgstr "" msgstr ""
#: src/Model/Item.php:2940 src/Module/Post/Tag/Add.php:123 #: src/Model/Item.php:2995 src/Module/Post/Tag/Add.php:123
msgid "post" msgid "post"
msgstr "" msgstr ""
#: src/Model/Item.php:3109 #: src/Model/Item.php:3165
#, php-format #, php-format
msgid "%s is blocked" msgid "%s is blocked"
msgstr "" msgstr ""
#: src/Model/Item.php:3111 #: src/Model/Item.php:3167
#, php-format #, php-format
msgid "%s is ignored" msgid "%s is ignored"
msgstr "" msgstr ""
#: src/Model/Item.php:3113 #: src/Model/Item.php:3169
#, php-format #, php-format
msgid "Content from %s is collapsed" msgid "Content from %s is collapsed"
msgstr "" msgstr ""
#: src/Model/Item.php:3117 #: src/Model/Item.php:3173
#, php-format #, php-format
msgid "Content warning: %s" msgid "Content warning: %s"
msgstr "" msgstr ""
#: src/Model/Item.php:3561 #: src/Model/Item.php:3625
msgid "bytes" msgid "bytes"
msgstr "" msgstr ""
#: src/Model/Item.php:3592 #: src/Model/Item.php:3656
#, php-format #, php-format
msgid "%2$s (%3$d%%, %1$d vote)" msgid "%2$s (%3$d%%, %1$d vote)"
msgid_plural "%2$s (%3$d%%, %1$d votes)" msgid_plural "%2$s (%3$d%%, %1$d votes)"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: src/Model/Item.php:3594 #: src/Model/Item.php:3658
#, php-format #, php-format
msgid "%2$s (%1$d vote)" msgid "%2$s (%1$d vote)"
msgid_plural "%2$s (%1$d votes)" msgid_plural "%2$s (%1$d votes)"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: src/Model/Item.php:3599 #: src/Model/Item.php:3663
#, php-format #, php-format
msgid "%d voter. Poll end: %s" msgid "%d voter. Poll end: %s"
msgid_plural "%d voters. Poll end: %s" msgid_plural "%d voters. Poll end: %s"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: src/Model/Item.php:3601 #: src/Model/Item.php:3665
#, php-format #, php-format
msgid "%d voter." msgid "%d voter."
msgid_plural "%d voters." msgid_plural "%d voters."
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: src/Model/Item.php:3603 #: src/Model/Item.php:3667
#, php-format #, php-format
msgid "Poll end: %s" msgid "Poll end: %s"
msgstr "" msgstr ""
#: src/Model/Item.php:3637 src/Model/Item.php:3638 #: src/Model/Item.php:3701 src/Model/Item.php:3702
msgid "View on separate page" msgid "View on separate page"
msgstr "" msgstr ""
@ -4226,7 +4226,7 @@ msgid "Policies"
msgstr "" msgstr ""
#: src/Module/Admin/Site.php:406 src/Module/Calendar/Event/Form.php:252 #: src/Module/Admin/Site.php:406 src/Module/Calendar/Event/Form.php:252
#: src/Module/Contact.php:525 src/Module/Profile/Profile.php:276 #: src/Module/Contact.php:547 src/Module/Profile/Profile.php:276
msgid "Advanced" msgid "Advanced"
msgstr "" msgstr ""
@ -5170,9 +5170,9 @@ msgstr ""
#: src/Module/Admin/Summary.php:98 #: src/Module/Admin/Summary.php:98
msgid "" msgid ""
"The last update failed. Please run \"php bin/console.php dbstructure " "The last update failed. Please run \"php bin/console.php dbstructure update"
"update\" from the command line and have a look at the errors that might " "\" from the command line and have a look at the errors that might appear. "
"appear. (Some of the errors are possibly inside the logfile.)" "(Some of the errors are possibly inside the logfile.)"
msgstr "" msgstr ""
#: src/Module/Admin/Summary.php:102 #: src/Module/Admin/Summary.php:102
@ -5333,8 +5333,8 @@ msgstr ""
#, php-format #, php-format
msgid "" msgid ""
"Show some informations regarding the needed information to operate the node " "Show some informations regarding the needed information to operate the node "
"according e.g. to <a href=\"%s\" target=\"_blank\" rel=\"noopener " "according e.g. to <a href=\"%s\" target=\"_blank\" rel=\"noopener noreferrer"
"noreferrer\">EU-GDPR</a>." "\">EU-GDPR</a>."
msgstr "" msgstr ""
#: src/Module/Admin/Tos.php:81 #: src/Module/Admin/Tos.php:81
@ -5537,7 +5537,7 @@ msgstr ""
msgid "Item Source" msgid "Item Source"
msgstr "" msgstr ""
#: src/Module/BaseProfile.php:52 src/Module/Contact.php:487 #: src/Module/BaseProfile.php:52 src/Module/Contact.php:507
msgid "Profile Details" msgid "Profile Details"
msgstr "" msgstr ""
@ -5850,142 +5850,142 @@ msgid_plural "%d contacts edited."
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: src/Module/Contact.php:349 #: src/Module/Contact.php:348
msgid "Show all contacts" msgid "Show all contacts"
msgstr "" msgstr ""
#: src/Module/Contact.php:354 src/Module/Contact.php:423 #: src/Module/Contact.php:353 src/Module/Contact.php:432
#: src/Module/Moderation/BaseUsers.php:85 #: src/Module/Moderation/BaseUsers.php:85
msgid "Pending" msgid "Pending"
msgstr "" msgstr ""
#: src/Module/Contact.php:357 #: src/Module/Contact.php:356
msgid "Only show pending contacts" msgid "Only show pending contacts"
msgstr "" msgstr ""
#: src/Module/Contact.php:362 src/Module/Contact.php:424 #: src/Module/Contact.php:361 src/Module/Contact.php:435
#: src/Module/Moderation/BaseUsers.php:93 #: src/Module/Moderation/BaseUsers.php:93
msgid "Blocked" msgid "Blocked"
msgstr "" msgstr ""
#: src/Module/Contact.php:365 #: src/Module/Contact.php:364
msgid "Only show blocked contacts" msgid "Only show blocked contacts"
msgstr "" msgstr ""
#: src/Module/Contact.php:370 src/Module/Contact.php:426 #: src/Module/Contact.php:369 src/Module/Contact.php:441
#: src/Object/Post.php:351 #: src/Object/Post.php:360
msgid "Ignored" msgid "Ignored"
msgstr "" msgstr ""
#: src/Module/Contact.php:373 #: src/Module/Contact.php:372
msgid "Only show ignored contacts" msgid "Only show ignored contacts"
msgstr "" msgstr ""
#: src/Module/Contact.php:378 src/Module/Contact.php:427 #: src/Module/Contact.php:377 src/Module/Contact.php:444
msgid "Collapsed" msgid "Collapsed"
msgstr "" msgstr ""
#: src/Module/Contact.php:381 #: src/Module/Contact.php:380
msgid "Only show collapsed contacts" msgid "Only show collapsed contacts"
msgstr "" msgstr ""
#: src/Module/Contact.php:386 src/Module/Contact.php:428 #: src/Module/Contact.php:385 src/Module/Contact.php:447
msgid "Archived" msgid "Archived"
msgstr "" msgstr ""
#: src/Module/Contact.php:389 #: src/Module/Contact.php:388
msgid "Only show archived contacts" msgid "Only show archived contacts"
msgstr "" msgstr ""
#: src/Module/Contact.php:394 src/Module/Contact.php:425 #: src/Module/Contact.php:393 src/Module/Contact.php:438
msgid "Hidden" msgid "Hidden"
msgstr "" msgstr ""
#: src/Module/Contact.php:397 #: src/Module/Contact.php:396
msgid "Only show hidden contacts" msgid "Only show hidden contacts"
msgstr "" msgstr ""
#: src/Module/Contact.php:405 #: src/Module/Contact.php:404
msgid "Organize your contact circles" msgid "Organize your contact circles"
msgstr "" msgstr ""
#: src/Module/Contact.php:439 #: src/Module/Contact.php:459
msgid "Search your contacts" msgid "Search your contacts"
msgstr "" msgstr ""
#: src/Module/Contact.php:440 src/Module/Search/Index.php:207 #: src/Module/Contact.php:460 src/Module/Search/Index.php:207
#, php-format #, php-format
msgid "Results for: %s" msgid "Results for: %s"
msgstr "" msgstr ""
#: src/Module/Contact.php:447 #: src/Module/Contact.php:467
msgid "Update" msgid "Update"
msgstr "" msgstr ""
#: src/Module/Contact.php:448 src/Module/Contact/Profile.php:477 #: src/Module/Contact.php:468 src/Module/Contact/Profile.php:477
#: src/Module/Moderation/Blocklist/Contact.php:117 #: src/Module/Moderation/Blocklist/Contact.php:117
#: src/Module/Moderation/Users/Blocked.php:138 #: src/Module/Moderation/Users/Blocked.php:138
#: src/Module/Moderation/Users/Index.php:154 #: src/Module/Moderation/Users/Index.php:154
msgid "Unblock" msgid "Unblock"
msgstr "" msgstr ""
#: src/Module/Contact.php:449 src/Module/Contact/Profile.php:485 #: src/Module/Contact.php:469 src/Module/Contact/Profile.php:485
msgid "Unignore" msgid "Unignore"
msgstr "" msgstr ""
#: src/Module/Contact.php:450 src/Module/Contact/Profile.php:493 #: src/Module/Contact.php:470 src/Module/Contact/Profile.php:493
msgid "Uncollapse" msgid "Uncollapse"
msgstr "" msgstr ""
#: src/Module/Contact.php:452 #: src/Module/Contact.php:472
msgid "Batch Actions" msgid "Batch Actions"
msgstr "" msgstr ""
#: src/Module/Contact.php:495 #: src/Module/Contact.php:515
msgid "Conversations started by this contact" msgid "Conversations started by this contact"
msgstr "" msgstr ""
#: src/Module/Contact.php:500 #: src/Module/Contact.php:520
msgid "Posts and Comments" msgid "Posts and Comments"
msgstr "" msgstr ""
#: src/Module/Contact.php:503 #: src/Module/Contact.php:523
msgid "Individual Posts and Replies" msgid "Individual Posts and Replies"
msgstr "" msgstr ""
#: src/Module/Contact.php:511 #: src/Module/Contact.php:531
msgid "Posts containing media objects" msgid "Posts containing media objects"
msgstr "" msgstr ""
#: src/Module/Contact.php:518 #: src/Module/Contact.php:539
msgid "View all known contacts" msgid "View all known contacts"
msgstr "" msgstr ""
#: src/Module/Contact.php:528 #: src/Module/Contact.php:550
msgid "Advanced Contact Settings" msgid "Advanced Contact Settings"
msgstr "" msgstr ""
#: src/Module/Contact.php:564 #: src/Module/Contact.php:586
msgid "Mutual Friendship" msgid "Mutual Friendship"
msgstr "" msgstr ""
#: src/Module/Contact.php:568 #: src/Module/Contact.php:590
msgid "is a fan of yours" msgid "is a fan of yours"
msgstr "" msgstr ""
#: src/Module/Contact.php:572 #: src/Module/Contact.php:594
msgid "you are a fan of" msgid "you are a fan of"
msgstr "" msgstr ""
#: src/Module/Contact.php:590 #: src/Module/Contact.php:612
msgid "Pending outgoing contact request" msgid "Pending outgoing contact request"
msgstr "" msgstr ""
#: src/Module/Contact.php:592 #: src/Module/Contact.php:614
msgid "Pending incoming contact request" msgid "Pending incoming contact request"
msgstr "" msgstr ""
#: src/Module/Contact.php:605 src/Module/Contact/Profile.php:346 #: src/Module/Contact.php:627 src/Module/Contact/Profile.php:346
#, php-format #, php-format
msgid "Visit %s's profile [%s]" msgid "Visit %s's profile [%s]"
msgstr "" msgstr ""
@ -6556,7 +6556,7 @@ msgstr ""
msgid "Posts that mention or involve you" msgid "Posts that mention or involve you"
msgstr "" msgstr ""
#: src/Module/Conversation/Network.php:289 src/Object/Post.php:363 #: src/Module/Conversation/Network.php:289 src/Object/Post.php:372
msgid "Starred" msgid "Starred"
msgstr "" msgstr ""
@ -8421,8 +8421,8 @@ msgstr ""
#: src/Module/Profile/Profile.php:158 #: src/Module/Profile/Profile.php:158
#, php-format #, php-format
msgid "" msgid ""
"You're currently viewing your profile as <b>%s</b> <a href=\"%s\" " "You're currently viewing your profile as <b>%s</b> <a href=\"%s\" class="
"class=\"btn btn-sm pull-right\">Cancel</a>" "\"btn btn-sm pull-right\">Cancel</a>"
msgstr "" msgstr ""
#: src/Module/Profile/Profile.php:167 src/Module/Settings/Account.php:575 #: src/Module/Profile/Profile.php:167 src/Module/Settings/Account.php:575
@ -8970,8 +8970,8 @@ msgstr ""
#: src/Module/Security/TwoFactor/Verify.php:100 #: src/Module/Security/TwoFactor/Verify.php:100
#, php-format #, php-format
msgid "" msgid ""
"If you do not have access to your authentication code you can use a <a " "If you do not have access to your authentication code you can use a <a href="
"href=\"%s\">two-factor recovery code</a>." "\"%s\">two-factor recovery code</a>."
msgstr "" msgstr ""
#: src/Module/Security/TwoFactor/Verify.php:101 #: src/Module/Security/TwoFactor/Verify.php:101
@ -10440,8 +10440,8 @@ msgstr ""
#: src/Module/Settings/TwoFactor/Verify.php:149 #: src/Module/Settings/TwoFactor/Verify.php:149
#, php-format #, php-format
msgid "" msgid ""
"<p>Or you can open the following URL in your mobile device:</p><p><a " "<p>Or you can open the following URL in your mobile device:</p><p><a href="
"href=\"%s\">%s</a></p>" "\"%s\">%s</a></p>"
msgstr "" msgstr ""
#: src/Module/Settings/TwoFactor/Verify.php:156 #: src/Module/Settings/TwoFactor/Verify.php:156
@ -10531,9 +10531,9 @@ msgstr ""
msgid "" msgid ""
"At any point in time a logged in user can export their account data from the " "At any point in time a logged in user can export their account data from the "
"<a href=\"%1$s/settings/userexport\">account settings</a>. If the user wants " "<a href=\"%1$s/settings/userexport\">account settings</a>. If the user wants "
"to delete their account they can do so at <a href=\"%1$s/settings/" "to delete their account they can do so at <a href=\"%1$s/settings/removeme\">"
"removeme\">%1$s/settings/removeme</a>. The deletion of the account will be " "%1$s/settings/removeme</a>. The deletion of the account will be permanent. "
"permanent. Deletion of the data will also be requested from the nodes of the " "Deletion of the data will also be requested from the nodes of the "
"communication partners." "communication partners."
msgstr "" msgstr ""
@ -11259,254 +11259,254 @@ msgstr ""
msgid "%s posted an update." msgid "%s posted an update."
msgstr "" msgstr ""
#: src/Object/Post.php:135 #: src/Object/Post.php:139
msgid "Private Message" msgid "Private Message"
msgstr "" msgstr ""
#: src/Object/Post.php:139 #: src/Object/Post.php:143
msgid "Public Message" msgid "Public Message"
msgstr "" msgstr ""
#: src/Object/Post.php:143 #: src/Object/Post.php:147
msgid "Unlisted Message" msgid "Unlisted Message"
msgstr "" msgstr ""
#: src/Object/Post.php:178 #: src/Object/Post.php:182
msgid "This entry was edited" msgid "This entry was edited"
msgstr "" msgstr ""
#: src/Object/Post.php:206 #: src/Object/Post.php:210
msgid "Connector Message" msgid "Connector Message"
msgstr "" msgstr ""
#: src/Object/Post.php:222 src/Object/Post.php:224 #: src/Object/Post.php:226 src/Object/Post.php:228
msgid "Edit" msgid "Edit"
msgstr "" msgstr ""
#: src/Object/Post.php:248 #: src/Object/Post.php:252
msgid "Delete globally" msgid "Delete globally"
msgstr "" msgstr ""
#: src/Object/Post.php:248 #: src/Object/Post.php:252
msgid "Remove locally" msgid "Remove locally"
msgstr "" msgstr ""
#: src/Object/Post.php:266 #: src/Object/Post.php:270
#, php-format #, php-format
msgid "Block %s" msgid "Block %s"
msgstr "" msgstr ""
#: src/Object/Post.php:271 #: src/Object/Post.php:275
#, php-format #, php-format
msgid "Ignore %s" msgid "Ignore %s"
msgstr "" msgstr ""
#: src/Object/Post.php:276 #: src/Object/Post.php:280
#, php-format #, php-format
msgid "Collapse %s" msgid "Collapse %s"
msgstr "" msgstr ""
#: src/Object/Post.php:281 #: src/Object/Post.php:285
msgid "Save to folder" msgid "Save to folder"
msgstr "" msgstr ""
#: src/Object/Post.php:316 #: src/Object/Post.php:325
msgid "I will attend" msgid "I will attend"
msgstr "" msgstr ""
#: src/Object/Post.php:316 #: src/Object/Post.php:325
msgid "I will not attend" msgid "I will not attend"
msgstr "" msgstr ""
#: src/Object/Post.php:316 #: src/Object/Post.php:325
msgid "I might attend" msgid "I might attend"
msgstr "" msgstr ""
#: src/Object/Post.php:346 #: src/Object/Post.php:355
msgid "Ignore thread" msgid "Ignore thread"
msgstr "" msgstr ""
#: src/Object/Post.php:347 #: src/Object/Post.php:356
msgid "Unignore thread" msgid "Unignore thread"
msgstr "" msgstr ""
#: src/Object/Post.php:348 #: src/Object/Post.php:357
msgid "Toggle ignore status" msgid "Toggle ignore status"
msgstr "" msgstr ""
#: src/Object/Post.php:358 #: src/Object/Post.php:367
msgid "Add star" msgid "Add star"
msgstr "" msgstr ""
#: src/Object/Post.php:359 #: src/Object/Post.php:368
msgid "Remove star" msgid "Remove star"
msgstr "" msgstr ""
#: src/Object/Post.php:360 #: src/Object/Post.php:369
msgid "Toggle star status" msgid "Toggle star status"
msgstr "" msgstr ""
#: src/Object/Post.php:371 #: src/Object/Post.php:380
msgid "Pin" msgid "Pin"
msgstr "" msgstr ""
#: src/Object/Post.php:372 #: src/Object/Post.php:381
msgid "Unpin" msgid "Unpin"
msgstr "" msgstr ""
#: src/Object/Post.php:373 #: src/Object/Post.php:382
msgid "Toggle pin status" msgid "Toggle pin status"
msgstr "" msgstr ""
#: src/Object/Post.php:376 #: src/Object/Post.php:385
msgid "Pinned" msgid "Pinned"
msgstr "" msgstr ""
#: src/Object/Post.php:381 #: src/Object/Post.php:390
msgid "Add tag" msgid "Add tag"
msgstr "" msgstr ""
#: src/Object/Post.php:394 #: src/Object/Post.php:403
msgid "Quote share this" msgid "Quote share this"
msgstr "" msgstr ""
#: src/Object/Post.php:394 #: src/Object/Post.php:403
msgid "Quote Share" msgid "Quote Share"
msgstr "" msgstr ""
#: src/Object/Post.php:397 #: src/Object/Post.php:406
msgid "Reshare this" msgid "Reshare this"
msgstr "" msgstr ""
#: src/Object/Post.php:397 #: src/Object/Post.php:406
msgid "Reshare" msgid "Reshare"
msgstr "" msgstr ""
#: src/Object/Post.php:398 #: src/Object/Post.php:407
msgid "Cancel your Reshare" msgid "Cancel your Reshare"
msgstr "" msgstr ""
#: src/Object/Post.php:398 #: src/Object/Post.php:407
msgid "Unshare" msgid "Unshare"
msgstr "" msgstr ""
#: src/Object/Post.php:449 #: src/Object/Post.php:458
#, php-format #, php-format
msgid "%s (Received %s)" msgid "%s (Received %s)"
msgstr "" msgstr ""
#: src/Object/Post.php:454 #: src/Object/Post.php:464
msgid "Comment this item on your system" msgid "Comment this item on your system"
msgstr "" msgstr ""
#: src/Object/Post.php:454 #: src/Object/Post.php:464
msgid "Remote comment" msgid "Remote comment"
msgstr "" msgstr ""
#: src/Object/Post.php:475 #: src/Object/Post.php:486
msgid "Share via ..." msgid "Share via ..."
msgstr "" msgstr ""
#: src/Object/Post.php:475 #: src/Object/Post.php:486
msgid "Share via external services" msgid "Share via external services"
msgstr "" msgstr ""
#: src/Object/Post.php:504 #: src/Object/Post.php:515
msgid "to" msgid "to"
msgstr "" msgstr ""
#: src/Object/Post.php:505 #: src/Object/Post.php:516
msgid "via" msgid "via"
msgstr "" msgstr ""
#: src/Object/Post.php:506 #: src/Object/Post.php:517
msgid "Wall-to-Wall" msgid "Wall-to-Wall"
msgstr "" msgstr ""
#: src/Object/Post.php:507 #: src/Object/Post.php:518
msgid "via Wall-To-Wall:" msgid "via Wall-To-Wall:"
msgstr "" msgstr ""
#: src/Object/Post.php:552 #: src/Object/Post.php:563
#, php-format #, php-format
msgid "Reply to %s" msgid "Reply to %s"
msgstr "" msgstr ""
#: src/Object/Post.php:555 #: src/Object/Post.php:566
msgid "More" msgid "More"
msgstr "" msgstr ""
#: src/Object/Post.php:573 #: src/Object/Post.php:584
msgid "Notifier task is pending" msgid "Notifier task is pending"
msgstr "" msgstr ""
#: src/Object/Post.php:574 #: src/Object/Post.php:585
msgid "Delivery to remote servers is pending" msgid "Delivery to remote servers is pending"
msgstr "" msgstr ""
#: src/Object/Post.php:575 #: src/Object/Post.php:586
msgid "Delivery to remote servers is underway" msgid "Delivery to remote servers is underway"
msgstr "" msgstr ""
#: src/Object/Post.php:576 #: src/Object/Post.php:587
msgid "Delivery to remote servers is mostly done" msgid "Delivery to remote servers is mostly done"
msgstr "" msgstr ""
#: src/Object/Post.php:577 #: src/Object/Post.php:588
msgid "Delivery to remote servers is done" msgid "Delivery to remote servers is done"
msgstr "" msgstr ""
#: src/Object/Post.php:597 #: src/Object/Post.php:608
#, php-format #, php-format
msgid "%d comment" msgid "%d comment"
msgid_plural "%d comments" msgid_plural "%d comments"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: src/Object/Post.php:598 #: src/Object/Post.php:609
msgid "Show more" msgid "Show more"
msgstr "" msgstr ""
#: src/Object/Post.php:599 #: src/Object/Post.php:610
msgid "Show fewer" msgid "Show fewer"
msgstr "" msgstr ""
#: src/Object/Post.php:635 #: src/Object/Post.php:646
#, php-format #, php-format
msgid "Reshared by: %s" msgid "Reshared by: %s"
msgstr "" msgstr ""
#: src/Object/Post.php:640 #: src/Object/Post.php:651
#, php-format #, php-format
msgid "Viewed by: %s" msgid "Viewed by: %s"
msgstr "" msgstr ""
#: src/Object/Post.php:645 #: src/Object/Post.php:656
#, php-format #, php-format
msgid "Liked by: %s" msgid "Liked by: %s"
msgstr "" msgstr ""
#: src/Object/Post.php:650 #: src/Object/Post.php:661
#, php-format #, php-format
msgid "Disliked by: %s" msgid "Disliked by: %s"
msgstr "" msgstr ""
#: src/Object/Post.php:655 #: src/Object/Post.php:666
#, php-format #, php-format
msgid "Attended by: %s" msgid "Attended by: %s"
msgstr "" msgstr ""
#: src/Object/Post.php:660 #: src/Object/Post.php:671
#, php-format #, php-format
msgid "Maybe attended by: %s" msgid "Maybe attended by: %s"
msgstr "" msgstr ""
#: src/Object/Post.php:665 #: src/Object/Post.php:676
#, php-format #, php-format
msgid "Not attended by: %s" msgid "Not attended by: %s"
msgstr "" msgstr ""
#: src/Object/Post.php:670 #: src/Object/Post.php:681
#, php-format #, php-format
msgid "Reacted with %s by: %s" msgid "Reacted with %s by: %s"
msgstr "" msgstr ""