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

View File

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

View File

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

View File

@ -875,23 +875,23 @@ class BBCode
function ($match) use ($callback, $uriid) {
$attributes = self::extractShareAttributes($match[2]);
$author_contact = Contact::getByURL($attributes['profile'], false, ['id', 'url', 'addr', 'name', 'micro']);
$author_contact['url'] = ($author_contact['url'] ?? $attributes['profile']);
$author_contact['addr'] = ($author_contact['addr'] ?? '');
$author_account = Contact::selectFirstAccount(['id', 'url', 'addr', 'name', 'micro', 'guid', 'updated'], ['url' => $attributes['profile']]);
$author_account['url'] = ($author_account['url'] ?? $attributes['profile']);
$author_account['addr'] = ($author_account['addr'] ?? '');
$attributes['author'] = ($author_contact['name'] ?? '') ?: $attributes['author'];
$attributes['avatar'] = ($author_contact['micro'] ?? '') ?: $attributes['avatar'];
$attributes['profile'] = ($author_contact['url'] ?? '') ?: $attributes['profile'];
$attributes['author'] = ($author_account['name'] ?? '') ?: $attributes['author'];
$attributes['avatar'] = ($author_account['micro'] ?? '') ?: $attributes['avatar'];
$attributes['profile'] = ($author_account['url'] ?? '') ?: $attributes['profile'];
if (!empty($author_contact['id'])) {
$attributes['avatar'] = Contact::getAvatarUrlForId($author_contact['id'], Proxy::SIZE_THUMB);
if (!empty($author_account['id'])) {
$attributes['avatar'] = Contact::getAvatarUrlForId($author_account['guid'], $author_account['updated'], Proxy::SIZE_THUMB);
} elseif ($attributes['avatar']) {
$attributes['avatar'] = self::proxyUrl($attributes['avatar'], self::INTERNAL, $uriid, Proxy::SIZE_THUMB);
}
$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
);

View File

@ -104,19 +104,18 @@ class ContactBlock
$contact_uriids = array_column($personal_contacts, 'uri-id');
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);
$micropro = [];
while ($contact = DBA::fetch($contacts_stmt)) {
$contacts[] = $contact;
$micropro[] = HTML::micropro($contact, true, 'mpfriend');
while ($account = DBA::fetch($accounts_stmt)) {
$contacts[] = $account;
$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
*
* @template widget/vcard.tpl
* @param array $contact
* @param array $account Account array (from account-* view)
* @param bool $hide_mention
* @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'])) {
Logger::warning('Incomplete contact', ['contact' => $contact ?? []]);
if (!isset($account['network']) || !isset($account['id'])) {
Logger::warning('Incomplete contact', ['contact' => $account ?? []]);
}
$contact_url = Contact::getProfileLink($contact);
$contact_url = Contact::getProfileLink($account);
if ($contact['network'] != '') {
$network_link = Strings::formatNetworkName($contact['network'], $contact_url);
$network_avatar = ContactSelector::networkToIcon($contact['network'], $contact_url);
if ($account['network'] != '') {
$network_link = Strings::formatNetworkName($account['network'], $contact_url);
$network_avatar = ContactSelector::networkToIcon($account['network'], $contact_url);
} else {
$network_link = '';
$network_avatar = '';
@ -68,15 +68,15 @@ class VCard
$mention_link = '';
$showgroup_link = '';
$photo = Contact::getPhoto($contact);
$photo = Contact::getPhoto($account);
if (DI::userSession()->getLocalUserId()) {
if ($contact['uid']) {
$id = $contact['id'];
$rel = $contact['rel'];
$pending = $contact['pending'];
if ($account['uid']) {
$id = $account['id'];
$rel = $account['rel'];
$pending = $account['pending'];
} 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;
$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])) {
$unfollow_link = 'contact/unfollow?url=' . urlencode($contact_url) . '&auto=1';
} 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;
}
if ($contact['contact-type'] == Contact::TYPE_COMMUNITY) {
if ($account['contact-type'] == Contact::TYPE_COMMUNITY) {
if (!$hide_mention) {
$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;
} elseif (!$hide_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'), [
'$contact' => $contact,
'$contact' => $account,
'$photo' => $photo,
'$url' => Contact::magicLinkByContact($contact, $contact_url),
'$about' => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['about'] ?? ''),
'$url' => Contact::magicLinkByContact($account, $contact_url),
'$about' => BBCode::convertForUriId($account['uri-id'] ?? 0, $account['about'] ?? ''),
'$xmpp' => DI::l10n()->t('XMPP:'),
'$matrix' => DI::l10n()->t('Matrix:'),
'$location' => DI::l10n()->t('Location:'),
'$network_link' => $network_link,
'$network_avatar' => $network_avatar,
'$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_link' => $follow_link,
'$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
{
$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)) {
throw new HTTPException\NotFoundException('Contact ' . $contactUriId . ' not found');
}

View File

@ -56,27 +56,27 @@ class User extends BaseFactory
{
$cdata = Contact::getPublicAndUserContactID($contactId, $uid);
if (!empty($cdata)) {
$publicContact = Contact::getById($cdata['public']);
$publicAccount = Contact::selectFirstAccount([], ['id' => $cdata['public']]);
$userContact = Contact::getById($cdata['user']);
} else {
$publicContact = Contact::getById($contactId);
$publicAccount = Contact::selectFirstAccount([], ['id' => $contactId]);
$userContact = [];
}
$apcontact = APContact::getByURL($publicContact['url'], false);
$apcontact = APContact::getByURL($publicAccount['url'], false);
$status = null;
if (!$skip_status) {
$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]]);
if (!empty($post['uri-id'])) {
$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
*
* @param array $contact contact array
* @param array $account Account array (from account-* view)
* @param string $size Size of the avatar picture
* @param bool $no_update Don't perform an update if no cached avatar was found
* @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')) {
switch ($size) {
case Proxy::SIZE_MICRO:
if (!empty($contact['micro']) && !Photo::isPhotoURI($contact['micro'])) {
return $contact['micro'];
if (!empty($account['micro']) && !Photo::isPhotoURI($account['micro'])) {
return $account['micro'];
}
break;
case Proxy::SIZE_THUMB:
if (!empty($contact['thumb']) && !Photo::isPhotoURI($contact['thumb'])) {
return $contact['thumb'];
if (!empty($account['thumb']) && !Photo::isPhotoURI($account['thumb'])) {
return $account['thumb'];
}
break;
case Proxy::SIZE_SMALL:
if (!empty($contact['photo']) && !Photo::isPhotoURI($contact['photo'])) {
return $contact['photo'];
if (!empty($account['photo']) && !Photo::isPhotoURI($account['photo'])) {
return $account['photo'];
}
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
*
* @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
* @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
*
* @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
* @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
*
* @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
* @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 $size One of the Proxy::SIZE_* constants
* @param string $guid Contact guid (from account-* views)
* @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
* @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);
$url = DI::baseUrl() . '/photo/contact/';
@ -2194,7 +2186,7 @@ class Contact
$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` = ?)",
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'] ?? '');
$account = self::selectFirstAccountUser(['id', 'guid', 'updated'], $condition, ['order' => ['uid' => true]]);
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 $size One of the Proxy::SIZE_* constants
* @param string $guid Contact guid (from account-* views)
* @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
* @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);
$url = DI::baseUrl() . '/photo/header/';
@ -2263,7 +2247,7 @@ class Contact
$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'];
$contact = Contact::selectFirst(['id'], ['uri-id' => $profile['uri-id'], 'uid' => 0]);
if (!$contact) {
$account = Contact::selectFirstAccount(['id', 'guid', 'updated'], ['uri-id' => $profile['uri-id']]);
if (!$account) {
return $o;
}
$cid = $contact['id'];
$cid = $account['id'];
$follow_link = null;
$unfollow_link = null;
@ -449,7 +449,7 @@ class Profile
$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']);

View File

@ -104,37 +104,37 @@ class Advanced extends BaseModule
{
$cid = $this->parameters['id'];
$contact = Model\Contact::selectFirst([], ['id' => $cid, 'uid' => DI::userSession()->getLocalUserId()]);
if (empty($contact)) {
$account = Model\Contact::selectFirstAccountUser([], ['id' => $cid, 'uid' => DI::userSession()->getLocalUserId()]);
if (empty($account)) {
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";
// This data is fetched automatically for most networks.
// 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';
} else {
$readonly = '';
}
$tab_str = Contact::getTabsHTML($contact, Contact::TAB_ADVANCED);
$tab_str = Contact::getTabsHTML($account, Contact::TAB_ADVANCED);
$tpl = Renderer::getMarkupTemplate('contact/advanced.tpl');
return Renderer::replaceMacros($tpl, [
'$tab_str' => $tab_str,
'$returnaddr' => $returnaddr,
'$return' => $this->t('Return to contact editor'),
'$contact_id' => $contact['id'],
'$contact_id' => $account['id'],
'$lbl_submit' => $this->t('Submit'),
'$name' => ['name', $this->t('Name'), $contact['name'], '', '', $readonly],
'$nick' => ['nick', $this->t('Account Nickname'), $contact['nick'], '', '', 'readonly'],
'$url' => ['url', $this->t('Account URL'), $contact['url'], '', '', 'readonly'],
'poll' => ['poll', $this->t('Poll/Feed URL'), $contact['poll'], '', '', ($contact['network'] == Protocol::FEED) ? '' : 'readonly'],
'$name' => ['name', $this->t('Name'), $account['name'], '', '', $readonly],
'$nick' => ['nick', $this->t('Account Nickname'), $account['nick'], '', '', 'readonly'],
'$url' => ['url', $this->t('Account URL'), $account['url'], '', '', '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],
]);
}

View File

@ -66,14 +66,14 @@ class Contacts extends BaseModule
throw new HTTPException\BadRequestException($this->t('Invalid contact.'));
}
$contact = Model\Contact::getById($cid, []);
if (empty($contact)) {
$account = Model\Contact::selectFirstAccount([], ['id' => $cid]);
if (empty($account)) {
throw new HTTPException\NotFoundException($this->t('Contact not found.'));
}
$localContactId = Model\Contact::getPublicIdByUserId($this->userSession->getLocalUserId());
$this->page['aside'] = Widget\VCard::getHTML($contact);
$this->page['aside'] = Widget\VCard::getHTML($account);
$condition = [
'blocked' => false,
@ -123,7 +123,7 @@ class Contacts extends BaseModule
$title = $this->tt('Mutual friend (%s)', 'Mutual friends (%s)', $total);
$desc = $this->t(
'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;
case 'common':
@ -131,7 +131,7 @@ class Contacts extends BaseModule
$title = $this->tt('Common contact (%s)', 'Common contacts (%s)', $total);
$desc = $this->t(
'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;
default:
@ -139,7 +139,7 @@ class Contacts extends BaseModule
$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);

View File

@ -86,19 +86,19 @@ class Conversations extends BaseModule
throw new NotFoundException($this->t('Contact not found.'));
}
$contact = Model\Contact::getById($data['public']);
if (empty($contact)) {
$account = Model\Contact::selectFirstAccount([], ['id' => $data['public']]);
if (empty($account)) {
throw new NotFoundException($this->t('Contact not found.'));
}
// 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.'));
}
$localRelationship = $this->localRelationship->getForUserContact($this->userSession->getLocalUserId(), $contact['id']);
$localRelationship = $this->localRelationship->getForUserContact($this->userSession->getLocalUserId(), $account['id']);
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
@ -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-typeahead.css'));
$this->page['aside'] .= Widget\VCard::getHTML($contact, true);
$this->page['aside'] .= Widget\VCard::getHTML($account, true);
Nav::setSelected('contact');
@ -115,12 +115,12 @@ class Conversations extends BaseModule
'lockstate' => ACL::getLockstateForUserId($this->userSession->getLocalUserId()) ? 'lock' : 'unlock',
'acl' => ACL::getFullSelectorHTML($this->page, $this->userSession->getLocalUserId(), true, []),
'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 .= Contact::getTabsHTML($contact, Contact::TAB_CONVERSATIONS);
$o .= Model\Contact::getThreadsFromId($contact['id'], $this->userSession->getLocalUserId(), 0, 0, $request['last_created'] ?? '');
$o .= Contact::getTabsHTML($account, Contact::TAB_CONVERSATIONS);
$o .= Model\Contact::getThreadsFromId($account['id'], $this->userSession->getLocalUserId(), 0, 0, $request['last_created'] ?? '');
return $o;
}

View File

@ -116,23 +116,23 @@ class Follow extends BaseModule
$submit = '';
}
$contact = Contact::getByURL($url, true);
$account = Contact::selectFirstAccount([], ['id' => Contact::getIdForURL($url, 0, true)]);
// Possibly it is a mail contact
if (empty($contact)) {
$contact = Probe::uri($url, Protocol::MAIL, $uid);
if (empty($account)) {
$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
$this->followRemoteItem($url);
$this->sysMessages->addNotice($this->t('The network type couldn\'t be detected. Contact can\'t be added.'));
$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')) {
$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) {
$contact['url'] = $contact['addr'];
$account['url'] = $account['addr'];
}
if (!empty($request['auto'])) {
$this->process($contact['url']);
$this->process($account['url']);
}
$requestUrl = $this->baseUrl . '/contact/follow';
@ -173,27 +173,27 @@ class Follow extends BaseModule
'$cancel' => $this->t('Cancel'),
'$action' => $requestUrl,
'$name' => $contact['name'],
'$url' => $contact['url'],
'$zrl' => Profile::zrl($contact['url']),
'$name' => $account['name'],
'$url' => $account['url'],
'$zrl' => Profile::zrl($account['url']),
'$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:')],
]);
$this->page['aside'] = '';
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'),
['$title' => $this->t('Posts and Replies')]
);
// Show last public posts
$output .= Contact::getPostsFromUrl($contact['url'], $this->session->getLocalUserId());
$output .= Contact::getPostsFromUrl($account['url'], $this->session->getLocalUserId());
}
return $output;

View File

@ -56,16 +56,16 @@ class Media extends BaseModule
{
$cid = $this->parameters['id'];
$contact = Model\Contact::selectFirst([], ['id' => $cid]);
if (empty($contact)) {
$account = Model\Contact::selectFirstAccount([], ['id' => $cid]);
if (empty($account)) {
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;
}

View File

@ -154,43 +154,43 @@ class Profile extends BaseModule
throw new HTTPException\NotFoundException($this->t('Contact not found.'));
}
$contact = Contact::getById($data['public']);
if (!$this->db->isResult($contact)) {
$account = Contact::selectFirstAccount([], ['id' => $data['public']]);
if (!$this->db->isResult($account)) {
throw new HTTPException\NotFoundException($this->t('Contact not found.'));
}
// 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.'));
}
$localRelationship = $this->localRelationship->getForUserContact($this->session->getLocalUserId(), $contact['id']);
$localRelationship = $this->localRelationship->getForUserContact($this->session->getLocalUserId(), $account['id']);
if ($localRelationship->rel === Contact::SELF) {
$this->baseUrl->redirect('profile/' . $contact['nick'] . '/profile');
$this->baseUrl->redirect('profile/' . $account['nick'] . '/profile');
}
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'];
if ($cmd === 'update' && $localRelationship->rel !== Contact::NOTHING) {
Module\Contact::updateContactFromPoll($contact['id']);
Module\Contact::updateContactFromPoll($account['id']);
}
if ($cmd === 'updateprofile') {
$this->updateContactFromProbe($contact['id']);
$this->updateContactFromProbe($account['id']);
}
if ($cmd === 'block') {
if ($localRelationship->blocked) {
// @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');
} else {
// @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');
}
@ -201,12 +201,12 @@ class Profile extends BaseModule
if ($cmd === 'ignore') {
if ($localRelationship->ignored) {
// @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');
} else {
// @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');
}
@ -217,12 +217,12 @@ class Profile extends BaseModule
if ($cmd === 'collapse') {
if ($localRelationship->collapsed) {
// @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');
} else {
// @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');
}
@ -230,10 +230,10 @@ class Profile extends BaseModule
$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 = '';
if (!in_array($localRelationship->rel, [Contact::NOTHING, Contact::SELF])) {
@ -251,18 +251,18 @@ class Profile extends BaseModule
]);
switch ($localRelationship->rel) {
case Contact::FRIEND: $relation_text = $this->t('You are mutual friends with %s', $contact['name']); break;
case Contact::FOLLOWER: $relation_text = $this->t('You are sharing with %s', $contact['name']); break;
case Contact::SHARING: $relation_text = $this->t('%s is sharing with you', $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', $account['name']); break;
case Contact::SHARING: $relation_text = $this->t('%s is sharing with you', $account['name']); break;
default:
$relation_text = '';
}
if (!Protocol::supportsFollow($contact['network'])) {
if (!Protocol::supportsFollow($account['network'])) {
$relation_text = '';
}
$url = Contact::magicLinkByContact($contact);
$url = Contact::magicLinkByContact($account);
if (strpos($url, 'contact/redir/') === 0) {
$sparkle = ' class="sparkle" ';
} else {
@ -272,34 +272,34 @@ class Profile extends BaseModule
$insecure = $this->t('Private communications are not available for this contact.');
// @TODO: Figure out why gsid can be empty
if (empty($contact['gsid'])) {
$this->logger->notice('Empty gsid for contact', ['contact' => $contact]);
if (empty($account['gsid'])) {
$this->logger->notice('Empty gsid for contact', ['contact' => $account]);
}
$serverIgnored =
$contact['gsid'] &&
$this->userGServer->isIgnoredByUser($this->session->getLocalUserId(), $contact['gsid']) ?
$account['gsid'] &&
$this->userGServer->isIgnoredByUser($this->session->getLocalUserId(), $account['gsid']) ?
$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) {
$last_update .= ' ' . ($contact['failed'] ? $this->t('(Update was not successful)') : $this->t('(Update was successful)'));
if ($account['last-update'] > DBA::NULL_DATETIME) {
$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
$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;
if ($contact['network'] == Protocol::FEED) {
if ($account['network'] == Protocol::FEED) {
$fetch_further_information = [
'fetch_further_information',
$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');
if ($contact['network'] == Protocol::FEED) {
if ($account['network'] == Protocol::FEED) {
$remote_self_options = [
Contact::MIRROR_DEACTIVATED => $this->t('No mirroring'),
Contact::MIRROR_OWN_POST => $this->t('Mirror as my own posting')
];
} elseif ($contact['network'] == Protocol::ACTIVITYPUB) {
} elseif ($account['network'] == Protocol::ACTIVITYPUB) {
$remote_self_options = [
Contact::MIRROR_DEACTIVATED => $this->t('No mirroring'),
Contact::MIRROR_NATIVE_RESHARE => $this->t('Native reshare')
];
} elseif ($contact['network'] == Protocol::DFRN) {
} elseif ($account['network'] == Protocol::DFRN) {
$remote_self_options = [
Contact::MIRROR_DEACTIVATED => $this->t('No mirroring'),
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;
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);
}
$contact_actions = $this->getContactActions($contact, $localRelationship);
$contact_actions = $this->getContactActions($account, $localRelationship);
if ($localRelationship->rel !== Contact::NOTHING) {
$lbl_info1 = $this->t('Contact Information / Notes');
@ -364,11 +364,11 @@ class Profile extends BaseModule
'$submit' => $this->t('Submit'),
'$lbl_info1' => $lbl_info1,
'$lbl_info2' => $this->t('Their personal note'),
'$reason' => trim($contact['reason'] ?? ''),
'$reason' => trim($account['reason'] ?? ''),
'$infedit' => $this->t('Edit contact notes'),
'$common_link' => 'contact/' . $contact['id'] . '/contacts/common',
'$common_link' => 'contact/' . $account['id'] . '/contacts/common',
'$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'),
'$ignorecont' => $this->t('Ignore contact'),
'$lblrecent' => $this->t('View conversations'),
@ -381,13 +381,13 @@ class Profile extends BaseModule
'$updpub' => $this->t('Update public posts'),
'$last_update' => $last_update,
'$udnow' => $this->t('Update now'),
'$contact_id' => $contact['id'],
'$contact_id' => $account['id'],
'$pending' => $localRelationship->pending ? $this->t('Awaiting connection acknowledge') : '',
'$blocked' => $localRelationship->blocked ? $this->t('Currently blocked') : '',
'$ignored' => $localRelationship->ignored ? $this->t('Currently ignored') : '',
'$collapsed' => $localRelationship->collapsed ? $this->t('Currently collapsed') : '',
'$archived' => ($contact['archive'] ? $this->t('Currently archived') : ''),
'$insecure' => (in_array($contact['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::MAIL, Protocol::DIASPORA]) ? '' : $insecure),
'$archived' => ($account['archive'] ? $this->t('Currently archived') : ''),
'$insecure' => (in_array($account['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::MAIL, Protocol::DIASPORA]) ? '' : $insecure),
'$serverIgnored' => $serverIgnored,
'$manageServers' => $this->t('Manage remote servers'),
'$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')],
'$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')],
'$photo' => Contact::getPhoto($contact),
'$name' => $contact['name'],
'$photo' => Contact::getPhoto($account),
'$name' => $account['name'],
'$sparkle' => $sparkle,
'$url' => $url,
'$profileurllabel' => $this->t('Profile URL'),
'$profileurl' => $contact['url'],
'$account_type' => Contact::getAccountType($contact['contact-type']),
'$location' => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['location']),
'$profileurl' => $account['url'],
'$account_type' => Contact::getAccountType($account['contact-type']),
'$location' => BBCode::convertForUriId($account['uri-id'] ?? 0, $account['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:'),
'$matrix' => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['matrix']),
'$matrix' => BBCode::convertForUriId($account['uri-id'] ?? 0, $account['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:'),
'$keywords' => $contact['keywords'],
'$keywords' => $account['keywords'],
'$keywords_label' => $this->t('Tags:'),
'$contact_action_button' => $this->t('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],
]);
$arr = ['contact' => $contact, 'output' => $o];
$arr = ['contact' => $account, 'output' => $o];
Hook::callAll('contact_edit', $arr);

View File

@ -105,13 +105,12 @@ class Directory extends BaseModule
* array for displaying directory entries.
*
* @param array $contact The directory entry from the database.
* @param string $photo_size Avatar size (thumb, photo or micro).
*
* @return array
*
* @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']);

View File

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

View File

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

View File

@ -99,11 +99,12 @@ class Introduction extends BaseFactory
try {
$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`.`photo` AS `fphoto`, `suggest-contact`.`request` AS `frequest`
FROM `intro`
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`
WHERE `intro`.`uid` = ? $sql_extra
LIMIT ?, ?",

View File

@ -2258,7 +2258,7 @@ class Probe
'name' => $owner['name'], 'nick' => $owner['nick'], 'guid' => $approfile['diaspora:guid'] ?? '',
'url' => $owner['url'], 'addr' => $owner['addr'], 'alias' => $owner['alias'],
'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),
'keywords' => $owner['keywords'], 'location' => $owner['location'], 'about' => $owner['about'],
'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->url = $account['url'];
$this->avatar = Contact::getAvatarUrlForId($account['id'] ?? 0 ?: $account['pid'], Proxy::SIZE_SMALL, $account['updated'], $account['guid'] ?? '');
$this->avatar_static = Contact::getAvatarUrlForId($account['id'] ?? 0 ?: $account['pid'], Proxy::SIZE_SMALL, $account['updated'], $account['guid'] ?? '', true);
$this->header = Contact::getHeaderUrlForId($account['id'] ?? 0 ?: $account['pid'], '', $account['updated'], $account['guid'] ?? '');
$this->header_static = Contact::getHeaderUrlForId($account['id'] ?? 0 ?: $account['pid'], '', $account['updated'], $account['guid'] ?? '', true);
$this->avatar = Contact::getAvatarUrlForId($account['guid'], $account['updated'], Proxy::SIZE_SMALL);
$this->avatar_static = Contact::getAvatarUrlForId($account['guid'], $account['updated'], Proxy::SIZE_SMALL, true);
$this->header = Contact::getHeaderUrlForId($account['guid'], $account['updated']);
$this->header_static = Contact::getHeaderUrlForId($account['guid'], $account['updated'], null, true);
$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->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 $userContact Optional full contact table record with uid != 0
* @param null $status
@ -136,18 +136,18 @@ class User extends BaseDataTransferObject
*
* @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;
$this->id = (int)$publicContact['id'];
$this->id_str = (string) $publicContact['id'];
$this->name = $publicContact['name'] ?: $publicContact['nick'];
$this->screen_name = $publicContact['nick'] ?: $publicContact['name'];
$this->location = $publicContact['location'] ?:
ContactSelector::networkToName($publicContact['network'], $publicContact['url'], $publicContact['protocol']);
$this->id = (int)$publicAccount['id'];
$this->id_str = (string) $publicAccount['id'];
$this->name = $publicAccount['name'] ?: $publicAccount['nick'];
$this->screen_name = $publicAccount['nick'] ?: $publicAccount['name'];
$this->location = $publicAccount['location'] ?:
ContactSelector::networkToName($publicAccount['network'], $publicAccount['url'], $publicAccount['protocol']);
$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
$this->entities = [
'url' => ['urls' => []],
@ -156,17 +156,17 @@ class User extends BaseDataTransferObject
if (!$include_user_entities) {
unset($this->entities);
}
$this->description = (!empty($publicContact['about']) ? BBCode::toPlaintext($publicContact['about']) : '');
$this->profile_image_url_https = Contact::getAvatarUrlForUrl($publicContact['url'], $uid, Proxy::SIZE_MICRO);
$this->description = (!empty($publicAccount['about']) ? BBCode::toPlaintext($publicAccount['about']) : '');
$this->profile_image_url_https = Contact::getAvatarUrlForUrl($publicAccount['url'], $uid, Proxy::SIZE_MICRO);
$this->protected = false;
$this->followers_count = $apcontact['followers_count'] ?? 0;
$this->friends_count = $apcontact['following_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->verified = $uid != 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_image = false;
@ -181,9 +181,9 @@ class User extends BaseDataTransferObject
unset($this->withheld_scope);
// Deprecated
$this->profile_image_url = Contact::getAvatarUrlForUrl($publicContact['url'], $uid, Proxy::SIZE_MICRO);
$this->profile_image_url_profile_size = Contact::getAvatarUrlForUrl($publicContact['url'], $uid, Proxy::SIZE_THUMB);
$this->profile_image_url_large = Contact::getAvatarUrlForUrl($publicContact['url'], $uid, Proxy::SIZE_LARGE);
$this->profile_image_url = Contact::getAvatarUrlForUrl($publicAccount['url'], $uid, Proxy::SIZE_MICRO);
$this->profile_image_url_profile_size = Contact::getAvatarUrlForUrl($publicAccount['url'], $uid, Proxy::SIZE_THUMB);
$this->profile_image_url_large = Contact::getAvatarUrlForUrl($publicAccount['url'], $uid, Proxy::SIZE_LARGE);
$this->utc_offset = 0;
$this->time_zone = 'UTC';
$this->geo_enabled = false;
@ -199,7 +199,7 @@ class User extends BaseDataTransferObject
// Friendica-specific
$this->uid = (int)$uid;
$this->cid = (int)($userContact['id'] ?? 0);
$this->pid = (int)$publicContact['id'];
$this->statusnet_profile_url = $publicContact['url'];
$this->pid = (int)$publicAccount['id'];
$this->statusnet_profile_url = $publicAccount['url'];
}
}

View File

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

View File

@ -571,6 +571,7 @@ return [
'/{type}/{id:\d+}' => [Module\Photo::class, [R::GET]],
'/{type:contact|header}/{guid}' => [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+}/{guid}' => [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
* 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
* @throws Exception
* @throws ImagickException
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
function frio_remote_nav(array &$nav_info)
{
if (DI::mode()->has(App\Mode::MAINTENANCEDISABLED)) {
// get the homelink from $_SESSION
$homelink = Profile::getMyURL();
$homelink = DI::userSession()->getMyUrl();
if (!$homelink) {
$homelink = DI::session()->get('visitor_home', '');
}
// 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
$fields = ['id', 'url', 'avatar', 'micro', 'name', 'nick', 'baseurl', 'updated'];
$fields = ['id', 'url', 'guid', 'avatar', 'micro', 'name', 'nick', 'baseurl', 'updated'];
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()) {
$remoteUser = Contact::getById(DI::userSession()->getRemoteUserId(), $fields);
$remoteUser = Contact::selectFirstAccountUser($fields, ['id' => DI::userSession()->getRemoteUserId()]);
$nav_info['nav']['remote'] = DI::l10n()->t('Guest');
} elseif (Profile::getMyURL()) {
$remoteUser = Contact::getByURL($homelink, null, $fields);
} elseif (DI::userSession()->getMyUrl()) {
$remoteUser = Contact::selectFirstAccount($fields, ['id' => Contact::getIdForURL($homelink)]);
$nav_info['nav']['remote'] = DI::l10n()->t('Visitor');
} else {
$remoteUser = null;