diff --git a/src/DI.php b/src/DI.php index e6d9c341b..709f57707 100644 --- a/src/DI.php +++ b/src/DI.php @@ -292,6 +292,14 @@ abstract class DI return self::$dice->create(Repository\PermissionSet::class); } + /** + * @return Repository\ProfileField + */ + public static function profileField() + { + return self::$dice->create(Repository\ProfileField::class); + } + // // "Protocol" namespace instances // diff --git a/src/Model/Profile.php b/src/Model/Profile.php index c40516907..774f419ee 100644 --- a/src/Model/Profile.php +++ b/src/Model/Profile.php @@ -5,10 +5,7 @@ namespace Friendica\Model; use Friendica\App; -use Friendica\Content\Feature; -use Friendica\Content\ForumManager; use Friendica\Content\Text\BBCode; -use Friendica\Content\Text\HTML; use Friendica\Content\Widget\ContactBlock; use Friendica\Core\Cache\Duration; use Friendica\Core\Hook; @@ -25,7 +22,6 @@ use Friendica\Util\DateTimeFormat; use Friendica\Util\Network; use Friendica\Util\Proxy as ProxyUtils; use Friendica\Util\Strings; -use Friendica\Util\Temporal; class Profile { @@ -690,149 +686,6 @@ class Profile ]); } - public static function getAdvanced(App $a) - { - $uid = intval($a->profile['uid']); - - if ($a->profile['name']) { - $profile = []; - - $profile['fullname'] = [DI::l10n()->t('Full Name:'), $a->profile['name']]; - - if (Feature::isEnabled($uid, 'profile_membersince')) { - $profile['membersince'] = [DI::l10n()->t('Member since:'), DateTimeFormat::local($a->profile['register_date'])]; - } - - if ($a->profile['gender']) { - $profile['gender'] = [DI::l10n()->t('Gender:'), DI::l10n()->t($a->profile['gender'])]; - } - - if (!empty($a->profile['dob']) && $a->profile['dob'] > DBA::NULL_DATE) { - $year_bd_format = DI::l10n()->t('j F, Y'); - $short_bd_format = DI::l10n()->t('j F'); - - $val = DI::l10n()->getDay( - intval($a->profile['dob']) ? - DateTimeFormat::utc($a->profile['dob'] . ' 00:00 +00:00', $year_bd_format) - : DateTimeFormat::utc('2001-' . substr($a->profile['dob'], 5) . ' 00:00 +00:00', $short_bd_format) - ); - - $profile['birthday'] = [DI::l10n()->t('Birthday:'), $val]; - } - - if (!empty($a->profile['dob']) - && $a->profile['dob'] > DBA::NULL_DATE - && $age = Temporal::getAgeByTimezone($a->profile['dob'], $a->profile['timezone']) - ) { - $profile['age'] = [DI::l10n()->t('Age: ') , DI::l10n()->tt('%d year old', '%d years old', $age)]; - } - - if ($a->profile['marital']) { - $profile['marital'] = [DI::l10n()->t('Status:'), DI::l10n()->t($a->profile['marital'])]; - } - - /// @TODO Maybe use x() here, plus below? - if ($a->profile['with']) { - $profile['marital']['with'] = $a->profile['with']; - } - - if (strlen($a->profile['howlong']) && $a->profile['howlong'] > DBA::NULL_DATETIME) { - $profile['howlong'] = Temporal::getRelativeDate($a->profile['howlong'], DI::l10n()->t('for %1$d %2$s')); - } - - if ($a->profile['sexual']) { - $profile['sexual'] = [DI::l10n()->t('Sexual Preference:'), DI::l10n()->t($a->profile['sexual'])]; - } - - if ($a->profile['homepage']) { - $profile['homepage'] = [DI::l10n()->t('Homepage:'), HTML::toLink($a->profile['homepage'])]; - } - - if ($a->profile['hometown']) { - $profile['hometown'] = [DI::l10n()->t('Hometown:'), HTML::toLink($a->profile['hometown'])]; - } - - if ($a->profile['pub_keywords']) { - $profile['pub_keywords'] = [DI::l10n()->t('Tags:'), $a->profile['pub_keywords']]; - } - - if ($a->profile['politic']) { - $profile['politic'] = [DI::l10n()->t('Political Views:'), $a->profile['politic']]; - } - - if ($a->profile['religion']) { - $profile['religion'] = [DI::l10n()->t('Religion:'), $a->profile['religion']]; - } - - if ($txt = BBCode::convert($a->profile['about'])) { - $profile['about'] = [DI::l10n()->t('About:'), $txt]; - } - - if ($txt = BBCode::convert($a->profile['interest'])) { - $profile['interest'] = [DI::l10n()->t('Hobbies/Interests:'), $txt]; - } - - if ($txt = BBCode::convert($a->profile['likes'])) { - $profile['likes'] = [DI::l10n()->t('Likes:'), $txt]; - } - - if ($txt = BBCode::convert($a->profile['dislikes'])) { - $profile['dislikes'] = [DI::l10n()->t('Dislikes:'), $txt]; - } - - if ($txt = BBCode::convert($a->profile['contact'])) { - $profile['contact'] = [DI::l10n()->t('Contact information and Social Networks:'), $txt]; - } - - if ($txt = BBCode::convert($a->profile['music'])) { - $profile['music'] = [DI::l10n()->t('Musical interests:'), $txt]; - } - - if ($txt = BBCode::convert($a->profile['book'])) { - $profile['book'] = [DI::l10n()->t('Books, literature:'), $txt]; - } - - if ($txt = BBCode::convert($a->profile['tv'])) { - $profile['tv'] = [DI::l10n()->t('Television:'), $txt]; - } - - if ($txt = BBCode::convert($a->profile['film'])) { - $profile['film'] = [DI::l10n()->t('Film/dance/culture/entertainment:'), $txt]; - } - - if ($txt = BBCode::convert($a->profile['romance'])) { - $profile['romance'] = [DI::l10n()->t('Love/Romance:'), $txt]; - } - - if ($txt = BBCode::convert($a->profile['work'])) { - $profile['work'] = [DI::l10n()->t('Work/employment:'), $txt]; - } - - if ($txt = BBCode::convert($a->profile['education'])) { - $profile['education'] = [DI::l10n()->t('School/education:'), $txt]; - } - - //show subcribed forum if it is enabled in the usersettings - if (Feature::isEnabled($uid, 'forumlist_profile')) { - $profile['forumlist'] = [DI::l10n()->t('Forums:'), ForumManager::profileAdvanced($uid)]; - } - - if ($a->profile['uid'] == local_user()) { - $profile['edit'] = [DI::baseUrl() . '/settings/profile', DI::l10n()->t('Edit profile'), '', DI::l10n()->t('Edit profile')]; - } - - $tpl = Renderer::getMarkupTemplate('profile/advanced.tpl'); - return Renderer::replaceMacros($tpl, [ - '$title' => DI::l10n()->t('Profile'), - '$basic' => DI::l10n()->t('Basic'), - '$advanced' => DI::l10n()->t('Advanced'), - '$profile' => $profile - ]); - } - - return ''; - } - /** * @param App $a * @param string $current diff --git a/src/Module/Profile/Index.php b/src/Module/Profile/Index.php index 9c5ce05d0..c0ca0ea06 100644 --- a/src/Module/Profile/Index.php +++ b/src/Module/Profile/Index.php @@ -3,16 +3,27 @@ namespace Friendica\Module\Profile; use Friendica\BaseModule; +use Friendica\Content\Feature; +use Friendica\Content\ForumManager; use Friendica\Content\Nav; +use Friendica\Content\Text\BBCode; +use Friendica\Content\Text\HTML; use Friendica\Core\Hook; +use Friendica\Core\Protocol; +use Friendica\Core\Renderer; use Friendica\Core\Session; use Friendica\Core\System; use Friendica\Database\DBA; use Friendica\DI; -use Friendica\Model\Profile as ProfileModel; +use Friendica\Model\Contact; +use Friendica\Model\Profile; +use Friendica\Model\Term; use Friendica\Model\User; use Friendica\Module\Security\Login; +use Friendica\Network\HTTPException; use Friendica\Protocol\ActivityPub; +use Friendica\Util\DateTimeFormat; +use Friendica\Util\Temporal; require_once 'boot.php'; @@ -46,77 +57,245 @@ class Index extends BaseModule { $a = DI::app(); - ProfileModel::load($a, $parameters['nickname']); + Profile::load($a, $parameters['nickname']); - $remote_contact_id = Session::getRemoteContactID($a->profile['uid']); + if (!$a->profile) { + throw new HTTPException\NotFoundException(DI::l10n()->t('Profile not found.')); + } + + $remote_contact_id = Session::getRemoteContactID($a->profile_uid); if (DI::config()->get('system', 'block_public') && !local_user() && !$remote_contact_id) { return Login::form(); } - DI::page()['htmlhead'] .= "\n"; + $is_owner = local_user() == $a->profile_uid; - $blocked = !local_user() && !$remote_contact_id && DI::config()->get('system', 'block_public'); - $userblock = !local_user() && !$remote_contact_id && $a->profile['hidewall']; + $o = ''; + if (!empty($a->profile['hidewall']) && !$is_owner && !$remote_contact_id) { + throw new HTTPException\ForbiddenException(DI::l10n()->t('Access to this profile has been restricted.')); + } if (!empty($a->profile['page-flags']) && $a->profile['page-flags'] == User::PAGE_FLAGS_COMMUNITY) { DI::page()['htmlhead'] .= '' . "\n"; } - if (!empty($a->profile['openidserver'])) { - DI::page()['htmlhead'] .= '' . "\n"; - } - - if (!empty($a->profile['openid'])) { - $delegate = strstr($a->profile['openid'], '://') ? $a->profile['openid'] : 'https://' . $a->profile['openid']; - DI::page()['htmlhead'] .= '' . "\n"; - } - - // site block - if (!$blocked && !$userblock) { - $keywords = str_replace(['#', ',', ' ', ',,'], ['', ' ', ',', ','], $a->profile['pub_keywords'] ?? ''); - if (strlen($keywords)) { - DI::page()['htmlhead'] .= '' . "\n"; - } - } - - DI::page()['htmlhead'] .= '' . "\n"; - - if (!$a->profile['net-publish'] || $a->profile['hidewall']) { - DI::page()['htmlhead'] .= '' . "\n"; - } - - DI::page()['htmlhead'] .= '' . "\n"; - DI::page()['htmlhead'] .= '' . "\n"; - DI::page()['htmlhead'] .= '' . "\n"; - DI::page()['htmlhead'] .= '' . "\n"; - $uri = urlencode('acct:' . $a->profile['nickname'] . '@' . DI::baseUrl()->getHostname() . (DI::baseUrl()->getUrlPath() ? '/' . DI::baseUrl()->getUrlPath() : '')); - DI::page()['htmlhead'] .= '' . "\n"; - header('Link: <' . DI::baseUrl() . '/xrd/?uri=' . $uri . '>; rel="lrdd"; type="application/xrd+xml"', false); - - $dfrn_pages = ['request', 'confirm', 'notify', 'poll']; - foreach ($dfrn_pages as $dfrn) { - DI::page()['htmlhead'] .= '' . "\n"; - } - DI::page()['htmlhead'] .= '' . "\n"; - - $o = ''; + DI::page()['htmlhead'] .= self::buildHtmlHead($a->profile, $parameters['nickname'], $remote_contact_id); Nav::setSelected('home'); $is_owner = local_user() == $a->profile['uid']; + $o = Profile::getTabs($a, 'profile', $is_owner, $a->profile['nickname']); if (!empty($a->profile['hidewall']) && !$is_owner && !$remote_contact_id) { notice(DI::l10n()->t('Access to this profile has been restricted.')); return ''; } - $o .= ProfileModel::getTabs($a, 'profile', $is_owner, $a->profile['nickname']); + $view_as_contacts = []; + $view_as_contact_id = 0; + if ($is_owner) { + $view_as_contact_id = intval($_GET['viewas'] ?? 0); - $o .= ProfileModel::getAdvanced($a); + $view_as_contacts = Contact::selectToArray(['id', 'name'], [ + 'uid' => local_user(), + 'rel' => [Contact::FOLLOWER, Contact::SHARING, Contact::FRIEND], + 'network' => Protocol::DFRN, + 'blocked' => false, + ]); + + // User manually provided a contact ID they aren't privy to, silently defaulting to their own view + if (!in_array($view_as_contact_id, array_column($view_as_contacts, 'id'))) { + $view_as_contact_id = 0; + } + } + + $basic_fields = []; + + $basic_fields += self::buildField('fullname', DI::l10n()->t('Full Name:'), $a->profile['name']); + + if (Feature::isEnabled($a->profile_uid, 'profile_membersince')) { + $basic_fields += self::buildField( + 'membersince', + DI::l10n()->t('Member since:'), + DateTimeFormat::local($a->profile['register_date']) + ); + } + + if (!empty($a->profile['dob']) && $a->profile['dob'] > DBA::NULL_DATE) { + $year_bd_format = DI::l10n()->t('j F, Y'); + $short_bd_format = DI::l10n()->t('j F'); + + $dob = DI::l10n()->getDay( + intval($a->profile['dob']) ? + DateTimeFormat::utc($a->profile['dob'] . ' 00:00 +00:00', $year_bd_format) + : DateTimeFormat::utc('2001-' . substr($a->profile['dob'], 5) . ' 00:00 +00:00', $short_bd_format) + ); + + $basic_fields += self::buildField('dob', DI::l10n()->t('Birthday:'), $dob); + + if ($age = Temporal::getAgeByTimezone($a->profile['dob'], $a->profile['timezone'])) { + $basic_fields += self::buildField('age', DI::l10n()->t('Age: '), DI::l10n()->tt('%d year old', '%d years old', $age)); + } + } + + if ($a->profile['pdesc']) { + $basic_fields += self::buildField('pdesc', DI::l10n()->t('Description:'), HTML::toLink($a->profile['pdesc'])); + } + + if ($a->profile['xmpp']) { + $basic_fields += self::buildField('xmpp', DI::l10n()->t('XMPP:'), $a->profile['xmpp']); + } + + if ($a->profile['homepage']) { + $basic_fields += self::buildField('homepage', DI::l10n()->t('Homepage:'), HTML::toLink($a->profile['homepage'])); + } + + $o .= Profile::getTabs($a, 'profile', $is_owner, $a->profile['nickname']); + if ( + $a->profile['address'] + || $a->profile['locality'] + || $a->profile['postal-code'] + || $a->profile['region'] + || $a->profile['country-name'] + ) { + $basic_fields += self::buildField('location', DI::l10n()->t('Location:'), Profile::formatLocation($a->profile)); + } + + if ($a->profile['pub_keywords']) { + $tags = []; + foreach (explode(',', $a->profile['pub_keywords']) as $tag_label) { + $tags[] = [ + 'url' => '/search?tag=' . $tag_label, + 'label' => Term::TAG_CHARACTER[Term::HASHTAG] . $tag_label, + ]; + } + + $basic_fields += self::buildField('pub_keywords', DI::l10n()->t('Tags:'), $tags); + } + + $custom_fields = []; + + // Defaults to the current logged in user self contact id to show self-only fields + $contact_id = $view_as_contact_id ?: $remote_contact_id ?: 0; + + if ($is_owner && $contact_id === 0) { + $profile_fields = DI::profileField()->selectByUserId($a->profile_uid); + } else { + $profile_fields = DI::profileField()->selectByContactId($contact_id, $a->profile_uid); + } + + foreach ($profile_fields as $profile_field) { + $custom_fields += self::buildField( + 'custom_' . $profile_field->order, + $profile_field->label, + BBCode::convert($profile_field->value), + 'aprofile custom' + ); + }; + + //show subcribed forum if it is enabled in the usersettings + if (Feature::isEnabled($a->profile_uid, 'forumlist_profile')) { + $custom_fields += self::buildField( + 'forumlist', + DI::l10n()->t('Forums:'), + ForumManager::profileAdvanced($a->profile_uid) + ); + } + + $tpl = Renderer::getMarkupTemplate('profile/index.tpl'); + $o .= Renderer::replaceMacros($tpl, [ + '$title' => DI::l10n()->t('Profile'), + '$view_as_contacts' => $view_as_contacts, + '$view_as_contact_id' => $view_as_contact_id, + '$view_as' => DI::l10n()->t('View profile as:'), + '$basic' => DI::l10n()->t('Basic'), + '$advanced' => DI::l10n()->t('Advanced'), + '$is_owner' => $a->profile_uid == local_user(), + '$query_string' => DI::args()->getQueryString(), + '$basic_fields' => $basic_fields, + '$custom_fields' => $custom_fields, + '$profile' => $a->profile, + '$edit_link' => [ + 'url' => DI::baseUrl() . '/settings/profile', DI::l10n()->t('Edit profile'), + 'title' => '', + 'label' => DI::l10n()->t('Edit profile') + ], + ]); Hook::callAll('profile_advanced', $o); return $o; } + + /** + * Creates a profile field structure to be used in the profile template + * + * @param string $name Arbitrary name of the field + * @param string $label Display label of the field + * @param mixed $value Display value of the field + * @param string $class Optional CSS class to apply to the field + * @return array + */ + private static function buildField(string $name, string $label, $value, string $class = 'aprofile') + { + return [$name => [ + 'id' => 'aprofile-' . $name, + 'class' => $class, + 'label' => $label, + 'value' => $value, + ]]; + } + + private static function buildHtmlHead(array $profile, string $nickname, int $remote_contact_id) + { + $baseUrl = DI::baseUrl(); + + $htmlhead = "\n"; + + if (!empty($profile['page-flags']) && $profile['page-flags'] == User::PAGE_FLAGS_COMMUNITY) { + $htmlhead .= '' . "\n"; + } + + if (!empty($profile['openidserver'])) { + $htmlhead .= '' . "\n"; + } + + if (!empty($profile['openid'])) { + $delegate = strstr($profile['openid'], '://') ? $profile['openid'] : 'https://' . $profile['openid']; + $htmlhead .= '' . "\n"; + } + + // site block + $blocked = !local_user() && !$remote_contact_id && DI::config()->get('system', 'block_public'); + $userblock = !local_user() && !$remote_contact_id && $profile['hidewall']; + if (!$blocked && !$userblock) { + $keywords = str_replace(['#', ',', ' ', ',,'], ['', ' ', ',', ','], $profile['pub_keywords'] ?? ''); + if (strlen($keywords)) { + $htmlhead .= '' . "\n"; + } + } + + $htmlhead .= '' . "\n"; + + if (!$profile['net-publish'] || $profile['hidewall']) { + $htmlhead .= '' . "\n"; + } + + $htmlhead .= '' . "\n"; + $htmlhead .= '' . "\n"; + $htmlhead .= '' . "\n"; + $htmlhead .= '' . "\n"; + $uri = urlencode('acct:' . $profile['nickname'] . '@' . $baseUrl->getHostname() . ($baseUrl->getUrlPath() ? '/' . $baseUrl->getUrlPath() : '')); + $htmlhead .= '' . "\n"; + header('Link: <' . $baseUrl . '/xrd/?uri=' . $uri . '>; rel="lrdd"; type="application/xrd+xml"', false); + + $dfrn_pages = ['request', 'confirm', 'notify', 'poll']; + foreach ($dfrn_pages as $dfrn) { + $htmlhead .= '' . "\n"; + } + $htmlhead .= '' . "\n"; + + return $htmlhead; + } } diff --git a/src/Module/Settings/Profile/Index.php b/src/Module/Settings/Profile/Index.php index e1b1d96b9..c71783367 100644 --- a/src/Module/Settings/Profile/Index.php +++ b/src/Module/Settings/Profile/Index.php @@ -334,7 +334,7 @@ class Index extends BaseSettingsModule '$region' => ['region', DI::l10n()->t('Region/State:'), $profile['region']], '$postal_code' => ['postal_code', DI::l10n()->t('Postal/Zip Code:'), $profile['postal-code']], '$country_name' => ['country_name', DI::l10n()->t('Country:'), $profile['country-name']], - '$age' => ((intval($profile['dob'])) ? '(' . DI::l10n()->t('Age: ') . Temporal::getAgeByTimezone($profile['dob'], $a->user['timezone'], $a->user['timezone']) . ')' : ''), + '$age' => ((intval($profile['dob'])) ? '(' . DI::l10n()->t('Age: ') . DI::l10n()->tt('%d year old', '%d years old', Temporal::getAgeByTimezone($profile['dob'], $a->user['timezone'])) . ')' : ''), '$gender' => DI::l10n()->t(ContactSelector::gender($profile['gender'])), '$marital' => ['selector' => ContactSelector::maritalStatus($profile['marital']), 'value' => DI::l10n()->t($profile['marital'])], '$with' => ['with', DI::l10n()->t('Who: (if applicable)'), strip_tags($profile['with']), DI::l10n()->t('Examples: cathy123, Cathy Williams, cathy@example.com')], diff --git a/view/templates/profile/advanced.tpl b/view/templates/profile/advanced.tpl deleted file mode 100644 index 44e763f39..000000000 --- a/view/templates/profile/advanced.tpl +++ /dev/null @@ -1,184 +0,0 @@ - -{{include file="section_title.tpl"}} - -