X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FModel%2FProfile.php;h=361f1a7e4b20305fbb73180a010e4dfb352a1577;hb=1d86d79778bb7e2d00bd9031877e566a6037144e;hp=c5652c6f7a2e992f6561acefe799c9bab3f2253c;hpb=122ad0af14f046c2462a03fe33967dc41abfc8b5;p=friendica.git diff --git a/src/Model/Profile.php b/src/Model/Profile.php index c5652c6f7a..361f1a7e4b 100644 --- a/src/Model/Profile.php +++ b/src/Model/Profile.php @@ -1,6 +1,6 @@ $uid]); } + /** + * Update a profile entry and distribute the changes if needed + * + * @param array $fields + * @param integer $uid + * @return boolean + */ + public static function update(array $fields, int $uid): bool + { + $old_owner = User::getOwnerDataById($uid); + if (empty($old_owner)) { + return false; + } + + if (!DBA::update('profile', $fields, ['uid' => $uid])) { + return false; + } + + $update = Contact::updateSelfFromUserID($uid); + + $owner = User::getOwnerDataById($uid); + if (empty($owner)) { + return false; + } + + if ($old_owner['name'] != $owner['name']) { + User::update(['username' => $owner['name']], $uid); + } + + $profile_fields = ['postal-code', 'dob', 'prv_keywords', 'homepage']; + foreach ($profile_fields as $field) { + if ($old_owner[$field] != $owner[$field]) { + $update = true; + } + } + + if ($update) { + self::publishUpdate($uid, ($old_owner['net-publish'] != $owner['net-publish'])); + } + + return true; + } + + /** + * Publish a changed profile + * @param int $uid + * @param bool $force Force publishing to the directory + */ + public static function publishUpdate(int $uid, bool $force = false) + { + $owner = User::getOwnerDataById($uid); + if (empty($owner)) { + return; + } + + if ($owner['net-publish'] || $force) { + // Update global directory in background + if (Search::getGlobalDirectory()) { + Worker::add(PRIORITY_LOW, 'Directory', $owner['url']); + } + } + + Worker::add(PRIORITY_LOW, 'ProfileUpdate', $uid); + } + /** * Returns a formatted location string from the given profile array * @@ -136,58 +205,36 @@ class Profile * the theme is chosen before the _init() function of a theme is run, which will usually * load a lot of theme-specific content * - * @param App $a - * @param string $nickname string - * @param array $profiledata array - * @param boolean $show_connect Show connect link - * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * @param App $a + * @param string $nickname string + * @param bool $show_contacts + * @return array Profile + * + * @throws HTTPException\NotFoundException + * @throws HTTPException\InternalServerErrorException * @throws \ImagickException */ - public static function load(App $a, $nickname, array $profiledata = [], $show_connect = true) + public static function load(App $a, string $nickname, bool $show_contacts = true) { - $user = DBA::selectFirst('user', ['uid'], ['nickname' => $nickname, 'account_removed' => false]); - - if (!DBA::isResult($user) && empty($profiledata)) { - Logger::log('profile error: ' . DI::args()->getQueryString(), Logger::DEBUG); - return; - } - - if (count($profiledata) > 0) { - // Ensure to have a "nickname" field - if (empty($profiledata['nickname']) && !empty($profiledata['nick'])) { - $profiledata['nickname'] = $profiledata['nick']; - } - - // Add profile data to sidebar - DI::page()['aside'] .= self::sidebar($a, $profiledata, true, $show_connect); - - if (!DBA::isResult($user)) { - return; - } - } - - $profile = !empty($user['uid']) ? User::getOwnerDataById($user['uid'], false) : []; - - if (empty($profile) && empty($profiledata)) { + $profile = User::getOwnerDataByNick($nickname); + if (empty($profile)) { Logger::log('profile error: ' . DI::args()->getQueryString(), Logger::DEBUG); - return; + return []; } - if (empty($profile)) { - $profile = ['uid' => 0, 'name' => $nickname]; + // System user, aborting + if ($profile['uid'] === 0) { + DI::logger()->warning('System user found in Profile::load', ['nickname' => $nickname, 'callstack' => System::callstack(20)]); + throw new HTTPException\NotFoundException(DI::l10n()->t('User not found.')); } - $a->profile = $profile; - $a->profile_uid = $profile['uid']; - - $a->profile['mobile-theme'] = DI::pConfig()->get($a->profile['uid'], 'system', 'mobile_theme'); - $a->profile['network'] = Protocol::DFRN; + $a->setProfileOwner($profile['uid']); - DI::page()['title'] = $a->profile['name'] . ' @ ' . DI::config()->get('config', 'sitename'); + DI::page()['title'] = $profile['name'] . ' @ ' . DI::config()->get('config', 'sitename'); - if (!$profiledata && !DI::pConfig()->get(local_user(), 'system', 'always_my_theme')) { - $a->setCurrentTheme($a->profile['theme']); - $a->setCurrentMobileTheme($a->profile['mobile-theme']); + if (!DI::pConfig()->get(local_user(), 'system', 'always_my_theme')) { + $a->setCurrentTheme($profile['theme']); + $a->setCurrentMobileTheme(DI::pConfig()->get($a->getProfileOwner(), 'system', 'mobile_theme')); } /* @@ -201,39 +248,15 @@ class Profile require_once $theme_info_file; } - $block = ((DI::config()->get('system', 'block_public') && !Session::isAuthenticated()) ? true : false); + $block = (DI::config()->get('system', 'block_public') && !Session::isAuthenticated()); /** * @todo * By now, the contact block isn't shown, when a different profile is given * But: When this profile was on the same server, then we could display the contacts */ - if (!$profiledata) { - DI::page()['aside'] .= self::sidebar($a, $a->profile, $block, $show_connect); - } - - return; - } + DI::page()['aside'] .= self::getVCardHtml($profile, $block, $show_contacts); - /** - * Get all profile data of a local user - * - * If the viewer is an authenticated remote viewer, the profile displayed is the - * one that has been configured for his/her viewing in the Contact manager. - * Passing a non-zero profile ID can also allow a preview of a selected profile - * by the owner - * - * Includes all available profile data - * - * @param string $nickname nick - * @param int $uid uid - * @param int $profile_id ID of the profile - * @return array - * @throws \Exception - */ - public static function getByNickname($nickname, $uid = 0) - { - $profile = DBA::selectFirst('owner-view', [], ['nickname' => $nickname, 'uid' => $uid]); return $profile; } @@ -243,9 +266,9 @@ class Profile * It is very difficult to templatise the HTML completely * because of all the conditional logic. * - * @param array $profile - * @param int $block - * @param boolean $show_connect Show connect link + * @param array $profile Profile array + * @param bool $block Block personal details + * @param bool $show_contacts Show contact block * * @return string HTML sidebar module * @@ -258,52 +281,40 @@ class Profile * @hooks 'profile_sidebar' * array $arr */ - private static function sidebar(App $a, array $profile, $block = 0, $show_connect = true) + public static function getVCardHtml(array $profile, bool $block, bool $show_contacts) { $o = ''; $location = false; - // This function can also use contact information in $profile - $is_contact = !empty($profile['cid']); + $profile_contact = []; - if (empty($profile['nickname'])) { - Logger::warning('Received profile with no nickname', ['profile' => $profile, 'callstack' => System::callstack(10)]); - return $o; + if (local_user() && ($profile['uid'] ?? 0) != local_user()) { + $profile_contact = Contact::getByURL($profile['nurl'], null, [], local_user()); + } + if (!empty($profile['cid']) && self::getMyURL()) { + $profile_contact = Contact::selectFirst([], ['id' => $profile['cid']]); } - $profile['picdate'] = urlencode($profile['picdate'] ?? ''); + $profile['picdate'] = urlencode($profile['picdate']); - if (($profile['network'] != '') && ($profile['network'] != Protocol::DFRN)) { - $profile['network_link'] = Strings::formatNetworkName($profile['network'], $profile['url']); - } else { - $profile['network_link'] = ''; - } + $profile['network_link'] = ''; Hook::callAll('profile_sidebar_enter', $profile); - if (isset($profile['url'])) { - $profile_url = $profile['url']; - } else { - $profile_url = DI::baseUrl()->get() . '/profile/' . $profile['nickname']; - } + $profile_url = $profile['url']; + + $cid = $profile['id']; $follow_link = null; $unfollow_link = null; - $subscribe_feed_link = null; $wallmessage_link = null; + // Who is the logged-in user to this profile? $visitor_contact = []; if (!empty($profile['uid']) && self::getMyURL()) { $visitor_contact = Contact::selectFirst(['rel'], ['uid' => $profile['uid'], 'nurl' => Strings::normaliseLink(self::getMyURL())]); } - $profile_contact = []; - if (!empty($profile['cid']) && self::getMyURL()) { - $profile_contact = Contact::selectFirst(['rel'], ['id' => $profile['cid']]); - } - - $profile_is_dfrn = $profile['network'] == Protocol::DFRN; - $profile_is_native = in_array($profile['network'], Protocol::NATIVE_SUPPORT); $local_user_is_self = self::getMyURL() && ($profile['url'] == self::getMyURL()); $visitor_is_authenticated = (bool)self::getMyURL(); $visitor_is_following = @@ -314,13 +325,13 @@ class Profile || in_array($profile_contact['rel'] ?? 0, [Contact::FOLLOWER, Contact::FRIEND]); $visitor_base_path = self::getMyURL() ? preg_replace('=/profile/(.*)=ism', '', self::getMyURL()) : ''; - if (!$local_user_is_self && $show_connect) { + if (!$local_user_is_self) { if (!$visitor_is_authenticated) { // Remote follow is only available for local profiles if (!empty($profile['nickname']) && strpos($profile_url, DI::baseUrl()->get()) === 0) { $follow_link = 'remote_follow/' . $profile['nickname']; } - } elseif ($profile_is_native) { + } else { if ($visitor_is_following) { $unfollow_link = $visitor_base_path . '/unfollow?url=' . urlencode($profile_url) . '&auto=1'; } else { @@ -328,21 +339,19 @@ class Profile } } - if ($profile_is_dfrn) { - $subscribe_feed_link = 'dfrn_poll/' . $profile['nickname']; - } - - if (Contact::canReceivePrivateMessages($profile)) { + if (Contact::canReceivePrivateMessages($profile_contact)) { if ($visitor_is_followed || $visitor_is_following) { - $wallmessage_link = $visitor_base_path . '/message/new/' . base64_encode($profile['addr'] ?? ''); + $wallmessage_link = $visitor_base_path . '/message/new/' . $profile_contact['id']; } elseif ($visitor_is_authenticated && !empty($profile['unkmail'])) { $wallmessage_link = 'wallmessage/' . $profile['nickname']; } } } - // show edit profile to yourself - if (!$is_contact && $local_user_is_self) { + // show edit profile to yourself, but only if this is not meant to be + // rendered as a "contact". i.e., if 'self' (a "contact" table column) isn't + // set in $profile. + if (!isset($profile['self']) && $local_user_is_self) { $profile['edit'] = [DI::baseUrl() . '/settings/profile', DI::l10n()->t('Edit profile'), '', DI::l10n()->t('Edit profile')]; $profile['menu'] = [ 'chg_photo' => DI::l10n()->t('Change profile photo'), @@ -361,6 +370,7 @@ class Profile $homepage = !empty($profile['homepage']) ? DI::l10n()->t('Homepage:') : false; $about = !empty($profile['about']) ? DI::l10n()->t('About:') : false; $xmpp = !empty($profile['xmpp']) ? DI::l10n()->t('XMPP:') : false; + $matrix = !empty($profile['matrix']) ? DI::l10n()->t('Matrix:') : false; if ((!empty($profile['hidewall']) || $block) && !Session::isAuthenticated()) { $location = $homepage = $about = false; @@ -395,10 +405,10 @@ class Profile $updated = date('c', strtotime($profile['last-item'])); } - if (!$block) { - $contact_block = ContactBlock::getHTML($a->profile); + if (!$block && $show_contacts) { + $contact_block = ContactBlock::getHTML($profile, local_user()); - if (is_array($a->profile) && !$a->profile['hide-friends']) { + if (is_array($profile) && !$profile['hide-friends']) { $contact_count = DBA::count('contact', [ 'uid' => $profile['uid'], 'self' => false, @@ -412,36 +422,40 @@ class Profile } } - $p = []; + // Expected profile/vcard.tpl profile.* template variables + $p = [ + 'address' => null, + 'edit' => null, + 'upubkey' => null, + ]; foreach ($profile as $k => $v) { $k = str_replace('-', '_', $k); $p[$k] = $v; } if (isset($p['about'])) { - $p['about'] = BBCode::convert($p['about']); + $p['about'] = BBCode::convertForUriId($profile['uri-id'] ?? 0, $p['about']); } if (isset($p['address'])) { - $p['address'] = BBCode::convert($p['address']); + $p['address'] = BBCode::convertForUriId($profile['uri-id'] ?? 0, $p['address']); } - if (isset($p['photo'])) { - $p['photo'] = ProxyUtils::proxifyUrl($p['photo'], false, ProxyUtils::SIZE_SMALL); - } + $p['photo'] = Contact::getAvatarUrlForId($cid, ProxyUtils::SIZE_SMALL); - $p['url'] = Contact::magicLink(($p['url'] ?? '') ?: $profile_url); + $p['url'] = Contact::magicLinkById($cid, $profile['url']); $tpl = Renderer::getMarkupTemplate('profile/vcard.tpl'); $o .= Renderer::replaceMacros($tpl, [ '$profile' => $p, '$xmpp' => $xmpp, + '$matrix' => $matrix, '$follow' => DI::l10n()->t('Follow'), '$follow_link' => $follow_link, '$unfollow' => DI::l10n()->t('Unfollow'), '$unfollow_link' => $unfollow_link, '$subscribe_feed' => DI::l10n()->t('Atom feed'), - '$subscribe_feed_link' => $subscribe_feed_link, + '$subscribe_feed_link' => $profile['poll'], '$wallmessage' => DI::l10n()->t('Message'), '$wallmessage_link' => $wallmessage_link, '$account_type' => $account_type, @@ -539,9 +553,9 @@ class Profile $today = (((strtotime($rr['start'] . ' +00:00') < $now) && (strtotime($rr['finish'] . ' +00:00') > $now)) ? true : false); - $rr['link'] = Contact::magicLink($rr['url']); + $rr['link'] = Contact::magicLinkById($rr['cid']); $rr['title'] = $rr['name']; - $rr['date'] = DI::l10n()->getDay(DateTimeFormat::convert($rr['start'], $a->timezone, 'UTC', $rr['adjust'] ? $bd_format : $bd_short)) . (($today) ? ' ' . DI::l10n()->t('[today]') : ''); + $rr['date'] = DI::l10n()->getDay(DateTimeFormat::local($rr['start'], $bd_short)) . (($today) ? ' ' . DI::l10n()->t('[today]') : ''); $rr['startime'] = null; $rr['today'] = $today; } @@ -592,7 +606,7 @@ class Profile $condition = ['parent-uri' => $rr['uri'], 'uid' => $rr['uid'], 'author-id' => public_contact(), 'vid' => [Verb::getID(Activity::ATTEND), Verb::getID(Activity::ATTENDMAYBE)], 'visible' => true, 'deleted' => false]; - if (!Item::exists($condition)) { + if (!Post::exists($condition)) { continue; } @@ -600,33 +614,33 @@ class Profile $total++; } - $strt = DateTimeFormat::convert($rr['start'], $rr['adjust'] ? $a->timezone : 'UTC', 'UTC', 'Y-m-d'); - if ($strt === DateTimeFormat::timezoneNow($a->timezone, 'Y-m-d')) { + $strt = DateTimeFormat::local($rr['start'], 'Y-m-d'); + if ($strt === DateTimeFormat::localNow('Y-m-d')) { $istoday = true; } - $title = strip_tags(html_entity_decode(BBCode::convert($rr['summary']), ENT_QUOTES, 'UTF-8')); + $title = strip_tags(html_entity_decode(BBCode::convertForUriId($rr['uri-id'], $rr['summary']), ENT_QUOTES, 'UTF-8')); if (strlen($title) > 35) { $title = substr($title, 0, 32) . '... '; } - $description = substr(strip_tags(BBCode::convert($rr['desc'])), 0, 32) . '... '; + $description = substr(strip_tags(BBCode::convertForUriId($rr['uri-id'], $rr['desc'])), 0, 32) . '... '; if (!$description) { $description = DI::l10n()->t('[No description]'); } - $strt = DateTimeFormat::convert($rr['start'], $rr['adjust'] ? $a->timezone : 'UTC'); + $strt = DateTimeFormat::local($rr['start']); - if (substr($strt, 0, 10) < DateTimeFormat::timezoneNow($a->timezone, 'Y-m-d')) { + if (substr($strt, 0, 10) < DateTimeFormat::localNow('Y-m-d')) { continue; } - $today = ((substr($strt, 0, 10) === DateTimeFormat::timezoneNow($a->timezone, 'Y-m-d')) ? true : false); + $today = substr($strt, 0, 10) === DateTimeFormat::localNow('Y-m-d'); $rr['title'] = $title; $rr['description'] = $description; - $rr['date'] = DI::l10n()->getDay(DateTimeFormat::convert($rr['start'], $rr['adjust'] ? $a->timezone : 'UTC', 'UTC', $bd_format)) . (($today) ? ' ' . DI::l10n()->t('[today]') : ''); + $rr['date'] = DI::l10n()->getDay(DateTimeFormat::local($rr['start'], $bd_format)) . (($today) ? ' ' . DI::l10n()->t('[today]') : ''); $rr['startime'] = $strt; $rr['today'] = $today; @@ -728,7 +742,7 @@ class Profile $magic_path = $basepath . '/magic' . '?owa=1&dest=' . $dest . '&' . $addr_request; // We have to check if the remote server does understand /magic without invoking something - $serverret = DI::httpRequest()->get($basepath . '/magic'); + $serverret = DI::httpClient()->get($basepath . '/magic'); if ($serverret->isSuccess()) { Logger::log('Doing magic auth for visitor ' . $my_url . ' to ' . $magic_path, Logger::DEBUG); System::externalRedirect($magic_path); @@ -749,11 +763,11 @@ class Profile // Try to find the public contact entry of the visitor. $cid = Contact::getIdForURL($handle); if (!$cid) { - Logger::log('unable to finger ' . $handle, Logger::DEBUG); + Logger::info('Handle not found', ['handle' => $handle]); return []; } - $visitor = DBA::selectFirst('contact', [], ['id' => $cid]); + $visitor = Contact::getById($cid); // Authenticate the visitor. $_SESSION['authenticated'] = 1; @@ -765,13 +779,26 @@ class Profile Session::setVisitorsContacts(); - $a->contact = $visitor; + $a->setContactId($visitor['id']); Logger::info('Authenticated visitor', ['url' => $visitor['url']]); return $visitor; } + /** + * Set the visitor cookies (see remote_user()) for signed HTTP requests + * @return array Visitor contact array + */ + public static function addVisitorCookieForHTTPSigner() + { + $requester = HTTPSignature::getSigner('', $_SERVER); + if (empty($requester)) { + return []; + } + return Profile::addVisitorCookieForHandle($requester); + } + /** * OpenWebAuth authentication. * @@ -813,7 +840,7 @@ class Profile */ Hook::callAll('magic_auth_success', $arr); - $a->contact = $arr['visitor']; + $a->setContactId($arr['visitor']['id']); info(DI::l10n()->t('OpenWebAuth: %1$s welcomes %2$s', DI::baseUrl()->getHostname(), $visitor['name'])); @@ -855,7 +882,7 @@ class Profile */ public static function getThemeUid(App $a) { - $uid = !empty($a->profile_uid) ? intval($a->profile_uid) : 0; + $uid = !empty($a->getProfileOwner()) ? intval($a->getProfileOwner()) : 0; if (local_user() && (DI::pConfig()->get(local_user(), 'system', 'always_my_theme') || !$uid)) { return local_user(); }