Stop generating photo URLs with contact IDs in favor of guids

- This prevents an enumeration attack against avatar pictures
- Replace several DB calls to `contact` table with `account-view` or `account-user-view`
- Remove unused parameter photo_size from Directory::formatEntry
This commit is contained in:
Hypolite Petovan 2024-02-23 16:56:41 -05:00
parent ac087749e3
commit 1e4e5b57e3
27 changed files with 286 additions and 296 deletions

View file

@ -308,16 +308,15 @@ function message_content(App $a)
$body_e = BBCode::convertForUriId($message['uri-id'], $message['body']); $body_e = BBCode::convertForUriId($message['uri-id'], $message['body']);
$to_name_e = $message['name']; $to_name_e = $message['name'];
$contact = Contact::getByURL($message['from-url'], false, ['thumb', 'addr', 'id', 'avatar', 'url']); $account = Contact::selectFirstAccount(['thumb', 'addr', 'id', 'avatar', 'url', 'guid', 'updated'], ['url' => $message['from-url']]);
$from_photo = Contact::getThumb($contact);
$mails[] = [ $mails[] = [
'id' => $message['id'], 'id' => $message['id'],
'from_name' => $from_name_e, 'from_name' => $from_name_e,
'from_url' => $from_url, 'from_url' => $from_url,
'from_addr' => $contact['addr'] ?? $from_url, 'from_addr' => $account['addr'] ?? $from_url,
'sparkle' => $sparkle, 'sparkle' => $sparkle,
'from_photo' => $from_photo, 'from_photo' => Contact::getThumb($account),
'subject' => $subject_e, 'subject' => $subject_e,
'body' => $body_e, 'body' => $body_e,
'delete' => DI::l10n()->t('Delete message'), 'delete' => DI::l10n()->t('Delete message'),
@ -439,16 +438,15 @@ function render_messages(array $msg, string $t): string
continue; continue;
} }
$contact = Contact::getByURL($rr['url'], false, ['thumb', 'addr', 'id', 'avatar', 'url']); $account = Contact::selectFirstAccount(['thumb', 'addr', 'id', 'avatar', 'url', 'guid', 'updated'], ['url' => $rr['url']]);
$from_photo = Contact::getThumb($contact);
$rslt .= Renderer::replaceMacros($tpl, [ $rslt .= Renderer::replaceMacros($tpl, [
'$id' => $rr['id'], '$id' => $rr['id'],
'$from_name' => $participants, '$from_name' => $participants,
'$from_url' => Contact::magicLink($rr['url']), '$from_url' => Contact::magicLink($rr['url']),
'$from_addr' => $contact['addr'] ?? '', '$from_addr' => $account['addr'] ?? '',
'$sparkle' => ' sparkle', '$sparkle' => ' sparkle',
'$from_photo' => $from_photo, '$from_photo' => Contact::getThumb($account),
'$subject' => $rr['title'], '$subject' => $rr['title'],
'$delete' => DI::l10n()->t('Delete conversation'), '$delete' => DI::l10n()->t('Delete conversation'),
'$body' => $body_e, '$body' => $body_e,

View file

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

View file

@ -590,18 +590,18 @@ class Item
public function getAuthorAvatar(array $item): string public function getAuthorAvatar(array $item): string
{ {
if (in_array($item['network'], [Protocol::FEED, Protocol::MAIL])) { if (in_array($item['network'], [Protocol::FEED, Protocol::MAIL])) {
$author_avatar = $item['contact-id']; $author_id = $item['contact-id'];
$author_updated = ''; $author_updated = '';
$author_thumb = $item['contact-avatar']; $author_thumb = $item['contact-avatar'];
} else { } else {
$author_avatar = $item['author-id']; $author_id = $item['author-id'];
$author_updated = $item['author-updated']; $author_updated = $item['author-updated'];
$author_thumb = $item['author-avatar']; $author_thumb = $item['author-avatar'];
} }
if (empty($author_thumb) || Photo::isPhotoURI($author_thumb)) { if (empty($author_thumb) || Photo::isPhotoURI($author_thumb)) {
$author_thumb = Contact::getAvatarUrlForId($author_avatar, Proxy::SIZE_THUMB, $author_updated); $author = Contact::selectFirstAccount(['guid', 'updated'], ['id' => $author_id]);
$author_thumb = Contact::getAvatarUrlForId($author['guid'], $author['updated'] ?? $author_updated, Proxy::SIZE_THUMB);
} }
return $author_thumb; return $author_thumb;
@ -610,17 +610,18 @@ class Item
public function getOwnerAvatar(array $item): string public function getOwnerAvatar(array $item): string
{ {
if (in_array($item['network'], [Protocol::FEED, Protocol::MAIL])) { if (in_array($item['network'], [Protocol::FEED, Protocol::MAIL])) {
$owner_avatar = $item['contact-id']; $owner_id = $item['contact-id'];
$owner_updated = ''; $owner_updated = '';
$owner_thumb = $item['contact-avatar']; $owner_thumb = $item['contact-avatar'];
} else { } else {
$owner_avatar = $item['owner-id']; $owner_id = $item['owner-id'];
$owner_updated = $item['owner-updated']; $owner_updated = $item['owner-updated'];
$owner_thumb = $item['owner-avatar']; $owner_thumb = $item['owner-avatar'];
} }
if (empty($owner_thumb) || Photo::isPhotoURI($owner_thumb)) { if (empty($owner_thumb) || Photo::isPhotoURI($owner_thumb)) {
$owner_thumb = Contact::getAvatarUrlForId($owner_avatar, Proxy::SIZE_THUMB, $owner_updated); $owner = Contact::selectFirstAccount(['guid', 'updated'], ['id' => $owner_id]);
$owner_thumb = Contact::getAvatarUrlForId($owner['guid'], $owner['updated'] ?? $owner_updated, Proxy::SIZE_THUMB);
} }
return $owner_thumb; return $owner_thumb;
@ -1078,7 +1079,7 @@ class Item
$to_author = DBA::selectFirst('account-view', ['ap-followers'], ['id' => $to['author-id']]); $to_author = DBA::selectFirst('account-view', ['ap-followers'], ['id' => $to['author-id']]);
$parent = Post::selectFirstPost(['author-id'], ['uri-id' => $parentUriId]); $parent = Post::selectFirstPost(['author-id'], ['uri-id' => $parentUriId]);
$parent_author = DBA::selectFirst('account-view', ['ap-followers'], ['id' => $parent['author-id']]); $parent_author = DBA::selectFirst('account-view', ['ap-followers'], ['id' => $parent['author-id']]);
$followers = ''; $followers = '';
foreach (array_column(Tag::getByURIId($parentUriId, [Tag::TO, Tag::CC, Tag::BCC]), 'url') as $url) { foreach (array_column(Tag::getByURIId($parentUriId, [Tag::TO, Tag::CC, Tag::BCC]), 'url') as $url) {
if ($url == $parent_author['ap-followers']) { if ($url == $parent_author['ap-followers']) {

View file

@ -234,10 +234,10 @@ class Nav
$nav['usermenu'][] = ['notes/', $this->l10n->t('Personal notes'), '', $this->l10n->t('Your personal notes')]; $nav['usermenu'][] = ['notes/', $this->l10n->t('Personal notes'), '', $this->l10n->t('Your personal notes')];
// user info // user info
$contact = $this->database->selectFirst('contact', ['id', 'url', 'avatar', 'micro', 'name', 'nick', 'baseurl', 'updated'], ['uid' => $this->session->getLocalUserId(), 'self' => true]); $account = Contact::selectFirstAccountUser(['id', 'url', 'guid', 'avatar', 'micro', 'name', 'nick', 'baseurl', 'updated'], ['uid' => $this->session->getLocalUserId(), 'self' => true]);
$userinfo = [ $userinfo = [
'icon' => Contact::getMicro($contact), 'icon' => Contact::getMicro($account),
'name' => $contact['name'], 'name' => $account['name'],
]; ];
} }

View file

@ -875,23 +875,23 @@ class BBCode
function ($match) use ($callback, $uriid) { function ($match) use ($callback, $uriid) {
$attributes = self::extractShareAttributes($match[2]); $attributes = self::extractShareAttributes($match[2]);
$author_contact = Contact::getByURL($attributes['profile'], false, ['id', 'url', 'addr', 'name', 'micro']); $author_account = Contact::selectFirstAccount(['id', 'url', 'addr', 'name', 'micro', 'guid', 'updated'], ['url' => $attributes['profile']]);
$author_contact['url'] = ($author_contact['url'] ?? $attributes['profile']); $author_account['url'] = ($author_account['url'] ?? $attributes['profile']);
$author_contact['addr'] = ($author_contact['addr'] ?? ''); $author_account['addr'] = ($author_account['addr'] ?? '');
$attributes['author'] = ($author_contact['name'] ?? '') ?: $attributes['author']; $attributes['author'] = ($author_account['name'] ?? '') ?: $attributes['author'];
$attributes['avatar'] = ($author_contact['micro'] ?? '') ?: $attributes['avatar']; $attributes['avatar'] = ($author_account['micro'] ?? '') ?: $attributes['avatar'];
$attributes['profile'] = ($author_contact['url'] ?? '') ?: $attributes['profile']; $attributes['profile'] = ($author_account['url'] ?? '') ?: $attributes['profile'];
if (!empty($author_contact['id'])) { if (!empty($author_account['id'])) {
$attributes['avatar'] = Contact::getAvatarUrlForId($author_contact['id'], Proxy::SIZE_THUMB); $attributes['avatar'] = Contact::getAvatarUrlForId($author_account['guid'], $author_account['updated'], Proxy::SIZE_THUMB);
} elseif ($attributes['avatar']) { } elseif ($attributes['avatar']) {
$attributes['avatar'] = self::proxyUrl($attributes['avatar'], self::INTERNAL, $uriid, Proxy::SIZE_THUMB); $attributes['avatar'] = self::proxyUrl($attributes['avatar'], self::INTERNAL, $uriid, Proxy::SIZE_THUMB);
} }
$content = preg_replace(Strings::autoLinkRegEx(), '<a href="$1">$1</a>', $match[3]); $content = preg_replace(Strings::autoLinkRegEx(), '<a href="$1">$1</a>', $match[3]);
return $match[1] . $callback($attributes, $author_contact, $content, trim($match[1]) != ''); return $match[1] . $callback($attributes, $author_account, $content, trim($match[1]) != '');
}, },
$text $text
); );

View file

@ -104,19 +104,18 @@ class ContactBlock
$contact_uriids = array_column($personal_contacts, 'uri-id'); $contact_uriids = array_column($personal_contacts, 'uri-id');
if (!empty($contact_uriids)) { if (!empty($contact_uriids)) {
$contacts_stmt = DBA::select('contact', ['id', 'uid', 'addr', 'url', 'alias', 'name', 'thumb', 'avatar', 'network'], ['uri-id' => $contact_uriids, 'uid' => $contact_uid]); $accounts_stmt = DBA::select('account-user-view', ['id', 'uid', 'addr', 'url', 'alias', 'name', 'thumb', 'avatar', 'network', 'guid', 'updated'], ['uri-id' => $contact_uriids, 'uid' => $contact_uid]);
if (DBA::isResult($contacts_stmt)) { if (DBA::isResult($accounts_stmt)) {
$contacts_title = DI::l10n()->tt('%d Contact', '%d Contacts', $total); $contacts_title = DI::l10n()->tt('%d Contact', '%d Contacts', $total);
$micropro = [];
while ($contact = DBA::fetch($contacts_stmt)) { while ($account = DBA::fetch($accounts_stmt)) {
$contacts[] = $contact; $contacts[] = $account;
$micropro[] = HTML::micropro($contact, true, 'mpfriend'); $micropro[] = HTML::micropro($account, true, 'mpfriend');
} }
} }
DBA::close($contacts_stmt); DBA::close($accounts_stmt);
} }
} }

View file

@ -41,21 +41,21 @@ class VCard
* Get HTML for vcard block * Get HTML for vcard block
* *
* @template widget/vcard.tpl * @template widget/vcard.tpl
* @param array $contact * @param array $account Account array (from account-* view)
* @param bool $hide_mention * @param bool $hide_mention
* @return string * @return string
*/ */
public static function getHTML(array $contact, bool $hide_mention = false): string public static function getHTML(array $account, bool $hide_mention = false): string
{ {
if (!isset($contact['network']) || !isset($contact['id'])) { if (!isset($account['network']) || !isset($account['id'])) {
Logger::warning('Incomplete contact', ['contact' => $contact ?? []]); Logger::warning('Incomplete contact', ['contact' => $account ?? []]);
} }
$contact_url = Contact::getProfileLink($contact); $contact_url = Contact::getProfileLink($account);
if ($contact['network'] != '') { if ($account['network'] != '') {
$network_link = Strings::formatNetworkName($contact['network'], $contact_url); $network_link = Strings::formatNetworkName($account['network'], $contact_url);
$network_avatar = ContactSelector::networkToIcon($contact['network'], $contact_url); $network_avatar = ContactSelector::networkToIcon($account['network'], $contact_url);
} else { } else {
$network_link = ''; $network_link = '';
$network_avatar = ''; $network_avatar = '';
@ -68,15 +68,15 @@ class VCard
$mention_link = ''; $mention_link = '';
$showgroup_link = ''; $showgroup_link = '';
$photo = Contact::getPhoto($contact); $photo = Contact::getPhoto($account);
if (DI::userSession()->getLocalUserId()) { if (DI::userSession()->getLocalUserId()) {
if ($contact['uid']) { if ($account['uid']) {
$id = $contact['id']; $id = $account['id'];
$rel = $contact['rel']; $rel = $account['rel'];
$pending = $contact['pending']; $pending = $account['pending'];
} else { } else {
$pcontact = Contact::selectFirst([], ['uid' => DI::userSession()->getLocalUserId(), 'uri-id' => $contact['uri-id'], 'deleted' => false]); $pcontact = Contact::selectFirst([], ['uid' => DI::userSession()->getLocalUserId(), 'uri-id' => $account['uri-id'], 'deleted' => false]);
$id = $pcontact['id'] ?? 0; $id = $pcontact['id'] ?? 0;
$rel = $pcontact['rel'] ?? Contact::NOTHING; $rel = $pcontact['rel'] ?? Contact::NOTHING;
@ -87,7 +87,7 @@ class VCard
} }
} }
if (empty($contact['self']) && Protocol::supportsFollow($contact['network'])) { if (empty($account['self']) && Protocol::supportsFollow($account['network'])) {
if (in_array($rel, [Contact::SHARING, Contact::FRIEND])) { if (in_array($rel, [Contact::SHARING, Contact::FRIEND])) {
$unfollow_link = 'contact/unfollow?url=' . urlencode($contact_url) . '&auto=1'; $unfollow_link = 'contact/unfollow?url=' . urlencode($contact_url) . '&auto=1';
} elseif (!$pending) { } elseif (!$pending) {
@ -95,34 +95,34 @@ class VCard
} }
} }
if (in_array($rel, [Contact::FOLLOWER, Contact::FRIEND]) && Contact::canReceivePrivateMessages($contact)) { if (in_array($rel, [Contact::FOLLOWER, Contact::FRIEND]) && Contact::canReceivePrivateMessages($account)) {
$wallmessage_link = 'message/new/' . $id; $wallmessage_link = 'message/new/' . $id;
} }
if ($contact['contact-type'] == Contact::TYPE_COMMUNITY) { if ($account['contact-type'] == Contact::TYPE_COMMUNITY) {
if (!$hide_mention) { if (!$hide_mention) {
$mention_label = DI::l10n()->t('Post to group'); $mention_label = DI::l10n()->t('Post to group');
$mention_link = 'compose/0?body=!' . $contact['addr']; $mention_link = 'compose/0?body=!' . $account['addr'];
} }
$showgroup_link = 'network/group/' . $id; $showgroup_link = 'network/group/' . $id;
} elseif (!$hide_mention) { } elseif (!$hide_mention) {
$mention_label = DI::l10n()->t('Mention'); $mention_label = DI::l10n()->t('Mention');
$mention_link = 'compose/0?body=@' . $contact['addr']; $mention_link = 'compose/0?body=@' . $account['addr'];
} }
} }
return Renderer::replaceMacros(Renderer::getMarkupTemplate('widget/vcard.tpl'), [ return Renderer::replaceMacros(Renderer::getMarkupTemplate('widget/vcard.tpl'), [
'$contact' => $contact, '$contact' => $account,
'$photo' => $photo, '$photo' => $photo,
'$url' => Contact::magicLinkByContact($contact, $contact_url), '$url' => Contact::magicLinkByContact($account, $contact_url),
'$about' => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['about'] ?? ''), '$about' => BBCode::convertForUriId($account['uri-id'] ?? 0, $account['about'] ?? ''),
'$xmpp' => DI::l10n()->t('XMPP:'), '$xmpp' => DI::l10n()->t('XMPP:'),
'$matrix' => DI::l10n()->t('Matrix:'), '$matrix' => DI::l10n()->t('Matrix:'),
'$location' => DI::l10n()->t('Location:'), '$location' => DI::l10n()->t('Location:'),
'$network_link' => $network_link, '$network_link' => $network_link,
'$network_avatar' => $network_avatar, '$network_avatar' => $network_avatar,
'$network' => DI::l10n()->t('Network:'), '$network' => DI::l10n()->t('Network:'),
'$account_type' => Contact::getAccountType($contact['contact-type']), '$account_type' => Contact::getAccountType($account['contact-type']),
'$follow' => DI::l10n()->t('Follow'), '$follow' => DI::l10n()->t('Follow'),
'$follow_link' => $follow_link, '$follow_link' => $follow_link,
'$unfollow' => DI::l10n()->t('Unfollow'), '$unfollow' => DI::l10n()->t('Unfollow'),

View file

@ -81,7 +81,7 @@ class Account extends BaseFactory
*/ */
public function createFromUriId(int $contactUriId, int $uid = 0): \Friendica\Object\Api\Mastodon\Account public function createFromUriId(int $contactUriId, int $uid = 0): \Friendica\Object\Api\Mastodon\Account
{ {
$account = DBA::selectFirst('account-user-view', [], ['uri-id' => $contactUriId, 'uid' => [0, $uid]], ['order' => ['id' => true]]); $account = Contact::selectFirstAccountUser([], ['uri-id' => $contactUriId, 'uid' => [0, $uid]], ['order' => ['id' => true]]);
if (empty($account)) { if (empty($account)) {
throw new HTTPException\NotFoundException('Contact ' . $contactUriId . ' not found'); throw new HTTPException\NotFoundException('Contact ' . $contactUriId . ' not found');
} }

View file

@ -56,27 +56,27 @@ class User extends BaseFactory
{ {
$cdata = Contact::getPublicAndUserContactID($contactId, $uid); $cdata = Contact::getPublicAndUserContactID($contactId, $uid);
if (!empty($cdata)) { if (!empty($cdata)) {
$publicContact = Contact::getById($cdata['public']); $publicAccount = Contact::selectFirstAccount([], ['id' => $cdata['public']]);
$userContact = Contact::getById($cdata['user']); $userContact = Contact::getById($cdata['user']);
} else { } else {
$publicContact = Contact::getById($contactId); $publicAccount = Contact::selectFirstAccount([], ['id' => $contactId]);
$userContact = []; $userContact = [];
} }
$apcontact = APContact::getByURL($publicContact['url'], false); $apcontact = APContact::getByURL($publicAccount['url'], false);
$status = null; $status = null;
if (!$skip_status) { if (!$skip_status) {
$post = Post::selectFirstPost(['uri-id'], $post = Post::selectFirstPost(['uri-id'],
['author-id' => $publicContact['id'], 'gravity' => [Item::GRAVITY_COMMENT, Item::GRAVITY_PARENT], 'private' => [Item::PUBLIC, Item::UNLISTED]], ['author-id' => $publicAccount['id'], 'gravity' => [Item::GRAVITY_COMMENT, Item::GRAVITY_PARENT], 'private' => [Item::PUBLIC, Item::UNLISTED]],
['order' => ['uri-id' => true]]); ['order' => ['uri-id' => true]]);
if (!empty($post['uri-id'])) { if (!empty($post['uri-id'])) {
$status = $this->status->createFromUriId($post['uri-id'], $uid)->toArray(); $status = $this->status->createFromUriId($post['uri-id'], $uid)->toArray();
} }
} }
return new \Friendica\Object\Api\Twitter\User($publicContact, $apcontact, $userContact, $status, $include_user_entities); return new \Friendica\Object\Api\Twitter\User($publicAccount, $apcontact, $userContact, $status, $include_user_entities);
} }
/** /**

View file

@ -1818,72 +1818,72 @@ class Contact
/** /**
* Return the photo path for a given contact array in the given size * Return the photo path for a given contact array in the given size
* *
* @param array $contact contact array * @param array $account Account array (from account-* view)
* @param string $size Size of the avatar picture * @param string $size Size of the avatar picture
* @param bool $no_update Don't perform an update if no cached avatar was found * @param bool $no_update Don't perform an update if no cached avatar was found
* @return string photo path * @return string photo path
*/ */
private static function getAvatarPath(array $contact, string $size, bool $no_update = false): string private static function getAvatarPath(array $account, string $size, bool $no_update = false): string
{ {
$contact = self::checkAvatarCacheByArray($contact, $no_update); $account = self::checkAvatarCacheByArray($account, $no_update);
if (DI::config()->get('system', 'avatar_cache')) { if (DI::config()->get('system', 'avatar_cache')) {
switch ($size) { switch ($size) {
case Proxy::SIZE_MICRO: case Proxy::SIZE_MICRO:
if (!empty($contact['micro']) && !Photo::isPhotoURI($contact['micro'])) { if (!empty($account['micro']) && !Photo::isPhotoURI($account['micro'])) {
return $contact['micro']; return $account['micro'];
} }
break; break;
case Proxy::SIZE_THUMB: case Proxy::SIZE_THUMB:
if (!empty($contact['thumb']) && !Photo::isPhotoURI($contact['thumb'])) { if (!empty($account['thumb']) && !Photo::isPhotoURI($account['thumb'])) {
return $contact['thumb']; return $account['thumb'];
} }
break; break;
case Proxy::SIZE_SMALL: case Proxy::SIZE_SMALL:
if (!empty($contact['photo']) && !Photo::isPhotoURI($contact['photo'])) { if (!empty($account['photo']) && !Photo::isPhotoURI($account['photo'])) {
return $contact['photo']; return $account['photo'];
} }
break; break;
} }
} }
return self::getAvatarUrlForId($contact['id'] ?? 0, $size, $contact['updated'] ?? ''); return self::getAvatarUrlForId($account['guid'], $account['updated'], $size);
} }
/** /**
* Return the photo path for a given contact array * Return the photo path for a given contact array
* *
* @param array $contact Contact array * @param array $account Account array (from account-* view)
* @param bool $no_update Don't perform an update if no cached avatar was found * @param bool $no_update Don't perform an update if no cached avatar was found
* @return string photo path * @return string photo path
*/ */
public static function getPhoto(array $contact, bool $no_update = false): string public static function getPhoto(array $account, bool $no_update = false): string
{ {
return self::getAvatarPath($contact, Proxy::SIZE_SMALL, $no_update); return self::getAvatarPath($account, Proxy::SIZE_SMALL, $no_update);
} }
/** /**
* Return the photo path (thumb size) for a given contact array * Return the photo path (thumb size) for a given contact array
* *
* @param array $contact Contact array * @param array $account Account array (from account-* view)
* @param bool $no_update Don't perform an update if no cached avatar was found * @param bool $no_update Don't perform an update if no cached avatar was found
* @return string photo path * @return string photo path
*/ */
public static function getThumb(array $contact, bool $no_update = false): string public static function getThumb(array $account, bool $no_update = false): string
{ {
return self::getAvatarPath($contact, Proxy::SIZE_THUMB, $no_update); return self::getAvatarPath($account, Proxy::SIZE_THUMB, $no_update);
} }
/** /**
* Return the photo path (micro size) for a given contact array * Return the photo path (micro size) for a given contact array
* *
* @param array $contact Contact array * @param array $account Account array (from account-* view)
* @param bool $no_update Don't perform an update if no cached avatar was found * @param bool $no_update Don't perform an update if no cached avatar was found
* @return string photo path * @return string photo path
*/ */
public static function getMicro(array $contact, bool $no_update = false): string public static function getMicro(array $account, bool $no_update = false): string
{ {
return self::getAvatarPath($contact, Proxy::SIZE_MICRO, $no_update); return self::getAvatarPath($account, Proxy::SIZE_MICRO, $no_update);
} }
/** /**
@ -2148,24 +2148,16 @@ class Contact
} }
/** /**
* Get avatar link for given contact id * Get avatar link for given contact guid
* *
* @param integer $cid contact id * @param string $guid Contact guid (from account-* views)
* @param string $size One of the Proxy::SIZE_* constants
* @param string $updated Contact update date * @param string $updated Contact update date
* @param ?string $size One of the Proxy::SIZE_* constants or empty for original size
* @param bool $static If "true" a parameter is added to convert the avatar to a static one * @param bool $static If "true" a parameter is added to convert the avatar to a static one
* @return string avatar link * @return string avatar link
*/ */
public static function getAvatarUrlForId(int $cid, string $size = '', string $updated = '', string $guid = '', bool $static = false): string public static function getAvatarUrlForId(string $guid, string $updated, string $size = null, bool $static = false): string
{ {
// We have to fetch the "updated" variable when it wasn't provided
// The parameter can be provided to improve performance
if (empty($updated)) {
$account = DBA::selectFirst('account-user-view', ['updated', 'guid'], ['id' => $cid]);
$updated = $account['updated'] ?? '';
$guid = $account['guid'] ?? '';
}
$guid = urlencode($guid); $guid = urlencode($guid);
$url = DI::baseUrl() . '/photo/contact/'; $url = DI::baseUrl() . '/photo/contact/';
@ -2194,7 +2186,7 @@ class Contact
$query_params['static'] = true; $query_params['static'] = true;
} }
return $url . ($guid ?: $cid) . (!empty($query_params) ? '?' . http_build_query($query_params) : ''); return $url . $guid . (!empty($query_params) ? '?' . http_build_query($query_params) : '');
} }
/** /**
@ -2211,29 +2203,21 @@ class Contact
"`nurl` = ? AND ((`uid` = ? AND `network` IN (?, ?)) OR `uid` = ?)", "`nurl` = ? AND ((`uid` = ? AND `network` IN (?, ?)) OR `uid` = ?)",
Strings::normaliseLink($url), $uid, Protocol::FEED, Protocol::MAIL, 0 Strings::normaliseLink($url), $uid, Protocol::FEED, Protocol::MAIL, 0
]; ];
$contact = self::selectFirst(['id', 'updated'], $condition, ['order' => ['uid' => true]]); $account = self::selectFirstAccountUser(['id', 'guid', 'updated'], $condition, ['order' => ['uid' => true]]);
return self::getAvatarUrlForId($contact['id'] ?? 0, $size, $contact['updated'] ?? ''); return self::getAvatarUrlForId($account['guid'] ?? '', $account['updated'] ?? DBA::NULL_DATETIME, $size);
} }
/** /**
* Get header link for given contact id * Get header link for given contact guid
* *
* @param integer $cid contact id * @param string $guid Contact guid (from account-* views)
* @param string $size One of the Proxy::SIZE_* constants
* @param string $updated Contact update date * @param string $updated Contact update date
* @param ?string $size One of the Proxy::SIZE_* constants or empty for original size
* @param bool $static If "true" a parameter is added to convert the header to a static one * @param bool $static If "true" a parameter is added to convert the header to a static one
* @return string header link * @return string header link
*/ */
public static function getHeaderUrlForId(int $cid, string $size = '', string $updated = '', string $guid = '', bool $static = false): string public static function getHeaderUrlForId(string $guid, string $updated, string $size = null, bool $static = false): string
{ {
// We have to fetch the "updated" variable when it wasn't provided
// The parameter can be provided to improve performance
if (empty($updated) || empty($guid)) {
$account = DBA::selectFirst('account-user-view', ['updated', 'guid'], ['id' => $cid]);
$updated = $account['updated'] ?? '';
$guid = $account['guid'] ?? '';
}
$guid = urlencode($guid); $guid = urlencode($guid);
$url = DI::baseUrl() . '/photo/header/'; $url = DI::baseUrl() . '/photo/header/';
@ -2263,7 +2247,7 @@ class Contact
$query_params['static'] = true; $query_params['static'] = true;
} }
return $url . ($guid ?: $cid) . (!empty($query_params) ? '?' . http_build_query($query_params) : ''); return $url . $guid . (!empty($query_params) ? '?' . http_build_query($query_params) : '');
} }
/** /**

View file

@ -308,12 +308,12 @@ class Profile
$profile_url = $profile['url']; $profile_url = $profile['url'];
$contact = Contact::selectFirst(['id'], ['uri-id' => $profile['uri-id'], 'uid' => 0]); $account = Contact::selectFirstAccount(['id', 'guid', 'updated'], ['uri-id' => $profile['uri-id']]);
if (!$contact) { if (!$account) {
return $o; return $o;
} }
$cid = $contact['id']; $cid = $account['id'];
$follow_link = null; $follow_link = null;
$unfollow_link = null; $unfollow_link = null;
@ -449,7 +449,7 @@ class Profile
$p['address'] = BBCode::convertForUriId($profile['uri-id'] ?? 0, $p['address']); $p['address'] = BBCode::convertForUriId($profile['uri-id'] ?? 0, $p['address']);
} }
$p['photo'] = Contact::getAvatarUrlForId($cid, Proxy::SIZE_SMALL); $p['photo'] = Contact::getAvatarUrlForId($account['guid'], $account['updated'], Proxy::SIZE_SMALL);
$p['url'] = Contact::magicLinkById($cid, $profile['url']); $p['url'] = Contact::magicLinkById($cid, $profile['url']);

View file

@ -104,37 +104,37 @@ class Advanced extends BaseModule
{ {
$cid = $this->parameters['id']; $cid = $this->parameters['id'];
$contact = Model\Contact::selectFirst([], ['id' => $cid, 'uid' => DI::userSession()->getLocalUserId()]); $account = Model\Contact::selectFirstAccountUser([], ['id' => $cid, 'uid' => DI::userSession()->getLocalUserId()]);
if (empty($contact)) { if (empty($account)) {
throw new BadRequestException($this->t('Contact not found.')); throw new BadRequestException($this->t('Contact not found.'));
} }
$this->page['aside'] = Widget\VCard::getHTML($contact); $this->page['aside'] = Widget\VCard::getHTML($account);
$returnaddr = "contact/$cid"; $returnaddr = "contact/$cid";
// This data is fetched automatically for most networks. // This data is fetched automatically for most networks.
// Editing does only makes sense for mail and feed contacts. // Editing does only makes sense for mail and feed contacts.
if (!in_array($contact['network'], [Protocol::FEED, Protocol::MAIL])) { if (!in_array($account['network'], [Protocol::FEED, Protocol::MAIL])) {
$readonly = 'readonly'; $readonly = 'readonly';
} else { } else {
$readonly = ''; $readonly = '';
} }
$tab_str = Contact::getTabsHTML($contact, Contact::TAB_ADVANCED); $tab_str = Contact::getTabsHTML($account, Contact::TAB_ADVANCED);
$tpl = Renderer::getMarkupTemplate('contact/advanced.tpl'); $tpl = Renderer::getMarkupTemplate('contact/advanced.tpl');
return Renderer::replaceMacros($tpl, [ return Renderer::replaceMacros($tpl, [
'$tab_str' => $tab_str, '$tab_str' => $tab_str,
'$returnaddr' => $returnaddr, '$returnaddr' => $returnaddr,
'$return' => $this->t('Return to contact editor'), '$return' => $this->t('Return to contact editor'),
'$contact_id' => $contact['id'], '$contact_id' => $account['id'],
'$lbl_submit' => $this->t('Submit'), '$lbl_submit' => $this->t('Submit'),
'$name' => ['name', $this->t('Name'), $contact['name'], '', '', $readonly], '$name' => ['name', $this->t('Name'), $account['name'], '', '', $readonly],
'$nick' => ['nick', $this->t('Account Nickname'), $contact['nick'], '', '', 'readonly'], '$nick' => ['nick', $this->t('Account Nickname'), $account['nick'], '', '', 'readonly'],
'$url' => ['url', $this->t('Account URL'), $contact['url'], '', '', 'readonly'], '$url' => ['url', $this->t('Account URL'), $account['url'], '', '', 'readonly'],
'poll' => ['poll', $this->t('Poll/Feed URL'), $contact['poll'], '', '', ($contact['network'] == Protocol::FEED) ? '' : 'readonly'], 'poll' => ['poll', $this->t('Poll/Feed URL'), $account['poll'], '', '', ($account['network'] == Protocol::FEED) ? '' : 'readonly'],
'photo' => ['photo', $this->t('New photo from this URL'), '', '', '', $readonly], 'photo' => ['photo', $this->t('New photo from this URL'), '', '', '', $readonly],
]); ]);
} }

View file

@ -66,14 +66,14 @@ class Contacts extends BaseModule
throw new HTTPException\BadRequestException($this->t('Invalid contact.')); throw new HTTPException\BadRequestException($this->t('Invalid contact.'));
} }
$contact = Model\Contact::getById($cid, []); $account = Model\Contact::selectFirstAccount([], ['id' => $cid]);
if (empty($contact)) { if (empty($account)) {
throw new HTTPException\NotFoundException($this->t('Contact not found.')); throw new HTTPException\NotFoundException($this->t('Contact not found.'));
} }
$localContactId = Model\Contact::getPublicIdByUserId($this->userSession->getLocalUserId()); $localContactId = Model\Contact::getPublicIdByUserId($this->userSession->getLocalUserId());
$this->page['aside'] = Widget\VCard::getHTML($contact); $this->page['aside'] = Widget\VCard::getHTML($account);
$condition = [ $condition = [
'blocked' => false, 'blocked' => false,
@ -123,7 +123,7 @@ class Contacts extends BaseModule
$title = $this->tt('Mutual friend (%s)', 'Mutual friends (%s)', $total); $title = $this->tt('Mutual friend (%s)', 'Mutual friends (%s)', $total);
$desc = $this->t( $desc = $this->t(
'These contacts both follow and are followed by <strong>%s</strong>.', 'These contacts both follow and are followed by <strong>%s</strong>.',
htmlentities($contact['name'], ENT_COMPAT, 'UTF-8') htmlentities($account['name'], ENT_COMPAT, 'UTF-8')
); );
break; break;
case 'common': case 'common':
@ -131,7 +131,7 @@ class Contacts extends BaseModule
$title = $this->tt('Common contact (%s)', 'Common contacts (%s)', $total); $title = $this->tt('Common contact (%s)', 'Common contacts (%s)', $total);
$desc = $this->t( $desc = $this->t(
'Both <strong>%s</strong> and yourself have publicly interacted with these contacts (follow, comment or likes on public posts).', 'Both <strong>%s</strong> and yourself have publicly interacted with these contacts (follow, comment or likes on public posts).',
htmlentities($contact['name'], ENT_COMPAT, 'UTF-8') htmlentities($account['name'], ENT_COMPAT, 'UTF-8')
); );
break; break;
default: default:
@ -139,7 +139,7 @@ class Contacts extends BaseModule
$title = $this->tt('Contact (%s)', 'Contacts (%s)', $total); $title = $this->tt('Contact (%s)', 'Contacts (%s)', $total);
} }
$o = Module\Contact::getTabsHTML($contact, Module\Contact::TAB_CONTACTS); $o = Module\Contact::getTabsHTML($account, Module\Contact::TAB_CONTACTS);
$tabs = self::getContactFilterTabs('contact/' . $cid, $type, true); $tabs = self::getContactFilterTabs('contact/' . $cid, $type, true);

View file

@ -86,19 +86,19 @@ class Conversations extends BaseModule
throw new NotFoundException($this->t('Contact not found.')); throw new NotFoundException($this->t('Contact not found.'));
} }
$contact = Model\Contact::getById($data['public']); $account = Model\Contact::selectFirstAccount([], ['id' => $data['public']]);
if (empty($contact)) { if (empty($account)) {
throw new NotFoundException($this->t('Contact not found.')); throw new NotFoundException($this->t('Contact not found.'));
} }
// Don't display contacts that are about to be deleted // Don't display contacts that are about to be deleted
if (!empty($contact['deleted']) || !empty($contact['network']) && $contact['network'] == Protocol::PHANTOM) { if (!empty($account['deleted']) || !empty($account['network']) && $account['network'] == Protocol::PHANTOM) {
throw new NotFoundException($this->t('Contact not found.')); throw new NotFoundException($this->t('Contact not found.'));
} }
$localRelationship = $this->localRelationship->getForUserContact($this->userSession->getLocalUserId(), $contact['id']); $localRelationship = $this->localRelationship->getForUserContact($this->userSession->getLocalUserId(), $account['id']);
if ($localRelationship->rel === Model\Contact::SELF) { if ($localRelationship->rel === Model\Contact::SELF) {
$this->baseUrl->redirect('profile/' . $contact['nick']); $this->baseUrl->redirect('profile/' . $account['nick']);
} }
// Load necessary libraries for the status editor // Load necessary libraries for the status editor
@ -107,7 +107,7 @@ class Conversations extends BaseModule
$this->page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.css')); $this->page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.css'));
$this->page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput-typeahead.css')); $this->page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput-typeahead.css'));
$this->page['aside'] .= Widget\VCard::getHTML($contact, true); $this->page['aside'] .= Widget\VCard::getHTML($account, true);
Nav::setSelected('contact'); Nav::setSelected('contact');
@ -115,12 +115,12 @@ class Conversations extends BaseModule
'lockstate' => ACL::getLockstateForUserId($this->userSession->getLocalUserId()) ? 'lock' : 'unlock', 'lockstate' => ACL::getLockstateForUserId($this->userSession->getLocalUserId()) ? 'lock' : 'unlock',
'acl' => ACL::getFullSelectorHTML($this->page, $this->userSession->getLocalUserId(), true, []), 'acl' => ACL::getFullSelectorHTML($this->page, $this->userSession->getLocalUserId(), true, []),
'bang' => '', 'bang' => '',
'content' => ($contact['contact-type'] == ModelContact::TYPE_COMMUNITY ? '!' : '@') . ($contact['addr'] ?: $contact['url']), 'content' => ($account['contact-type'] == ModelContact::TYPE_COMMUNITY ? '!' : '@') . ($account['addr'] ?: $account['url']),
]; ];
$o = $this->conversation->statusEditor($options); $o = $this->conversation->statusEditor($options);
$o .= Contact::getTabsHTML($contact, Contact::TAB_CONVERSATIONS); $o .= Contact::getTabsHTML($account, Contact::TAB_CONVERSATIONS);
$o .= Model\Contact::getThreadsFromId($contact['id'], $this->userSession->getLocalUserId(), 0, 0, $request['last_created'] ?? ''); $o .= Model\Contact::getThreadsFromId($account['id'], $this->userSession->getLocalUserId(), 0, 0, $request['last_created'] ?? '');
return $o; return $o;
} }

View file

@ -116,23 +116,23 @@ class Follow extends BaseModule
$submit = ''; $submit = '';
} }
$contact = Contact::getByURL($url, true); $account = Contact::selectFirstAccount([], ['id' => Contact::getIdForURL($url, 0, true)]);
// Possibly it is a mail contact // Possibly it is a mail contact
if (empty($contact)) { if (empty($account)) {
$contact = Probe::uri($url, Protocol::MAIL, $uid); $account = Probe::uri($url, Protocol::MAIL, $uid);
} }
if (empty($contact) || ($contact['network'] == Protocol::PHANTOM)) { if (empty($account) || ($account['network'] == Protocol::PHANTOM)) {
// Possibly it is a remote item and not an account // Possibly it is a remote item and not an account
$this->followRemoteItem($url); $this->followRemoteItem($url);
$this->sysMessages->addNotice($this->t('The network type couldn\'t be detected. Contact can\'t be added.')); $this->sysMessages->addNotice($this->t('The network type couldn\'t be detected. Contact can\'t be added.'));
$submit = ''; $submit = '';
$contact = ['url' => $url, 'network' => Protocol::PHANTOM, 'name' => $url, 'keywords' => '']; $account = ['url' => $url, 'network' => Protocol::PHANTOM, 'name' => $url, 'keywords' => ''];
} }
$protocol = Contact::getProtocol($contact['url'], $contact['network']); $protocol = Contact::getProtocol($account['url'], $account['network']);
if (($protocol == Protocol::DIASPORA) && !$this->config->get('system', 'diaspora_enabled')) { if (($protocol == Protocol::DIASPORA) && !$this->config->get('system', 'diaspora_enabled')) {
$this->sysMessages->addNotice($this->t('Diaspora support isn\'t enabled. Contact can\'t be added.')); $this->sysMessages->addNotice($this->t('Diaspora support isn\'t enabled. Contact can\'t be added.'));
@ -145,11 +145,11 @@ class Follow extends BaseModule
} }
if ($protocol == Protocol::MAIL) { if ($protocol == Protocol::MAIL) {
$contact['url'] = $contact['addr']; $account['url'] = $account['addr'];
} }
if (!empty($request['auto'])) { if (!empty($request['auto'])) {
$this->process($contact['url']); $this->process($account['url']);
} }
$requestUrl = $this->baseUrl . '/contact/follow'; $requestUrl = $this->baseUrl . '/contact/follow';
@ -173,27 +173,27 @@ class Follow extends BaseModule
'$cancel' => $this->t('Cancel'), '$cancel' => $this->t('Cancel'),
'$action' => $requestUrl, '$action' => $requestUrl,
'$name' => $contact['name'], '$name' => $account['name'],
'$url' => $contact['url'], '$url' => $account['url'],
'$zrl' => Profile::zrl($contact['url']), '$zrl' => Profile::zrl($account['url']),
'$myaddr' => $myaddr, '$myaddr' => $myaddr,
'$keywords' => $contact['keywords'], '$keywords' => $account['keywords'],
'$does_know_you' => ['knowyou', $this->t('%s knows you', $contact['name'])], '$does_know_you' => ['knowyou', $this->t('%s knows you', $account['name'])],
'$addnote_field' => ['dfrn-request-message', $this->t('Add a personal note:')], '$addnote_field' => ['dfrn-request-message', $this->t('Add a personal note:')],
]); ]);
$this->page['aside'] = ''; $this->page['aside'] = '';
if (!in_array($protocol, [Protocol::PHANTOM, Protocol::MAIL])) { if (!in_array($protocol, [Protocol::PHANTOM, Protocol::MAIL])) {
$this->page['aside'] = VCard::getHTML($contact); $this->page['aside'] = VCard::getHTML($account);
$output .= Renderer::replaceMacros(Renderer::getMarkupTemplate('section_title.tpl'), $output .= Renderer::replaceMacros(Renderer::getMarkupTemplate('section_title.tpl'),
['$title' => $this->t('Posts and Replies')] ['$title' => $this->t('Posts and Replies')]
); );
// Show last public posts // Show last public posts
$output .= Contact::getPostsFromUrl($contact['url'], $this->session->getLocalUserId()); $output .= Contact::getPostsFromUrl($account['url'], $this->session->getLocalUserId());
} }
return $output; return $output;

View file

@ -56,16 +56,16 @@ class Media extends BaseModule
{ {
$cid = $this->parameters['id']; $cid = $this->parameters['id'];
$contact = Model\Contact::selectFirst([], ['id' => $cid]); $account = Model\Contact::selectFirstAccount([], ['id' => $cid]);
if (empty($contact)) { if (empty($account)) {
throw new BadRequestException(DI::l10n()->t('Contact not found.')); throw new BadRequestException(DI::l10n()->t('Contact not found.'));
} }
DI::page()['aside'] = Widget\VCard::getHTML($contact); DI::page()['aside'] = Widget\VCard::getHTML($account);
$o = Contact::getTabsHTML($contact, Contact::TAB_MEDIA); $o = Contact::getTabsHTML($account, Contact::TAB_MEDIA);
$o .= ModelContact::getPostsFromUrl($contact['url'], $this->userSession->getLocalUserId(), true); $o .= ModelContact::getPostsFromUrl($account['url'], $this->userSession->getLocalUserId(), true);
return $o; return $o;
} }

View file

@ -154,43 +154,43 @@ class Profile extends BaseModule
throw new HTTPException\NotFoundException($this->t('Contact not found.')); throw new HTTPException\NotFoundException($this->t('Contact not found.'));
} }
$contact = Contact::getById($data['public']); $account = Contact::selectFirstAccount([], ['id' => $data['public']]);
if (!$this->db->isResult($contact)) { if (!$this->db->isResult($account)) {
throw new HTTPException\NotFoundException($this->t('Contact not found.')); throw new HTTPException\NotFoundException($this->t('Contact not found.'));
} }
// Don't display contacts that are about to be deleted // Don't display contacts that are about to be deleted
if ($this->db->isResult($contact) && (!empty($contact['deleted']) || !empty($contact['network']) && $contact['network'] == Protocol::PHANTOM)) { if ($this->db->isResult($account) && (!empty($account['deleted']) || !empty($account['network']) && $account['network'] == Protocol::PHANTOM)) {
throw new HTTPException\NotFoundException($this->t('Contact not found.')); throw new HTTPException\NotFoundException($this->t('Contact not found.'));
} }
$localRelationship = $this->localRelationship->getForUserContact($this->session->getLocalUserId(), $contact['id']); $localRelationship = $this->localRelationship->getForUserContact($this->session->getLocalUserId(), $account['id']);
if ($localRelationship->rel === Contact::SELF) { if ($localRelationship->rel === Contact::SELF) {
$this->baseUrl->redirect('profile/' . $contact['nick'] . '/profile'); $this->baseUrl->redirect('profile/' . $account['nick'] . '/profile');
} }
if (isset($this->parameters['action'])) { if (isset($this->parameters['action'])) {
self::checkFormSecurityTokenRedirectOnError('contact/' . $contact['id'], 'contact_action', 't'); self::checkFormSecurityTokenRedirectOnError('contact/' . $account['id'], 'contact_action', 't');
$cmd = $this->parameters['action']; $cmd = $this->parameters['action'];
if ($cmd === 'update' && $localRelationship->rel !== Contact::NOTHING) { if ($cmd === 'update' && $localRelationship->rel !== Contact::NOTHING) {
Module\Contact::updateContactFromPoll($contact['id']); Module\Contact::updateContactFromPoll($account['id']);
} }
if ($cmd === 'updateprofile') { if ($cmd === 'updateprofile') {
$this->updateContactFromProbe($contact['id']); $this->updateContactFromProbe($account['id']);
} }
if ($cmd === 'block') { if ($cmd === 'block') {
if ($localRelationship->blocked) { if ($localRelationship->blocked) {
// @TODO Backward compatibility, replace with $localRelationship->unblock() // @TODO Backward compatibility, replace with $localRelationship->unblock()
Contact\User::setBlocked($contact['id'], $this->session->getLocalUserId(), false); Contact\User::setBlocked($account['id'], $this->session->getLocalUserId(), false);
$message = $this->t('Contact has been unblocked'); $message = $this->t('Contact has been unblocked');
} else { } else {
// @TODO Backward compatibility, replace with $localRelationship->block() // @TODO Backward compatibility, replace with $localRelationship->block()
Contact\User::setBlocked($contact['id'], $this->session->getLocalUserId(), true); Contact\User::setBlocked($account['id'], $this->session->getLocalUserId(), true);
$message = $this->t('Contact has been blocked'); $message = $this->t('Contact has been blocked');
} }
@ -201,12 +201,12 @@ class Profile extends BaseModule
if ($cmd === 'ignore') { if ($cmd === 'ignore') {
if ($localRelationship->ignored) { if ($localRelationship->ignored) {
// @TODO Backward compatibility, replace with $localRelationship->unblock() // @TODO Backward compatibility, replace with $localRelationship->unblock()
Contact\User::setIgnored($contact['id'], $this->session->getLocalUserId(), false); Contact\User::setIgnored($account['id'], $this->session->getLocalUserId(), false);
$message = $this->t('Contact has been unignored'); $message = $this->t('Contact has been unignored');
} else { } else {
// @TODO Backward compatibility, replace with $localRelationship->block() // @TODO Backward compatibility, replace with $localRelationship->block()
Contact\User::setIgnored($contact['id'], $this->session->getLocalUserId(), true); Contact\User::setIgnored($account['id'], $this->session->getLocalUserId(), true);
$message = $this->t('Contact has been ignored'); $message = $this->t('Contact has been ignored');
} }
@ -217,12 +217,12 @@ class Profile extends BaseModule
if ($cmd === 'collapse') { if ($cmd === 'collapse') {
if ($localRelationship->collapsed) { if ($localRelationship->collapsed) {
// @TODO Backward compatibility, replace with $localRelationship->unblock() // @TODO Backward compatibility, replace with $localRelationship->unblock()
Contact\User::setCollapsed($contact['id'], $this->session->getLocalUserId(), false); Contact\User::setCollapsed($account['id'], $this->session->getLocalUserId(), false);
$message = $this->t('Contact has been uncollapsed'); $message = $this->t('Contact has been uncollapsed');
} else { } else {
// @TODO Backward compatibility, replace with $localRelationship->block() // @TODO Backward compatibility, replace with $localRelationship->block()
Contact\User::setCollapsed($contact['id'], $this->session->getLocalUserId(), true); Contact\User::setCollapsed($account['id'], $this->session->getLocalUserId(), true);
$message = $this->t('Contact has been collapsed'); $message = $this->t('Contact has been collapsed');
} }
@ -230,10 +230,10 @@ class Profile extends BaseModule
$this->systemMessages->addInfo($message); $this->systemMessages->addInfo($message);
} }
$this->baseUrl->redirect('contact/' . $contact['id']); $this->baseUrl->redirect('contact/' . $account['id']);
} }
$vcard_widget = Widget\VCard::getHTML($contact); $vcard_widget = Widget\VCard::getHTML($account);
$circles_widget = ''; $circles_widget = '';
if (!in_array($localRelationship->rel, [Contact::NOTHING, Contact::SELF])) { if (!in_array($localRelationship->rel, [Contact::NOTHING, Contact::SELF])) {
@ -251,18 +251,18 @@ class Profile extends BaseModule
]); ]);
switch ($localRelationship->rel) { switch ($localRelationship->rel) {
case Contact::FRIEND: $relation_text = $this->t('You are mutual friends with %s', $contact['name']); break; case Contact::FRIEND: $relation_text = $this->t('You are mutual friends with %s', $account['name']); break;
case Contact::FOLLOWER: $relation_text = $this->t('You are sharing with %s', $contact['name']); break; case Contact::FOLLOWER: $relation_text = $this->t('You are sharing with %s', $account['name']); break;
case Contact::SHARING: $relation_text = $this->t('%s is sharing with you', $contact['name']); break; case Contact::SHARING: $relation_text = $this->t('%s is sharing with you', $account['name']); break;
default: default:
$relation_text = ''; $relation_text = '';
} }
if (!Protocol::supportsFollow($contact['network'])) { if (!Protocol::supportsFollow($account['network'])) {
$relation_text = ''; $relation_text = '';
} }
$url = Contact::magicLinkByContact($contact); $url = Contact::magicLinkByContact($account);
if (strpos($url, 'contact/redir/') === 0) { if (strpos($url, 'contact/redir/') === 0) {
$sparkle = ' class="sparkle" '; $sparkle = ' class="sparkle" ';
} else { } else {
@ -272,34 +272,34 @@ class Profile extends BaseModule
$insecure = $this->t('Private communications are not available for this contact.'); $insecure = $this->t('Private communications are not available for this contact.');
// @TODO: Figure out why gsid can be empty // @TODO: Figure out why gsid can be empty
if (empty($contact['gsid'])) { if (empty($account['gsid'])) {
$this->logger->notice('Empty gsid for contact', ['contact' => $contact]); $this->logger->notice('Empty gsid for contact', ['contact' => $account]);
} }
$serverIgnored = $serverIgnored =
$contact['gsid'] && $account['gsid'] &&
$this->userGServer->isIgnoredByUser($this->session->getLocalUserId(), $contact['gsid']) ? $this->userGServer->isIgnoredByUser($this->session->getLocalUserId(), $account['gsid']) ?
$this->t('This contact is on a server you ignored.') $this->t('This contact is on a server you ignored.')
: ''; : '';
$last_update = (($contact['last-update'] <= DBA::NULL_DATETIME) ? $this->t('Never') : DateTimeFormat::local($contact['last-update'], 'D, j M Y, g:i A')); $last_update = (($account['last-update'] <= DBA::NULL_DATETIME) ? $this->t('Never') : DateTimeFormat::local($account['last-update'], 'D, j M Y, g:i A'));
if ($contact['last-update'] > DBA::NULL_DATETIME) { if ($account['last-update'] > DBA::NULL_DATETIME) {
$last_update .= ' ' . ($contact['failed'] ? $this->t('(Update was not successful)') : $this->t('(Update was successful)')); $last_update .= ' ' . ($account['failed'] ? $this->t('(Update was not successful)') : $this->t('(Update was successful)'));
} }
$lblsuggest = (($contact['network'] === Protocol::DFRN) ? $this->t('Suggest friends') : ''); $lblsuggest = (($account['network'] === Protocol::DFRN) ? $this->t('Suggest friends') : '');
$poll_enabled = in_array($contact['network'], [Protocol::DFRN, Protocol::OSTATUS, Protocol::FEED, Protocol::MAIL]); $poll_enabled = in_array($account['network'], [Protocol::DFRN, Protocol::OSTATUS, Protocol::FEED, Protocol::MAIL]);
$nettype = $this->t('Network type: %s', ContactSelector::networkToName($contact['network'], $contact['url'], $contact['protocol'], $contact['gsid'])); $nettype = $this->t('Network type: %s', ContactSelector::networkToName($account['network'], $account['url'], $account['protocol'], $account['gsid']));
// tabs // tabs
$tab_str = Module\Contact::getTabsHTML($contact, Module\Contact::TAB_PROFILE); $tab_str = Module\Contact::getTabsHTML($account, Module\Contact::TAB_PROFILE);
$lost_contact = (($contact['archive'] && $contact['term-date'] > DBA::NULL_DATETIME && $contact['term-date'] < DateTimeFormat::utcNow()) ? $this->t('Communications lost with this contact!') : ''); $lost_contact = (($account['archive'] && $account['term-date'] > DBA::NULL_DATETIME && $account['term-date'] < DateTimeFormat::utcNow()) ? $this->t('Communications lost with this contact!') : '');
$fetch_further_information = null; $fetch_further_information = null;
if ($contact['network'] == Protocol::FEED) { if ($account['network'] == Protocol::FEED) {
$fetch_further_information = [ $fetch_further_information = [
'fetch_further_information', 'fetch_further_information',
$this->t('Fetch further information for feeds'), $this->t('Fetch further information for feeds'),
@ -314,20 +314,20 @@ class Profile extends BaseModule
]; ];
} }
$allow_remote_self = in_array($contact['network'], [Protocol::ACTIVITYPUB, Protocol::FEED, Protocol::DFRN, Protocol::DIASPORA, Protocol::TWITTER]) $allow_remote_self = in_array($account['network'], [Protocol::ACTIVITYPUB, Protocol::FEED, Protocol::DFRN, Protocol::DIASPORA, Protocol::TWITTER])
&& $this->config->get('system', 'allow_users_remote_self'); && $this->config->get('system', 'allow_users_remote_self');
if ($contact['network'] == Protocol::FEED) { if ($account['network'] == Protocol::FEED) {
$remote_self_options = [ $remote_self_options = [
Contact::MIRROR_DEACTIVATED => $this->t('No mirroring'), Contact::MIRROR_DEACTIVATED => $this->t('No mirroring'),
Contact::MIRROR_OWN_POST => $this->t('Mirror as my own posting') Contact::MIRROR_OWN_POST => $this->t('Mirror as my own posting')
]; ];
} elseif ($contact['network'] == Protocol::ACTIVITYPUB) { } elseif ($account['network'] == Protocol::ACTIVITYPUB) {
$remote_self_options = [ $remote_self_options = [
Contact::MIRROR_DEACTIVATED => $this->t('No mirroring'), Contact::MIRROR_DEACTIVATED => $this->t('No mirroring'),
Contact::MIRROR_NATIVE_RESHARE => $this->t('Native reshare') Contact::MIRROR_NATIVE_RESHARE => $this->t('Native reshare')
]; ];
} elseif ($contact['network'] == Protocol::DFRN) { } elseif ($account['network'] == Protocol::DFRN) {
$remote_self_options = [ $remote_self_options = [
Contact::MIRROR_DEACTIVATED => $this->t('No mirroring'), Contact::MIRROR_DEACTIVATED => $this->t('No mirroring'),
Contact::MIRROR_OWN_POST => $this->t('Mirror as my own posting'), Contact::MIRROR_OWN_POST => $this->t('Mirror as my own posting'),
@ -340,14 +340,14 @@ class Profile extends BaseModule
]; ];
} }
$channel_frequency = Contact\User::getChannelFrequency($contact['id'], $this->session->getLocalUserId()); $channel_frequency = Contact\User::getChannelFrequency($account['id'], $this->session->getLocalUserId());
$poll_interval = null; $poll_interval = null;
if ((($contact['network'] == Protocol::FEED) && !$this->config->get('system', 'adjust_poll_frequency')) || ($contact['network'] == Protocol::MAIL)) { if ((($account['network'] == Protocol::FEED) && !$this->config->get('system', 'adjust_poll_frequency')) || ($account['network'] == Protocol::MAIL)) {
$poll_interval = ContactSelector::pollInterval($localRelationship->priority, !$poll_enabled); $poll_interval = ContactSelector::pollInterval($localRelationship->priority, !$poll_enabled);
} }
$contact_actions = $this->getContactActions($contact, $localRelationship); $contact_actions = $this->getContactActions($account, $localRelationship);
if ($localRelationship->rel !== Contact::NOTHING) { if ($localRelationship->rel !== Contact::NOTHING) {
$lbl_info1 = $this->t('Contact Information / Notes'); $lbl_info1 = $this->t('Contact Information / Notes');
@ -364,11 +364,11 @@ class Profile extends BaseModule
'$submit' => $this->t('Submit'), '$submit' => $this->t('Submit'),
'$lbl_info1' => $lbl_info1, '$lbl_info1' => $lbl_info1,
'$lbl_info2' => $this->t('Their personal note'), '$lbl_info2' => $this->t('Their personal note'),
'$reason' => trim($contact['reason'] ?? ''), '$reason' => trim($account['reason'] ?? ''),
'$infedit' => $this->t('Edit contact notes'), '$infedit' => $this->t('Edit contact notes'),
'$common_link' => 'contact/' . $contact['id'] . '/contacts/common', '$common_link' => 'contact/' . $account['id'] . '/contacts/common',
'$relation_text' => $relation_text, '$relation_text' => $relation_text,
'$visit' => $this->t('Visit %s\'s profile [%s]', $contact['name'], $contact['url']), '$visit' => $this->t('Visit %s\'s profile [%s]', $account['name'], $account['url']),
'$blockunblock' => $this->t('Block/Unblock contact'), '$blockunblock' => $this->t('Block/Unblock contact'),
'$ignorecont' => $this->t('Ignore contact'), '$ignorecont' => $this->t('Ignore contact'),
'$lblrecent' => $this->t('View conversations'), '$lblrecent' => $this->t('View conversations'),
@ -381,13 +381,13 @@ class Profile extends BaseModule
'$updpub' => $this->t('Update public posts'), '$updpub' => $this->t('Update public posts'),
'$last_update' => $last_update, '$last_update' => $last_update,
'$udnow' => $this->t('Update now'), '$udnow' => $this->t('Update now'),
'$contact_id' => $contact['id'], '$contact_id' => $account['id'],
'$pending' => $localRelationship->pending ? $this->t('Awaiting connection acknowledge') : '', '$pending' => $localRelationship->pending ? $this->t('Awaiting connection acknowledge') : '',
'$blocked' => $localRelationship->blocked ? $this->t('Currently blocked') : '', '$blocked' => $localRelationship->blocked ? $this->t('Currently blocked') : '',
'$ignored' => $localRelationship->ignored ? $this->t('Currently ignored') : '', '$ignored' => $localRelationship->ignored ? $this->t('Currently ignored') : '',
'$collapsed' => $localRelationship->collapsed ? $this->t('Currently collapsed') : '', '$collapsed' => $localRelationship->collapsed ? $this->t('Currently collapsed') : '',
'$archived' => ($contact['archive'] ? $this->t('Currently archived') : ''), '$archived' => ($account['archive'] ? $this->t('Currently archived') : ''),
'$insecure' => (in_array($contact['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::MAIL, Protocol::DIASPORA]) ? '' : $insecure), '$insecure' => (in_array($account['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::MAIL, Protocol::DIASPORA]) ? '' : $insecure),
'$serverIgnored' => $serverIgnored, '$serverIgnored' => $serverIgnored,
'$manageServers' => $this->t('Manage remote servers'), '$manageServers' => $this->t('Manage remote servers'),
'$cinfo' => ['info', '', $localRelationship->info, ''], '$cinfo' => ['info', '', $localRelationship->info, ''],
@ -395,22 +395,22 @@ class Profile extends BaseModule
'$notify_new_posts' => ['notify_new_posts', $this->t('Notification for new posts'), ($localRelationship->notifyNewPosts), $this->t('Send a notification of every new post of this contact')], '$notify_new_posts' => ['notify_new_posts', $this->t('Notification for new posts'), ($localRelationship->notifyNewPosts), $this->t('Send a notification of every new post of this contact')],
'$fetch_further_information' => $fetch_further_information, '$fetch_further_information' => $fetch_further_information,
'$ffi_keyword_denylist' => ['ffi_keyword_denylist', $this->t('Keyword Deny List'), $localRelationship->ffiKeywordDenylist, $this->t('Comma separated list of keywords that should not be converted to hashtags, when "Fetch information and keywords" is selected')], '$ffi_keyword_denylist' => ['ffi_keyword_denylist', $this->t('Keyword Deny List'), $localRelationship->ffiKeywordDenylist, $this->t('Comma separated list of keywords that should not be converted to hashtags, when "Fetch information and keywords" is selected')],
'$photo' => Contact::getPhoto($contact), '$photo' => Contact::getPhoto($account),
'$name' => $contact['name'], '$name' => $account['name'],
'$sparkle' => $sparkle, '$sparkle' => $sparkle,
'$url' => $url, '$url' => $url,
'$profileurllabel' => $this->t('Profile URL'), '$profileurllabel' => $this->t('Profile URL'),
'$profileurl' => $contact['url'], '$profileurl' => $account['url'],
'$account_type' => Contact::getAccountType($contact['contact-type']), '$account_type' => Contact::getAccountType($account['contact-type']),
'$location' => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['location']), '$location' => BBCode::convertForUriId($account['uri-id'] ?? 0, $account['location']),
'$location_label' => $this->t('Location:'), '$location_label' => $this->t('Location:'),
'$xmpp' => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['xmpp']), '$xmpp' => BBCode::convertForUriId($account['uri-id'] ?? 0, $account['xmpp']),
'$xmpp_label' => $this->t('XMPP:'), '$xmpp_label' => $this->t('XMPP:'),
'$matrix' => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['matrix']), '$matrix' => BBCode::convertForUriId($account['uri-id'] ?? 0, $account['matrix']),
'$matrix_label' => $this->t('Matrix:'), '$matrix_label' => $this->t('Matrix:'),
'$about' => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['about'], BBCode::EXTERNAL), '$about' => BBCode::convertForUriId($account['uri-id'] ?? 0, $account['about'], BBCode::EXTERNAL),
'$about_label' => $this->t('About:'), '$about_label' => $this->t('About:'),
'$keywords' => $contact['keywords'], '$keywords' => $account['keywords'],
'$keywords_label' => $this->t('Tags:'), '$keywords_label' => $this->t('Tags:'),
'$contact_action_button' => $this->t('Actions'), '$contact_action_button' => $this->t('Actions'),
'$contact_actions' => $contact_actions, '$contact_actions' => $contact_actions,
@ -434,7 +434,7 @@ class Profile extends BaseModule
'$frequency_never' => ['channel_frequency', $this->t('Never display posts'), Contact\User::FREQUENCY_NEVER, $this->t('Posts from this contact will never be displayed in any channel'), $channel_frequency == Contact\User::FREQUENCY_NEVER], '$frequency_never' => ['channel_frequency', $this->t('Never display posts'), Contact\User::FREQUENCY_NEVER, $this->t('Posts from this contact will never be displayed in any channel'), $channel_frequency == Contact\User::FREQUENCY_NEVER],
]); ]);
$arr = ['contact' => $contact, 'output' => $o]; $arr = ['contact' => $account, 'output' => $o];
Hook::callAll('contact_edit', $arr); Hook::callAll('contact_edit', $arr);

View file

@ -105,13 +105,12 @@ class Directory extends BaseModule
* array for displaying directory entries. * array for displaying directory entries.
* *
* @param array $contact The directory entry from the database. * @param array $contact The directory entry from the database.
* @param string $photo_size Avatar size (thumb, photo or micro).
* *
* @return array * @return array
* *
* @throws \Exception * @throws \Exception
*/ */
public static function formatEntry(array $contact, string $photo_size = 'photo'): array public static function formatEntry(array $contact): array
{ {
$itemurl = (($contact['addr'] != "") ? $contact['addr'] : $contact['url']); $itemurl = (($contact['addr'] != "") ? $contact['addr'] : $contact['url']);

View file

@ -100,6 +100,7 @@ class Photo extends BaseApi
$id = $account['id']; $id = $account['id'];
} }
// Contact Id Fallback, to remove after version 2024.03
if (isset($this->parameters['contact_id'])) { if (isset($this->parameters['contact_id'])) {
$id = intval($this->parameters['contact_id']); $id = intval($this->parameters['contact_id']);
} }

View file

@ -95,7 +95,7 @@ class Acl extends BaseModule
$contacts = []; $contacts = [];
foreach ($result as $contact) { foreach ($result as $contact) {
$contacts[] = [ $contacts[] = [
'photo' => Contact::getMicro($contact, true), 'photo' => $contact['micro'],
'name' => htmlspecialchars($contact['name']), 'name' => htmlspecialchars($contact['name']),
'nick' => $contact['addr'] ?: $contact['url'], 'nick' => $contact['addr'] ?: $contact['url'],
'network' => $contact['network'], 'network' => $contact['network'],
@ -219,17 +219,17 @@ class Acl extends BaseModule
} }
$groups = []; $groups = [];
foreach ($contacts as $contact) { foreach ($contacts as $account) {
$entry = [ $entry = [
'type' => self::TYPE_MENTION_CONTACT, 'type' => self::TYPE_MENTION_CONTACT,
'photo' => Contact::getMicro($contact, true), 'photo' => Contact::getMicro($account, true),
'name' => htmlspecialchars($contact['name']), 'name' => htmlspecialchars($account['name']),
'id' => intval($contact['id']), 'id' => intval($account['id']),
'network' => $contact['network'], 'network' => $account['network'],
'link' => $contact['url'], 'link' => $account['url'],
'nick' => htmlentities(($contact['attag'] ?? '') ?: $contact['nick']), 'nick' => htmlentities(($account['attag'] ?? '') ?: $account['nick']),
'addr' => htmlentities(($contact['addr'] ?? '') ?: $contact['url']), 'addr' => htmlentities(($account['addr'] ?? '') ?: $account['url']),
'group' => $contact['contact-type'] == Contact::TYPE_COMMUNITY, 'group' => $account['contact-type'] == Contact::TYPE_COMMUNITY,
]; ];
if ($entry['group']) { if ($entry['group']) {
$groups[] = $entry; $groups[] = $entry;
@ -274,18 +274,18 @@ class Acl extends BaseModule
$this->database->close($authors); $this->database->close($authors);
foreach (array_diff($item_authors, $known_contacts) as $author) { foreach (array_diff($item_authors, $known_contacts) as $author) {
$contact = Contact::getByURL($author, false, ['micro', 'name', 'id', 'network', 'nick', 'addr', 'url', 'forum', 'avatar']); $account = Contact::selectFirstAccount(['micro', 'name', 'id', 'network', 'nick', 'addr', 'url', 'forum', 'avatar', 'guid', 'updated'], ['url' => $author]);
if ($contact) { if ($account) {
$unknown_contacts[] = [ $unknown_contacts[] = [
'type' => self::TYPE_MENTION_CONTACT, 'type' => self::TYPE_MENTION_CONTACT,
'photo' => Contact::getMicro($contact, true), 'photo' => Contact::getMicro($account, true),
'name' => htmlspecialchars($contact['name']), 'name' => htmlspecialchars($account['name']),
'id' => intval($contact['id']), 'id' => intval($account['id']),
'network' => $contact['network'], 'network' => $account['network'],
'link' => $contact['url'], 'link' => $account['url'],
'nick' => htmlentities(($contact['nick'] ?? '') ?: $contact['addr']), 'nick' => htmlentities(($account['nick'] ?? '') ?: $account['addr']),
'addr' => htmlentities(($contact['addr'] ?? '') ?: $contact['url']), 'addr' => htmlentities(($account['addr'] ?? '') ?: $account['url']),
'group' => $contact['forum'] 'group' => $account['forum']
]; ];
} }
} }

View file

@ -99,11 +99,12 @@ class Introduction extends BaseFactory
try { try {
$stmtNotifications = $this->dba->p( $stmtNotifications = $this->dba->p(
"SELECT `intro`.`id` AS `intro_id`, `intro`.*, `contact`.*, "SELECT `intro`.`id` AS `intro_id`, `intro`.*, `contact`.*, `item-uri`.`guid`
`suggest-contact`.`name` AS `fname`, `suggest-contact`.`url` AS `furl`, `suggest-contact`.`addr` AS `faddr`, `suggest-contact`.`name` AS `fname`, `suggest-contact`.`url` AS `furl`, `suggest-contact`.`addr` AS `faddr`,
`suggest-contact`.`photo` AS `fphoto`, `suggest-contact`.`request` AS `frequest` `suggest-contact`.`photo` AS `fphoto`, `suggest-contact`.`request` AS `frequest`
FROM `intro` FROM `intro`
LEFT JOIN `contact` ON `contact`.`id` = `intro`.`contact-id` LEFT JOIN `contact` ON `contact`.`id` = `intro`.`contact-id`
LEFT JOIN `item-uri` ON `item-uri`.`id` = `contact`.`uri-id`)
LEFT JOIN `contact` AS `suggest-contact` ON `intro`.`suggest-cid` = `suggest-contact`.`id` LEFT JOIN `contact` AS `suggest-contact` ON `intro`.`suggest-cid` = `suggest-contact`.`id`
WHERE `intro`.`uid` = ? $sql_extra WHERE `intro`.`uid` = ? $sql_extra
LIMIT ?, ?", LIMIT ?, ?",

View file

@ -2258,7 +2258,7 @@ class Probe
'name' => $owner['name'], 'nick' => $owner['nick'], 'guid' => $approfile['diaspora:guid'] ?? '', 'name' => $owner['name'], 'nick' => $owner['nick'], 'guid' => $approfile['diaspora:guid'] ?? '',
'url' => $owner['url'], 'addr' => $owner['addr'], 'alias' => $owner['alias'], 'url' => $owner['url'], 'addr' => $owner['addr'], 'alias' => $owner['alias'],
'photo' => User::getAvatarUrl($owner), 'photo' => User::getAvatarUrl($owner),
'header' => $owner['header'] ? Contact::getHeaderUrlForId($owner['id'], $owner['updated']) : '', 'header' => $owner['header'] ? Contact::getHeaderUrlForId($owner['guid'], $owner['updated']) : '',
'account-type' => $owner['contact-type'], 'community' => ($owner['contact-type'] == User::ACCOUNT_TYPE_COMMUNITY), 'account-type' => $owner['contact-type'], 'community' => ($owner['contact-type'] == User::ACCOUNT_TYPE_COMMUNITY),
'keywords' => $owner['keywords'], 'location' => $owner['location'], 'about' => $owner['about'], 'keywords' => $owner['keywords'], 'location' => $owner['location'], 'about' => $owner['about'],
'xmpp' => $owner['xmpp'], 'matrix' => $owner['matrix'], 'xmpp' => $owner['xmpp'], 'matrix' => $owner['matrix'],

View file

@ -108,10 +108,10 @@ class Account extends BaseDataTransferObject
$this->note = BBCode::convertForUriId($account['uri-id'], $account['about'], BBCode::EXTERNAL); $this->note = BBCode::convertForUriId($account['uri-id'], $account['about'], BBCode::EXTERNAL);
$this->url = $account['url']; $this->url = $account['url'];
$this->avatar = Contact::getAvatarUrlForId($account['id'] ?? 0 ?: $account['pid'], Proxy::SIZE_SMALL, $account['updated'], $account['guid'] ?? ''); $this->avatar = Contact::getAvatarUrlForId($account['guid'], $account['updated'], Proxy::SIZE_SMALL);
$this->avatar_static = Contact::getAvatarUrlForId($account['id'] ?? 0 ?: $account['pid'], Proxy::SIZE_SMALL, $account['updated'], $account['guid'] ?? '', true); $this->avatar_static = Contact::getAvatarUrlForId($account['guid'], $account['updated'], Proxy::SIZE_SMALL, true);
$this->header = Contact::getHeaderUrlForId($account['id'] ?? 0 ?: $account['pid'], '', $account['updated'], $account['guid'] ?? ''); $this->header = Contact::getHeaderUrlForId($account['guid'], $account['updated']);
$this->header_static = Contact::getHeaderUrlForId($account['id'] ?? 0 ?: $account['pid'], '', $account['updated'], $account['guid'] ?? '', true); $this->header_static = Contact::getHeaderUrlForId($account['guid'], $account['updated'], null, true);
$this->followers_count = $account['ap-followers_count'] ?? $account['diaspora-interacted_count'] ?? 0; $this->followers_count = $account['ap-followers_count'] ?? $account['diaspora-interacted_count'] ?? 0;
$this->following_count = $account['ap-following_count'] ?? $account['diaspora-interacting_count'] ?? 0; $this->following_count = $account['ap-following_count'] ?? $account['diaspora-interacting_count'] ?? 0;
$this->statuses_count = $account['ap-statuses_count'] ?? $account['diaspora-post_count'] ?? 0; $this->statuses_count = $account['ap-statuses_count'] ?? $account['diaspora-post_count'] ?? 0;

View file

@ -128,7 +128,7 @@ class User extends BaseDataTransferObject
*/ */
/** /**
* @param array $publicContact Full contact table record with uid = 0 * @param array $publicAccount Full account-view table record
* @param array $apcontact Optional full apcontact table record * @param array $apcontact Optional full apcontact table record
* @param array $userContact Optional full contact table record with uid != 0 * @param array $userContact Optional full contact table record with uid != 0
* @param null $status * @param null $status
@ -136,18 +136,18 @@ class User extends BaseDataTransferObject
* *
* @throws InternalServerErrorException * @throws InternalServerErrorException
*/ */
public function __construct(array $publicContact, array $apcontact = [], array $userContact = [], $status = null, bool $include_user_entities = true) public function __construct(array $publicAccount, array $apcontact = [], array $userContact = [], $status = null, bool $include_user_entities = true)
{ {
$uid = $userContact['uid'] ?? 0; $uid = $userContact['uid'] ?? 0;
$this->id = (int)$publicContact['id']; $this->id = (int)$publicAccount['id'];
$this->id_str = (string) $publicContact['id']; $this->id_str = (string) $publicAccount['id'];
$this->name = $publicContact['name'] ?: $publicContact['nick']; $this->name = $publicAccount['name'] ?: $publicAccount['nick'];
$this->screen_name = $publicContact['nick'] ?: $publicContact['name']; $this->screen_name = $publicAccount['nick'] ?: $publicAccount['name'];
$this->location = $publicContact['location'] ?: $this->location = $publicAccount['location'] ?:
ContactSelector::networkToName($publicContact['network'], $publicContact['url'], $publicContact['protocol']); ContactSelector::networkToName($publicAccount['network'], $publicAccount['url'], $publicAccount['protocol']);
$this->derived = []; $this->derived = [];
$this->url = $publicContact['url']; $this->url = $publicAccount['url'];
// No entities needed since we don't perform any shortening in the URL or description // No entities needed since we don't perform any shortening in the URL or description
$this->entities = [ $this->entities = [
'url' => ['urls' => []], 'url' => ['urls' => []],
@ -156,17 +156,17 @@ class User extends BaseDataTransferObject
if (!$include_user_entities) { if (!$include_user_entities) {
unset($this->entities); unset($this->entities);
} }
$this->description = (!empty($publicContact['about']) ? BBCode::toPlaintext($publicContact['about']) : ''); $this->description = (!empty($publicAccount['about']) ? BBCode::toPlaintext($publicAccount['about']) : '');
$this->profile_image_url_https = Contact::getAvatarUrlForUrl($publicContact['url'], $uid, Proxy::SIZE_MICRO); $this->profile_image_url_https = Contact::getAvatarUrlForUrl($publicAccount['url'], $uid, Proxy::SIZE_MICRO);
$this->protected = false; $this->protected = false;
$this->followers_count = $apcontact['followers_count'] ?? 0; $this->followers_count = $apcontact['followers_count'] ?? 0;
$this->friends_count = $apcontact['following_count'] ?? 0; $this->friends_count = $apcontact['following_count'] ?? 0;
$this->listed_count = 0; $this->listed_count = 0;
$this->created_at = DateTimeFormat::utc($publicContact['created'], DateTimeFormat::API); $this->created_at = DateTimeFormat::utc($publicAccount['created'], DateTimeFormat::API);
$this->favourites_count = 0; $this->favourites_count = 0;
$this->verified = $uid != 0; $this->verified = $uid != 0;
$this->statuses_count = $apcontact['statuses_count'] ?? 0; $this->statuses_count = $apcontact['statuses_count'] ?? 0;
$this->profile_banner_url = Contact::getHeaderUrlForId($publicContact['id'], '', $publicContact['updated']); $this->profile_banner_url = Contact::getHeaderUrlForId($publicAccount['guid'], $publicAccount['updated']);
$this->default_profile = false; $this->default_profile = false;
$this->default_profile_image = false; $this->default_profile_image = false;
@ -181,9 +181,9 @@ class User extends BaseDataTransferObject
unset($this->withheld_scope); unset($this->withheld_scope);
// Deprecated // Deprecated
$this->profile_image_url = Contact::getAvatarUrlForUrl($publicContact['url'], $uid, Proxy::SIZE_MICRO); $this->profile_image_url = Contact::getAvatarUrlForUrl($publicAccount['url'], $uid, Proxy::SIZE_MICRO);
$this->profile_image_url_profile_size = Contact::getAvatarUrlForUrl($publicContact['url'], $uid, Proxy::SIZE_THUMB); $this->profile_image_url_profile_size = Contact::getAvatarUrlForUrl($publicAccount['url'], $uid, Proxy::SIZE_THUMB);
$this->profile_image_url_large = Contact::getAvatarUrlForUrl($publicContact['url'], $uid, Proxy::SIZE_LARGE); $this->profile_image_url_large = Contact::getAvatarUrlForUrl($publicAccount['url'], $uid, Proxy::SIZE_LARGE);
$this->utc_offset = 0; $this->utc_offset = 0;
$this->time_zone = 'UTC'; $this->time_zone = 'UTC';
$this->geo_enabled = false; $this->geo_enabled = false;
@ -199,7 +199,7 @@ class User extends BaseDataTransferObject
// Friendica-specific // Friendica-specific
$this->uid = (int)$uid; $this->uid = (int)$uid;
$this->cid = (int)($userContact['id'] ?? 0); $this->cid = (int)($userContact['id'] ?? 0);
$this->pid = (int)$publicContact['id']; $this->pid = (int)$publicAccount['id'];
$this->statusnet_profile_url = $publicContact['url']; $this->statusnet_profile_url = $publicAccount['url'];
} }
} }

View file

@ -414,7 +414,7 @@ class Transmitter
} }
if (!empty($owner['header'])) { if (!empty($owner['header'])) {
$data['image'] = ['type' => 'Image', 'url' => Contact::getHeaderUrlForId($owner['id'], '', $owner['updated'])]; $data['image'] = ['type' => 'Image', 'url' => Contact::getHeaderUrlForId($owner['guid'], $owner['updated'])];
$resourceid = Photo::ridFromURI($owner['header']); $resourceid = Photo::ridFromURI($owner['header']);
if (!empty($resourceid)) { if (!empty($resourceid)) {
@ -449,22 +449,26 @@ class Transmitter
/** /**
* Get a minimal actor array for the C2S API * Get a minimal actor array for the C2S API
* *
* @param integer $cid * @param integer $cid Public contact id
* @return array * @return array
* @throws \Exception
*/ */
private static function getActorArrayByCid(int $cid): array private static function getActorArrayByCid(int $cid): array
{ {
$contact = Contact::getById($cid); $account = Contact::selectFirstAccount(
['url', 'contact-type', 'alias', 'nick', 'name', 'guid', 'updated', 'manually-approve', 'unsearchable'],
['id' => $cid]
);
$data = [ $data = [
'id' => $contact['url'], 'id' => $account['url'],
'type' => $data['type'] = ActivityPub::ACCOUNT_TYPES[$contact['contact-type']], 'type' => $data['type'] = ActivityPub::ACCOUNT_TYPES[$account['contact-type']],
'url' => $contact['alias'], 'url' => $account['alias'],
'preferredUsername' => $contact['nick'], 'preferredUsername' => $account['nick'],
'name' => $contact['name'], 'name' => $account['name'],
'icon' => ['type' => 'Image', 'url' => Contact::getAvatarUrlForId($cid, '', $contact['updated'])], 'icon' => ['type' => 'Image', 'url' => Contact::getAvatarUrlForId($account['guid'], $account['updated'])],
'image' => ['type' => 'Image', 'url' => Contact::getHeaderUrlForId($cid, '', $contact['updated'])], 'image' => ['type' => 'Image', 'url' => Contact::getHeaderUrlForId($account['guid'], $account['updated'])],
'manuallyApprovesFollowers' => (bool)$contact['manually-approve'], 'manuallyApprovesFollowers' => (bool)$account['manually-approve'],
'discoverable' => !$contact['unsearchable'], 'discoverable' => !$account['unsearchable'],
]; ];
if (empty($data['url'])) { if (empty($data['url'])) {

View file

@ -571,6 +571,7 @@ return [
'/{type}/{id:\d+}' => [Module\Photo::class, [R::GET]], '/{type}/{id:\d+}' => [Module\Photo::class, [R::GET]],
'/{type:contact|header}/{guid}' => [Module\Photo::class, [R::GET]], '/{type:contact|header}/{guid}' => [Module\Photo::class, [R::GET]],
'/{type}/{nickname_ext}' => [Module\Photo::class, [R::GET]], '/{type}/{nickname_ext}' => [Module\Photo::class, [R::GET]],
// Contact Id Fallback, to remove after version 2024.03
'/{type:contact|header}/{customsize:\d+}/{contact_id:\d+}' => [Module\Photo::class, [R::GET]], '/{type:contact|header}/{customsize:\d+}/{contact_id:\d+}' => [Module\Photo::class, [R::GET]],
'/{type:contact|header}/{customsize:\d+}/{guid}' => [Module\Photo::class, [R::GET]], '/{type:contact|header}/{customsize:\d+}/{guid}' => [Module\Photo::class, [R::GET]],
'/{type}/{customsize:\d+}/{id:\d+}' => [Module\Photo::class, [R::GET]], '/{type}/{customsize:\d+}/{id:\d+}' => [Module\Photo::class, [R::GET]],

View file

@ -188,29 +188,29 @@ function frio_contact_photo_menu(&$args)
* Some links will point to the local pages because the user would expect * Some links will point to the local pages because the user would expect
* local page (these pages are: search, community, help, apps, directory). * local page (these pages are: search, community, help, apps, directory).
* *
* @param App $a The App class
* @param array $nav_info The original nav info array: nav, banner, userinfo, sitelocation * @param array $nav_info The original nav info array: nav, banner, userinfo, sitelocation
* @throws Exception * @throws ImagickException
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
function frio_remote_nav(array &$nav_info) function frio_remote_nav(array &$nav_info)
{ {
if (DI::mode()->has(App\Mode::MAINTENANCEDISABLED)) { if (DI::mode()->has(App\Mode::MAINTENANCEDISABLED)) {
// get the homelink from $_SESSION // get the homelink from $_SESSION
$homelink = Profile::getMyURL(); $homelink = DI::userSession()->getMyUrl();
if (!$homelink) { if (!$homelink) {
$homelink = DI::session()->get('visitor_home', ''); $homelink = DI::session()->get('visitor_home', '');
} }
// since $userinfo isn't available for the hook we write it to the nav array // since $userinfo isn't available for the hook we write it to the nav array
// this isn't optimal because the contact query will be done now twice // this isn't optimal because the contact query will be done now twice
$fields = ['id', 'url', 'avatar', 'micro', 'name', 'nick', 'baseurl', 'updated']; $fields = ['id', 'url', 'guid', 'avatar', 'micro', 'name', 'nick', 'baseurl', 'updated'];
if (DI::userSession()->isAuthenticated()) { if (DI::userSession()->isAuthenticated()) {
$remoteUser = Contact::selectFirst($fields, ['uid' => DI::userSession()->getLocalUserId(), 'self' => true]); $remoteUser = Contact::selectFirstAccountUser($fields, ['uid' => DI::userSession()->getLocalUserId(), 'self' => true]);
} elseif (!DI::userSession()->getLocalUserId() && DI::userSession()->getRemoteUserId()) { } elseif (!DI::userSession()->getLocalUserId() && DI::userSession()->getRemoteUserId()) {
$remoteUser = Contact::getById(DI::userSession()->getRemoteUserId(), $fields); $remoteUser = Contact::selectFirstAccountUser($fields, ['id' => DI::userSession()->getRemoteUserId()]);
$nav_info['nav']['remote'] = DI::l10n()->t('Guest'); $nav_info['nav']['remote'] = DI::l10n()->t('Guest');
} elseif (Profile::getMyURL()) { } elseif (DI::userSession()->getMyUrl()) {
$remoteUser = Contact::getByURL($homelink, null, $fields); $remoteUser = Contact::selectFirstAccount($fields, ['id' => Contact::getIdForURL($homelink)]);
$nav_info['nav']['remote'] = DI::l10n()->t('Visitor'); $nav_info['nav']['remote'] = DI::l10n()->t('Visitor');
} else { } else {
$remoteUser = null; $remoteUser = null;