diff --git a/database.sql b/database.sql index 2528b0145..87866c291 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ -- 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`, IF (`contact`.`url` = `author`.`url` AND `contact`.`name` != '', `contact`.`name`, `author`.`name`) AS `author-name`, `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`, `author`.`network` AS `author-network`, `author`.`blocked` AS `author-blocked`, @@ -1980,6 +1981,7 @@ CREATE VIEW `post-user-view` AS SELECT `owner`.`addr` AS `owner-addr`, IF (`contact`.`url` = `owner`.`url` AND `contact`.`name` != '', `contact`.`name`, `owner`.`name`) AS `owner-name`, `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`, `owner`.`network` AS `owner-network`, `owner`.`blocked` AS `owner-blocked`, @@ -2145,6 +2147,7 @@ CREATE VIEW `post-thread-user-view` AS SELECT `author`.`addr` AS `author-addr`, IF (`contact`.`url` = `author`.`url` AND `contact`.`name` != '', `contact`.`name`, `author`.`name`) AS `author-name`, `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`, `author`.`network` AS `author-network`, `author`.`blocked` AS `author-blocked`, @@ -2157,6 +2160,7 @@ CREATE VIEW `post-thread-user-view` AS SELECT `owner`.`addr` AS `owner-addr`, IF (`contact`.`url` = `owner`.`url` AND `contact`.`name` != '', `contact`.`name`, `owner`.`name`) AS `owner-name`, `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`, `owner`.`network` AS `owner-network`, `owner`.`blocked` AS `owner-blocked`, @@ -2308,6 +2312,7 @@ CREATE VIEW `post-view` AS SELECT `author`.`addr` AS `author-addr`, `author`.`name` AS `author-name`, `author`.`nick` AS `author-nick`, + `author`.`alias` AS `author-alias`, `author`.`thumb` AS `author-avatar`, `author`.`network` AS `author-network`, `author`.`blocked` AS `author-blocked`, @@ -2320,6 +2325,7 @@ CREATE VIEW `post-view` AS SELECT `owner`.`addr` AS `owner-addr`, `owner`.`name` AS `owner-name`, `owner`.`nick` AS `owner-nick`, + `owner`.`alias` AS `owner-alias`, `owner`.`thumb` AS `owner-avatar`, `owner`.`network` AS `owner-network`, `owner`.`blocked` AS `owner-blocked`, @@ -2448,6 +2454,7 @@ CREATE VIEW `post-thread-view` AS SELECT `author`.`addr` AS `author-addr`, `author`.`name` AS `author-name`, `author`.`nick` AS `author-nick`, + `author`.`alias` AS `author-alias`, `author`.`thumb` AS `author-avatar`, `author`.`network` AS `author-network`, `author`.`blocked` AS `author-blocked`, @@ -2460,6 +2467,7 @@ CREATE VIEW `post-thread-view` AS SELECT `owner`.`addr` AS `owner-addr`, `owner`.`name` AS `owner-name`, `owner`.`nick` AS `owner-nick`, + `owner`.`alias` AS `owner-alias`, `owner`.`thumb` AS `owner-avatar`, `owner`.`network` AS `owner-network`, `owner`.`blocked` AS `owner-blocked`, diff --git a/mod/photos.php b/mod/photos.php index 233d28a5e..1b9120576 100644 --- a/mod/photos.php +++ b/mod/photos.php @@ -117,7 +117,7 @@ function photos_init(App $a) $tpl = Renderer::getMarkupTemplate("photos_head.tpl"); - DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl,[ + DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl, [ '$ispublic' => DI::l10n()->t('everybody') ]); } @@ -214,13 +214,15 @@ function photos_post(App $a) // get the list of photos we are about to delete 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, $page_owner_uid, $album )); } 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(), $album )); @@ -258,7 +260,6 @@ function photos_post(App $a) // same as above but remove single photo if ($visitor) { $condition = ['contact-id' => $visitor, 'uid' => $page_owner_uid, 'resource-id' => DI::args()->getArgv()[3]]; - } else { $condition = ['uid' => DI::userSession()->getLocalUserId(), 'resource-id' => DI::args()->getArgv()[3]]; } @@ -405,7 +406,7 @@ function photos_post(App $a) if (strpos($tag, '@') === 0) { $profile = ''; $contact = null; - $name = substr($tag,1); + $name = substr($tag, 1); if ((strpos($name, '@')) || (strpos($name, 'http://'))) { $newname = $name; @@ -441,13 +442,15 @@ function photos_post(App $a) if ($tagcid) { $contact = DBA::selectFirst('contact', [], ['id' => $tagcid, 'uid' => $page_owner_uid]); } else { - $newname = str_replace('_',' ',$name); + $newname = str_replace('_', ' ', $name); //select someone from this user's contacts by name $contact = DBA::selectFirst('contact', [], ['name' => $newname, 'uid' => $page_owner_uid]); if (!DBA::isResult($contact)) { //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], ['order' => ['attag' => true]] ); @@ -689,11 +692,13 @@ function photos_content(App $a) $uploader = ''; - $ret = ['post_url' => 'profile/' . $user['nickname'] . '/photos', - 'addon_text' => $uploader, - 'default_upload' => true]; + $ret = [ + 'post_url' => 'profile/' . $user['nickname'] . '/photos', + '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_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')); // Per Friendica definition a value of '0' means unlimited: - If ($mis_bytes == 0) { + if ($mis_bytes == 0) { $mis_bytes = INF; } @@ -719,7 +724,7 @@ function photos_content(App $a) $aclselect_e = ($visitor ? '' : ACL::getFullSelectorHTML(DI::page(), $a->getLoggedInUserId())); - $o .= Renderer::replaceMacros($tpl,[ + $o .= Renderer::replaceMacros($tpl, [ '$pagename' => DI::l10n()->t('Upload Photos'), '$sessid' => session_id(), '$usage' => $usage_message, @@ -747,7 +752,7 @@ function photos_content(App $a) if ($datatype === 'album') { // if $datum is not a valid hex, redirect to the default page if (is_null($datum) || !Strings::isHex($datum)) { - DI::baseUrl()->redirect('photos/' . $user['nickname']. '/album'); + DI::baseUrl()->redirect('photos/' . $user['nickname'] . '/album'); } $album = hex2bin($datum); @@ -756,7 +761,8 @@ function photos_content(App $a) } $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`", $owner_uid, $album @@ -775,7 +781,8 @@ function photos_content(App $a) $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(`created`) as `created` FROM `photo` WHERE `uid` = ? AND `album` = ? @@ -809,7 +816,7 @@ function photos_content(App $a) $album_e = $album; - $o .= Renderer::replaceMacros($edit_tpl,[ + $o .= Renderer::replaceMacros($edit_tpl, [ '$nametext' => DI::l10n()->t('New album name: '), '$nickname' => $user['nickname'], '$album' => $album_e, @@ -843,16 +850,16 @@ function photos_content(App $a) $desc_e = $rr['desc']; $photos[] = [ - 'id' => $rr['id'], - 'twist' => ' ' . ($twist ? 'rotleft' : 'rotright') . rand(2,4), - 'link' => 'photos/' . $user['nickname'] . '/image/' . $rr['resource-id'] + 'id' => $rr['id'], + 'twist' => ' ' . ($twist ? 'rotleft' : 'rotright') . rand(2, 4), + 'link' => 'photos/' . $user['nickname'] . '/image/' . $rr['resource-id'] . ($order_field === 'created' ? '?order=created' : ''), 'title' => DI::l10n()->t('View Photo'), - 'src' => 'photo/' . $rr['resource-id'] . '-' . $rr['scale'] . '.' .$ext, - 'alt' => $imgalt_e, - 'desc'=> $desc_e, - 'ext' => $ext, - 'hash'=> $rr['resource-id'], + 'src' => 'photo/' . $rr['resource-id'] . '-' . $rr['scale'] . '.' . $ext, + 'alt' => $imgalt_e, + 'desc' => $desc_e, + 'ext' => $ext, + 'hash' => $rr['resource-id'], ]; } } @@ -870,7 +877,6 @@ function photos_content(App $a) ]); return $o; - } // Display one photo @@ -955,7 +961,7 @@ function photos_content(App $a) } $tpl = Renderer::getMarkupTemplate('photo_edit_head.tpl'); - DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl,[ + DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl, [ '$prevlink' => $prevlink, '$nextlink' => $nextlink ]); @@ -967,7 +973,7 @@ function photos_content(App $a) if ($nextlink) { $nextlink = [$nextlink, '']; } - } + } } if (count($ph) == 1) { @@ -1007,12 +1013,12 @@ function photos_content(App $a) } $photo = [ - 'href' => 'photo/' . $hires['resource-id'] . '-' . $hires['scale'] . '.' . $phototypes[$hires['type']], - 'title'=> DI::l10n()->t('View Full Size'), - 'src' => 'photo/' . $lores['resource-id'] . '-' . $lores['scale'] . '.' . $phototypes[$lores['type']] . '?_u=' . DateTimeFormat::utcNow('ymdhis'), - 'height' => $hires['height'], - 'width' => $hires['width'], - 'album' => $hires['album'], + 'href' => 'photo/' . $hires['resource-id'] . '-' . $hires['scale'] . '.' . $phototypes[$hires['type']], + 'title' => DI::l10n()->t('View Full Size'), + 'src' => 'photo/' . $lores['resource-id'] . '-' . $lores['scale'] . '.' . $phototypes[$lores['type']] . '?_u=' . DateTimeFormat::utcNow('ymdhis'), + 'height' => $hires['height'], + 'width' => $hires['width'], + 'album' => $hires['album'], 'filename' => $hires['filename'], ]; @@ -1079,12 +1085,12 @@ function photos_content(App $a) $edit = Renderer::replaceMacros($edit_tpl, [ '$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, ''], '$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_cw' => ['rotate', DI::l10n()->t("Rotate CW \x28right\x29"),1,''], - '$rotate_ccw' => ['rotate', DI::l10n()->t("Rotate CCW \x28left\x29"),2,''], + '$rotate_none' => ['rotate', DI::l10n()->t('Do not rotate'), 0, '', true], + '$rotate_cw' => ['rotate', DI::l10n()->t("Rotate CW \x28right\x29"), 1, ''], + '$rotate_ccw' => ['rotate', DI::l10n()->t("Rotate CCW \x28left\x29"), 2, ''], '$nickname' => $user['nickname'], '$resource_id' => $ph[0]['resource-id'], @@ -1179,7 +1185,7 @@ function photos_content(App $a) $qcomment = $words ? explode("\n", $words) : []; } - $comments .= Renderer::replaceMacros($cmnt_tpl,[ + $comments .= Renderer::replaceMacros($cmnt_tpl, [ '$return_path' => '', '$jsreload' => $return_path, '$id' => $link_item['id'], @@ -1203,13 +1209,19 @@ function photos_content(App $a) $activity = DI::activity(); if (($activity->match($item['verb'], Activity::LIKE) || - $activity->match($item['verb'], Activity::DISLIKE)) && - ($item['gravity'] != Item::GRAVITY_PARENT)) { + $activity->match($item['verb'], Activity::DISLIKE)) && + ($item['gravity'] != Item::GRAVITY_PARENT) + ) { continue; } - $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_url = Contact::magicLinkByContact($author); if (strpos($profile_url, 'contact/redir/') === 0) { $sparkle = ' sparkle'; @@ -1228,7 +1240,7 @@ function photos_content(App $a) $title_e = $item['title']; $body_e = BBCode::convertForUriId($item['uri-id'], $item['body']); - $comments .= Renderer::replaceMacros($template,[ + $comments .= Renderer::replaceMacros($template, [ '$id' => $item['id'], '$profile_url' => $profile_url, '$name' => $item['author-name'], diff --git a/src/Content/Conversation.php b/src/Content/Conversation.php index be858e05d..93dad904a 100644 --- a/src/Content/Conversation.php +++ b/src/Content/Conversation.php @@ -154,7 +154,8 @@ class Conversation 'uid' => 0, 'id' => $activity['author-id'], 'network' => $activity['author-network'], - 'url' => $activity['author-link'] + 'url' => $activity['author-link'], + 'alias' => $activity['author-alias'], ]; $url = Contact::magicLinkByContact($author); if (strpos($url, 'contact/redir/') === 0) { @@ -272,7 +273,7 @@ class Conversation $phrase = $this->l10n->tt(' attends', ' attend', $total, $spanatts); break; case 'attendno': - $phrase = $this->l10n->tt(' doesn\'t attend',' don\'t attend', $total, $spanatts); + $phrase = $this->l10n->tt(' doesn\'t attend', ' don\'t attend', $total, $spanatts); break; case 'attendmaybe': $phrase = $this->l10n->tt(' attends maybe', ' attend maybe', $total, $spanatts); @@ -538,7 +539,7 @@ class Conversation if (!$update) { $live_update_div = '
' . "\r\n" . "\r\n"; + . "?f='; \r\n"; } } elseif ($mode === self::MODE_SEARCH) { $live_update_div = '' . "\r\n"; @@ -626,7 +627,13 @@ class Conversation $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); $sparkle = ''; @@ -857,7 +864,8 @@ class Conversation $row['causer-avatar'] = $contact['thumb']; $row['causer-name'] = $contact['name']; } elseif (($row['gravity'] == ItemModel::GRAVITY_ACTIVITY) && ($row['verb'] == Activity::ANNOUNCE) && - ($row['author-id'] == $activity['causer-id'])) { + ($row['author-id'] == $activity['causer-id']) + ) { return $row; } } @@ -893,9 +901,15 @@ class Conversation } 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.', '' . htmlentities($row['causer-name']) . ''); + $row['reshared'] = $this->l10n->t('%s reshared this.', '' . htmlentities($row['causer-name']) . ''); } $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; @@ -995,15 +1009,21 @@ class Conversation $condition = DBA::mergeConditions($condition, ["(`gravity` != ? OR `origin`)", ItemModel::GRAVITY_ACTIVITY]); } - $condition = DBA::mergeConditions($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` 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, - ["`visible` AND NOT `deleted` AND NOT `author-blocked` AND NOT `owner-blocked` + $condition = DBA::mergeConditions( + $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` = ?)", - Contact::SHARING, Contact::FRIEND, 0]); + Contact::SHARING, Contact::FRIEND, 0 + ] + ); $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-owner'] = !$always_display && in_array($row['owner-id'], $collapses); - if (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))) { + if ( + 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]); } } @@ -1146,7 +1168,7 @@ class Conversation $condition = DBA::mergeConditions(['parent-uri-id' => $uriids, 'gravity' => ItemModel::GRAVITY_ACTIVITY, 'verb' => $verbs], ["NOT `deleted`"]); $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 = []; @@ -1287,7 +1309,7 @@ class Conversation // Searches the post item in the children $j = 0; while ($child['children'][$j]['verb'] !== Activity::POST && $j < count($child['children'])) { - $j ++; + $j++; } $moved_item = $child['children'][$j]; @@ -1361,8 +1383,10 @@ class Conversation * items and add them as children of their top-level post. */ foreach ($parents as $i => $parent) { - $parents[$i]['children'] = array_merge($this->getItemChildren($item_array, $parent, true), - $this->getItemChildren($item_array, $parent, false)); + $parents[$i]['children'] = array_merge( + $this->getItemChildren($item_array, $parent, true), + $this->getItemChildren($item_array, $parent, false) + ); } foreach ($parents as $i => $parent) { diff --git a/src/Content/GroupManager.php b/src/Content/GroupManager.php index d7848b41b..fc43080d9 100644 --- a/src/Content/GroupManager.php +++ b/src/Content/GroupManager.php @@ -78,7 +78,7 @@ class GroupManager $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); if (!$contacts) { return $groupList; @@ -87,6 +87,7 @@ class GroupManager while ($contact = DBA::fetch($contacts)) { $groupList[] = [ 'url' => $contact['url'], + 'alias' => $contact['alias'], 'name' => $contact['name'], 'id' => $contact['id'], 'micro' => $contact['micro'], diff --git a/src/Content/Item.php b/src/Content/Item.php index 3981978f4..49193627f 100644 --- a/src/Content/Item.php +++ b/src/Content/Item.php @@ -305,18 +305,20 @@ class Item } $author_arr = [ - 'uid' => 0, - 'id' => $item['author-id'], + 'uid' => 0, + 'id' => $item['author-id'], '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_arr = [ - 'uid' => 0, - 'id' => $obj['author-id'], + 'uid' => 0, + 'id' => $obj['author-id'], '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]'; @@ -372,10 +374,11 @@ class Item } $author = [ - 'uid' => 0, - 'id' => $item['author-id'], + 'uid' => 0, + 'id' => $item['author-id'], 'network' => $item['author-network'], - 'url' => $item['author-link'], + 'url' => $item['author-link'], + 'alias' => $item['author-alias'], ]; $profile_link = Contact::magicLinkByContact($author, $item['author-link']); if (strpos($profile_link, 'contact/redir/') === 0) { diff --git a/src/Content/Text/HTML.php b/src/Content/Text/HTML.php index 01151a6a5..d30e7ead0 100644 --- a/src/Content/Text/HTML.php +++ b/src/Content/Text/HTML.php @@ -422,7 +422,8 @@ class HTML { $URLSearchString = "^\[\]"; - $matches = ["/\[url\=([$URLSearchString]*)\].*?\[\/url\]/ism", + $matches = [ + "/\[url\=([$URLSearchString]*)\].*?\[\/url\]/ism", "/\[url\]([$URLSearchString]*)\[\/url\]/ism", "/\[img\=[0-9]*x[0-9]*\](.*?)\[\/img\]/ism", "/\[img\](.*?)\[\/img\]/ism", @@ -531,8 +532,10 @@ class HTML $ignore = false; // A list of some links that should be ignored - $list = ["/user/", "/tag/", "/group/", "/circle/", "/profile/", "/search?search=", "/search?tag=", "mailto:", "/u/", "/node/", - "//plus.google.com/", "//twitter.com/"]; + $list = [ + "/user/", "/tag/", "/group/", "/circle/", "/profile/", "/search?search=", "/search?tag=", "mailto:", "/u/", "/node/", + "//plus.google.com/", "//twitter.com/" + ]; foreach ($list as $listitem) { if (strpos($treffer[1], $listitem) !== false) { $ignore = true; @@ -941,7 +944,8 @@ class HTML $domain = '(?:(?!-)[A-Za-z0-9-]{1,63}(?set('URI.SafeIframeRegexp', + $config->set( + 'URI.SafeIframeRegexp', '%^https://(?: ' . implode('|', $allowedIframeDomains) . ' ) @@ -1050,7 +1054,8 @@ class HTML if (isset($mediaType->parameters['charset'])) { return strtolower($mediaType->parameters['charset']); } - } catch(\InvalidArgumentException $e) {} + } catch (\InvalidArgumentException $e) { + } return null; } diff --git a/src/Content/Widget.php b/src/Content/Widget.php index c18a3041b..c8c0230c6 100644 --- a/src/Content/Widget.php +++ b/src/Content/Widget.php @@ -476,7 +476,6 @@ class Widget $nextday = substr($nextday, 4); $dnow = substr($dnow, 0, 8) . '01'; $dthen = substr($dthen, 0, 8) . '01'; - /* * 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]; $dnow = DateTimeFormat::utc($dnow . ' -1 month', 'Y-m-d'); - } } @@ -508,7 +506,7 @@ class Widget $cutoff_year = intval(DateTimeFormat::localNow('Y')) - $visible_years; $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'), '$size' => $visible_years, '$cutoff_year' => $cutoff_year, @@ -543,7 +541,14 @@ class Widget ['ref' => 'community', 'name' => DI::l10n()->t('Groups')], ]; - return self::filter('accounttype', DI::l10n()->t('Account Types'), '', - DI::l10n()->t('All'), $base, $accounts, $accounttype); + return self::filter( + 'accounttype', + DI::l10n()->t('Account Types'), + '', + DI::l10n()->t('All'), + $base, + $accounts, + $accounttype + ); } } diff --git a/src/Content/Widget/ContactBlock.php b/src/Content/Widget/ContactBlock.php index e206dbcb4..fbc492eaa 100644 --- a/src/Content/Widget/ContactBlock.php +++ b/src/Content/Widget/ContactBlock.php @@ -104,7 +104,7 @@ class ContactBlock $contact_uriids = array_column($personal_contacts, 'uri-id'); 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)) { $contacts_title = DI::l10n()->tt('%d Contact', '%d Contacts', $total); diff --git a/src/Content/Widget/VCard.php b/src/Content/Widget/VCard.php index e462ab0aa..62a8e9085 100644 --- a/src/Content/Widget/VCard.php +++ b/src/Content/Widget/VCard.php @@ -29,6 +29,7 @@ use Friendica\Core\Renderer; use Friendica\Core\System; use Friendica\DI; use Friendica\Model\Contact; +use Friendica\Util\Network; use Friendica\Util\Strings; /** @@ -50,9 +51,15 @@ class VCard 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'] != '') { - $network_link = Strings::formatNetworkName($contact['network'], $contact['url']); - $network_avatar = ContactSelector::networkToIcon($contact['network'], $contact['url']); + $network_link = Strings::formatNetworkName($contact['network'], $contact_url); + $network_avatar = ContactSelector::networkToIcon($contact['network'], $contact_url); } else { $network_link = ''; $network_avatar = ''; @@ -83,9 +90,9 @@ class VCard if (empty($contact['self']) && Protocol::supportsFollow($contact['network'])) { 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) { - $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'), [ '$contact' => $contact, '$photo' => $photo, - '$url' => Contact::magicLinkByContact($contact, $contact['url']), + '$url' => Contact::magicLinkByContact($contact, $contact_url), '$about' => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['about'] ?? ''), '$xmpp' => DI::l10n()->t('XMPP:'), '$matrix' => DI::l10n()->t('Matrix:'), diff --git a/src/Model/APContact.php b/src/Model/APContact.php index dc062fe5b..2a5b89928 100644 --- a/src/Model/APContact.php +++ b/src/Model/APContact.php @@ -119,6 +119,11 @@ class APContact return []; } + if (!Network::isValidHttpUrl($url) && !filter_var($url, FILTER_VALIDATE_EMAIL)) { + Logger::info('Invalid URL', ['url' => $url]); + return []; + } + $fetched_contact = []; if (empty($update)) { diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 925cc8179..d079a4ef6 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -23,7 +23,7 @@ namespace Friendica\Model; use Friendica\Contact\Avatar; 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\Text\HTML; use Friendica\Core\Hook; @@ -111,12 +111,12 @@ class Contact * @} */ - const MIRROR_DEACTIVATED = 0; - const MIRROR_FORWARDED = 1; // Deprecated, now does the same like MIRROR_OWN_POST - const MIRROR_OWN_POST = 2; - const MIRROR_NATIVE_RESHARE = 3; + const MIRROR_DEACTIVATED = 0; + const MIRROR_FORWARDED = 1; // Deprecated, now does the same like MIRROR_OWN_POST + const MIRROR_OWN_POST = 2; + const MIRROR_NATIVE_RESHARE = 3; - /** + /** * @param array $fields Array of selected fields, empty for all * @param array $condition Array of fields for condition * @param array $params Array of several parameters @@ -725,8 +725,11 @@ class Contact */ public static function createSelfFromUserId(int $uid): bool { - $user = DBA::selectFirst('user', ['uid', 'username', 'nickname', 'pubkey', 'prvkey'], - ['uid' => $uid, 'account_expired' => false]); + $user = DBA::selectFirst( + 'user', + ['uid', 'username', 'nickname', 'pubkey', 'prvkey'], + ['uid' => $uid, 'account_expired' => false] + ); if (!DBA::isResult($user)) { return false; } @@ -786,9 +789,11 @@ class Contact */ 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', - '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]); if (!DBA::isResult($self)) { return false; @@ -800,8 +805,10 @@ class Contact return false; } - $fields = ['name', 'photo', 'thumb', 'about', 'address', 'locality', 'region', - 'country-name', 'pub_keywords', 'xmpp', 'matrix', 'net-publish']; + $fields = [ + 'name', 'photo', 'thumb', 'about', 'address', 'locality', 'region', + 'country-name', 'pub_keywords', 'xmpp', 'matrix', 'net-publish' + ]; $profile = DBA::selectFirst('profile', $fields, ['uid' => $uid]); if (!DBA::isResult($profile)) { return false; @@ -830,7 +837,7 @@ class Contact 'addr' => $user['nickname'] . '@' . substr(DI::baseUrl(), strpos(DI::baseUrl(), '://') + 3), 'request' => DI::baseUrl() . '/dfrn_request/' . $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'], ]; @@ -850,7 +857,7 @@ class Contact // We are adding a timestamp value so that other systems won't use cached content $timestamp = strtotime($fields['avatar-date']); - $prefix = DI::baseUrl() . '/photo/' .$avatar['resource-id'] . '-'; + $prefix = DI::baseUrl() . '/photo/' . $avatar['resource-id'] . '-'; $suffix = '.' . $file_suffix . '?ts=' . $timestamp; $fields['photo'] = $prefix . '4' . $suffix; @@ -1188,22 +1195,22 @@ class Contact */ if (empty($contact['uid'])) { $menu = [ - 'profile' => [DI::l10n()->t('View Profile') , $profile_link , true ], - 'network' => [DI::l10n()->t('Network Posts') , $posts_link , false], - 'edit' => [DI::l10n()->t('View Contact') , $contact_url , false], - 'follow' => [DI::l10n()->t('Connect/Follow'), $follow_link , true ], - 'unfollow' => [DI::l10n()->t('Unfollow') , $unfollow_link, true ], + 'profile' => [DI::l10n()->t('View Profile'), $profile_link, true], + 'network' => [DI::l10n()->t('Network Posts'), $posts_link, false], + 'edit' => [DI::l10n()->t('View Contact'), $contact_url, false], + 'follow' => [DI::l10n()->t('Connect/Follow'), $follow_link, true], + 'unfollow' => [DI::l10n()->t('Unfollow'), $unfollow_link, true], ]; } else { $menu = [ - 'status' => [DI::l10n()->t('View Status') , $status_link , true ], - 'profile' => [DI::l10n()->t('View Profile') , $profile_link , true ], - 'photos' => [DI::l10n()->t('View Photos') , $photos_link , true ], - 'network' => [DI::l10n()->t('Network Posts') , $posts_link , false], - 'edit' => [DI::l10n()->t('View Contact') , $contact_url , false], - 'pm' => [DI::l10n()->t('Send PM') , $pm_url , false], - 'follow' => [DI::l10n()->t('Connect/Follow'), $follow_link , true ], - 'unfollow' => [DI::l10n()->t('Unfollow') , $unfollow_link, true ], + 'status' => [DI::l10n()->t('View Status'), $status_link, true], + 'profile' => [DI::l10n()->t('View Profile'), $profile_link, true], + 'photos' => [DI::l10n()->t('View Photos'), $photos_link, true], + 'network' => [DI::l10n()->t('Network Posts'), $posts_link, false], + 'edit' => [DI::l10n()->t('View Contact'), $contact_url, false], + 'pm' => [DI::l10n()->t('Send PM'), $pm_url, false], + 'follow' => [DI::l10n()->t('Connect/Follow'), $follow_link, true], + 'unfollow' => [DI::l10n()->t('Unfollow'), $unfollow_link, true], ]; if (!empty($contact['pending'])) { @@ -1310,9 +1317,11 @@ class Contact if (($uid == 0) && (empty($data['network']) || ($data['network'] == Protocol::PHANTOM))) { // 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?) - $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', - '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]); if (!DBA::isResult($personal_contact)) { @@ -1458,7 +1467,7 @@ class Contact if (!empty($contact['batch'])) { $condition = ['archive' => true, 'uid' => 0, 'network' => Protocol::FEDERATED, 'batch' => $contact['batch'], 'contact-type' => self::TYPE_RELAY]; return DBA::exists('contact', $condition); - } + } return false; } @@ -1551,11 +1560,15 @@ class Contact $contact_field = ((($contact["contact-type"] == self::TYPE_COMMUNITY) || ($contact['network'] == Protocol::MAIL)) ? 'owner-id' : 'author-id'); 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, - $cid, Item::GRAVITY_PARENT, $cid, Item::GRAVITY_ACTIVITY, Verb::getID(Activity::ANNOUNCE), Conversation::PARCEL_DIASPORA, DI::userSession()->getLocalUserId()]; + $condition = [ + "((`$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 { - $condition = ["`$contact_field` = ? AND `gravity` IN (?, ?) AND " . $sql, - $cid, Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT, DI::userSession()->getLocalUserId()]; + $condition = [ + "`$contact_field` = ? AND `gravity` IN (?, ?) AND " . $sql, + $cid, Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT, DI::userSession()->getLocalUserId() + ]; } if (!empty($parent)) { @@ -1568,16 +1581,26 @@ class Contact } if ($only_media) { - $condition = DBA::mergeConditions($condition, ["`uri-id` IN (SELECT `uri-id` FROM `post-media` WHERE `type` IN (?, ?, ?))", - Post\Media::AUDIO, Post\Media::IMAGE, Post\Media::VIDEO]); + $condition = DBA::mergeConditions($condition, [ + "`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()) { - $itemsPerPage = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'itemspage_mobile_network', - DI::config()->get('system', 'itemspage_network_mobile')); + $itemsPerPage = DI::pConfig()->get( + DI::userSession()->getLocalUserId(), + 'system', + 'itemspage_mobile_network', + DI::config()->get('system', 'itemspage_network_mobile') + ); } else { - $itemsPerPage = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'itemspage_network', - DI::config()->get('system', 'itemspage_network')); + $itemsPerPage = DI::pConfig()->get( + DI::userSession()->getLocalUserId(), + 'system', + 'itemspage_network', + DI::config()->get('system', 'itemspage_network') + ); } $pager = new Pager(DI::l10n(), DI::args()->getQueryString(), $itemsPerPage); @@ -1611,8 +1634,10 @@ class Contact if ($pager->getStart() == 0) { $cdata = self::getPublicAndUserContactID($cid, DI::userSession()->getLocalUserId()); if (!empty($cdata['public'])) { - $condition = ["`uri-id` IN (SELECT `uri-id` FROM `collection-view` WHERE `cid` = ? AND `type` = ?)", - $cdata['public'], Post\Collection::FEATURED]; + $condition = [ + "`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)); $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']))) { Logger::info('Adding avatar cache file', ['id' => $cid, 'contact' => $contact]); 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 { - $condition = ["`nurl` = ? AND ((`uid` = ? AND `network` IN (?, ?)) OR `uid` = ?)", - Strings::normaliseLink($url), $uid, Protocol::FEED, Protocol::MAIL, 0]; + $condition = [ + "`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]]); 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) { - $contact = DBA::selectFirst('contact', ['uid', 'avatar', 'photo', 'thumb', 'micro', 'blurhash', 'xmpp', 'addr', 'nurl', 'url', 'network', 'uri-id'], - ['id' => $cid, 'self' => false]); + $contact = DBA::selectFirst( + 'contact', + ['uid', 'avatar', 'photo', 'thumb', 'micro', 'blurhash', 'xmpp', 'addr', 'nurl', 'url', 'network', 'uri-id'], + ['id' => $cid, 'self' => false] + ); if (!DBA::isResult($contact)) { return; } @@ -2268,10 +2298,12 @@ class Contact } if ($default_avatar && Proxy::isLocalImage($avatar)) { - $fields = ['avatar' => $avatar, 'avatar-date' => DateTimeFormat::utcNow(), + $fields = [ + 'avatar' => $avatar, 'avatar-date' => DateTimeFormat::utcNow(), 'photo' => $avatar, '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]); } @@ -2330,8 +2362,11 @@ class Contact $uids = []; if (($uid == 0) && !in_array($contact['network'], [Protocol::FEED, Protocol::MAIL])) { // Collect all user contacts of the given public contact - $personal_contacts = DBA::select('contact', ['id', 'uid'], - ["`nurl` = ? AND `id` != ? AND NOT `self`", $contact['nurl'], $cid]); + $personal_contacts = DBA::select( + 'contact', + ['id', 'uid'], + ["`nurl` = ? AND `id` != ? AND NOT `self`", $contact['nurl'], $cid] + ); while ($personal_contact = DBA::fetch($personal_contacts)) { $cids[] = $personal_contact['id']; $uids[] = $personal_contact['uid']; @@ -2650,10 +2685,12 @@ class Contact // These fields aren't updated by this routine: // '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', '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]); if (!DBA::isResult($contact)) { return false; @@ -2677,7 +2714,7 @@ class Contact return true; } - $has_local_data = self::hasLocalData($id, $contact); + $has_local_data = self::hasLocalData($id, $contact); $uid = $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 check after the probing to be able to correct falsely detected contact types. 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]); Logger::info('Not updating relais', ['id' => $id, 'url' => $contact['url']]); return true; @@ -2773,7 +2811,7 @@ class Contact } $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 $keep = ['batch', 'notify', 'poll', 'request', 'confirm', 'poco', 'baseurl']; @@ -3078,11 +3116,11 @@ class Contact 'protocol' => $protocol, 'pubkey' => $ret['pubkey'], 'rel' => $new_relation, - 'priority'=> $ret['priority'], - 'writable'=> $writeable, + 'priority' => $ret['priority'], + 'writable' => $writeable, 'hidden' => $hidden, 'blocked' => 0, - 'readonly'=> 0, + 'readonly' => 0, 'pending' => $pending, 'subhub' => $subhub ]); @@ -3172,8 +3210,10 @@ class Contact } // Contact is blocked at user-level - if (!empty($contact['id']) && !empty($importer['id']) && - Contact\User::isBlocked($contact['id'], $importer['id'])) { + if ( + !empty($contact['id']) && !empty($importer['id']) && + Contact\User::isBlocked($contact['id'], $importer['id']) + ) { return false; } @@ -3181,9 +3221,12 @@ class Contact self::unmarkForArchival($contact); if (($contact['rel'] == self::SHARING) - || ($sharing && $contact['rel'] == self::FOLLOWER)) { - self::update(['rel' => self::FRIEND, 'writable' => true, 'pending' => false], - ['id' => $contact['id'], 'uid' => $importer['uid']]); + || ($sharing && $contact['rel'] == self::FOLLOWER) + ) { + 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 @@ -3422,7 +3465,7 @@ class Contact */ 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); } @@ -3439,7 +3482,7 @@ class Contact */ 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()) { return $destination; @@ -3568,8 +3611,10 @@ class Contact $params['limit'] = $limit; } - $condition = DBA::mergeConditions($condition, - ["(`addr` LIKE ? OR `name` LIKE ? OR `nick` LIKE ?)", $search, $search, $search]); + $condition = DBA::mergeConditions( + $condition, + ["(`addr` LIKE ? OR `name` LIKE ? OR `nick` LIKE ?)", $search, $search, $search] + ); return DBA::selectToArray('account-user-view', [], $condition, $params); } @@ -3619,7 +3664,7 @@ class Contact */ 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` > ?", 0, Protocol::DFRN, DateTimeFormat::utc('now - 1 month'), ], ['order' => ['RAND()']]); diff --git a/src/Model/Event.php b/src/Model/Event.php index dc818d8f1..bc0337064 100644 --- a/src/Model/Event.php +++ b/src/Model/Event.php @@ -553,7 +553,8 @@ class Event WHERE `event`.`id` = ? AND `event`.`uid` = ? $sql_perms", - $event_id, $owner_uid + $event_id, + $owner_uid )); if (empty($events)) { throw new HTTPException\NotFoundException(DI::l10n()->t('Event not found.')); @@ -616,7 +617,8 @@ class Event AND `start` <= ? $sql_perms", $owner_uid, - $start, $start, + $start, + $start, $finish )); @@ -661,9 +663,9 @@ class Event $copy = null; $drop = null; 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; - $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') , '', '']; + $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; + $drop = ['calendar/api/delete/' . $event['id'], DI::l10n()->t('Delete event'), '', '']; } $title = BBCode::convertForUriId($event['uri-id'], Strings::escapeHtml($event['summary'])); @@ -708,7 +710,7 @@ class Event } switch ($format) { - // Format the exported data as a CSV file. + // Format the exported data as a CSV file. case "csv": $o .= '"Subject", "Start Date", "Start Time", "Description", "End Date", "End Time", "Location"' . PHP_EOL; @@ -728,7 +730,7 @@ class Event } break; - // Format the exported data as a ics file. + // Format the exported data as a ics file. case "ical": $o = 'BEGIN:VCALENDAR' . PHP_EOL . 'VERSION:2.0' . PHP_EOL @@ -929,8 +931,13 @@ class Event $location = self::locationToArray($item['event-location']); // Construct the profile link (magic-auth). - $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); $tpl = Renderer::getMarkupTemplate('event_stream_item.tpl'); diff --git a/src/Model/Item.php b/src/Model/Item.php index cdeb6d3d1..17ac27cc3 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -94,7 +94,7 @@ class Item 'wall', 'private', 'starred', 'origin', 'parent-origin', 'title', 'body', 'language', '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', - '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', 'causer-id', 'causer-link', 'causer-name', 'causer-avatar', 'causer-contact-type', 'causer-network', '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 - const DELIVER_FIELDLIST = ['uid', 'id', 'parent', 'uri-id', 'uri', 'thr-parent', 'parent-uri', 'guid', - 'parent-guid', 'conversation', 'received', 'created', 'edited', 'verb', 'object-type', 'object', 'target', - 'private', 'title', 'body', 'raw-body', 'location', 'coord', 'app', - 'inform', 'deleted', 'extid', 'post-type', 'post-reason', 'gravity', - 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', - 'author-id', 'author-addr', 'author-link', 'author-name', 'author-avatar', 'owner-id', 'owner-link', 'contact-uid', - 'signed_text', 'network', 'wall', 'contact-id', 'plink', 'origin', - 'thr-parent-id', 'parent-uri-id', 'quote-uri', 'quote-uri-id', 'postopts', 'pubmail', - 'event-created', 'event-edited', 'event-start', 'event-finish', - 'event-summary', 'event-desc', 'event-location', 'event-type', - 'event-nofinish', 'event-ignore', 'event-id']; + const DELIVER_FIELDLIST = [ + 'uid', 'id', 'parent', 'uri-id', 'uri', 'thr-parent', 'parent-uri', 'guid', + 'parent-guid', 'conversation', 'received', 'created', 'edited', 'verb', 'object-type', 'object', 'target', + 'private', 'title', 'body', 'raw-body', 'location', 'coord', 'app', + 'inform', 'deleted', 'extid', 'post-type', 'post-reason', 'gravity', + 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', + 'author-id', 'author-addr', 'author-link', 'author-name', 'author-avatar', 'owner-id', 'owner-link', 'contact-uid', + 'signed_text', 'network', 'wall', 'contact-id', 'plink', 'origin', + 'thr-parent-id', 'parent-uri-id', 'quote-uri', 'quote-uri-id', 'postopts', 'pubmail', + 'event-created', 'event-edited', 'event-start', 'event-finish', + 'event-summary', 'event-desc', 'event-location', 'event-type', + 'event-nofinish', 'event-ignore', 'event-id' + ]; // All fields in the item table - const ITEM_FIELDLIST = ['id', 'uid', 'parent', 'uri', 'parent-uri', 'thr-parent', - 'guid', 'uri-id', 'parent-uri-id', 'thr-parent-id', 'conversation', 'vid', - 'quote-uri', 'quote-uri-id', 'contact-id', 'wall', 'gravity', 'extid', 'psid', - 'created', 'edited', 'commented', 'received', 'changed', 'verb', - 'postopts', 'plink', 'resource-id', 'event-id', 'inform', - 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'post-type', 'post-reason', - 'private', 'pubmail', 'visible', 'starred', - 'unseen', 'deleted', 'origin', 'mention', 'global', 'network', - 'title', 'content-warning', 'body', 'location', 'coord', 'app', - 'rendered-hash', 'rendered-html', 'object-type', 'object', 'target-type', 'target', - 'author-id', 'author-link', 'author-name', 'author-avatar', 'author-network', - 'owner-id', 'owner-link', 'owner-name', 'owner-avatar', 'causer-id']; + const ITEM_FIELDLIST = [ + 'id', 'uid', 'parent', 'uri', 'parent-uri', 'thr-parent', + 'guid', 'uri-id', 'parent-uri-id', 'thr-parent-id', 'conversation', 'vid', + 'quote-uri', 'quote-uri-id', 'contact-id', 'wall', 'gravity', 'extid', 'psid', + 'created', 'edited', 'commented', 'received', 'changed', 'verb', + 'postopts', 'plink', 'resource-id', 'event-id', 'inform', + 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'post-type', 'post-reason', + 'private', 'pubmail', 'visible', 'starred', + 'unseen', 'deleted', 'origin', 'mention', 'global', 'network', + 'title', 'content-warning', 'body', 'location', 'coord', 'app', + 'rendered-hash', 'rendered-html', 'object-type', 'object', 'target-type', 'target', + '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. // 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::ATTEND, Activity::ATTENDNO, Activity::ATTENDMAYBE, Activity::FOLLOW, - Activity::ANNOUNCE]; + Activity::ANNOUNCE + ]; // Privacy levels const PUBLIC = 0; @@ -191,8 +196,10 @@ class Item } // We only need to call the line by line update for specific fields - if (empty($fields['body']) && empty($fields['file']) && - empty($fields['attach']) && empty($fields['edited'])) { + if ( + empty($fields['body']) && empty($fields['file']) && + empty($fields['attach']) && empty($fields['edited']) + ) { return $rows; } @@ -319,9 +326,11 @@ class Item { Logger::info('Mark item for deletion by id', ['id' => $item_id, 'callstack' => System::callstack()]); // 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', - '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]); if (!DBA::isResult($item)) { Logger::info('Item not found.', ['id' => $item_id]); @@ -359,7 +368,7 @@ class Item // If item has attachments, drop them $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)) { Attach::delete(['id' => $matches[1], 'uid' => $item['uid']]); } @@ -473,7 +482,7 @@ class Item private static function contactId(array $item): int { if ($item['uid'] == 0) { - return $item['author-id']; + return $item['owner-id']; } if ($item['origin']) { @@ -481,6 +490,23 @@ class Item 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)) { $cdata = Contact::getPublicAndUserContactID($item['causer-id'], $item['uid']); if (!empty($cdata['user'])) { @@ -488,24 +514,7 @@ class Item } } - if ($item['gravity'] == self::GRAVITY_PARENT) { - 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)) { + if ($contact_id) { return $contact_id; } @@ -551,8 +560,10 @@ class Item return true; } - $condition = ['uri-id' => $item['uri-id'], 'uid' => $item['uid'], - 'network' => [$item['network'], Protocol::DFRN]]; + $condition = [ + 'uri-id' => $item['uri-id'], 'uid' => $item['uid'], + 'network' => [$item['network'], Protocol::DFRN] + ]; if (Post::exists($condition)) { Logger::notice('duplicated item with the same uri found.', $condition); return true; @@ -567,8 +578,10 @@ class Item } } elseif ($item['network'] == Protocol::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` = ?", - $item['body'], $item['network'], $item['created'], $item['contact-id'], $item['uid']]; + $condition = [ + "`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)) { Logger::notice('duplicated item with the same body found.', $item); return true; @@ -642,8 +655,10 @@ class Item return false; } - $condition = ['verb' => Activity::FOLLOW, 'uid' => $item['uid'], - 'parent-uri' => $item['parent-uri'], 'author-id' => $item['author-id']]; + $condition = [ + 'verb' => Activity::FOLLOW, 'uid' => $item['uid'], + 'parent-uri' => $item['parent-uri'], 'author-id' => $item['author-id'] + ]; if (Post::exists($condition)) { // 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']]); @@ -695,7 +710,8 @@ class Item private static function getDuplicateID(array $item): int { 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['uid'], Protocol::ACTIVITYPUB, @@ -751,10 +767,12 @@ class Item */ 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', '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']]; $params = ['order' => ['id' => false]]; $parent = Post::selectFirst($fields, $condition, $params); @@ -779,9 +797,11 @@ class Item return $parent; } - $condition = ['uri-id' => $parent['parent-uri-id'], + $condition = [ + 'uri-id' => $parent['parent-uri-id'], 'parent-uri-id' => $parent['parent-uri-id'], - 'uid' => $parent['uid']]; + 'uid' => $parent['uid'] + ]; $params = ['order' => ['id' => false]]; $toplevel_parent = Post::selectFirst($fields, $condition, $params); @@ -946,7 +966,7 @@ class Item // Communities aren't working with the Diaspora protocol if (($uid != 0) && ($item['network'] == Protocol::DIASPORA)) { $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'); return 0; } @@ -966,12 +986,16 @@ class Item $item['gravity'] = self::getGravity($item); - $default = ['url' => $item['author-link'], 'name' => $item['author-name'], - 'photo' => $item['author-avatar'], 'network' => $item['network']]; + $default = [ + '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); - $default = ['url' => $item['owner-link'], 'name' => $item['owner-name'], - 'photo' => $item['owner-avatar'], 'network' => $item['network']]; + $default = [ + '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['post-reason'] = self::getPostReason($item); @@ -982,8 +1006,10 @@ class Item $item['contact-id'] = self::contactId($item); - if (!empty($item['direction']) && in_array($item['direction'], [Conversation::PUSH, Conversation::RELAY]) && - empty($item['origin']) && self::isTooOld($item)) { + if ( + !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]); return 0; } @@ -1124,7 +1150,8 @@ class Item $item['allow_gid'], $item['deny_cid'], $item['deny_gid'] - ))->id; + ) + )->id; if (!empty($item['extid'])) { $item['external-id'] = ItemURI::getIdByURI($item['extid']); @@ -1356,7 +1383,8 @@ class Item // Don't relay participation messages 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']]); $transmit = false; } @@ -1429,8 +1457,10 @@ class Item */ private static function setOwnerforResharedItem(array $item) { - $parent = Post::selectFirst(['id', 'causer-id', 'owner-id', 'author-id', 'author-link', 'origin', 'post-reason'], - ['uri-id' => $item['thr-parent-id'], 'uid' => $item['uid']]); + $parent = Post::selectFirst( + ['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)) { Logger::error('Parent not found', ['uri-id' => $item['thr-parent-id'], 'uid' => $item['uid']]); return; @@ -1502,9 +1532,11 @@ class Item } // Only distribute public items from native networks - $condition = ['id' => $itemid, 'uid' => 0, - 'network' => array_merge(Protocol::FEDERATED ,['']), - 'visible' => true, 'deleted' => false, 'private' => [self::PUBLIC, self::UNLISTED]]; + $condition = [ + 'id' => $itemid, 'uid' => 0, + 'network' => array_merge(Protocol::FEDERATED, ['']), + 'visible' => true, 'deleted' => false, 'private' => [self::PUBLIC, self::UNLISTED] + ]; $item = Post::selectFirst(array_merge(self::ITEM_FIELDLIST, ['protocol']), $condition); if (!DBA::isResult($item)) { Logger::warning('Item not found', ['condition' => $condition]); @@ -1625,7 +1657,8 @@ class Item if (($uid != 0) && (($item['gravity'] == self::GRAVITY_PARENT) || $is_reshare) && 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']]); return 0; } @@ -1821,7 +1854,7 @@ class Item } // 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; } @@ -2124,12 +2157,16 @@ class Item } // Now do the same for the system wide contacts with uid=0 if ($arr['private'] != self::PRIVATE) { - Contact::update(['failed' => false, 'local-data' => true, 'success_update' => $arr['received'], 'last-item' => $arr['received']], - ['id' => $arr['owner-id']]); + Contact::update( + ['failed' => false, 'local-data' => true, 'success_update' => $arr['received'], 'last-item' => $arr['received']], + ['id' => $arr['owner-id']] + ); if ($arr['owner-id'] != $arr['author-id']) { - Contact::update(['failed' => false, 'local-data' => true, 'success_update' => $arr['received'], 'last-item' => $arr['received']], - ['id' => $arr['author-id']]); + Contact::update( + ['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 if (DI::config()->get('system', 'local_tags')) { - $body = preg_replace("/#\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", - "#[url=" . DI::baseUrl() . "/search?tag=$2]$2[/url]", $body); + $body = preg_replace( + "/#\[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 - $body = preg_replace_callback("/\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", + $body = preg_replace_callback( + "/\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", function ($match) { return ("[url=" . str_replace("#", "#", $match[1]) . "]" . str_replace("#", "#", $match[2]) . "[/url]"); - }, $body); + }, + $body + ); - $body = preg_replace_callback("/\[bookmark\=([$URLSearchString]*)\](.*?)\[\/bookmark\]/ism", + $body = preg_replace_callback( + "/\[bookmark\=([$URLSearchString]*)\](.*?)\[\/bookmark\]/ism", function ($match) { return ("[bookmark=" . str_replace("#", "#", $match[1]) . "]" . str_replace("#", "#", $match[2]) . "[/bookmark]"); - }, $body); + }, + $body + ); - $body = preg_replace_callback("/\[attachment (.*?)\](.*?)\[\/attachment\]/ism", + $body = preg_replace_callback( + "/\[attachment (.*?)\](.*?)\[\/attachment\]/ism", function ($match) { return ("[attachment " . str_replace("#", "#", $match[1]) . "]" . $match[2] . "[/attachment]"); - }, $body); + }, + $body + ); // Repair recursive urls - $body = preg_replace("/#\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", - "#$2", $body); + $body = preg_replace( + "/#\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", + "#$2", + $body + ); foreach ($tags as $tag) { 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 { - $mention = false; - $owner = User::getOwnerDataById($uid); if (!DBA::isResult($owner)) { Logger::warning('User not found, quitting here.', ['uid' => $uid]); @@ -2331,10 +2381,13 @@ class Item } $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'], - ['uid' => $contact['uid'], 'self' => true]); + $self = DBA::selectFirst( + 'contact', + ['id', 'name', 'url', 'thumb'], + ['uid' => $contact['uid'], 'self' => true] + ); if (!DBA::isResult($self)) { Logger::error('Self contact not found', ['uid' => $contact['uid']]); return false; @@ -2370,7 +2423,7 @@ class Item // Store the original post $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 { $datarray['private'] = self::PUBLIC; $datarray['app'] = 'Feed'; @@ -2497,7 +2550,8 @@ class Item if (($obj1['allow_cid'] == $obj2['allow_cid']) && ($obj1['allow_gid'] == $obj2['allow_gid']) && ($obj1['deny_cid'] == $obj2['deny_cid']) - && ($obj1['deny_gid'] == $obj2['deny_gid'])) { + && ($obj1['deny_gid'] == $obj2['deny_gid']) + ) { return true; } @@ -2544,8 +2598,10 @@ class Item return; } - $condition = ["`uid` = ? AND NOT `deleted` AND `gravity` = ?", - $uid, self::GRAVITY_PARENT]; + $condition = [ + "`uid` = ? AND NOT `deleted` AND `gravity` = ?", + $uid, self::GRAVITY_PARENT + ]; /* * $expire_network_only = save your own wall posts @@ -2619,8 +2675,10 @@ class Item return false; } - $condition = ["`uid` = ? AND `wall` = ? AND NOT `deleted` AND `visible` AND `received` >= ?", - $uid, $wall, $user['register_date']]; + $condition = [ + "`uid` = ? AND `wall` = ? AND NOT `deleted` AND `visible` AND `received` >= ?", + $uid, $wall, $user['register_date'] + ]; $params = ['order' => ['received' => false]]; $thread = Post::selectFirstThread(['received'], $condition, $params); if (DBA::isResult($thread)) { @@ -2752,8 +2810,10 @@ class Item $vids = Verb::getID($activity); } - $condition = ['vid' => $vids, 'deleted' => false, 'gravity' => self::GRAVITY_ACTIVITY, - 'author-id' => $author_id, 'uid' => $uid, 'thr-parent-id' => $uri_id]; + $condition = [ + '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); if (DBA::isResult($like_item)) { @@ -2857,12 +2917,14 @@ class Item // Profile owner - everything is visible $condition = []; } elseif ($remote_user) { - // Authenticated visitor - fetch the matching permissionsets + // Authenticated visitor - fetch the matching permissionsets $permissionSets = DI::permissionSet()->selectByContactId($remote_user, $owner_id); if (!empty($set)) { - $condition = ["(`private` != ? OR (`private` = ? AND `wall` + $condition = [ + "(`private` != ? OR (`private` = ? AND `wall` AND `psid` IN (" . implode(', ', array_fill(0, count($set), '?')) . ")))", - self::PRIVATE, self::PRIVATE]; + self::PRIVATE, self::PRIVATE + ]; $condition = array_merge($condition, $permissionSets->column('id')); } } @@ -2958,7 +3020,8 @@ class Item $rendered_hash = $item['rendered-hash'] ?? ''; $rendered_html = $item['rendered-html'] ?? ''; - if ($rendered_hash == '' + if ( + $rendered_hash == '' || $rendered_html == '' || $rendered_hash != hash('md5', BBCode::VERSION . '::' . $body) || DI::config()->get('system', 'ignore_cache') @@ -3263,8 +3326,10 @@ class Item } foreach ([0, 1, 2] as $size) { - if (preg_match('#/photo/.*-' . $size . '\.#ism', $url) && - strpos(preg_replace('#(/photo/.*)-[012]\.#ism', '$1-' . $size . '.', $body), $url)) { + if ( + preg_match('#/photo/.*-' . $size . '\.#ism', $url) && + strpos(preg_replace('#(/photo/.*)-[012]\.#ism', '$1-' . $size . '.', $body), $url) + ) { return true; } } @@ -3461,7 +3526,8 @@ class Item 'text' => '', 'title' => $attachment['name'] ?? '', 'type' => 'link', - 'url' => $attachment['url']]; + 'url' => $attachment['url'] + ]; if ($preview && !empty($attachment['preview'])) { if ($attachment['preview-width'] >= 500) { @@ -3551,8 +3617,13 @@ class Item continue; } - $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'] + ]; $the_url = Contact::magicLinkByContact($author, $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']); } elseif (!empty($question['endtime'])) { $summary = DI::l10n()->t('Poll end: %s', Temporal::getRelativeDate($question['endtime'])); - } else { + } else { $summary = ''; } @@ -3610,7 +3681,7 @@ class Item '$options' => $options, '$summary' => $summary, ]); - } + } DI::profiler()->stopRecording(); return $content; } @@ -3639,8 +3710,13 @@ class Item ]; if (!empty($plink) && ($item['private'] == self::PRIVATE)) { - $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'], + ]; $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? + * This does apply to posts that are sent via ! and not in parallel to a group via @ * * @param int $uri_id * @@ -3671,7 +3748,13 @@ class Item */ 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])) { return true; } diff --git a/src/Model/Tag.php b/src/Model/Tag.php index 9f0f8d29a..1645dc125 100644 --- a/src/Model/Tag.php +++ b/src/Model/Tag.php @@ -487,7 +487,7 @@ class Tag * * @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); foreach ($tags as $tag) { diff --git a/src/Module/Contact.php b/src/Module/Contact.php index aad97fd16..6e480c193 100644 --- a/src/Module/Contact.php +++ b/src/Module/Contact.php @@ -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; $tpl = Renderer::getMarkupTemplate('contacts-head.tpl'); - DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl, [ - ]); + DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl, []); $o = ''; Nav::setSelected('contact'); @@ -412,20 +411,41 @@ class Contact extends BaseModule $tabs_html = Renderer::replaceMacros($tabs_tpl, ['$tabs' => $tabs]); switch ($rel) { - case 'followers': $header = DI::l10n()->t('Followers'); break; - case 'following': $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'); + case 'followers': + $header = DI::l10n()->t('Followers'); + break; + case 'following': + $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) { - case 'pending': $header .= ' - ' . DI::l10n()->t('Pending'); break; - case 'blocked': $header .= ' - ' . DI::l10n()->t('Blocked'); 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; + case 'pending': + $header .= ' - ' . DI::l10n()->t('Pending'); + break; + case 'blocked': + $header .= ' - ' . DI::l10n()->t('Blocked'); + 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) : ''; @@ -512,7 +532,8 @@ class Contact extends BaseModule 'id' => 'media-tab', 'accesskey' => 'd', ], - ['label' => DI::l10n()->t('Contacts'), + [ + 'label' => DI::l10n()->t('Contacts'), 'url' => 'contact/' . $pcid . '/contacts', 'sel' => (($active_tab == self::TAB_CONTACTS) ? 'active' : ''), '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)) { - $tabs[] = ['label' => DI::l10n()->t('Advanced'), + $tabs[] = [ + 'label' => DI::l10n()->t('Advanced'), 'url' => 'contact/' . $cid . '/advanced/', 'sel' => (($active_tab == self::TAB_ADVANCED) ? 'active' : ''), 'title' => DI::l10n()->t('Advanced Contact Settings'), diff --git a/src/Module/Contact/Unfollow.php b/src/Module/Contact/Unfollow.php index 9bbc34eee..670fe57e6 100644 --- a/src/Module/Contact/Unfollow.php +++ b/src/Module/Contact/Unfollow.php @@ -93,7 +93,7 @@ class Unfollow extends \Friendica\BaseModule 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)) { $this->systemMessages->addNotice($this->t("You aren't following this contact.")); $this->baseUrl->redirect($base_return_path); diff --git a/src/Module/Magic.php b/src/Module/Magic.php index 481a748fc..dc0d4f8ad 100644 --- a/src/Module/Magic.php +++ b/src/Module/Magic.php @@ -29,12 +29,14 @@ use Friendica\Core\Session\Capability\IHandleUserSessions; use Friendica\Core\System; use Friendica\Database\Database; use Friendica\Model\Contact; +use Friendica\Model\GServer; use Friendica\Model\User; use Friendica\Network\HTTPClient\Capability\ICanSendHttpRequests; use Friendica\Network\HTTPClient\Client\HttpClientOptions; use Friendica\Util\HTTPSignature; use Friendica\Util\Profiler; use Friendica\Util\Strings; +use GuzzleHttp\Psr7\Uri; use Psr\Log\LoggerInterface; /** @@ -83,6 +85,8 @@ class Magic extends BaseModule $this->logger->debug('bdest detected', ['dest' => $dest]); } + $target = $dest ?: $addr; + if ($addr ?: $dest) { $contact = Contact::getByURL($addr ?: $dest); } @@ -110,14 +114,21 @@ class Magic extends BaseModule // OpenWebAuth $owner = User::getOwnerDataById($this->userSession->getLocalUserId()); - $gserver = $this->dba->selectFirst('gserver', ['url'], ['id' => $contact['gsid']]); - if (empty($gserver)) { - $this->logger->notice('Server not found, redirecting to destination.', ['gsid' => $contact['gsid'], 'dest' => $dest]); + if (!empty($contact['gsid'])) { + $gserver = $this->dba->selectFirst('gserver', ['url'], ['id' => $contact['gsid']]); + 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); } - $basepath = $gserver['url']; - $header = [ 'Accept' => 'application/x-dfrn+json, application/x-zot+json', 'X-Open-Web-Auth' => Strings::getRandomHex() diff --git a/src/Object/Post.php b/src/Object/Post.php index 27f618fb6..28db23894 100644 --- a/src/Object/Post.php +++ b/src/Object/Post.php @@ -91,9 +91,13 @@ class Post } $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'), - 'url' => $this->getDataValue('author-link')]; + 'url' => $this->getDataValue('author-link'), + 'alias' => $this->getDataValue('author-alias') + ]; $this->redirect_url = Contact::magicLinkByContact($author); if (!$this->isToplevel()) { $this->threaded = true; @@ -286,8 +290,13 @@ class Post } if (DI::userSession()->isAuthenticated()) { - $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); } else { $profile_link = $item['author-link']; @@ -388,7 +397,7 @@ class Post } 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')]; if ($shareable) { $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 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'), - str_replace('{uri}', urlencode($item['uri']), DI::session()->get('remote_comment'))]; + $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 $buttons = []; @@ -670,7 +681,6 @@ class Post $title = DI::l10n()->t('Reacted with %s by: %s', $element['emoji'], $actors); $icon = []; break; - break; } $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')) { Logger::warning('Post object does not belong to local user', ['post' => $item, 'local_user' => DI::userSession()->getLocalUserId()]); return false; - } elseif (DI::activity()->match($item->getDataValue('verb'), Activity::LIKE) || - DI::activity()->match($item->getDataValue('verb'), Activity::DISLIKE)) { + } elseif ( + 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]); return false; } @@ -1004,8 +1016,10 @@ class Post } $profile = Contact::getByURL($term['url'], false, ['addr', 'contact-type']); - if (!empty($profile['addr']) && (($profile['contact-type'] ?? Contact::TYPE_UNKNOWN) != Contact::TYPE_COMMUNITY) && - ($profile['addr'] != $owner['addr']) && !strstr($text, $profile['addr'])) { + if ( + !empty($profile['addr']) && (($profile['contact-type'] ?? Contact::TYPE_UNKNOWN) != Contact::TYPE_COMMUNITY) && + ($profile['addr'] != $owner['addr']) && !strstr($text, $profile['addr']) + ) { $text .= '@' . $profile['addr'] . ' '; } } @@ -1134,6 +1148,7 @@ class Post 'id' => $this->getDataValue('owner-id'), 'network' => $this->getDataValue('owner-network'), 'url' => $this->getDataValue('owner-link'), + 'alias' => $this->getDataValue('owner-alias'), ]; $this->owner_url = Contact::magicLinkByContact($owner); } diff --git a/src/Protocol/ActivityPub/Delivery.php b/src/Protocol/ActivityPub/Delivery.php index ddf5816ed..04f5841c2 100644 --- a/src/Protocol/ActivityPub/Delivery.php +++ b/src/Protocol/ActivityPub/Delivery.php @@ -160,7 +160,7 @@ class Delivery if (!empty($actor)) { $drop = !ActivityPub\Transmitter::sendRelayFollow($actor); 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. // 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. diff --git a/src/Protocol/ActivityPub/Processor.php b/src/Protocol/ActivityPub/Processor.php index 453525454..9758e4fd4 100644 --- a/src/Protocol/ActivityPub/Processor.php +++ b/src/Protocol/ActivityPub/Processor.php @@ -432,9 +432,38 @@ class Processor $item['owner-link'] = $item['author-link']; $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 { - $actor = APContact::getByURL($item['owner-link'], false); - $item['isGroup'] = ($actor['type'] ?? 'Person') == 'Group'; + $owner = APContact::getByURL($item['owner-link'], false); + } + + 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']; @@ -1059,10 +1088,10 @@ class Processor $item['causer-id'] = ($item['gravity'] == Item::GRAVITY_PARENT) ? $item['owner-id'] : $item['author-id']; } - if ($item['isGroup'] ?? false) { - $item['contact-id'] = Contact::getIdForURL($activity['actor'], $receiver); + if ($item['isGroup']) { + $item['contact-id'] = Contact::getIdForURL($item['group-link'], $receiver); } 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'])) { @@ -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 (!($item['isGroup'] ?? false)) { + if (!$item['isGroup']) { 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']]); continue; @@ -1096,7 +1125,7 @@ class Processor } 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))) { 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; @@ -1533,6 +1562,7 @@ class Processor $activity['id'] = $object['id']; $activity['to'] = $object['to'] ?? []; $activity['cc'] = $object['cc'] ?? []; + $activity['audience'] = $object['audience'] ?? []; $activity['actor'] = $actor; $activity['object'] = $object; $activity['published'] = $published; diff --git a/src/Protocol/ActivityPub/Receiver.php b/src/Protocol/ActivityPub/Receiver.php index b272c31dd..7a07e1a7f 100644 --- a/src/Protocol/ActivityPub/Receiver.php +++ b/src/Protocol/ActivityPub/Receiver.php @@ -291,16 +291,17 @@ class Receiver /** * Prepare the object array * - * @param array $activity Array with activity data - * @param integer $uid User ID - * @param boolean $push Message had been pushed to our system - * @param boolean $trust_source Do we trust the source? + * @param array $activity Array with activity data + * @param integer $uid User ID + * @param boolean $push Message had been pushed to our system + * @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 * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @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'); $type = JsonLD::fetchElement($activity, '@type'); @@ -319,7 +320,7 @@ class Receiver $fetched = false; 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); if (!empty($fetched_activity)) { @@ -355,7 +356,7 @@ class Receiver $type = JsonLD::fetchElement($activity, '@type'); // 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 = []; foreach ($receiverdata as $key => $data) { $receivers[$key] = $data['uid']; @@ -379,7 +380,7 @@ class Receiver // We possibly need some user to fetch private content, // 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'); if (empty($object_id)) { @@ -394,28 +395,6 @@ class Receiver $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 if (in_array($type, ['as:Flag'])) { $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)) { // 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. - $object_data = self::processObject($activity); + $object_data = self::processObject($activity, $original_actor); $object_data['name'] = $type; $object_data['author'] = JsonLD::fetchElement($activity, 'as:actor', '@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 - $object_data = self::prepareObjectData($activity, $uid, $push, $trust_source); - if (empty($object_data)) { - Logger::info('No object data found', ['activity' => $activity]); - return true; + // Lemmy announces activities. + // To simplify the further processing, we modify the received object. + // For announced "create" activities we remove the middle layer. + // For the rest (like, dislike, update, ...) we just process the activity directly. + $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. - // We are changing the announces into regular activities. - if (($type == 'as:Announce') && in_array($object_data['type'] ?? '', array_merge(self::ACTIVITY_TYPES, ['as:Delete', 'as:Undo', 'as:Update']))) { - Logger::debug('Change type of announce to activity', ['type' => $object_data['type']]); - $type = $object_data['type']; + // $trust_source is called by reference and is set to true if the content was retrieved successfully + $object_data = self::prepareObjectData($activity, $uid, $push, $trust_source, $original_actor); + if (empty($object_data)) { + Logger::info('No object data found', ['activity' => $activity, 'callstack' => System::callstack(20)]); + return true; } if (!empty($body) && empty($object_data['raw'])) { @@ -688,6 +681,18 @@ class Receiver 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 * @@ -1009,10 +1014,10 @@ class Receiver * * @return int user id */ - public static function getBestUserForActivity(array $activity): int + public static function getBestUserForActivity(array $activity, string $actor = ''): int { $uid = 0; - $actor = JsonLD::fetchElement($activity, 'as:actor', '@id') ?? ''; + $actor = $actor ?: JsonLD::fetchElement($activity, 'as:actor', '@id') ?? ''; $receivers = self::getReceivers($activity, $actor, [], false, false); foreach ($receivers as $receiver) { @@ -1129,7 +1134,7 @@ class Receiver } // 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); continue; } @@ -1196,12 +1201,16 @@ class Receiver // "birdsitelive" is a service that mirrors tweets into the fediverse // 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 always process deletion activities. + $activity_type = JsonLD::fetchElement($activity, '@type'); if (empty($receivers) && $fetch_unlisted && Contact::isPlatform($actor, 'birdsitelive')) { $receivers[0] = ['uid' => 0, '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')]); + } elseif (empty($receivers) && in_array($activity_type, ['as:Delete', 'as:Undo'])) { + $receivers[0] = ['uid' => 0, 'type' => self::TARGET_GLOBAL]; } 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; @@ -1437,21 +1446,9 @@ class Receiver 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 if (in_array($type, self::CONTENT_TYPES) || ($type == 'pt:CacheFile')) { - $object_data = self::processObject($object); + $object_data = self::processObject($object, ''); if (!empty($data)) { $object_data['raw-object'] = json_encode($data); @@ -1855,12 +1852,13 @@ class Receiver /** * 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 * @throws \Exception */ - private static function processObject(array $object) + private static function processObject(array $object, string $actor) { if (!JsonLD::fetchElement($object, '@id')) { return false; @@ -1868,7 +1866,7 @@ class Receiver $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 = []; foreach ($receiverdata as $key => $data) { $receivers[$key] = $data['uid']; diff --git a/src/Protocol/ActivityPub/Transmitter.php b/src/Protocol/ActivityPub/Transmitter.php index e817198ec..8aaa5ba09 100644 --- a/src/Protocol/ActivityPub/Transmitter.php +++ b/src/Protocol/ActivityPub/Transmitter.php @@ -492,7 +492,6 @@ class Transmitter * Returns an array with permissions of the thread parent of the given item array * * @param array $item - * @param bool $is_group_thread * * @return array with permissions * @throws \Friendica\Network\HTTPException\InternalServerErrorException @@ -514,6 +513,7 @@ class Transmitter 'cc' => [], 'bto' => [], 'bcc' => [], + 'audience' => [], ]; $parent_profile = APContact::getByURL($parent['author-link']); @@ -525,8 +525,8 @@ class Transmitter $exclude[] = $item['owner-link']; } - $type = [Tag::TO => 'to', Tag::CC => 'cc', Tag::BTO => 'bto', Tag::BCC => 'bcc']; - foreach (Tag::getByURIId($item['thr-parent-id'], [Tag::TO, Tag::CC, Tag::BTO, Tag::BCC]) as $receiver) { + $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, Tag::AUDIENCE]) as $receiver) { if (!empty($parent_profile['followers']) && $receiver['url'] == $parent_profile['followers'] && !empty($item_profile['followers'])) { if (!$is_group_thread) { $permissions[$type[$receiver['type']]][] = $item_profile['followers']; @@ -600,6 +600,45 @@ class Transmitter $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)) { // Will be activated in a later step $networks = Protocol::FEDERATED; @@ -608,7 +647,7 @@ class Transmitter $networks = [Protocol::ACTIVITYPUB, Protocol::OSTATUS]; } - $data = ['to' => [], 'cc' => [], 'bcc' => [] , 'audience' => []]; + $data = ['to' => [], 'cc' => [], 'bcc' => [] , 'audience' => $audience]; if ($item['gravity'] == Item::GRAVITY_PARENT) { $actor_profile = APContact::getByURL($item['owner-link']); @@ -616,23 +655,7 @@ class Transmitter $actor_profile = APContact::getByURL($item['author-link']); } - $exclusive = false; - $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]); + $terms = Tag::getByURIId($item['uri-id'], [Tag::MENTION, Tag::IMPLICIT_MENTION, Tag::EXCLUSIVE_MENTION, Tag::AUDIENCE]); if ($item['private'] != Item::PRIVATE) { // Directly mention the original author upon a quoted reshare. @@ -644,7 +667,9 @@ class Transmitter $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 if ($item['private'] == Item::PUBLIC) { @@ -656,6 +681,9 @@ class Transmitter foreach ($terms as $term) { $profile = APContact::getByURL($term['url'], false); if (!empty($profile)) { + if (($term['type'] == Tag::AUDIENCE) && ($profile['type'] == 'Group')) { + $data['audience'][] = $profile['url']; + } if ($term['type'] == Tag::EXCLUSIVE_MENTION) { $exclusive = true; if (!empty($profile['followers']) && ($profile['type'] == 'Group')) { @@ -684,6 +712,9 @@ class Transmitter $profile = APContact::getByURL($term['url'], false); if (!empty($profile)) { + if (($term['type'] == Tag::AUDIENCE) && ($profile['type'] == 'Group')) { + $data['audience'][] = $profile['url']; + } if ($term['type'] == Tag::EXCLUSIVE_MENTION) { $exclusive = true; 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) { $condition = ['parent' => $item['parent'], 'uri-id' => $item['thr-parent-id']]; } else { @@ -814,20 +845,13 @@ class Transmitter } } - $receivers = ['to' => array_values($data['to']), 'cc' => array_values($data['cc']), 'bcc' => array_values($data['bcc'])]; - - if (!empty($data['audience'])) { - $receivers['audience'] = array_values($data['audience']); - if (count($receivers['audience']) == 1) { - $receivers['audience'] = $receivers['audience'][0]; - } - } + $receivers = ['to' => array_values($data['to']), 'cc' => array_values($data['cc']), 'bcc' => array_values($data['bcc']), 'audience' => array_values($data['audience'])]; if (!$blindcopy) { 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])) { foreach ($receivers[$element] as $receiver) { 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; } @@ -976,7 +1006,7 @@ class Transmitter $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])) { continue; } @@ -1000,7 +1030,7 @@ class Transmitter } else { $target = $profile['sharedinbox']; } - if (!self::archivedInbox($target)) { + if (!self::archivedInbox($target) && !in_array($contact['id'], $inboxes[$target] ?? [])) { $inboxes[$target][] = $contact['id'] ?? 0; } } @@ -1101,12 +1131,14 @@ class Transmitter unset($data['cc']); unset($data['bcc']); + unset($data['audience']); $object['to'] = $data['to']; $object['tag'] = [['type' => 'Mention', 'href' => $object['to'][0], 'name' => '']]; unset($object['cc']); unset($object['bcc']); + unset($object['audience']); $data['directMessage'] = true; diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index 92ed8c8df..33fd222c0 100644 --- a/static/dbstructure.config.php +++ b/static/dbstructure.config.php @@ -56,7 +56,7 @@ use Friendica\Database\DBA; // This file is required several times during the test in DbaDefinition which justifies this condition if (!defined('DB_UPDATE_VERSION')) { - define('DB_UPDATE_VERSION', 1520); + define('DB_UPDATE_VERSION', 1521); } return [ diff --git a/static/dbview.config.php b/static/dbview.config.php index b960cfaf3..17d99d949 100644 --- a/static/dbview.config.php +++ b/static/dbview.config.php @@ -149,6 +149,7 @@ "author-addr" => ["author", "addr"], "author-name" => "IF (`contact`.`url` = `author`.`url` AND `contact`.`name` != '', `contact`.`name`, `author`.`name`)", "author-nick" => ["author", "nick"], + "author-alias" => ["author", "alias"], "author-avatar" => "IF (`contact`.`url` = `author`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `author`.`thumb`)", "author-network" => ["author", "network"], "author-blocked" => ["author", "blocked"], @@ -161,6 +162,7 @@ "owner-addr" => ["owner", "addr"], "owner-name" => "IF (`contact`.`url` = `owner`.`url` AND `contact`.`name` != '', `contact`.`name`, `owner`.`name`)", "owner-nick" => ["owner", "nick"], + "owner-alias" => ["owner", "alias"], "owner-avatar" => "IF (`contact`.`url` = `owner`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `owner`.`thumb`)", "owner-network" => ["owner", "network"], "owner-blocked" => ["owner", "blocked"], @@ -324,6 +326,7 @@ "author-addr" => ["author", "addr"], "author-name" => "IF (`contact`.`url` = `author`.`url` AND `contact`.`name` != '', `contact`.`name`, `author`.`name`)", "author-nick" => ["author", "nick"], + "author-alias" => ["author", "alias"], "author-avatar" => "IF (`contact`.`url` = `author`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `author`.`thumb`)", "author-network" => ["author", "network"], "author-blocked" => ["author", "blocked"], @@ -336,6 +339,7 @@ "owner-addr" => ["owner", "addr"], "owner-name" => "IF (`contact`.`url` = `owner`.`url` AND `contact`.`name` != '', `contact`.`name`, `owner`.`name`)", "owner-nick" => ["owner", "nick"], + "owner-alias" => ["owner", "alias"], "owner-avatar" => "IF (`contact`.`url` = `owner`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `owner`.`thumb`)", "owner-network" => ["owner", "network"], "owner-blocked" => ["owner", "blocked"], @@ -485,6 +489,7 @@ "author-addr" => ["author", "addr"], "author-name" => ["author", "name"], "author-nick" => ["author", "nick"], + "author-alias" => ["author", "alias"], "author-avatar" => ["author", "thumb"], "author-network" => ["author", "network"], "author-blocked" => ["author", "blocked"], @@ -497,6 +502,7 @@ "owner-addr" => ["owner", "addr"], "owner-name" => ["owner", "name"], "owner-nick" => ["owner", "nick"], + "owner-alias" => ["owner", "alias"], "owner-avatar" => ["owner", "thumb"], "owner-network" => ["owner", "network"], "owner-blocked" => ["owner", "blocked"], @@ -623,6 +629,7 @@ "author-addr" => ["author", "addr"], "author-name" => ["author", "name"], "author-nick" => ["author", "nick"], + "author-alias" => ["author", "alias"], "author-avatar" => ["author", "thumb"], "author-network" => ["author", "network"], "author-blocked" => ["author", "blocked"], @@ -635,6 +642,7 @@ "owner-addr" => ["owner", "addr"], "owner-name" => ["owner", "name"], "owner-nick" => ["owner", "nick"], + "owner-alias" => ["owner", "alias"], "owner-avatar" => ["owner", "thumb"], "owner-network" => ["owner", "network"], "owner-blocked" => ["owner", "blocked"], diff --git a/view/lang/C/messages.po b/view/lang/C/messages.po index d8dd10d6a..25e64d481 100644 --- a/view/lang/C/messages.po +++ b/view/lang/C/messages.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: 2023.09-dev\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" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -45,7 +45,7 @@ msgid "Item not found." msgstr "" #: 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/BaseNotifications.php:98 src/Module/BaseSettings.php:52 #: src/Module/Calendar/Event/API.php:88 src/Module/Calendar/Event/Form.php:84 @@ -281,7 +281,7 @@ msgstr "" msgid "Your message:" 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 msgid "Upload photo" msgstr "" @@ -291,16 +291,16 @@ msgstr "" msgid "Insert web link" msgstr "" -#: mod/message.php:201 mod/message.php:357 mod/photos.php:1289 -#: src/Content/Conversation.php:391 src/Content/Conversation.php:735 +#: mod/message.php:201 mod/message.php:357 mod/photos.php:1301 +#: src/Content/Conversation.php:392 src/Content/Conversation.php:742 #: 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" msgstr "" -#: mod/message.php:202 mod/message.php:356 mod/photos.php:700 -#: mod/photos.php:817 mod/photos.php:1095 mod/photos.php:1136 -#: mod/photos.php:1192 mod/photos.php:1266 +#: mod/message.php:202 mod/message.php:356 mod/photos.php:705 +#: mod/photos.php:824 mod/photos.php:1101 mod/photos.php:1142 +#: mod/photos.php:1198 mod/photos.php:1278 #: src/Module/Calendar/Event/Form.php:250 src/Module/Contact/Advanced.php:132 #: src/Module/Contact/Profile.php:339 #: src/Module/Debug/ActivityPubConversion.php:140 @@ -311,7 +311,7 @@ msgstr "" #: 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/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/quattro/config.php:87 view/theme/vier/config.php:135 msgid "Submit" @@ -377,13 +377,13 @@ msgstr "" msgid "Personal notes are visible only by yourself." 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/Post/Edit.php:129 msgid "Save" 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/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 @@ -405,7 +405,7 @@ msgstr "" msgid "Recent Photos" 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 msgid "Upload New Photos" msgstr "" @@ -423,72 +423,72 @@ msgstr "" msgid "Album not found." msgstr "" -#: mod/photos.php:242 +#: mod/photos.php:244 msgid "Album successfully deleted" msgstr "" -#: mod/photos.php:244 +#: mod/photos.php:246 msgid "Album was empty." msgstr "" -#: mod/photos.php:276 +#: mod/photos.php:277 msgid "Failed to delete the photo." msgstr "" -#: mod/photos.php:542 +#: mod/photos.php:545 msgid "a photo" msgstr "" -#: mod/photos.php:542 +#: mod/photos.php:545 #, php-format msgid "%1$s was tagged in %2$s by %3$s" 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/Search/Index.php:65 msgid "Public access denied." msgstr "" -#: mod/photos.php:584 +#: mod/photos.php:587 msgid "No photos selected" msgstr "" -#: mod/photos.php:716 +#: mod/photos.php:721 #, php-format msgid "The maximum accepted image size is %s" msgstr "" -#: mod/photos.php:723 +#: mod/photos.php:728 msgid "Upload Photos" msgstr "" -#: mod/photos.php:727 mod/photos.php:813 +#: mod/photos.php:732 mod/photos.php:820 msgid "New album name: " msgstr "" -#: mod/photos.php:728 +#: mod/photos.php:733 msgid "or select existing album:" msgstr "" -#: mod/photos.php:729 +#: mod/photos.php:734 msgid "Do not show a status post for this upload" 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 msgid "Permissions" msgstr "" -#: mod/photos.php:794 +#: mod/photos.php:801 msgid "Do you really want to delete this photo album and all its photos?" msgstr "" -#: mod/photos.php:795 mod/photos.php:818 +#: mod/photos.php:802 mod/photos.php:825 msgid "Delete Album" 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/Unfollow.php:126 #: src/Module/Media/Attachment/Browser.php:77 @@ -498,130 +498,130 @@ msgstr "" msgid "Cancel" msgstr "" -#: mod/photos.php:822 +#: mod/photos.php:829 msgid "Edit Album" msgstr "" -#: mod/photos.php:823 +#: mod/photos.php:830 msgid "Drop Album" msgstr "" -#: mod/photos.php:827 +#: mod/photos.php:834 msgid "Show Newest First" msgstr "" -#: mod/photos.php:829 +#: mod/photos.php:836 msgid "Show Oldest First" msgstr "" -#: mod/photos.php:850 src/Module/Profile/Photos.php:350 +#: mod/photos.php:857 src/Module/Profile/Photos.php:350 msgid "View Photo" msgstr "" -#: mod/photos.php:883 +#: mod/photos.php:889 msgid "Permission denied. Access to this item may be restricted." msgstr "" -#: mod/photos.php:885 +#: mod/photos.php:891 msgid "Photo not available" msgstr "" -#: mod/photos.php:895 +#: mod/photos.php:901 msgid "Do you really want to delete this photo?" msgstr "" -#: mod/photos.php:896 mod/photos.php:1096 +#: mod/photos.php:902 mod/photos.php:1102 msgid "Delete Photo" msgstr "" -#: mod/photos.php:994 +#: mod/photos.php:1000 msgid "View photo" msgstr "" -#: mod/photos.php:996 +#: mod/photos.php:1002 msgid "Edit photo" msgstr "" -#: mod/photos.php:997 +#: mod/photos.php:1003 msgid "Delete photo" msgstr "" -#: mod/photos.php:998 +#: mod/photos.php:1004 msgid "Use as profile photo" msgstr "" -#: mod/photos.php:1005 +#: mod/photos.php:1011 msgid "Private Photo" msgstr "" -#: mod/photos.php:1011 +#: mod/photos.php:1017 msgid "View Full Size" msgstr "" -#: mod/photos.php:1064 +#: mod/photos.php:1070 msgid "Tags: " msgstr "" -#: mod/photos.php:1067 +#: mod/photos.php:1073 msgid "[Select tags to remove]" msgstr "" -#: mod/photos.php:1082 +#: mod/photos.php:1088 msgid "New album name" msgstr "" -#: mod/photos.php:1083 +#: mod/photos.php:1089 msgid "Caption" msgstr "" -#: mod/photos.php:1084 +#: mod/photos.php:1090 msgid "Add a Tag" msgstr "" -#: mod/photos.php:1084 +#: mod/photos.php:1090 msgid "Example: @bob, @Barbara_Jensen, @jim@example.com, #California, #camping" msgstr "" -#: mod/photos.php:1085 +#: mod/photos.php:1091 msgid "Do not rotate" msgstr "" -#: mod/photos.php:1086 +#: mod/photos.php:1092 msgid "Rotate CW (right)" msgstr "" -#: mod/photos.php:1087 +#: mod/photos.php:1093 msgid "Rotate CCW (left)" msgstr "" -#: mod/photos.php:1133 mod/photos.php:1189 mod/photos.php:1263 -#: src/Module/Contact.php:597 src/Module/Item/Compose.php:188 -#: src/Object/Post.php:1067 +#: mod/photos.php:1139 mod/photos.php:1195 mod/photos.php:1275 +#: src/Module/Contact.php:619 src/Module/Item/Compose.php:188 +#: src/Object/Post.php:1081 msgid "This is you" msgstr "" -#: mod/photos.php:1135 mod/photos.php:1191 mod/photos.php:1265 -#: src/Object/Post.php:551 src/Object/Post.php:1069 +#: mod/photos.php:1141 mod/photos.php:1197 mod/photos.php:1277 +#: src/Object/Post.php:562 src/Object/Post.php:1083 msgid "Comment" msgstr "" -#: mod/photos.php:1137 mod/photos.php:1193 mod/photos.php:1267 -#: src/Content/Conversation.php:406 src/Module/Calendar/Event/Form.php:248 +#: mod/photos.php:1143 mod/photos.php:1199 mod/photos.php:1279 +#: 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/Object/Post.php:1083 +#: src/Object/Post.php:1097 msgid "Preview" msgstr "" -#: mod/photos.php:1138 src/Content/Conversation.php:359 -#: src/Module/Post/Edit.php:130 src/Object/Post.php:1071 +#: mod/photos.php:1144 src/Content/Conversation.php:360 +#: src/Module/Post/Edit.php:130 src/Object/Post.php:1085 msgid "Loading..." 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" 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/Blocked.php:136 #: src/Module/Moderation/Users/Index.php:151 @@ -629,23 +629,23 @@ msgstr "" msgid "Delete" msgstr "" -#: mod/photos.php:1286 src/Object/Post.php:391 +#: mod/photos.php:1298 src/Object/Post.php:400 msgid "Like" msgstr "" -#: mod/photos.php:1287 src/Object/Post.php:391 +#: mod/photos.php:1299 src/Object/Post.php:400 msgid "I like this (toggle)" msgstr "" -#: mod/photos.php:1288 src/Object/Post.php:392 +#: mod/photos.php:1300 src/Object/Post.php:401 msgid "Dislike" 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)" msgstr "" -#: mod/photos.php:1312 +#: mod/photos.php:1324 msgid "Map" msgstr "" @@ -780,11 +780,11 @@ msgstr "" msgid "Followers" 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" 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" msgstr "" @@ -1122,65 +1122,65 @@ msgstr "" msgid "%s (via %s)" msgstr "" -#: src/Content/Conversation.php:218 +#: src/Content/Conversation.php:219 msgid "and" msgstr "" -#: src/Content/Conversation.php:221 +#: src/Content/Conversation.php:222 #, php-format msgid "and %d other people" msgstr "" -#: src/Content/Conversation.php:227 +#: src/Content/Conversation.php:228 #, php-format msgid "%2$s likes this." msgid_plural "%2$s like this." msgstr[0] "" msgstr[1] "" -#: src/Content/Conversation.php:229 +#: src/Content/Conversation.php:230 #, php-format msgid "%2$s doesn't like this." msgid_plural "%2$s don't like this." msgstr[0] "" msgstr[1] "" -#: src/Content/Conversation.php:231 +#: src/Content/Conversation.php:232 #, php-format msgid "%2$s attends." msgid_plural "%2$s attend." msgstr[0] "" msgstr[1] "" -#: src/Content/Conversation.php:233 +#: src/Content/Conversation.php:234 #, php-format msgid "%2$s doesn't attend." msgid_plural "%2$s don't attend." msgstr[0] "" msgstr[1] "" -#: src/Content/Conversation.php:235 +#: src/Content/Conversation.php:236 #, php-format msgid "%2$s attends maybe." msgid_plural "%2$s attend maybe." msgstr[0] "" msgstr[1] "" -#: src/Content/Conversation.php:237 +#: src/Content/Conversation.php:238 #, php-format msgid "%2$s reshared this." msgid_plural "%2$s reshared this." msgstr[0] "" msgstr[1] "" -#: src/Content/Conversation.php:266 +#: src/Content/Conversation.php:267 #, php-format msgid " likes this" msgid_plural " like this" msgstr[0] "" msgstr[1] "" -#: src/Content/Conversation.php:269 +#: src/Content/Conversation.php:270 #, php-format msgid " doesn't like this" msgid_plural "" @@ -1188,304 +1188,304 @@ msgid_plural "" msgstr[0] "" msgstr[1] "" -#: src/Content/Conversation.php:272 +#: src/Content/Conversation.php:273 #, php-format msgid " attends" msgid_plural " attend" msgstr[0] "" msgstr[1] "" -#: src/Content/Conversation.php:275 +#: src/Content/Conversation.php:276 #, php-format msgid " doesn't attend" msgid_plural " don't attend" msgstr[0] "" msgstr[1] "" -#: src/Content/Conversation.php:278 +#: src/Content/Conversation.php:279 #, php-format msgid " attends maybe" msgid_plural " attend maybe" msgstr[0] "" msgstr[1] "" -#: src/Content/Conversation.php:281 +#: src/Content/Conversation.php:282 #, php-format msgid " reshared this" msgid_plural " reshared this" msgstr[0] "" msgstr[1] "" -#: src/Content/Conversation.php:328 +#: src/Content/Conversation.php:329 msgid "Visible to everybody" msgstr "" -#: src/Content/Conversation.php:329 src/Module/Item/Compose.php:200 -#: src/Object/Post.php:1082 +#: src/Content/Conversation.php:330 src/Module/Item/Compose.php:200 +#: src/Object/Post.php:1096 msgid "Please enter a image/video/audio/webpage URL:" msgstr "" -#: src/Content/Conversation.php:330 +#: src/Content/Conversation.php:331 msgid "Tag term:" 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:" msgstr "" -#: src/Content/Conversation.php:332 +#: src/Content/Conversation.php:333 msgid "Where are you right now?" msgstr "" -#: src/Content/Conversation.php:333 +#: src/Content/Conversation.php:334 msgid "Delete item(s)?" 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" msgstr "" -#: src/Content/Conversation.php:355 +#: src/Content/Conversation.php:356 msgid "New Post" msgstr "" -#: src/Content/Conversation.php:358 +#: src/Content/Conversation.php:359 msgid "Share" 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" 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" 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" msgstr "" -#: src/Content/Conversation.php:364 src/Module/Item/Compose.php:190 -#: src/Module/Post/Edit.php:171 src/Object/Post.php:1072 +#: src/Content/Conversation.php:365 src/Module/Item/Compose.php:190 +#: src/Module/Post/Edit.php:171 src/Object/Post.php:1086 msgid "Bold" msgstr "" -#: src/Content/Conversation.php:365 src/Module/Item/Compose.php:191 -#: src/Module/Post/Edit.php:172 src/Object/Post.php:1073 +#: src/Content/Conversation.php:366 src/Module/Item/Compose.php:191 +#: src/Module/Post/Edit.php:172 src/Object/Post.php:1087 msgid "Italic" msgstr "" -#: src/Content/Conversation.php:366 src/Module/Item/Compose.php:192 -#: src/Module/Post/Edit.php:173 src/Object/Post.php:1074 +#: src/Content/Conversation.php:367 src/Module/Item/Compose.php:192 +#: src/Module/Post/Edit.php:173 src/Object/Post.php:1088 msgid "Underline" msgstr "" -#: src/Content/Conversation.php:367 src/Module/Item/Compose.php:193 -#: src/Module/Post/Edit.php:174 src/Object/Post.php:1076 +#: src/Content/Conversation.php:368 src/Module/Item/Compose.php:193 +#: src/Module/Post/Edit.php:174 src/Object/Post.php:1090 msgid "Quote" msgstr "" -#: src/Content/Conversation.php:368 src/Module/Item/Compose.php:194 -#: src/Module/Post/Edit.php:175 src/Object/Post.php:1077 +#: src/Content/Conversation.php:369 src/Module/Item/Compose.php:194 +#: src/Module/Post/Edit.php:175 src/Object/Post.php:1091 msgid "Add emojis" msgstr "" -#: src/Content/Conversation.php:369 src/Module/Item/Compose.php:195 -#: src/Object/Post.php:1075 +#: src/Content/Conversation.php:370 src/Module/Item/Compose.php:195 +#: src/Object/Post.php:1089 msgid "Content Warning" msgstr "" -#: src/Content/Conversation.php:370 src/Module/Item/Compose.php:196 -#: src/Module/Post/Edit.php:176 src/Object/Post.php:1078 +#: src/Content/Conversation.php:371 src/Module/Item/Compose.php:196 +#: src/Module/Post/Edit.php:176 src/Object/Post.php:1092 msgid "Code" msgstr "" -#: src/Content/Conversation.php:371 src/Module/Item/Compose.php:197 -#: src/Object/Post.php:1079 +#: src/Content/Conversation.php:372 src/Module/Item/Compose.php:197 +#: src/Object/Post.php:1093 msgid "Image" msgstr "" -#: src/Content/Conversation.php:372 src/Module/Item/Compose.php:198 -#: src/Module/Post/Edit.php:177 src/Object/Post.php:1080 +#: src/Content/Conversation.php:373 src/Module/Item/Compose.php:198 +#: src/Module/Post/Edit.php:177 src/Object/Post.php:1094 msgid "Link" msgstr "" -#: src/Content/Conversation.php:373 src/Module/Item/Compose.php:199 -#: src/Module/Post/Edit.php:178 src/Object/Post.php:1081 +#: src/Content/Conversation.php:374 src/Module/Item/Compose.php:199 +#: src/Module/Post/Edit.php:178 src/Object/Post.php:1095 msgid "Link or Media" msgstr "" -#: src/Content/Conversation.php:374 +#: src/Content/Conversation.php:375 msgid "Video" 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 msgid "Set your location" 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" 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" 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" 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 msgid "Set title" 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 msgid "Categories (comma-separated list)" 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" 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" 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" 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/Module/Post/Edit.php:181 msgid "Message" 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 msgid "Browser" 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" msgstr "" -#: src/Content/Conversation.php:679 src/Object/Post.php:244 +#: src/Content/Conversation.php:686 src/Object/Post.php:248 msgid "Pinned item" msgstr "" -#: src/Content/Conversation.php:695 src/Object/Post.php:502 -#: src/Object/Post.php:503 +#: src/Content/Conversation.php:702 src/Object/Post.php:513 +#: src/Object/Post.php:514 #, php-format msgid "View %s's profile @ %s" msgstr "" -#: src/Content/Conversation.php:708 src/Object/Post.php:490 +#: src/Content/Conversation.php:715 src/Object/Post.php:501 msgid "Categories:" msgstr "" -#: src/Content/Conversation.php:709 src/Object/Post.php:491 +#: src/Content/Conversation.php:716 src/Object/Post.php:502 msgid "Filed under:" msgstr "" -#: src/Content/Conversation.php:717 src/Object/Post.php:516 +#: src/Content/Conversation.php:724 src/Object/Post.php:527 #, php-format msgid "%s from %s" msgstr "" -#: src/Content/Conversation.php:733 +#: src/Content/Conversation.php:740 msgid "View in context" msgstr "" -#: src/Content/Conversation.php:798 +#: src/Content/Conversation.php:805 msgid "remove" msgstr "" -#: src/Content/Conversation.php:802 +#: src/Content/Conversation.php:809 msgid "Delete Selected Items" msgstr "" -#: src/Content/Conversation.php:867 src/Content/Conversation.php:870 -#: src/Content/Conversation.php:873 src/Content/Conversation.php:876 -#: src/Content/Conversation.php:879 +#: src/Content/Conversation.php:875 src/Content/Conversation.php:878 +#: src/Content/Conversation.php:881 src/Content/Conversation.php:884 +#: src/Content/Conversation.php:887 #, php-format msgid "You had been addressed (%s)." msgstr "" -#: src/Content/Conversation.php:882 +#: src/Content/Conversation.php:890 #, php-format msgid "You are following %s." msgstr "" -#: src/Content/Conversation.php:885 +#: src/Content/Conversation.php:893 msgid "You subscribed to one or more tags in this post." msgstr "" -#: src/Content/Conversation.php:898 +#: src/Content/Conversation.php:912 #, php-format msgid "%s reshared this." msgstr "" -#: src/Content/Conversation.php:900 +#: src/Content/Conversation.php:914 msgid "Reshared" msgstr "" -#: src/Content/Conversation.php:900 +#: src/Content/Conversation.php:914 #, php-format msgid "Reshared by %s <%s>" msgstr "" -#: src/Content/Conversation.php:903 +#: src/Content/Conversation.php:917 #, php-format msgid "%s is participating in this thread." msgstr "" -#: src/Content/Conversation.php:906 +#: src/Content/Conversation.php:920 msgid "Stored for general reasons" msgstr "" -#: src/Content/Conversation.php:909 +#: src/Content/Conversation.php:923 msgid "Global post" msgstr "" -#: src/Content/Conversation.php:912 +#: src/Content/Conversation.php:926 msgid "Sent via an relay server" msgstr "" -#: src/Content/Conversation.php:912 +#: src/Content/Conversation.php:926 #, php-format msgid "Sent via the relay server %s <%s>" msgstr "" -#: src/Content/Conversation.php:915 +#: src/Content/Conversation.php:929 msgid "Fetched" msgstr "" -#: src/Content/Conversation.php:915 +#: src/Content/Conversation.php:929 #, php-format msgid "Fetched because of %s <%s>" msgstr "" -#: src/Content/Conversation.php:918 +#: src/Content/Conversation.php:932 msgid "Stored because of a child post to complete this thread." msgstr "" -#: src/Content/Conversation.php:921 +#: src/Content/Conversation.php:935 msgid "Local delivery" msgstr "" -#: src/Content/Conversation.php:924 +#: src/Content/Conversation.php:938 msgid "Stored because of your activity (like, comment, star, ...)" msgstr "" -#: src/Content/Conversation.php:927 +#: src/Content/Conversation.php:941 msgid "Distributed" msgstr "" -#: src/Content/Conversation.php:930 +#: src/Content/Conversation.php:944 msgid "Pushed to us" msgstr "" @@ -1601,79 +1601,79 @@ msgid "" "Contact birthday events are private to you." msgstr "" -#: src/Content/GroupManager.php:151 src/Content/Nav.php:276 -#: src/Content/Text/HTML.php:877 src/Content/Widget.php:543 +#: src/Content/GroupManager.php:152 src/Content/Nav.php:276 +#: src/Content/Text/HTML.php:880 src/Content/Widget.php:541 msgid "Groups" msgstr "" -#: src/Content/GroupManager.php:153 +#: src/Content/GroupManager.php:154 msgid "External link to group" msgstr "" -#: src/Content/GroupManager.php:157 src/Content/Widget.php:518 +#: src/Content/GroupManager.php:158 src/Content/Widget.php:516 msgid "show less" msgstr "" -#: src/Content/GroupManager.php:158 src/Content/Widget.php:414 -#: src/Content/Widget.php:519 +#: src/Content/GroupManager.php:159 src/Content/Widget.php:414 +#: src/Content/Widget.php:517 msgid "show more" msgstr "" -#: src/Content/GroupManager.php:159 +#: src/Content/GroupManager.php:160 msgid "Create new group" msgstr "" -#: src/Content/Item.php:327 src/Model/Item.php:2931 +#: src/Content/Item.php:329 src/Model/Item.php:2986 msgid "event" msgstr "" -#: src/Content/Item.php:330 src/Content/Item.php:340 +#: src/Content/Item.php:332 src/Content/Item.php:342 msgid "status" 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 msgid "photo" 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 msgid "%1$s tagged %2$s's %3$s with %4$s" 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" msgstr "" -#: src/Content/Item.php:419 src/Model/Contact.php:1199 +#: src/Content/Item.php:422 src/Model/Contact.php:1206 msgid "View Status" msgstr "" -#: src/Content/Item.php:420 src/Content/Item.php:440 src/Model/Contact.php:1148 -#: src/Model/Contact.php:1191 src/Model/Contact.php:1200 +#: src/Content/Item.php:423 src/Content/Item.php:443 src/Model/Contact.php:1155 +#: src/Model/Contact.php:1198 src/Model/Contact.php:1207 #: src/Module/Directory.php:157 src/Module/Settings/Profile/Index.php:233 msgid "View Profile" msgstr "" -#: src/Content/Item.php:421 src/Model/Contact.php:1201 +#: src/Content/Item.php:424 src/Model/Contact.php:1208 msgid "View Photos" msgstr "" -#: src/Content/Item.php:422 src/Model/Contact.php:1192 -#: src/Model/Contact.php:1202 +#: src/Content/Item.php:425 src/Model/Contact.php:1199 +#: src/Model/Contact.php:1209 msgid "Network Posts" msgstr "" -#: src/Content/Item.php:423 src/Model/Contact.php:1193 -#: src/Model/Contact.php:1203 +#: src/Content/Item.php:426 src/Model/Contact.php:1200 +#: src/Model/Contact.php:1210 msgid "View Contact" msgstr "" -#: src/Content/Item.php:424 src/Model/Contact.php:1204 +#: src/Content/Item.php:427 src/Model/Contact.php:1211 msgid "Send PM" 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/Moderation/Blocklist/Contact.php:116 #: src/Module/Moderation/Users/Active.php:137 @@ -1681,7 +1681,7 @@ msgstr "" msgid "Block" 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/Notifications/Introductions.php:134 #: src/Module/Notifications/Introductions.php:206 @@ -1689,22 +1689,22 @@ msgstr "" msgid "Ignore" 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 msgid "Collapse" msgstr "" -#: src/Content/Item.php:431 src/Object/Post.php:471 +#: src/Content/Item.php:434 src/Object/Post.php:482 msgid "Languages" msgstr "" -#: src/Content/Item.php:437 src/Content/Widget.php:80 -#: src/Model/Contact.php:1194 src/Model/Contact.php:1205 +#: src/Content/Item.php:440 src/Content/Widget.php:80 +#: src/Model/Contact.php:1201 src/Model/Contact.php:1212 #: src/Module/Contact/Follow.php:167 view/theme/vier/theme.php:195 msgid "Connect/Follow" msgstr "" -#: src/Content/Item.php:862 +#: src/Content/Item.php:865 msgid "Unable to fetch user." msgstr "" @@ -1720,7 +1720,7 @@ msgstr "" msgid "Clear notifications" 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" msgstr "" @@ -1742,7 +1742,7 @@ msgid "Sign in" msgstr "" #: src/Content/Nav.php:227 src/Module/BaseProfile.php:57 -#: src/Module/Contact.php:492 +#: src/Module/Contact.php:512 msgid "Conversations" msgstr "" @@ -1751,7 +1751,7 @@ msgid "Conversations you started" msgstr "" #: 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/Welcome.php:57 view/theme/frio/theme.php:230 msgid "Profile" @@ -1771,7 +1771,7 @@ msgid "Your photos" msgstr "" #: 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 msgid "Media" msgstr "" @@ -1837,7 +1837,7 @@ msgstr "" msgid "Addon applications, utilities, games" 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 msgid "Search" msgstr "" @@ -1846,19 +1846,19 @@ msgstr "" msgid "Search site content" 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" 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 msgid "Tags" msgstr "" #: src/Content/Nav.php:272 src/Content/Nav.php:327 -#: src/Content/Text/HTML.php:873 src/Module/BaseProfile.php:127 -#: src/Module/BaseProfile.php:130 src/Module/Contact.php:419 -#: src/Module/Contact.php:515 view/theme/frio/theme.php:243 +#: src/Content/Text/HTML.php:876 src/Module/BaseProfile.php:127 +#: src/Module/BaseProfile.php:130 src/Module/Contact.php:427 +#: src/Module/Contact.php:536 view/theme/frio/theme.php:243 msgid "Contacts" msgstr "" @@ -2037,12 +2037,12 @@ msgid "" "%2$s %3$s" msgstr "" -#: src/Content/Text/BBCode.php:938 src/Model/Item.php:3649 -#: src/Model/Item.php:3655 src/Model/Item.php:3656 +#: src/Content/Text/BBCode.php:938 src/Model/Item.php:3718 +#: src/Model/Item.php:3724 src/Model/Item.php:3725 msgid "Link to source" 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" msgstr "" @@ -2062,15 +2062,15 @@ msgstr "" msgid "Invalid link protocol" msgstr "" -#: src/Content/Text/HTML.php:779 +#: src/Content/Text/HTML.php:782 msgid "Loading more entries..." msgstr "" -#: src/Content/Text/HTML.php:780 +#: src/Content/Text/HTML.php:783 msgid "The end" 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 msgid "Follow" msgstr "" @@ -2110,7 +2110,7 @@ msgstr "" msgid "Examples: Robert Morgenstein, Fishing" 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 msgid "Find" msgstr "" @@ -2142,7 +2142,7 @@ msgid "Local Directory" msgstr "" #: 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" msgstr "" @@ -2150,7 +2150,7 @@ msgstr "" msgid "Everyone" msgstr "" -#: src/Content/Widget.php:246 src/Module/Contact.php:418 +#: src/Content/Widget.php:246 src/Module/Contact.php:424 msgid "No relationship" msgstr "" @@ -2159,7 +2159,7 @@ msgid "Relationships" msgstr "" #: src/Content/Widget.php:253 src/Module/Circle.php:293 -#: src/Module/Contact.php:346 +#: src/Module/Contact.php:345 msgid "All Contacts" msgstr "" @@ -2190,23 +2190,23 @@ msgid_plural "%d contacts in common" msgstr[0] "" msgstr[1] "" -#: src/Content/Widget.php:512 +#: src/Content/Widget.php:510 msgid "Archives" msgstr "" -#: src/Content/Widget.php:520 +#: src/Content/Widget.php:518 msgid "On this date" msgstr "" -#: src/Content/Widget.php:540 +#: src/Content/Widget.php:538 msgid "Persons" msgstr "" -#: src/Content/Widget.php:541 +#: src/Content/Widget.php:539 msgid "Organisations" msgstr "" -#: src/Content/Widget.php:542 src/Model/Contact.php:1651 +#: src/Content/Widget.php:540 src/Model/Contact.php:1676 msgid "News" msgstr "" @@ -2214,7 +2214,7 @@ msgstr "" msgid "Account Types" 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" msgstr "" @@ -2264,31 +2264,31 @@ msgstr[1] "" msgid "More Trending Tags" 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 msgid "XMPP:" 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 msgid "Matrix:" msgstr "" -#: src/Content/Widget/VCard.php:104 src/Model/Event.php:82 -#: src/Model/Event.php:109 src/Model/Event.php:473 src/Model/Event.php:958 +#: 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:965 #: src/Model/Profile.php:373 src/Module/Contact/Profile.php:379 #: src/Module/Directory.php:147 src/Module/Notifications/Introductions.php:187 #: src/Module/Profile/Profile.php:221 msgid "Location:" 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 msgid "Network:" msgstr "" -#: src/Content/Widget/VCard.php:111 src/Model/Contact.php:1195 -#: src/Model/Contact.php:1206 src/Model/Profile.php:465 +#: src/Content/Widget/VCard.php:118 src/Model/Contact.php:1202 +#: src/Model/Contact.php:1213 src/Model/Profile.php:465 #: src/Module/Contact/Profile.php:429 msgid "Unfollow" msgstr "" @@ -2553,8 +2553,8 @@ msgstr "" #: src/Core/Installer.php:511 msgid "" -"The web installer needs to be able to create a file called \"local.config." -"php\" in the \"config\" folder of your web server and it is unable to do so." +"The web installer needs to be able to create a file called \"local.config.php" +"\" in the \"config\" folder of your web server and it is unable to do so." msgstr "" #: src/Core/Installer.php:512 @@ -3035,82 +3035,82 @@ msgstr "" msgid "Edit circles" 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:204 msgid "Approve" msgstr "" -#: src/Model/Contact.php:1647 +#: src/Model/Contact.php:1672 msgid "Organisation" msgstr "" -#: src/Model/Contact.php:1655 +#: src/Model/Contact.php:1680 msgid "Group" msgstr "" -#: src/Model/Contact.php:2951 +#: src/Model/Contact.php:2989 msgid "Disallowed profile URL." msgstr "" -#: src/Model/Contact.php:2956 src/Module/Friendica.php:83 +#: src/Model/Contact.php:2994 src/Module/Friendica.php:83 msgid "Blocked domain" msgstr "" -#: src/Model/Contact.php:2961 +#: src/Model/Contact.php:2999 msgid "Connect URL missing." msgstr "" -#: src/Model/Contact.php:2970 +#: src/Model/Contact.php:3008 msgid "" "The contact could not be added. Please check the relevant network " "credentials in your Settings -> Social Networks page." msgstr "" -#: src/Model/Contact.php:2988 +#: src/Model/Contact.php:3026 #, php-format msgid "Expected network %s does not match actual network %s" msgstr "" -#: src/Model/Contact.php:3005 +#: src/Model/Contact.php:3043 msgid "The profile address specified does not provide adequate information." msgstr "" -#: src/Model/Contact.php:3007 +#: src/Model/Contact.php:3045 msgid "No compatible communication protocols or feeds were discovered." msgstr "" -#: src/Model/Contact.php:3010 +#: src/Model/Contact.php:3048 msgid "An author or name was not found." msgstr "" -#: src/Model/Contact.php:3013 +#: src/Model/Contact.php:3051 msgid "No browser URL could be matched to this address." msgstr "" -#: src/Model/Contact.php:3016 +#: src/Model/Contact.php:3054 msgid "" "Unable to match @-style Identity Address with a known protocol or email " "contact." msgstr "" -#: src/Model/Contact.php:3017 +#: src/Model/Contact.php:3055 msgid "Use mailto: in front of address to force email check." msgstr "" -#: src/Model/Contact.php:3023 +#: src/Model/Contact.php:3061 msgid "" "The profile address specified belongs to a network which has been disabled " "on this site." msgstr "" -#: src/Model/Contact.php:3028 +#: src/Model/Contact.php:3066 msgid "" "Limited profile. This person will be unable to receive direct/personal " "notifications from you." msgstr "" -#: src/Model/Contact.php:3093 +#: src/Model/Contact.php:3131 msgid "Unable to retrieve contact information." msgstr "" @@ -3119,12 +3119,12 @@ msgid "l F d, Y \\@ g:i A \\G\\M\\TP (e)" msgstr "" #: 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:" msgstr "" #: 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:" msgstr "" @@ -3165,131 +3165,131 @@ msgstr "" msgid "Access to this profile has been restricted." 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." msgstr "" -#: src/Model/Event.php:637 +#: src/Model/Event.php:639 msgid "l, F j" msgstr "" -#: src/Model/Event.php:664 +#: src/Model/Event.php:666 msgid "Edit event" msgstr "" -#: src/Model/Event.php:665 +#: src/Model/Event.php:667 msgid "Duplicate event" msgstr "" -#: src/Model/Event.php:666 +#: src/Model/Event.php:668 msgid "Delete event" 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" msgstr "" -#: src/Model/Event.php:897 +#: src/Model/Event.php:899 msgid "D g:i A" msgstr "" -#: src/Model/Event.php:898 +#: src/Model/Event.php:900 msgid "g:i A" msgstr "" -#: src/Model/Event.php:959 src/Model/Event.php:961 +#: src/Model/Event.php:966 src/Model/Event.php:968 msgid "Show map" msgstr "" -#: src/Model/Event.php:960 +#: src/Model/Event.php:967 msgid "Hide map" msgstr "" -#: src/Model/Event.php:1053 +#: src/Model/Event.php:1060 #, php-format msgid "%s's birthday" msgstr "" -#: src/Model/Event.php:1054 +#: src/Model/Event.php:1061 #, php-format msgid "Happy Birthday %s" msgstr "" -#: src/Model/Item.php:2023 +#: src/Model/Item.php:2049 #, php-format msgid "Detected languages in this post:\\n%s" msgstr "" -#: src/Model/Item.php:2935 +#: src/Model/Item.php:2990 msgid "activity" msgstr "" -#: src/Model/Item.php:2937 +#: src/Model/Item.php:2992 msgid "comment" 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" msgstr "" -#: src/Model/Item.php:3109 +#: src/Model/Item.php:3165 #, php-format msgid "%s is blocked" msgstr "" -#: src/Model/Item.php:3111 +#: src/Model/Item.php:3167 #, php-format msgid "%s is ignored" msgstr "" -#: src/Model/Item.php:3113 +#: src/Model/Item.php:3169 #, php-format msgid "Content from %s is collapsed" msgstr "" -#: src/Model/Item.php:3117 +#: src/Model/Item.php:3173 #, php-format msgid "Content warning: %s" msgstr "" -#: src/Model/Item.php:3561 +#: src/Model/Item.php:3625 msgid "bytes" msgstr "" -#: src/Model/Item.php:3592 +#: src/Model/Item.php:3656 #, php-format msgid "%2$s (%3$d%%, %1$d vote)" msgid_plural "%2$s (%3$d%%, %1$d votes)" msgstr[0] "" msgstr[1] "" -#: src/Model/Item.php:3594 +#: src/Model/Item.php:3658 #, php-format msgid "%2$s (%1$d vote)" msgid_plural "%2$s (%1$d votes)" msgstr[0] "" msgstr[1] "" -#: src/Model/Item.php:3599 +#: src/Model/Item.php:3663 #, php-format msgid "%d voter. Poll end: %s" msgid_plural "%d voters. Poll end: %s" msgstr[0] "" msgstr[1] "" -#: src/Model/Item.php:3601 +#: src/Model/Item.php:3665 #, php-format msgid "%d voter." msgid_plural "%d voters." msgstr[0] "" msgstr[1] "" -#: src/Model/Item.php:3603 +#: src/Model/Item.php:3667 #, php-format msgid "Poll end: %s" 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" msgstr "" @@ -4226,7 +4226,7 @@ msgid "Policies" msgstr "" #: 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" msgstr "" @@ -5170,9 +5170,9 @@ msgstr "" #: src/Module/Admin/Summary.php:98 msgid "" -"The last update failed. Please run \"php bin/console.php dbstructure " -"update\" from the command line and have a look at the errors that might " -"appear. (Some of the errors are possibly inside the logfile.)" +"The last update failed. Please run \"php bin/console.php dbstructure update" +"\" from the command line and have a look at the errors that might appear. " +"(Some of the errors are possibly inside the logfile.)" msgstr "" #: src/Module/Admin/Summary.php:102 @@ -5333,8 +5333,8 @@ msgstr "" #, php-format msgid "" "Show some informations regarding the needed information to operate the node " -"according e.g. to EU-GDPR." +"according e.g. to EU-GDPR." msgstr "" #: src/Module/Admin/Tos.php:81 @@ -5537,7 +5537,7 @@ msgstr "" msgid "Item Source" msgstr "" -#: src/Module/BaseProfile.php:52 src/Module/Contact.php:487 +#: src/Module/BaseProfile.php:52 src/Module/Contact.php:507 msgid "Profile Details" msgstr "" @@ -5850,142 +5850,142 @@ msgid_plural "%d contacts edited." msgstr[0] "" msgstr[1] "" -#: src/Module/Contact.php:349 +#: src/Module/Contact.php:348 msgid "Show all contacts" 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 msgid "Pending" msgstr "" -#: src/Module/Contact.php:357 +#: src/Module/Contact.php:356 msgid "Only show pending contacts" 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 msgid "Blocked" msgstr "" -#: src/Module/Contact.php:365 +#: src/Module/Contact.php:364 msgid "Only show blocked contacts" msgstr "" -#: src/Module/Contact.php:370 src/Module/Contact.php:426 -#: src/Object/Post.php:351 +#: src/Module/Contact.php:369 src/Module/Contact.php:441 +#: src/Object/Post.php:360 msgid "Ignored" msgstr "" -#: src/Module/Contact.php:373 +#: src/Module/Contact.php:372 msgid "Only show ignored contacts" msgstr "" -#: src/Module/Contact.php:378 src/Module/Contact.php:427 +#: src/Module/Contact.php:377 src/Module/Contact.php:444 msgid "Collapsed" msgstr "" -#: src/Module/Contact.php:381 +#: src/Module/Contact.php:380 msgid "Only show collapsed contacts" msgstr "" -#: src/Module/Contact.php:386 src/Module/Contact.php:428 +#: src/Module/Contact.php:385 src/Module/Contact.php:447 msgid "Archived" msgstr "" -#: src/Module/Contact.php:389 +#: src/Module/Contact.php:388 msgid "Only show archived contacts" msgstr "" -#: src/Module/Contact.php:394 src/Module/Contact.php:425 +#: src/Module/Contact.php:393 src/Module/Contact.php:438 msgid "Hidden" msgstr "" -#: src/Module/Contact.php:397 +#: src/Module/Contact.php:396 msgid "Only show hidden contacts" msgstr "" -#: src/Module/Contact.php:405 +#: src/Module/Contact.php:404 msgid "Organize your contact circles" msgstr "" -#: src/Module/Contact.php:439 +#: src/Module/Contact.php:459 msgid "Search your contacts" 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 msgid "Results for: %s" msgstr "" -#: src/Module/Contact.php:447 +#: src/Module/Contact.php:467 msgid "Update" 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/Users/Blocked.php:138 #: src/Module/Moderation/Users/Index.php:154 msgid "Unblock" 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" 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" msgstr "" -#: src/Module/Contact.php:452 +#: src/Module/Contact.php:472 msgid "Batch Actions" msgstr "" -#: src/Module/Contact.php:495 +#: src/Module/Contact.php:515 msgid "Conversations started by this contact" msgstr "" -#: src/Module/Contact.php:500 +#: src/Module/Contact.php:520 msgid "Posts and Comments" msgstr "" -#: src/Module/Contact.php:503 +#: src/Module/Contact.php:523 msgid "Individual Posts and Replies" msgstr "" -#: src/Module/Contact.php:511 +#: src/Module/Contact.php:531 msgid "Posts containing media objects" msgstr "" -#: src/Module/Contact.php:518 +#: src/Module/Contact.php:539 msgid "View all known contacts" msgstr "" -#: src/Module/Contact.php:528 +#: src/Module/Contact.php:550 msgid "Advanced Contact Settings" msgstr "" -#: src/Module/Contact.php:564 +#: src/Module/Contact.php:586 msgid "Mutual Friendship" msgstr "" -#: src/Module/Contact.php:568 +#: src/Module/Contact.php:590 msgid "is a fan of yours" msgstr "" -#: src/Module/Contact.php:572 +#: src/Module/Contact.php:594 msgid "you are a fan of" msgstr "" -#: src/Module/Contact.php:590 +#: src/Module/Contact.php:612 msgid "Pending outgoing contact request" msgstr "" -#: src/Module/Contact.php:592 +#: src/Module/Contact.php:614 msgid "Pending incoming contact request" 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 msgid "Visit %s's profile [%s]" msgstr "" @@ -6556,7 +6556,7 @@ msgstr "" msgid "Posts that mention or involve you" 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" msgstr "" @@ -8421,8 +8421,8 @@ msgstr "" #: src/Module/Profile/Profile.php:158 #, php-format msgid "" -"You're currently viewing your profile as %s Cancel" +"You're currently viewing your profile as %s Cancel" msgstr "" #: src/Module/Profile/Profile.php:167 src/Module/Settings/Account.php:575 @@ -8970,8 +8970,8 @@ msgstr "" #: src/Module/Security/TwoFactor/Verify.php:100 #, php-format msgid "" -"If you do not have access to your authentication code you can use a two-factor recovery code." +"If you do not have access to your authentication code you can use a two-factor recovery code." msgstr "" #: src/Module/Security/TwoFactor/Verify.php:101 @@ -10440,8 +10440,8 @@ msgstr "" #: src/Module/Settings/TwoFactor/Verify.php:149 #, php-format msgid "" -"

Or you can open the following URL in your mobile device:

%s

" +"

Or you can open the following URL in your mobile device:

%s

" msgstr "" #: src/Module/Settings/TwoFactor/Verify.php:156 @@ -10531,9 +10531,9 @@ msgstr "" msgid "" "At any point in time a logged in user can export their account data from the " "account settings. If the user wants " -"to delete their account they can do so at %1$s/settings/removeme. The deletion of the account will be " -"permanent. Deletion of the data will also be requested from the nodes of the " +"to delete their account they can do so at " +"%1$s/settings/removeme. The deletion of the account will be permanent. " +"Deletion of the data will also be requested from the nodes of the " "communication partners." msgstr "" @@ -11259,254 +11259,254 @@ msgstr "" msgid "%s posted an update." msgstr "" -#: src/Object/Post.php:135 +#: src/Object/Post.php:139 msgid "Private Message" msgstr "" -#: src/Object/Post.php:139 +#: src/Object/Post.php:143 msgid "Public Message" msgstr "" -#: src/Object/Post.php:143 +#: src/Object/Post.php:147 msgid "Unlisted Message" msgstr "" -#: src/Object/Post.php:178 +#: src/Object/Post.php:182 msgid "This entry was edited" msgstr "" -#: src/Object/Post.php:206 +#: src/Object/Post.php:210 msgid "Connector Message" msgstr "" -#: src/Object/Post.php:222 src/Object/Post.php:224 +#: src/Object/Post.php:226 src/Object/Post.php:228 msgid "Edit" msgstr "" -#: src/Object/Post.php:248 +#: src/Object/Post.php:252 msgid "Delete globally" msgstr "" -#: src/Object/Post.php:248 +#: src/Object/Post.php:252 msgid "Remove locally" msgstr "" -#: src/Object/Post.php:266 +#: src/Object/Post.php:270 #, php-format msgid "Block %s" msgstr "" -#: src/Object/Post.php:271 +#: src/Object/Post.php:275 #, php-format msgid "Ignore %s" msgstr "" -#: src/Object/Post.php:276 +#: src/Object/Post.php:280 #, php-format msgid "Collapse %s" msgstr "" -#: src/Object/Post.php:281 +#: src/Object/Post.php:285 msgid "Save to folder" msgstr "" -#: src/Object/Post.php:316 +#: src/Object/Post.php:325 msgid "I will attend" msgstr "" -#: src/Object/Post.php:316 +#: src/Object/Post.php:325 msgid "I will not attend" msgstr "" -#: src/Object/Post.php:316 +#: src/Object/Post.php:325 msgid "I might attend" msgstr "" -#: src/Object/Post.php:346 +#: src/Object/Post.php:355 msgid "Ignore thread" msgstr "" -#: src/Object/Post.php:347 +#: src/Object/Post.php:356 msgid "Unignore thread" msgstr "" -#: src/Object/Post.php:348 +#: src/Object/Post.php:357 msgid "Toggle ignore status" msgstr "" -#: src/Object/Post.php:358 +#: src/Object/Post.php:367 msgid "Add star" msgstr "" -#: src/Object/Post.php:359 +#: src/Object/Post.php:368 msgid "Remove star" msgstr "" -#: src/Object/Post.php:360 +#: src/Object/Post.php:369 msgid "Toggle star status" msgstr "" -#: src/Object/Post.php:371 +#: src/Object/Post.php:380 msgid "Pin" msgstr "" -#: src/Object/Post.php:372 +#: src/Object/Post.php:381 msgid "Unpin" msgstr "" -#: src/Object/Post.php:373 +#: src/Object/Post.php:382 msgid "Toggle pin status" msgstr "" -#: src/Object/Post.php:376 +#: src/Object/Post.php:385 msgid "Pinned" msgstr "" -#: src/Object/Post.php:381 +#: src/Object/Post.php:390 msgid "Add tag" msgstr "" -#: src/Object/Post.php:394 +#: src/Object/Post.php:403 msgid "Quote share this" msgstr "" -#: src/Object/Post.php:394 +#: src/Object/Post.php:403 msgid "Quote Share" msgstr "" -#: src/Object/Post.php:397 +#: src/Object/Post.php:406 msgid "Reshare this" msgstr "" -#: src/Object/Post.php:397 +#: src/Object/Post.php:406 msgid "Reshare" msgstr "" -#: src/Object/Post.php:398 +#: src/Object/Post.php:407 msgid "Cancel your Reshare" msgstr "" -#: src/Object/Post.php:398 +#: src/Object/Post.php:407 msgid "Unshare" msgstr "" -#: src/Object/Post.php:449 +#: src/Object/Post.php:458 #, php-format msgid "%s (Received %s)" msgstr "" -#: src/Object/Post.php:454 +#: src/Object/Post.php:464 msgid "Comment this item on your system" msgstr "" -#: src/Object/Post.php:454 +#: src/Object/Post.php:464 msgid "Remote comment" msgstr "" -#: src/Object/Post.php:475 +#: src/Object/Post.php:486 msgid "Share via ..." msgstr "" -#: src/Object/Post.php:475 +#: src/Object/Post.php:486 msgid "Share via external services" msgstr "" -#: src/Object/Post.php:504 +#: src/Object/Post.php:515 msgid "to" msgstr "" -#: src/Object/Post.php:505 +#: src/Object/Post.php:516 msgid "via" msgstr "" -#: src/Object/Post.php:506 +#: src/Object/Post.php:517 msgid "Wall-to-Wall" msgstr "" -#: src/Object/Post.php:507 +#: src/Object/Post.php:518 msgid "via Wall-To-Wall:" msgstr "" -#: src/Object/Post.php:552 +#: src/Object/Post.php:563 #, php-format msgid "Reply to %s" msgstr "" -#: src/Object/Post.php:555 +#: src/Object/Post.php:566 msgid "More" msgstr "" -#: src/Object/Post.php:573 +#: src/Object/Post.php:584 msgid "Notifier task is pending" msgstr "" -#: src/Object/Post.php:574 +#: src/Object/Post.php:585 msgid "Delivery to remote servers is pending" msgstr "" -#: src/Object/Post.php:575 +#: src/Object/Post.php:586 msgid "Delivery to remote servers is underway" msgstr "" -#: src/Object/Post.php:576 +#: src/Object/Post.php:587 msgid "Delivery to remote servers is mostly done" msgstr "" -#: src/Object/Post.php:577 +#: src/Object/Post.php:588 msgid "Delivery to remote servers is done" msgstr "" -#: src/Object/Post.php:597 +#: src/Object/Post.php:608 #, php-format msgid "%d comment" msgid_plural "%d comments" msgstr[0] "" msgstr[1] "" -#: src/Object/Post.php:598 +#: src/Object/Post.php:609 msgid "Show more" msgstr "" -#: src/Object/Post.php:599 +#: src/Object/Post.php:610 msgid "Show fewer" msgstr "" -#: src/Object/Post.php:635 +#: src/Object/Post.php:646 #, php-format msgid "Reshared by: %s" msgstr "" -#: src/Object/Post.php:640 +#: src/Object/Post.php:651 #, php-format msgid "Viewed by: %s" msgstr "" -#: src/Object/Post.php:645 +#: src/Object/Post.php:656 #, php-format msgid "Liked by: %s" msgstr "" -#: src/Object/Post.php:650 +#: src/Object/Post.php:661 #, php-format msgid "Disliked by: %s" msgstr "" -#: src/Object/Post.php:655 +#: src/Object/Post.php:666 #, php-format msgid "Attended by: %s" msgstr "" -#: src/Object/Post.php:660 +#: src/Object/Post.php:671 #, php-format msgid "Maybe attended by: %s" msgstr "" -#: src/Object/Post.php:665 +#: src/Object/Post.php:676 #, php-format msgid "Not attended by: %s" msgstr "" -#: src/Object/Post.php:670 +#: src/Object/Post.php:681 #, php-format msgid "Reacted with %s by: %s" msgstr ""