From d1be68b75462df77100c9262134b70717635ddfe Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sun, 27 Oct 2019 10:08:14 -0400 Subject: [PATCH] Move Module\Profile to Module\Profile\Index - Move /profiles to Module\Settings\Profile\Index --- mod/network.php | 2 +- mod/profiles.php | 675 ------------------ mod/settings.php | 6 +- mod/update_profile.php | 49 -- src/Model/Profile.php | 85 +-- src/Module/BaseSettingsModule.php | 6 +- src/Module/Profile.php | 361 ---------- src/Module/Profile/Index.php | 121 ++++ src/Module/Profile/Status.php | 217 ++++++ src/Module/Settings/Profile/Index.php | 388 ++++++++++ src/Module/Update/Profile.php | 127 ++++ static/routes.config.php | 11 +- tests/src/App/RouterTest.php | 6 +- view/templates/profile-hide-wall.tpl | 17 - view/templates/profile-in-directory.tpl | 17 - view/templates/profile-in-netdir.tpl | 17 - view/templates/profile_edlink.tpl | 3 - .../profile/hide-friends.tpl} | 0 .../profile/index.tpl} | 30 +- .../profile/index_head.tpl} | 0 view/templates/settings/profile/link.tpl | 2 + view/templates/welcome.tpl | 4 +- .../profile/index.tpl} | 102 +-- .../profile/index.tpl} | 120 ++-- .../profile/link.tpl} | 0 25 files changed, 1007 insertions(+), 1359 deletions(-) delete mode 100644 mod/profiles.php delete mode 100644 mod/update_profile.php delete mode 100644 src/Module/Profile.php create mode 100644 src/Module/Profile/Index.php create mode 100644 src/Module/Profile/Status.php create mode 100644 src/Module/Settings/Profile/Index.php create mode 100644 src/Module/Update/Profile.php delete mode 100644 view/templates/profile-hide-wall.tpl delete mode 100644 view/templates/profile-in-directory.tpl delete mode 100644 view/templates/profile-in-netdir.tpl delete mode 100644 view/templates/profile_edlink.tpl rename view/templates/{profile-hide-friends.tpl => settings/profile/hide-friends.tpl} (100%) rename view/templates/{profile_edit.tpl => settings/profile/index.tpl} (91%) rename view/templates/{profed_head.tpl => settings/profile/index_head.tpl} (100%) create mode 100644 view/templates/settings/profile/link.tpl rename view/theme/frio/templates/{profile_edit.tpl => settings/profile/index.tpl} (76%) rename view/theme/vier/templates/{profile_edit.tpl => settings/profile/index.tpl} (93%) rename view/theme/vier/templates/{profile_edlink.tpl => settings/profile/link.tpl} (100%) diff --git a/mod/network.php b/mod/network.php index db6d8eaf4..1bbddf219 100644 --- a/mod/network.php +++ b/mod/network.php @@ -879,7 +879,7 @@ function network_tabs(App $a) { // item filter tabs /// @TODO fix this logic, reduce duplication - /// $a->page['content'] .= '
'; + /// DI::page()['content'] .= '
'; list($no_active, $all_active, $post_active, $conv_active, $new_active, $starred_active, $bookmarked_active) = network_query_get_sel_tab($a); // if no tabs are selected, defaults to activitys diff --git a/mod/profiles.php b/mod/profiles.php deleted file mode 100644 index 53da8510c..000000000 --- a/mod/profiles.php +++ /dev/null @@ -1,675 +0,0 @@ -argc > 2) && ($a->argv[1] === "drop") && intval($a->argv[2])) { - $r = q("SELECT * FROM `profile` WHERE `id` = %d AND `uid` = %d AND `is-default` = 0 LIMIT 1", - intval($a->argv[2]), - intval(local_user()) - ); - if (! DBA::isResult($r)) { - notice(DI::l10n()->t('Profile not found.') . EOL); - DI::baseUrl()->redirect('profiles'); - return; // NOTREACHED - } - - BaseModule::checkFormSecurityTokenRedirectOnError('/profiles', 'profile_drop', 't'); - - // move every contact using this profile as their default to the user default - - q("UPDATE `contact` SET `profile-id` = (SELECT `profile`.`id` AS `profile-id` FROM `profile` WHERE `profile`.`is-default` = 1 AND `profile`.`uid` = %d LIMIT 1) WHERE `profile-id` = %d AND `uid` = %d ", - intval(local_user()), - intval($a->argv[2]), - intval(local_user()) - ); - q("DELETE FROM `profile` WHERE `id` = %d AND `uid` = %d", - intval($a->argv[2]), - intval(local_user()) - ); - if (DBA::isResult($r)) { - info(DI::l10n()->t('Profile deleted.').EOL); - } - - DI::baseUrl()->redirect('profiles'); - return; // NOTREACHED - } - - if (($a->argc > 1) && ($a->argv[1] === 'new')) { - - BaseModule::checkFormSecurityTokenRedirectOnError('/profiles', 'profile_new', 't'); - - $r0 = q("SELECT `id` FROM `profile` WHERE `uid` = %d", - intval(local_user())); - - $num_profiles = (DBA::isResult($r0) ? count($r0) : 0); - - $name = DI::l10n()->t('Profile-') . ($num_profiles + 1); - - $r1 = q("SELECT `name`, `photo`, `thumb` FROM `profile` WHERE `uid` = %d AND `is-default` = 1 LIMIT 1", - intval(local_user())); - - q("INSERT INTO `profile` (`uid` , `profile-name` , `name`, `photo`, `thumb`) - VALUES ( %d, '%s', '%s', '%s', '%s' )", - intval(local_user()), - DBA::escape($name), - DBA::escape($r1[0]['name']), - DBA::escape($r1[0]['photo']), - DBA::escape($r1[0]['thumb']) - ); - - $r3 = q("SELECT `id` FROM `profile` WHERE `uid` = %d AND `profile-name` = '%s' LIMIT 1", - intval(local_user()), - DBA::escape($name) - ); - - info(DI::l10n()->t('New profile created.') . EOL); - if (DBA::isResult($r3) && count($r3) == 1) { - DI::baseUrl()->redirect('profiles/' . $r3[0]['id']); - } - - DI::baseUrl()->redirect('profiles'); - } - - if (($a->argc > 2) && ($a->argv[1] === 'clone')) { - - BaseModule::checkFormSecurityTokenRedirectOnError('/profiles', 'profile_clone', 't'); - - $r0 = q("SELECT `id` FROM `profile` WHERE `uid` = %d", - intval(local_user())); - - $num_profiles = (DBA::isResult($r0) ? count($r0) : 0); - - $name = DI::l10n()->t('Profile-') . ($num_profiles + 1); - $r1 = q("SELECT * FROM `profile` WHERE `uid` = %d AND `id` = %d LIMIT 1", - intval(local_user()), - intval($a->argv[2]) - ); - if(! DBA::isResult($r1)) { - notice(DI::l10n()->t('Profile unavailable to clone.') . EOL); - exit(); - } - unset($r1[0]['id']); - $r1[0]['is-default'] = 0; - $r1[0]['publish'] = 0; - $r1[0]['net-publish'] = 0; - $r1[0]['profile-name'] = DBA::escape($name); - - DBA::insert('profile', $r1[0]); - - $r3 = q("SELECT `id` FROM `profile` WHERE `uid` = %d AND `profile-name` = '%s' LIMIT 1", - intval(local_user()), - DBA::escape($name) - ); - info(DI::l10n()->t('New profile created.') . EOL); - if ((DBA::isResult($r3)) && (count($r3) == 1)) { - DI::baseUrl()->redirect('profiles/'.$r3[0]['id']); - } - - DI::baseUrl()->redirect('profiles'); - - return; // NOTREACHED - } - - - if (($a->argc > 1) && (intval($a->argv[1]))) { - $r = q("SELECT id FROM `profile` WHERE `id` = %d AND `uid` = %d LIMIT 1", - intval($a->argv[1]), - intval(local_user()) - ); - if (! DBA::isResult($r)) { - notice(DI::l10n()->t('Profile not found.') . EOL); - exit(); - } - - Profile::load($a, $a->user['nickname'], $r[0]['id']); - } -} - -function profile_clean_keywords($keywords) -{ - $keywords = str_replace(",", " ", $keywords); - $keywords = explode(" ", $keywords); - - $cleaned = []; - foreach ($keywords as $keyword) { - $keyword = trim(strtolower($keyword)); - $keyword = trim($keyword, "#"); - if ($keyword != "") { - $cleaned[] = $keyword; - } - } - - $keywords = implode(", ", $cleaned); - - return $keywords; -} - -function profiles_post(App $a) { - - if (! local_user()) { - notice(DI::l10n()->t('Permission denied.') . EOL); - return; - } - - $namechanged = false; - - Hook::callAll('profile_post', $_POST); - - if (($a->argc > 1) && ($a->argv[1] !== "new") && intval($a->argv[1])) { - $orig = q("SELECT * FROM `profile` WHERE `id` = %d AND `uid` = %d LIMIT 1", - intval($a->argv[1]), - intval(local_user()) - ); - if (! DBA::isResult($orig)) { - notice(DI::l10n()->t('Profile not found.') . EOL); - return; - } - - BaseModule::checkFormSecurityTokenRedirectOnError('/profiles', 'profile_edit'); - - $is_default = (($orig[0]['is-default']) ? 1 : 0); - - $profile_name = Strings::escapeTags(trim($_POST['profile_name'])); - if (! strlen($profile_name)) { - notice(DI::l10n()->t('Profile Name is required.') . EOL); - return; - } - - $dob = !empty($_POST['dob']) ? Strings::escapeHtml(trim($_POST['dob'])) : '0000-00-00'; - - $y = substr($dob, 0, 4); - if ((! ctype_digit($y)) || ($y < 1900)) { - $ignore_year = true; - } else { - $ignore_year = false; - } - if (!in_array($dob, ['0000-00-00', DBA::NULL_DATE])) { - if (strpos($dob, '0000-') === 0 || strpos($dob, '0001-') === 0) { - $ignore_year = true; - $dob = substr($dob, 5); - } - - if ($ignore_year) { - $dob = '0000-' . DateTimeFormat::utc('1900-' . $dob, 'm-d'); - } else { - $dob = DateTimeFormat::utc($dob, 'Y-m-d'); - } - } - - $name = Strings::escapeTags(trim($_POST['name'])); - - if (! strlen($name)) { - $name = '[No Name]'; - } - - if ($orig[0]['name'] != $name) { - $namechanged = true; - } - - $pdesc = Strings::escapeTags(trim($_POST['pdesc'] ?? '')); - $gender = Strings::escapeTags(trim($_POST['gender'] ?? '')); - $address = Strings::escapeTags(trim($_POST['address'] ?? '')); - $locality = Strings::escapeTags(trim($_POST['locality'] ?? '')); - $region = Strings::escapeTags(trim($_POST['region'] ?? '')); - $postal_code = Strings::escapeTags(trim($_POST['postal_code'] ?? '')); - $country_name = Strings::escapeTags(trim($_POST['country_name'] ?? '')); - $pub_keywords = profile_clean_keywords(Strings::escapeTags(trim($_POST['pub_keywords'] ?? ''))); - $prv_keywords = profile_clean_keywords(Strings::escapeTags(trim($_POST['prv_keywords'] ?? ''))); - $marital = Strings::escapeTags(trim($_POST['marital'] ?? '')); - $howlong = Strings::escapeTags(trim($_POST['howlong'] ?? '')); - - $with = (!empty($_POST['with']) ? Strings::escapeTags(trim($_POST['with'])) : ''); - - if (! strlen($howlong)) { - $howlong = DBA::NULL_DATETIME; - } else { - $howlong = DateTimeFormat::convert($howlong, 'UTC', date_default_timezone_get()); - } - // linkify the relationship target if applicable - - $withchanged = false; - - if (strlen($with)) { - if ($with != strip_tags($orig[0]['with'])) { - $withchanged = true; - $prf = ''; - $lookup = $with; - if (strpos($lookup, '@') === 0) { - $lookup = substr($lookup, 1); - } - $lookup = str_replace('_',' ', $lookup); - if (strpos($lookup, '@') || (strpos($lookup, 'http://'))) { - $newname = $lookup; - $links = @Probe::lrdd($lookup); - if (count($links)) { - foreach ($links as $link) { - if ($link['@attributes']['rel'] === 'http://webfinger.net/rel/profile-page') { - $prf = $link['@attributes']['href']; - } - } - } - } else { - $newname = $lookup; - - $r = q("SELECT * FROM `contact` WHERE `name` = '%s' AND `uid` = %d LIMIT 1", - DBA::escape($newname), - intval(local_user()) - ); - if (! DBA::isResult($r)) { - $r = q("SELECT * FROM `contact` WHERE `nick` = '%s' AND `uid` = %d LIMIT 1", - DBA::escape($lookup), - intval(local_user()) - ); - } - if (DBA::isResult($r)) { - $prf = $r[0]['url']; - $newname = $r[0]['name']; - } - } - - if ($prf) { - $with = str_replace($lookup, '' . $newname . '', $with); - if (strpos($with, '@') === 0) { - $with = substr($with, 1); - } - } - } else { - $with = $orig[0]['with']; - } - } - - /// @TODO Not flexible enough for later expansion, let's have more OOP here - $sexual = Strings::escapeTags(trim($_POST['sexual'])); - $xmpp = Strings::escapeTags(trim($_POST['xmpp'])); - $homepage = Strings::escapeTags(trim($_POST['homepage'])); - if ((strpos($homepage, 'http') !== 0) && (strlen($homepage))) { - // neither http nor https in URL, add them - $homepage = 'http://'.$homepage; - } - $hometown = Strings::escapeTags(trim($_POST['hometown'])); - $politic = Strings::escapeTags(trim($_POST['politic'])); - $religion = Strings::escapeTags(trim($_POST['religion'])); - - $likes = Strings::escapeHtml(trim($_POST['likes'])); - $dislikes = Strings::escapeHtml(trim($_POST['dislikes'])); - - $about = Strings::escapeHtml(trim($_POST['about'])); - $interest = Strings::escapeHtml(trim($_POST['interest'])); - $contact = Strings::escapeHtml(trim($_POST['contact'])); - $music = Strings::escapeHtml(trim($_POST['music'])); - $book = Strings::escapeHtml(trim($_POST['book'])); - $tv = Strings::escapeHtml(trim($_POST['tv'])); - $film = Strings::escapeHtml(trim($_POST['film'])); - $romance = Strings::escapeHtml(trim($_POST['romance'])); - $work = Strings::escapeHtml(trim($_POST['work'])); - $education = Strings::escapeHtml(trim($_POST['education'])); - - $hide_friends = (($_POST['hide-friends'] == 1) ? 1: 0); - - DI::pConfig()->set(local_user(), 'system', 'detailled_profile', !empty($_POST['detailed_profile']) ? 1: 0); - - $changes = []; - if ($is_default) { - if ($marital != $orig[0]['marital']) { - $changes[] = '[color=#ff0000]♥[/color] ' . DI::l10n()->t('Marital Status'); - } - if ($withchanged) { - $changes[] = '[color=#ff0000]♥[/color] ' . DI::l10n()->t('Romantic Partner'); - } - if ($likes != $orig[0]['likes']) { - $changes[] = DI::l10n()->t('Likes'); - } - if ($dislikes != $orig[0]['dislikes']) { - $changes[] = DI::l10n()->t('Dislikes'); - } - if ($work != $orig[0]['work']) { - $changes[] = DI::l10n()->t('Work/Employment'); - } - if ($religion != $orig[0]['religion']) { - $changes[] = DI::l10n()->t('Religion'); - } - if ($politic != $orig[0]['politic']) { - $changes[] = DI::l10n()->t('Political Views'); - } - if ($gender != $orig[0]['gender']) { - $changes[] = DI::l10n()->t('Gender'); - } - if ($sexual != $orig[0]['sexual']) { - $changes[] = DI::l10n()->t('Sexual Preference'); - } - if ($xmpp != $orig[0]['xmpp']) { - $changes[] = DI::l10n()->t('XMPP'); - } - if ($homepage != $orig[0]['homepage']) { - $changes[] = DI::l10n()->t('Homepage'); - } - if ($interest != $orig[0]['interest']) { - $changes[] = DI::l10n()->t('Interests'); - } - if ($address != $orig[0]['address']) { - $changes[] = DI::l10n()->t('Address'); - // New address not sent in notifications, potential privacy issues - // in case this leaks to unintended recipients. Yes, it's in the public - // profile but that doesn't mean we have to broadcast it to everybody. - } - if ($locality != $orig[0]['locality'] || $region != $orig[0]['region'] - || $country_name != $orig[0]['country-name']) { - $changes[] = DI::l10n()->t('Location'); - } - } - - $r = q("UPDATE `profile` - SET `profile-name` = '%s', - `name` = '%s', - `pdesc` = '%s', - `gender` = '%s', - `dob` = '%s', - `address` = '%s', - `locality` = '%s', - `region` = '%s', - `postal-code` = '%s', - `country-name` = '%s', - `marital` = '%s', - `with` = '%s', - `howlong` = '%s', - `sexual` = '%s', - `xmpp` = '%s', - `homepage` = '%s', - `hometown` = '%s', - `politic` = '%s', - `religion` = '%s', - `pub_keywords` = '%s', - `prv_keywords` = '%s', - `likes` = '%s', - `dislikes` = '%s', - `about` = '%s', - `interest` = '%s', - `contact` = '%s', - `music` = '%s', - `book` = '%s', - `tv` = '%s', - `film` = '%s', - `romance` = '%s', - `work` = '%s', - `education` = '%s', - `hide-friends` = %d - WHERE `id` = %d AND `uid` = %d", - DBA::escape($profile_name), - DBA::escape($name), - DBA::escape($pdesc), - DBA::escape($gender), - DBA::escape($dob), - DBA::escape($address), - DBA::escape($locality), - DBA::escape($region), - DBA::escape($postal_code), - DBA::escape($country_name), - DBA::escape($marital), - DBA::escape($with), - DBA::escape($howlong), - DBA::escape($sexual), - DBA::escape($xmpp), - DBA::escape($homepage), - DBA::escape($hometown), - DBA::escape($politic), - DBA::escape($religion), - DBA::escape($pub_keywords), - DBA::escape($prv_keywords), - DBA::escape($likes), - DBA::escape($dislikes), - DBA::escape($about), - DBA::escape($interest), - DBA::escape($contact), - DBA::escape($music), - DBA::escape($book), - DBA::escape($tv), - DBA::escape($film), - DBA::escape($romance), - DBA::escape($work), - DBA::escape($education), - intval($hide_friends), - intval($a->argv[1]), - intval(local_user()) - ); - - /// @TODO decide to use DBA::isResult() here and check $r - if ($r) { - info(DI::l10n()->t('Profile updated.') . EOL); - } - - if ($is_default) { - if ($namechanged) { - q("UPDATE `user` set `username` = '%s' where `uid` = %d", - DBA::escape($name), - intval(local_user()) - ); - } - - Contact::updateSelfFromUserID(local_user()); - - // Update global directory in background - $url = $_SESSION['my_url']; - if ($url && strlen(DI::config()->get('system', 'directory'))) { - Worker::add(PRIORITY_LOW, "Directory", $url); - } - - Worker::add(PRIORITY_LOW, 'ProfileUpdate', local_user()); - - // Update the global contact for the user - GContact::updateForUser(local_user()); - } - } -} - -function profiles_content(App $a) { - - if (! local_user()) { - notice(DI::l10n()->t('Permission denied.') . EOL); - return Login::form(); - } - - $o = ''; - - if (($a->argc > 1) && (intval($a->argv[1]))) { - $r = q("SELECT * FROM `profile` WHERE `id` = %d AND `uid` = %d LIMIT 1", - intval($a->argv[1]), - intval(local_user()) - ); - if (! DBA::isResult($r)) { - notice(DI::l10n()->t('Profile not found.') . EOL); - return; - } - - DI::page()['htmlhead'] .= Renderer::replaceMacros(Renderer::getMarkupTemplate('profed_head.tpl'), [ - '$baseurl' => DI::baseUrl()->get(true), - ]); - - $opt_tpl = Renderer::getMarkupTemplate("profile-hide-friends.tpl"); - $hide_friends = Renderer::replaceMacros($opt_tpl,[ - '$yesno' => [ - 'hide-friends', //Name - DI::l10n()->t('Hide contacts and friends:'), //Label - !!$r[0]['hide-friends'], //Value - '', //Help string - [DI::l10n()->t('No'), DI::l10n()->t('Yes')] //Off - On strings - ], - '$desc' => DI::l10n()->t('Hide your contact/friend list from viewers of this profile?'), - '$yes_str' => DI::l10n()->t('Yes'), - '$no_str' => DI::l10n()->t('No'), - '$yes_selected' => (($r[0]['hide-friends']) ? " checked=\"checked\" " : ""), - '$no_selected' => (($r[0]['hide-friends'] == 0) ? " checked=\"checked\" " : "") - ]); - - $personal_account = !(in_array($a->user["page-flags"], - [User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_PRVGROUP])); - - $detailed_profile = (DI::pConfig()->get(local_user(), 'system', 'detailled_profile') AND $personal_account); - - $is_default = (($r[0]['is-default']) ? 1 : 0); - $tpl = Renderer::getMarkupTemplate("profile_edit.tpl"); - $o .= Renderer::replaceMacros($tpl, [ - '$personal_account' => $personal_account, - '$detailled_profile' => $detailed_profile, - - '$details' => [ - 'detailed_profile', //Name - DI::l10n()->t('Show more profile fields:'), //Label - $detailed_profile, //Value - '', //Help string - [DI::l10n()->t('No'), DI::l10n()->t('Yes')] //Off - On strings - ], - - '$multi_profiles' => Feature::isEnabled(local_user(), 'multi_profiles'), - '$form_security_token' => BaseModule::getFormSecurityToken("profile_edit"), - '$form_security_token_photo' => BaseModule::getFormSecurityToken("profile_photo"), - '$profile_clone_link' => ((Feature::isEnabled(local_user(), 'multi_profiles')) ? 'profiles/clone/' . $r[0]['id'] . '?t=' . BaseModule::getFormSecurityToken("profile_clone") : ""), - '$profile_drop_link' => 'profiles/drop/' . $r[0]['id'] . '?t=' . BaseModule::getFormSecurityToken("profile_drop"), - - '$profile_action' => DI::l10n()->t('Profile Actions'), - '$banner' => DI::l10n()->t('Edit Profile Details'), - '$submit' => DI::l10n()->t('Submit'), - '$profpic' => DI::l10n()->t('Change Profile Photo'), - '$profpiclink' => '/photos/' . $a->user['nickname'], - '$viewprof' => DI::l10n()->t('View this profile'), - '$viewallprof' => DI::l10n()->t('View all profiles'), - '$editvis' => DI::l10n()->t('Edit visibility'), - '$cr_prof' => DI::l10n()->t('Create a new profile using these settings'), - '$cl_prof' => DI::l10n()->t('Clone this profile'), - '$del_prof' => DI::l10n()->t('Delete this profile'), - - '$lbl_basic_section' => DI::l10n()->t('Basic information'), - '$lbl_picture_section' => DI::l10n()->t('Profile picture'), - '$lbl_location_section' => DI::l10n()->t('Location'), - '$lbl_preferences_section' => DI::l10n()->t('Preferences'), - '$lbl_status_section' => DI::l10n()->t('Status information'), - '$lbl_about_section' => DI::l10n()->t('Additional information'), - '$lbl_interests_section' => DI::l10n()->t('Interests'), - '$lbl_personal_section' => DI::l10n()->t('Personal'), - '$lbl_relation_section' => DI::l10n()->t('Relation'), - '$lbl_miscellaneous_section' => DI::l10n()->t('Miscellaneous'), - - '$lbl_profile_photo' => DI::l10n()->t('Upload Profile Photo'), - '$lbl_gender' => DI::l10n()->t('Your Gender:'), - '$lbl_marital' => DI::l10n()->t(' Marital Status:'), - '$lbl_sexual' => DI::l10n()->t('Sexual Preference:'), - '$lbl_ex2' => DI::l10n()->t('Example: fishing photography software'), - - '$disabled' => (($is_default) ? 'onclick="return false;" style="color: #BBBBFF;"' : ''), - '$baseurl' => DI::baseUrl()->get(true), - '$profile_id' => $r[0]['id'], - '$profile_name' => ['profile_name', DI::l10n()->t('Profile Name:'), $r[0]['profile-name'], DI::l10n()->t('Required'), '*'], - '$is_default' => $is_default, - '$default' => (($is_default) ? '

' . DI::l10n()->t('This is your public profile.
It may be visible to anybody using the internet.') . '

' : ""), - '$name' => ['name', DI::l10n()->t('Your Full Name:'), $r[0]['name']], - '$pdesc' => ['pdesc', DI::l10n()->t('Title/Description:'), $r[0]['pdesc']], - '$dob' => Temporal::getDateofBirthField($r[0]['dob'], $a->user['timezone']), - '$hide_friends' => $hide_friends, - '$address' => ['address', DI::l10n()->t('Street Address:'), $r[0]['address']], - '$locality' => ['locality', DI::l10n()->t('Locality/City:'), $r[0]['locality']], - '$region' => ['region', DI::l10n()->t('Region/State:'), $r[0]['region']], - '$postal_code' => ['postal_code', DI::l10n()->t('Postal/Zip Code:'), $r[0]['postal-code']], - '$country_name' => ['country_name', DI::l10n()->t('Country:'), $r[0]['country-name']], - '$age' => ((intval($r[0]['dob'])) ? '(' . DI::l10n()->t('Age: ') . DI::l10n()->tt('%d year old', '%d years old', Temporal::getAgeByTimezone($r[0]['dob'], $a->user['timezone'])) . ')' : ''), - '$gender' => DI::l10n()->t(ContactSelector::gender($r[0]['gender'])), - '$marital' => ['selector' => ContactSelector::maritalStatus($r[0]['marital']), 'value' => DI::l10n()->t($r[0]['marital'])], - '$with' => ['with', DI::l10n()->t("Who: \x28if applicable\x29"), strip_tags($r[0]['with']), DI::l10n()->t('Examples: cathy123, Cathy Williams, cathy@example.com')], - '$howlong' => ['howlong', DI::l10n()->t('Since [date]:'), ($r[0]['howlong'] <= DBA::NULL_DATETIME ? '' : DateTimeFormat::local($r[0]['howlong']))], - '$sexual' => ['selector' => ContactSelector::sexualPreference($r[0]['sexual']), 'value' => DI::l10n()->t($r[0]['sexual'])], - '$about' => ['about', DI::l10n()->t('Tell us about yourself...'), $r[0]['about']], - '$xmpp' => ['xmpp', DI::l10n()->t("XMPP \x28Jabber\x29 address:"), $r[0]['xmpp'], DI::l10n()->t("The XMPP address will be propagated to your contacts so that they can follow you.")], - '$homepage' => ['homepage', DI::l10n()->t('Homepage URL:'), $r[0]['homepage']], - '$hometown' => ['hometown', DI::l10n()->t('Hometown:'), $r[0]['hometown']], - '$politic' => ['politic', DI::l10n()->t('Political Views:'), $r[0]['politic']], - '$religion' => ['religion', DI::l10n()->t('Religious Views:'), $r[0]['religion']], - '$pub_keywords' => ['pub_keywords', DI::l10n()->t('Public Keywords:'), $r[0]['pub_keywords'], DI::l10n()->t("\x28Used for suggesting potential friends, can be seen by others\x29")], - '$prv_keywords' => ['prv_keywords', DI::l10n()->t('Private Keywords:'), $r[0]['prv_keywords'], DI::l10n()->t("\x28Used for searching profiles, never shown to others\x29")], - '$likes' => ['likes', DI::l10n()->t('Likes:'), $r[0]['likes']], - '$dislikes' => ['dislikes', DI::l10n()->t('Dislikes:'), $r[0]['dislikes']], - '$music' => ['music', DI::l10n()->t('Musical interests'), $r[0]['music']], - '$book' => ['book', DI::l10n()->t('Books, literature'), $r[0]['book']], - '$tv' => ['tv', DI::l10n()->t('Television'), $r[0]['tv']], - '$film' => ['film', DI::l10n()->t('Film/dance/culture/entertainment'), $r[0]['film']], - '$interest' => ['interest', DI::l10n()->t('Hobbies/Interests'), $r[0]['interest']], - '$romance' => ['romance', DI::l10n()->t('Love/romance'), $r[0]['romance']], - '$work' => ['work', DI::l10n()->t('Work/employment'), $r[0]['work']], - '$education' => ['education', DI::l10n()->t('School/education'), $r[0]['education']], - '$contact' => ['contact', DI::l10n()->t('Contact information and Social Networks'), $r[0]['contact']], - ]); - - $arr = ['profile' => $r[0], 'entry' => $o]; - Hook::callAll('profile_edit', $arr); - - return $o; - } else { - // If we don't support multi profiles, don't display this list. - if (!Feature::isEnabled(local_user(), 'multi_profiles')) { - $r = q("SELECT * FROM `profile` WHERE `uid` = %d AND `is-default`=1", - local_user() - ); - if (DBA::isResult($r)) { - //Go to the default profile. - DI::baseUrl()->redirect('profiles/' . $r[0]['id']); - } - } - - $r = q("SELECT * FROM `profile` WHERE `uid` = %d", - local_user()); - - if (DBA::isResult($r)) { - - $tpl = Renderer::getMarkupTemplate('profile_entry.tpl'); - - $profiles = ''; - foreach ($r as $rr) { - $profiles .= Renderer::replaceMacros($tpl, [ - '$photo' => DI::baseUrl()->remove($rr['thumb']), - '$id' => $rr['id'], - '$alt' => DI::l10n()->t('Profile Image'), - '$profile_name' => $rr['profile-name'], - '$visible' => (($rr['is-default']) ? '' . DI::l10n()->t('visible to everybody') . '' - : '' . DI::l10n()->t('Edit visibility') . '') - ]); - } - - $tpl_header = Renderer::getMarkupTemplate('profile_listing_header.tpl'); - $o .= Renderer::replaceMacros($tpl_header,[ - '$header' => DI::l10n()->t('Edit/Manage Profiles'), - '$chg_photo' => DI::l10n()->t('Change profile photo'), - '$cr_new' => DI::l10n()->t('Create New Profile'), - '$cr_new_link' => 'profiles/new?t=' . BaseModule::getFormSecurityToken("profile_new"), - '$profiles' => $profiles - ]); - } - return $o; - } - -} diff --git a/mod/settings.php b/mod/settings.php index 00991d891..a07ad24cc 100644 --- a/mod/settings.php +++ b/mod/settings.php @@ -73,9 +73,9 @@ function settings_init(App $a) ]; $tabs[] = [ - 'label' => DI::l10n()->t('Profiles'), - 'url' => 'profiles', - 'selected' => (($a->argc == 1) && ($a->argv[0] === 'profiles')?'active':''), + 'label' => DI::l10n()->t('Profile'), + 'url' => 'settings/profile', + 'selected' => (($a->argc > 1) && ($a->argv[1] === 'profile')?'active':''), 'accesskey' => 'p', ]; diff --git a/mod/update_profile.php b/mod/update_profile.php deleted file mode 100644 index 348a384c2..000000000 --- a/mod/update_profile.php +++ /dev/null @@ -1,49 +0,0 @@ -\r\n"; - - // We can remove this hack once Internet Explorer recognises HTML5 natively - echo "
"; - - /** - * Grab the page inner contents by calling the content function from the profile module directly, - * but move any image src attributes to another attribute name. This is because - * some browsers will prefetch all the images for the page even if we don't need them. - * The only ones we need to fetch are those for new page additions, which we'll discover - * on the client side and then swap the image back. - */ - - $text = Profile::content([], $profile_uid); - - if (DI::pConfig()->get(local_user(), "system", "bandwidth_saver")) { - $replace = "
" . DI::l10n()->t("[Embedded content - reload page to view]") . "
"; - $pattern = "/<\s*audio[^>]*>(.*?)<\s*\/\s*audio>/i"; - $text = preg_replace($pattern, $replace, $text); - $pattern = "/<\s*video[^>]*>(.*?)<\s*\/\s*video>/i"; - $text = preg_replace($pattern, $replace, $text); - $pattern = "/<\s*embed[^>]*>(.*?)<\s*\/\s*embed>/i"; - $text = preg_replace($pattern, $replace, $text); - $pattern = "/<\s*iframe[^>]*>(.*?)<\s*\/\s*iframe>/i"; - $text = preg_replace($pattern, $replace, $text); - } - - // reportedly some versions of MSIE don't handle tabs in XMLHttpRequest documents very well - echo str_replace("\t", " ", $text); - echo "
"; - echo "\r\n"; - exit(); -} \ No newline at end of file diff --git a/src/Model/Profile.php b/src/Model/Profile.php index b993562ce..b77bfa8e1 100644 --- a/src/Model/Profile.php +++ b/src/Model/Profile.php @@ -202,7 +202,7 @@ class Profile if (local_user() && local_user() == $a->profile['uid'] && $profiledata) { DI::page()['aside'] .= Renderer::replaceMacros( - Renderer::getMarkupTemplate('profile_edlink.tpl'), + Renderer::getMarkupTemplate('settings/profile/link.tpl'), [ '$editprofile' => DI::l10n()->t('Edit profile'), '$profid' => $a->profile['id'] @@ -388,40 +388,12 @@ class Profile // show edit profile to yourself if (!$is_contact && $local_user_is_self) { - if (Feature::isEnabled(local_user(), 'multi_profiles')) { - $profile['edit'] = [DI::baseUrl() . '/profiles', DI::l10n()->t('Profiles'), '', DI::l10n()->t('Manage/edit profiles')]; - $r = q( - "SELECT * FROM `profile` WHERE `uid` = %d", - local_user() - ); - - $profile['menu'] = [ - 'chg_photo' => DI::l10n()->t('Change profile photo'), - 'cr_new' => DI::l10n()->t('Create New Profile'), - 'entries' => [], - ]; - - if (DBA::isResult($r)) { - foreach ($r as $rr) { - $profile['menu']['entries'][] = [ - 'photo' => $rr['thumb'], - 'id' => $rr['id'], - 'alt' => DI::l10n()->t('Profile Image'), - 'profile_name' => $rr['profile-name'], - 'isdefault' => $rr['is-default'], - 'visibile_to_everybody' => DI::l10n()->t('visible to everybody'), - 'edit_visibility' => DI::l10n()->t('Edit visibility'), - ]; - } - } - } else { - $profile['edit'] = [DI::baseUrl() . '/profiles/' . $profile['id'], DI::l10n()->t('Edit profile'), '', DI::l10n()->t('Edit profile')]; - $profile['menu'] = [ - 'chg_photo' => DI::l10n()->t('Change profile photo'), - 'cr_new' => null, - 'entries' => [], - ]; - } + $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'), + 'cr_new' => null, + 'entries' => [], + ]; } // Fetch the account type @@ -870,7 +842,7 @@ class Profile } if ($a->profile['uid'] == local_user()) { - $profile['edit'] = [DI::baseUrl() . '/profiles/' . $a->profile['id'], DI::l10n()->t('Edit profile'), '', DI::l10n()->t('Edit profile')]; + $profile['edit'] = [DI::baseUrl() . '/settings/profile', DI::l10n()->t('Edit profile'), '', DI::l10n()->t('Edit profile')]; } $tpl = Renderer::getMarkupTemplate('profile/advanced.tpl'); @@ -902,22 +874,22 @@ class Profile $baseProfileUrl = DI::baseUrl() . '/profile/' . $nickname; $tabs = [ - [ - 'label' => DI::l10n()->t('Status'), - 'url' => $baseProfileUrl, - 'sel' => !$current ? 'active' : '', - 'title' => DI::l10n()->t('Status Messages and Posts'), - 'id' => 'status-tab', - 'accesskey' => 'm', - ], [ 'label' => DI::l10n()->t('Profile'), - 'url' => $baseProfileUrl . '/?tab=profile', + 'url' => $baseProfileUrl, 'sel' => $current == 'profile' ? 'active' : '', 'title' => DI::l10n()->t('Profile Details'), 'id' => 'profile-tab', 'accesskey' => 'r', ], + [ + 'label' => DI::l10n()->t('Status'), + 'url' => $baseProfileUrl . '/status', + 'sel' => $current == 'status' ? 'active' : '', + 'title' => DI::l10n()->t('Status Messages and Posts'), + 'id' => 'status-tab', + 'accesskey' => 'm', + ], [ 'label' => DI::l10n()->t('Photos'), 'url' => DI::baseUrl() . '/photos/' . $nickname, @@ -970,17 +942,7 @@ class Profile ]; } - if (!empty($_SESSION['new_member']) && $is_owner) { - $tabs[] = [ - 'label' => DI::l10n()->t('Tips for New Members'), - 'url' => DI::baseUrl() . '/newmember', - 'sel' => false, - 'title' => DI::l10n()->t('Tips for New Members'), - 'id' => 'newmember-tab', - ]; - } - - if ($is_owner || empty($a->profile['hide-friends'])) { + if (empty($a->profile['hide-friends'])) { $tabs[] = [ 'label' => DI::l10n()->t('Contacts'), 'url' => $baseProfileUrl . '/contacts', @@ -991,7 +953,18 @@ class Profile ]; } + if (!empty($_SESSION['new_member']) && $is_owner) { + $tabs[] = [ + 'label' => DI::l10n()->t('Tips for New Members'), + 'url' => DI::baseUrl() . '/newmember', + 'sel' => false, + 'title' => DI::l10n()->t('Tips for New Members'), + 'id' => 'newmember-tab', + ]; + } + $arr = ['is_owner' => $is_owner, 'nickname' => $nickname, 'tab' => $current, 'tabs' => $tabs]; + Hook::callAll('profile_tabs', $arr); $tpl = Renderer::getMarkupTemplate('common_tabs.tpl'); diff --git a/src/Module/BaseSettingsModule.php b/src/Module/BaseSettingsModule.php index 2f51dde64..225f5ed4a 100644 --- a/src/Module/BaseSettingsModule.php +++ b/src/Module/BaseSettingsModule.php @@ -35,9 +35,9 @@ class BaseSettingsModule extends BaseModule ]; $tabs[] = [ - 'label' => DI::l10n()->t('Profiles'), - 'url' => 'profiles', - 'selected' => (($a->argc == 1) && ($a->argv[0] === 'profiles') ? 'active' : ''), + 'label' => DI::l10n()->t('Profile'), + 'url' => 'settings/profile', + 'selected' => (($a->argc > 1) && ($a->argv[1] === 'profile') ? 'active' : ''), 'accesskey' => 'p', ]; diff --git a/src/Module/Profile.php b/src/Module/Profile.php deleted file mode 100644 index e3ae7b3f5..000000000 --- a/src/Module/Profile.php +++ /dev/null @@ -1,361 +0,0 @@ -argc < 2) { - throw new \Friendica\Network\HTTPException\BadRequestException(); - } - - self::$which = filter_var($a->argv[1], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH | FILTER_FLAG_STRIP_BACKTICK); - - // @TODO: Replace with parameter from router - if (local_user() && $a->argc > 2 && $a->argv[2] === 'view') { - self::$which = $a->user['nickname']; - self::$profile = filter_var($a->argv[1], FILTER_SANITIZE_NUMBER_INT); - } - } - - public static function rawContent(array $parameters = []) - { - if (ActivityPub::isRequest()) { - $user = DBA::selectFirst('user', ['uid'], ['nickname' => self::$which]); - if (DBA::isResult($user)) { - // The function returns an empty array when the account is removed, expired or blocked - $data = ActivityPub\Transmitter::getProfile($user['uid']); - if (!empty($data)) { - System::jsonExit($data, 'application/activity+json'); - } - } - - if (DBA::exists('userd', ['username' => self::$which])) { - // Known deleted user - $data = ActivityPub\Transmitter::getDeletedUser(self::$which); - - System::jsonError(410, $data); - } else { - // Any other case (unknown, blocked, unverified, expired, no profile, no self contact) - System::jsonError(404, []); - } - } - } - - public static function content(array $parameters = [], $update = 0) - { - $a = DI::app(); - - if (!$update) { - ProfileModel::load($a, self::$which, self::$profile); - - $page = DI::page(); - - $page['htmlhead'] .= "\n"; - - $blocked = !local_user() && !Session::getRemoteContactID($a->profile['uid']) && DI::config()->get('system', 'block_public'); - $userblock = !local_user() && !Session::getRemoteContactID($a->profile['uid']) && $a->profile['hidewall']; - - if (!empty($a->profile['page-flags']) && $a->profile['page-flags'] == User::PAGE_FLAGS_COMMUNITY) { - $page['htmlhead'] .= '' . "\n"; - } - - if (!empty($a->profile['openidserver'])) { - $page['htmlhead'] .= '' . "\n"; - } - - if (!empty($a->profile['openid'])) { - $delegate = strstr($a->profile['openid'], '://') ? $a->profile['openid'] : 'https://' . $a->profile['openid']; - $page['htmlhead'] .= '' . "\n"; - } - - // site block - if (!$blocked && !$userblock) { - $keywords = str_replace(['#', ',', ' ', ',,'], ['', ' ', ',', ','], $a->profile['pub_keywords'] ?? ''); - if (strlen($keywords)) { - $page['htmlhead'] .= '' . "\n"; - } - } - - $page['htmlhead'] .= '' . "\n"; - - if (!$a->profile['net-publish'] || $a->profile['hidewall']) { - $page['htmlhead'] .= '' . "\n"; - } - - $page['htmlhead'] .= '' . "\n"; - $page['htmlhead'] .= '' . "\n"; - $page['htmlhead'] .= '' . "\n"; - $page['htmlhead'] .= '' . "\n"; - $uri = urlencode('acct:' . $a->profile['nickname'] . '@' . DI::baseUrl()->getHostname() . (DI::baseUrl()->getUrlPath() ? '/' . DI::baseUrl()->getUrlPath() : '')); - $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) { - $page['htmlhead'] .= '' . "\n"; - } - $page['htmlhead'] .= '' . "\n"; - } - - $category = $datequery = $datequery2 = ''; - - if ($a->argc > 2) { - for ($x = 2; $x < $a->argc; $x ++) { - if (DI::dtFormat()->isYearMonth($a->argv[$x])) { - if ($datequery) { - $datequery2 = Strings::escapeHtml($a->argv[$x]); - } else { - $datequery = Strings::escapeHtml($a->argv[$x]); - } - } else { - $category = $a->argv[$x]; - } - } - } - - if (empty($category)) { - $category = $_GET['category'] ?? ''; - } - - $hashtags = $_GET['tag'] ?? ''; - - if (DI::config()->get('system', 'block_public') && !local_user() && !Session::getRemoteContactID($a->profile['uid'])) { - return Login::form(); - } - - $o = ''; - - if ($update) { - // Ensure we've got a profile owner if updating. - $a->profile['uid'] = $update; - } elseif ($a->profile['uid'] == local_user()) { - Nav::setSelected('home'); - } - - $remote_contact = Session::getRemoteContactID($a->profile['uid']); - $is_owner = local_user() == $a->profile['uid']; - $last_updated_key = "profile:" . $a->profile['uid'] . ":" . local_user() . ":" . $remote_contact; - - if (!empty($a->profile['hidewall']) && !$is_owner && !$remote_contact) { - notice(DI::l10n()->t('Access to this profile has been restricted.') . EOL); - return ''; - } - - if (!$update) { - $tab = Strings::escapeTags(trim($_GET['tab'] ?? '')); - - $o .= ProfileModel::getTabs($a, $tab, $is_owner, $a->profile['nickname']); - - if ($tab === 'profile') { - $o .= ProfileModel::getAdvanced($a); - Hook::callAll('profile_advanced', $o); - return $o; - } - - $o .= Widget::commonFriendsVisitor($a->profile['uid']); - - $commpage = $a->profile['page-flags'] == User::PAGE_FLAGS_COMMUNITY; - $commvisitor = $commpage && $remote_contact; - - DI::page()['aside'] .= Widget::postedByYear(DI::baseUrl()->get(true) . '/profile/' . $a->profile['nickname'], $a->profile['uid'] ?? 0, true); - DI::page()['aside'] .= Widget::categories(DI::baseUrl()->get(true) . '/profile/' . $a->profile['nickname'], XML::escape($category)); - DI::page()['aside'] .= Widget::tagCloud(); - - if (Security::canWriteToUserWall($a->profile['uid'])) { - $x = [ - 'is_owner' => $is_owner, - 'allow_location' => ($is_owner || $commvisitor) && $a->profile['allow_location'], - 'default_location' => $is_owner ? $a->user['default-location'] : '', - 'nickname' => $a->profile['nickname'], - 'lockstate' => is_array($a->user) - && (strlen($a->user['allow_cid']) - || strlen($a->user['allow_gid']) - || strlen($a->user['deny_cid']) - || strlen($a->user['deny_gid']) - ) ? 'lock' : 'unlock', - 'acl' => $is_owner ? ACL::getFullSelectorHTML(DI::page(), $a->user, true) : '', - 'bang' => '', - 'visitor' => $is_owner || $commvisitor ? 'block' : 'none', - 'profile_uid' => $a->profile['uid'], - ]; - - $o .= status_editor($a, $x); - } - } - - // Get permissions SQL - if $remote_contact is true, our remote user has been pre-verified and we already have fetched his/her groups - $sql_extra = Item::getPermissionsSQLByUserId($a->profile['uid']); - $sql_extra2 = ''; - - $last_updated_array = Session::get('last_updated', []); - - if ($update) { - $last_updated = $last_updated_array[$last_updated_key] ?? 0; - - // If the page user is the owner of the page we should query for unseen - // items. Otherwise use a timestamp of the last succesful update request. - if ($is_owner || !$last_updated) { - $sql_extra4 = " AND `item`.`unseen`"; - } else { - $gmupdate = gmdate(DateTimeFormat::MYSQL, $last_updated); - $sql_extra4 = " AND `item`.`received` > '" . $gmupdate . "'"; - } - - $items_stmt = DBA::p( - "SELECT DISTINCT(`parent-uri`) AS `uri`, `item`.`created` - FROM `item` - INNER JOIN `contact` - ON `contact`.`id` = `item`.`contact-id` - AND NOT `contact`.`blocked` - AND NOT `contact`.`pending` - WHERE `item`.`uid` = ? - AND `item`.`visible` - AND (NOT `item`.`deleted` OR `item`.`gravity` = ?) - AND NOT `item`.`moderated` - AND `item`.`wall` - $sql_extra4 - $sql_extra - ORDER BY `item`.`received` DESC", - $a->profile['uid'], - GRAVITY_ACTIVITY - ); - - if (!DBA::isResult($items_stmt)) { - return ''; - } - - $pager = new Pager(DI::args()->getQueryString()); - } else { - $sql_post_table = ""; - - if (!empty($category)) { - $sql_post_table = sprintf("INNER JOIN (SELECT `oid` FROM `term` WHERE `term` = '%s' AND `otype` = %d AND `type` = %d AND `uid` = %d ORDER BY `tid` DESC) AS `term` ON `item`.`id` = `term`.`oid` ", - DBA::escape(Strings::protectSprintf($category)), intval(TERM_OBJ_POST), intval(TERM_CATEGORY), intval($a->profile['uid'])); - } - - if (!empty($hashtags)) { - $sql_post_table .= sprintf("INNER JOIN (SELECT `oid` FROM `term` WHERE `term` = '%s' AND `otype` = %d AND `type` = %d AND `uid` = %d ORDER BY `tid` DESC) AS `term` ON `item`.`id` = `term`.`oid` ", - DBA::escape(Strings::protectSprintf($hashtags)), intval(TERM_OBJ_POST), intval(TERM_HASHTAG), intval($a->profile['uid'])); - } - - if (!empty($datequery)) { - $sql_extra2 .= Strings::protectSprintf(sprintf(" AND `thread`.`received` <= '%s' ", DBA::escape(DateTimeFormat::convert($datequery, 'UTC', date_default_timezone_get())))); - } - if (!empty($datequery2)) { - $sql_extra2 .= Strings::protectSprintf(sprintf(" AND `thread`.`received` >= '%s' ", DBA::escape(DateTimeFormat::convert($datequery2, 'UTC', date_default_timezone_get())))); - } - - // Does the profile page belong to a forum? - // If not then we can improve the performance with an additional condition - $condition = ['uid' => $a->profile['uid'], 'page-flags' => [User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_PRVGROUP]]; - if (!DBA::exists('user', $condition)) { - $sql_extra3 = sprintf(" AND `thread`.`contact-id` = %d ", intval(intval($a->profile['contact_id']))); - } else { - $sql_extra3 = ""; - } - - // check if we serve a mobile device and get the user settings - // accordingly - if (DI::mode()->isMobile()) { - $itemspage_network = DI::pConfig()->get(local_user(), 'system', 'itemspage_mobile_network', 10); - } else { - $itemspage_network = DI::pConfig()->get(local_user(), 'system', 'itemspage_network', 20); - } - - // now that we have the user settings, see if the theme forces - // a maximum item number which is lower then the user choice - if (($a->force_max_items > 0) && ($a->force_max_items < $itemspage_network)) { - $itemspage_network = $a->force_max_items; - } - - $pager = new Pager(DI::args()->getQueryString(), $itemspage_network); - - $pager_sql = sprintf(" LIMIT %d, %d ", $pager->getStart(), $pager->getItemsPerPage()); - - $items_stmt = DBA::p( - "SELECT `item`.`uri` - FROM `thread` - STRAIGHT_JOIN `item` ON `item`.`id` = `thread`.`iid` - $sql_post_table - STRAIGHT_JOIN `contact` - ON `contact`.`id` = `thread`.`contact-id` - AND NOT `contact`.`blocked` - AND NOT `contact`.`pending` - WHERE `thread`.`uid` = ? - AND `thread`.`visible` - AND NOT `thread`.`deleted` - AND NOT `thread`.`moderated` - AND `thread`.`wall` - $sql_extra3 - $sql_extra - $sql_extra2 - ORDER BY `thread`.`received` DESC - $pager_sql", - $a->profile['uid'] - ); - } - - // Set a time stamp for this page. We will make use of it when we - // search for new items (update routine) - $last_updated_array[$last_updated_key] = time(); - Session::set('last_updated', $last_updated_array); - - if ($is_owner && !$update && !DI::config()->get('theme', 'hide_eventlist')) { - $o .= ProfileModel::getBirthdays(); - $o .= ProfileModel::getEventsReminderHTML(); - } - - if ($is_owner) { - $unseen = Item::exists(['wall' => true, 'unseen' => true, 'uid' => local_user()]); - if ($unseen) { - Item::update(['unseen' => false], ['wall' => true, 'unseen' => true, 'uid' => local_user()]); - } - } - - $items = DBA::toArray($items_stmt); - - if ($pager->getStart() == 0 && !empty($a->profile['uid'])) { - $pinned_items = Item::selectPinned($a->profile['uid'], ['uri', 'pinned'], ['true' . $sql_extra]); - $pinned = Item::inArray($pinned_items); - $items = array_merge($items, $pinned); - } - - $o .= conversation($a, $items, $pager, 'profile', $update, false, 'pinned_received', $a->profile['uid']); - - if (!$update) { - $o .= $pager->renderMinimal(count($items)); - } - - return $o; - } -} diff --git a/src/Module/Profile/Index.php b/src/Module/Profile/Index.php new file mode 100644 index 000000000..27d5047d1 --- /dev/null +++ b/src/Module/Profile/Index.php @@ -0,0 +1,121 @@ + $parameters['nickname']]); + if (DBA::isResult($user)) { + // The function returns an empty array when the account is removed, expired or blocked + $data = ActivityPub\Transmitter::getProfile($user['uid']); + if (!empty($data)) { + System::jsonExit($data, 'application/activity+json'); + } + } + + if (DBA::exists('userd', ['username' => $parameters['nickname']])) { + // Known deleted user + $data = ActivityPub\Transmitter::getDeletedUser($parameters['nickname']); + + System::jsonError(410, $data); + } else { + // Any other case (unknown, blocked, nverified, expired, no profile, no self contact) + System::jsonError(404, []); + } + } + } + + public static function content(array $parameters = []) + { + $a = DI::app(); + + if (DI::config()->get('system', 'block_public') && !local_user() && !Session::getRemoteContactID($a->profile['profile_uid'])) { + return Login::form(); + } + + ProfileModel::load($a, $parameters['nickname']); + + DI::page()['htmlhead'] .= "\n"; + + $blocked = !local_user() && !Session::getRemoteContactID($a->profile['profile_uid']) && DI::config()->get('system', 'block_public'); + $userblock = !local_user() && !Session::getRemoteContactID($a->profile['profile_uid']) && $a->profile['hidewall']; + + 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 = ''; + + Nav::setSelected('home'); + + $remote_contact = Session::getRemoteContactID($a->profile['profile_uid']); + $is_owner = local_user() == $a->profile['profile_uid']; + + if (!empty($a->profile['hidewall']) && !$is_owner && !$remote_contact) { + notice(DI::l10n()->t('Access to this profile has been restricted.')); + return ''; + } + + $o .= ProfileModel::getTabs($a, 'profile', $is_owner, $a->profile['nickname']); + + $o .= ProfileModel::getAdvanced($a); + + Hook::callAll('profile_advanced', $o); + + return $o; + } +} diff --git a/src/Module/Profile/Status.php b/src/Module/Profile/Status.php new file mode 100644 index 000000000..56247b20e --- /dev/null +++ b/src/Module/Profile/Status.php @@ -0,0 +1,217 @@ +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"; + + $category = $datequery = $datequery2 = ''; + + $dtFormat = DI::dtFormat(); + + if ($args->getArgc() > 2) { + for ($x = 2; $x < $args->getArgc(); $x++) { + if ($dtFormat->isYearMonth($args->get($x))) { + if ($datequery) { + $datequery2 = Strings::escapeHtml($args->get($x)); + } else { + $datequery = Strings::escapeHtml($args->get($x)); + } + } else { + $category = $args->get($x); + } + } + } + + if (empty($category)) { + $category = $_GET['category'] ?? ''; + } + + $hashtags = $_GET['tag'] ?? ''; + + if (DI::config()->get('system', 'block_public') && !local_user() && !Session::getRemoteContactID($a->profile['profile_uid'])) { + return Login::form(); + } + + $o = ''; + + if ($a->profile['profile_uid'] == local_user()) { + Nav::setSelected('home'); + } + + $remote_contact = Session::getRemoteContactID($a->profile['profile_uid']); + $is_owner = local_user() == $a->profile['profile_uid']; + $last_updated_key = "profile:" . $a->profile['profile_uid'] . ":" . local_user() . ":" . $remote_contact; + + if (!empty($a->profile['hidewall']) && !$is_owner && !$remote_contact) { + notice(DI::l10n()->t('Access to this profile has been restricted.') . EOL); + return ''; + } + + $o .= ProfileModel::getTabs($a, 'status', $is_owner, $a->profile['nickname']); + + $o .= Widget::commonFriendsVisitor($a->profile['profile_uid']); + + $commpage = $a->profile['page-flags'] == User::PAGE_FLAGS_COMMUNITY; + $commvisitor = $commpage && $remote_contact; + + DI::page()['aside'] .= Widget::postedByYear(DI::baseUrl() . '/profile/' . $a->profile['nickname'] . '/status', $a->profile['profile_uid'] ?? 0, true); + DI::page()['aside'] .= Widget::categories(DI::baseUrl() . '/profile/' . $a->profile['nickname'] . '/status', XML::escape($category)); + DI::page()['aside'] .= Widget::tagCloud(); + + if (Security::canWriteToUserWall($a->profile['profile_uid'])) { + $x = [ + 'is_owner' => $is_owner, + 'allow_location' => ($is_owner || $commvisitor) && $a->profile['allow_location'], + 'default_location' => $is_owner ? $a->user['default-location'] : '', + 'nickname' => $a->profile['nickname'], + 'lockstate' => is_array($a->user) + && (strlen($a->user['allow_cid']) + || strlen($a->user['allow_gid']) + || strlen($a->user['deny_cid']) + || strlen($a->user['deny_gid']) + ) ? 'lock' : 'unlock', + 'acl' => $is_owner ? ACL::getFullSelectorHTML(DI::page(), $a->user, true) : '', + 'bang' => '', + 'visitor' => $is_owner || $commvisitor ? 'block' : 'none', + 'profile_uid' => $a->profile['profile_uid'], + ]; + + $o .= status_editor($a, $x); + } + + // Get permissions SQL - if $remote_contact is true, our remote user has been pre-verified and we already have fetched his/her groups + $sql_extra = Item::getPermissionsSQLByUserId($a->profile['profile_uid']); + $sql_extra2 = ''; + + $last_updated_array = Session::get('last_updated', []); + + $sql_post_table = ""; + + if (!empty($category)) { + $sql_post_table = sprintf("INNER JOIN (SELECT `oid` FROM `term` WHERE `term` = '%s' AND `otype` = %d AND `type` = %d AND `uid` = %d ORDER BY `tid` DESC) AS `term` ON `item`.`id` = `term`.`oid` ", + DBA::escape(Strings::protectSprintf($category)), intval(TERM_OBJ_POST), intval(TERM_CATEGORY), intval($a->profile['profile_uid'])); + } + + if (!empty($hashtags)) { + $sql_post_table .= sprintf("INNER JOIN (SELECT `oid` FROM `term` WHERE `term` = '%s' AND `otype` = %d AND `type` = %d AND `uid` = %d ORDER BY `tid` DESC) AS `term` ON `item`.`id` = `term`.`oid` ", + DBA::escape(Strings::protectSprintf($hashtags)), intval(TERM_OBJ_POST), intval(TERM_HASHTAG), intval($a->profile['profile_uid'])); + } + + if (!empty($datequery)) { + $sql_extra2 .= Strings::protectSprintf(sprintf(" AND `thread`.`received` <= '%s' ", DBA::escape(DateTimeFormat::convert($datequery, 'UTC', date_default_timezone_get())))); + } + if (!empty($datequery2)) { + $sql_extra2 .= Strings::protectSprintf(sprintf(" AND `thread`.`received` >= '%s' ", DBA::escape(DateTimeFormat::convert($datequery2, 'UTC', date_default_timezone_get())))); + } + + // Does the profile page belong to a forum? + // If not then we can improve the performance with an additional condition + $condition = ['uid' => $a->profile['profile_uid'], 'page-flags' => [User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_PRVGROUP]]; + if (!DBA::exists('user', $condition)) { + $sql_extra3 = sprintf(" AND `thread`.`contact-id` = %d ", intval(intval($a->profile['contact_id']))); + } else { + $sql_extra3 = ""; + } + + // check if we serve a mobile device and get the user settings + // accordingly + if (DI::mode()->isMobile()) { + $itemspage_network = DI::pConfig()->get(local_user(), 'system', 'itemspage_mobile_network', 10); + } else { + $itemspage_network = DI::pConfig()->get(local_user(), 'system', 'itemspage_network', 20); + } + + // now that we have the user settings, see if the theme forces + // a maximum item number which is lower then the user choice + if (($a->force_max_items > 0) && ($a->force_max_items < $itemspage_network)) { + $itemspage_network = $a->force_max_items; + } + + $pager = new Pager($args->getQueryString(), $itemspage_network); + + $pager_sql = sprintf(" LIMIT %d, %d ", $pager->getStart(), $pager->getItemsPerPage()); + + $items_stmt = DBA::p( + "SELECT `item`.`uri` + FROM `thread` + STRAIGHT_JOIN `item` ON `item`.`id` = `thread`.`iid` + $sql_post_table + STRAIGHT_JOIN `contact` + ON `contact`.`id` = `thread`.`contact-id` + AND NOT `contact`.`blocked` + AND NOT `contact`.`pending` + WHERE `thread`.`uid` = ? + AND `thread`.`visible` + AND NOT `thread`.`deleted` + AND NOT `thread`.`moderated` + AND `thread`.`wall` + $sql_extra3 + $sql_extra + $sql_extra2 + ORDER BY `thread`.`received` DESC + $pager_sql", + $a->profile['profile_uid'] + ); + + // Set a time stamp for this page. We will make use of it when we + // search for new items (update routine) + $last_updated_array[$last_updated_key] = time(); + Session::set('last_updated', $last_updated_array); + + if ($is_owner && !DI::config()->get('theme', 'hide_eventlist')) { + $o .= ProfileModel::getBirthdays(); + $o .= ProfileModel::getEventsReminderHTML(); + } + + if ($is_owner) { + $unseen = Item::exists(['wall' => true, 'unseen' => true, 'uid' => local_user()]); + if ($unseen) { + Item::update(['unseen' => false], ['wall' => true, 'unseen' => true, 'uid' => local_user()]); + } + } + + $items = DBA::toArray($items_stmt); + + $o .= conversation($a, $items, $pager, 'profile', false, false, 'received', $a->profile['profile_uid']); + + $o .= $pager->renderMinimal(count($items)); + + return $o; + } +} diff --git a/src/Module/Settings/Profile/Index.php b/src/Module/Settings/Profile/Index.php new file mode 100644 index 000000000..e1b1d96b9 --- /dev/null +++ b/src/Module/Settings/Profile/Index.php @@ -0,0 +1,388 @@ +t('Profile Name is required.')); + return; + } + + $namechanged = $profile['username'] != $name; + + $pdesc = Strings::escapeTags(trim($_POST['pdesc'])); + $gender = Strings::escapeTags(trim($_POST['gender'])); + $address = Strings::escapeTags(trim($_POST['address'])); + $locality = Strings::escapeTags(trim($_POST['locality'])); + $region = Strings::escapeTags(trim($_POST['region'])); + $postal_code = Strings::escapeTags(trim($_POST['postal_code'])); + $country_name = Strings::escapeTags(trim($_POST['country_name'])); + $pub_keywords = self::cleanKeywords(Strings::escapeTags(trim($_POST['pub_keywords']))); + $prv_keywords = self::cleanKeywords(Strings::escapeTags(trim($_POST['prv_keywords']))); + $marital = Strings::escapeTags(trim($_POST['marital'])); + $howlong = Strings::escapeTags(trim($_POST['howlong'])); + + $with = (!empty($_POST['with']) ? Strings::escapeTags(trim($_POST['with'])) : ''); + + if (!strlen($howlong)) { + $howlong = DBA::NULL_DATETIME; + } else { + $howlong = DateTimeFormat::convert($howlong, 'UTC', date_default_timezone_get()); + } + + // linkify the relationship target if applicable + if (strlen($with)) { + if ($with != strip_tags($profile['with'])) { + $contact_url = ''; + $lookup = $with; + if (strpos($lookup, '@') === 0) { + $lookup = substr($lookup, 1); + } + $lookup = str_replace('_', ' ', $lookup); + if (strpos($lookup, '@') || (strpos($lookup, 'http://'))) { + $contact_name = $lookup; + $links = @Probe::lrdd($lookup); + if (count($links)) { + foreach ($links as $link) { + if ($link['@attributes']['rel'] === 'http://webfinger.net/rel/profile-page') { + $contact_url = $link['@attributes']['href']; + } + } + } + } else { + $contact_name = $lookup; + + $contact = Contact::selectFirst( + ['url', 'name'], + ['? IN (`name`, `nick`) AND `uid` = ?', $lookup, local_user()] + ); + + if (DBA::isResult($contact)) { + $contact_url = $contact['url']; + $contact_name = $contact['name']; + } + } + + if ($contact_url) { + $with = str_replace($lookup, '' . $contact_name . '', $with); + if (strpos($with, '@') === 0) { + $with = substr($with, 1); + } + } + } else { + $with = $profile['with']; + } + } + + /// @TODO Not flexible enough for later expansion, let's have more OOP here + $sexual = Strings::escapeTags(trim($_POST['sexual'])); + $xmpp = Strings::escapeTags(trim($_POST['xmpp'])); + $homepage = Strings::escapeTags(trim($_POST['homepage'])); + if ((strpos($homepage, 'http') !== 0) && (strlen($homepage))) { + // neither http nor https in URL, add them + $homepage = 'http://' . $homepage; + } + + $hometown = Strings::escapeTags(trim($_POST['hometown'])); + $politic = Strings::escapeTags(trim($_POST['politic'])); + $religion = Strings::escapeTags(trim($_POST['religion'])); + + $likes = Strings::escapeHtml(trim($_POST['likes'])); + $dislikes = Strings::escapeHtml(trim($_POST['dislikes'])); + + $about = Strings::escapeHtml(trim($_POST['about'])); + $interest = Strings::escapeHtml(trim($_POST['interest'])); + $contact = Strings::escapeHtml(trim($_POST['contact'])); + $music = Strings::escapeHtml(trim($_POST['music'])); + $book = Strings::escapeHtml(trim($_POST['book'])); + $tv = Strings::escapeHtml(trim($_POST['tv'])); + $film = Strings::escapeHtml(trim($_POST['film'])); + $romance = Strings::escapeHtml(trim($_POST['romance'])); + $work = Strings::escapeHtml(trim($_POST['work'])); + $education = Strings::escapeHtml(trim($_POST['education'])); + + $hide_friends = intval(!empty($_POST['hide-friends'])); + + DI::pConfig()->set(local_user(), 'system', 'detailed_profile', intval(!empty($_POST['detailed_profile']))); + + $result = DBA::update( + 'profile', + [ + 'name' => $name, + 'pdesc' => $pdesc, + 'gender' => $gender, + 'dob' => $dob, + 'address' => $address, + 'locality' => $locality, + 'region' => $region, + 'postal-code' => $postal_code, + 'country-name' => $country_name, + 'marital' => $marital, + 'with' => $with, + 'howlong' => $howlong, + 'sexual' => $sexual, + 'xmpp' => $xmpp, + 'homepage' => $homepage, + 'hometown' => $hometown, + 'politic' => $politic, + 'religion' => $religion, + 'pub_keywords' => $pub_keywords, + 'prv_keywords' => $prv_keywords, + 'likes' => $likes, + 'dislikes' => $dislikes, + 'about' => $about, + 'interest' => $interest, + 'contact' => $contact, + 'music' => $music, + 'book' => $book, + 'tv' => $tv, + 'film' => $film, + 'romance' => $romance, + 'work' => $work, + 'education' => $education, + 'hide-friends' => $hide_friends, + ], + [ + 'uid' => local_user(), + 'is-default' => true, + ] + ); + + if ($result) { + info(DI::l10n()->t('Profile updated.')); + } else { + notice(DI::l10n()->t('Profile couldn\'t be updated.')); + return; + } + + if ($namechanged) { + DBA::update('user', ['username' => $name], ['uid' => local_user()]); + } + + Contact::updateSelfFromUserID(local_user()); + + // Update global directory in background + if (Session::get('my_url') && strlen(DI::config()->get('system', 'directory'))) { + Worker::add(PRIORITY_LOW, 'Directory', Session::get('my_url')); + } + + Worker::add(PRIORITY_LOW, 'ProfileUpdate', local_user()); + + // Update the global contact for the user + GContact::updateForUser(local_user()); + } + + public static function content(array $parameters = []) + { + if (!local_user()) { + notice(DI::l10n()->t('You must be logged in to use this module')); + return Login::form(); + } + + parent::content(); + + $o = ''; + + $profile = ProfileModel::getByUID(local_user()); + if (!DBA::isResult($profile)) { + throw new HTTPException\NotFoundException(); + } + + $a = DI::app(); + + DI::page()['htmlhead'] .= Renderer::replaceMacros(Renderer::getMarkupTemplate('settings/profile/index_head.tpl'), [ + '$baseurl' => DI::baseUrl()->get(true), + ]); + + $opt_tpl = Renderer::getMarkupTemplate('settings/profile/hide-friends.tpl'); + $hide_friends = Renderer::replaceMacros($opt_tpl, [ + '$yesno' => [ + 'hide-friends', //Name + DI::l10n()->t('Hide contacts and friends:'), //Label + !!$profile['hide-friends'], //Value + '', //Help string + [DI::l10n()->t('No'), DI::l10n()->t('Yes')] //Off - On strings + ], + '$desc' => DI::l10n()->t('Hide your contact/friend list from viewers of this profile?'), + '$yes_str' => DI::l10n()->t('Yes'), + '$no_str' => DI::l10n()->t('No'), + '$yes_selected' => (($profile['hide-friends']) ? ' checked="checked"' : ''), + '$no_selected' => (($profile['hide-friends'] == 0) ? ' checked="checked"' : '') + ]); + + $personal_account = !in_array($a->user['page-flags'], [User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_PRVGROUP]); + + $detailed_profile = + $personal_account + && DI::pConfig()->get(local_user(), 'system', 'detailed_profile', + DI::pConfig()->get(local_user(), 'system', 'detailled_profile') + ) + ; + + $tpl = Renderer::getMarkupTemplate('settings/profile/index.tpl'); + $o .= Renderer::replaceMacros($tpl, [ + '$personal_account' => $personal_account, + '$detailed_profile' => $detailed_profile, + + '$details' => [ + 'detailed_profile', //Name + DI::l10n()->t('Show more profile fields:'), //Label + $detailed_profile, //Value + '', //Help string + [DI::l10n()->t('No'), DI::l10n()->t('Yes')] //Off - On strings + ], + + '$form_security_token' => self::getFormSecurityToken('settings_profile'), + '$form_security_token_photo' => self::getFormSecurityToken('settings_profile_photo'), + + '$profile_action' => DI::l10n()->t('Profile Actions'), + '$banner' => DI::l10n()->t('Edit Profile Details'), + '$submit' => DI::l10n()->t('Submit'), + '$profpic' => DI::l10n()->t('Change Profile Photo'), + '$profpiclink' => '/photos/' . $a->user['nickname'], + '$viewprof' => DI::l10n()->t('View this profile'), + '$viewallprof' => DI::l10n()->t('View all profiles'), + '$editvis' => DI::l10n()->t('Edit visibility'), + '$cr_prof' => DI::l10n()->t('Create a new profile using these settings'), + '$cl_prof' => DI::l10n()->t('Clone this profile'), + '$del_prof' => DI::l10n()->t('Delete this profile'), + + '$lbl_basic_section' => DI::l10n()->t('Basic information'), + '$lbl_picture_section' => DI::l10n()->t('Profile picture'), + '$lbl_location_section' => DI::l10n()->t('Location'), + '$lbl_preferences_section' => DI::l10n()->t('Preferences'), + '$lbl_status_section' => DI::l10n()->t('Status information'), + '$lbl_about_section' => DI::l10n()->t('Additional information'), + '$lbl_interests_section' => DI::l10n()->t('Interests'), + '$lbl_personal_section' => DI::l10n()->t('Personal'), + '$lbl_relation_section' => DI::l10n()->t('Relation'), + '$lbl_miscellaneous_section' => DI::l10n()->t('Miscellaneous'), + + '$lbl_profile_photo' => DI::l10n()->t('Upload Profile Photo'), + '$lbl_gender' => DI::l10n()->t('Your Gender:'), + '$lbl_marital' => DI::l10n()->t(' Marital Status:'), + '$lbl_sexual' => DI::l10n()->t('Sexual Preference:'), + '$lbl_ex2' => DI::l10n()->t('Example: fishing photography software'), + + '$default' => '

' . DI::l10n()->t('This is your public profile.
It may be visible to anybody using the internet.') . '

', + '$baseurl' => DI::baseUrl()->get(true), + '$nickname' => $a->user['nickname'], + '$name' => ['name', DI::l10n()->t('Display name:'), $profile['name']], + '$pdesc' => ['pdesc', DI::l10n()->t('Title/Description:'), $profile['pdesc']], + '$dob' => Temporal::getDateofBirthField($profile['dob'], $a->user['timezone']), + '$hide_friends' => $hide_friends, + '$address' => ['address', DI::l10n()->t('Street Address:'), $profile['address']], + '$locality' => ['locality', DI::l10n()->t('Locality/City:'), $profile['locality']], + '$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']) . ')' : ''), + '$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')], + '$howlong' => ['howlong', DI::l10n()->t('Since [date]:'), ($profile['howlong'] <= DBA::NULL_DATETIME ? '' : DateTimeFormat::local($profile['howlong']))], + '$sexual' => ['selector' => ContactSelector::sexualPreference($profile['sexual']), 'value' => DI::l10n()->t($profile['sexual'])], + '$about' => ['about', DI::l10n()->t('Tell us about yourself...'), $profile['about']], + '$xmpp' => ['xmpp', DI::l10n()->t('XMPP (Jabber) address:'), $profile['xmpp'], DI::l10n()->t('The XMPP address will be propagated to your contacts so that they can follow you.')], + '$homepage' => ['homepage', DI::l10n()->t('Homepage URL:'), $profile['homepage']], + '$hometown' => ['hometown', DI::l10n()->t('Hometown:'), $profile['hometown']], + '$politic' => ['politic', DI::l10n()->t('Political Views:'), $profile['politic']], + '$religion' => ['religion', DI::l10n()->t('Religious Views:'), $profile['religion']], + '$pub_keywords' => ['pub_keywords', DI::l10n()->t('Public Keywords:'), $profile['pub_keywords'], DI::l10n()->t('(Used for suggesting potential friends, can be seen by others)')], + '$prv_keywords' => ['prv_keywords', DI::l10n()->t('Private Keywords:'), $profile['prv_keywords'], DI::l10n()->t('(Used for searching profiles, never shown to others)')], + '$likes' => ['likes', DI::l10n()->t('Likes:'), $profile['likes']], + '$dislikes' => ['dislikes', DI::l10n()->t('Dislikes:'), $profile['dislikes']], + '$music' => ['music', DI::l10n()->t('Musical interests'), $profile['music']], + '$book' => ['book', DI::l10n()->t('Books, literature'), $profile['book']], + '$tv' => ['tv', DI::l10n()->t('Television'), $profile['tv']], + '$film' => ['film', DI::l10n()->t('Film/dance/culture/entertainment'), $profile['film']], + '$interest' => ['interest', DI::l10n()->t('Hobbies/Interests'), $profile['interest']], + '$romance' => ['romance', DI::l10n()->t('Love/romance'), $profile['romance']], + '$work' => ['work', DI::l10n()->t('Work/employment'), $profile['work']], + '$education' => ['education', DI::l10n()->t('School/education'), $profile['education']], + '$contact' => ['contact', DI::l10n()->t('Contact information and Social Networks'), $profile['contact']], + ]); + + $arr = ['profile' => $profile, 'entry' => $o]; + Hook::callAll('profile_edit', $arr); + + return $o; + } + + private static function cleanKeywords($keywords) + { + $keywords = str_replace(',', ' ', $keywords); + $keywords = explode(' ', $keywords); + + $cleaned = []; + foreach ($keywords as $keyword) { + $keyword = trim(strtolower($keyword)); + $keyword = trim($keyword, '#'); + if ($keyword != '') { + $cleaned[] = $keyword; + } + } + + $keywords = implode(', ', $cleaned); + + return $keywords; + } +} diff --git a/src/Module/Update/Profile.php b/src/Module/Update/Profile.php new file mode 100644 index 000000000..e67082191 --- /dev/null +++ b/src/Module/Update/Profile.php @@ -0,0 +1,127 @@ +get('system', 'block_public') && !local_user() && !Session::getRemoteContactID($a->profile['profile_uid'])) { + throw new ForbiddenException(); + } + + $o = ''; + + $profile_uid = intval($_GET['p'] ?? 0); + + // Ensure we've got a profile owner if updating. + $a->profile['profile_uid'] = $profile_uid; + + $remote_contact = Session::getRemoteContactID($a->profile['profile_uid']); + $is_owner = local_user() == $a->profile['profile_uid']; + $last_updated_key = "profile:" . $a->profile['profile_uid'] . ":" . local_user() . ":" . $remote_contact; + + if (!empty($a->profile['hidewall']) && !$is_owner && !$remote_contact) { + throw new ForbiddenException(DI::l10n()->t('Access to this profile has been restricted.')); + } + + // Get permissions SQL - if $remote_contact is true, our remote user has been pre-verified and we already have fetched his/her groups + $sql_extra = Item::getPermissionsSQLByUserId($a->profile['profile_uid']); + + $last_updated_array = Session::get('last_updated', []); + + $last_updated = $last_updated_array[$last_updated_key] ?? 0; + + // If the page user is the owner of the page we should query for unseen + // items. Otherwise use a timestamp of the last succesful update request. + if ($is_owner || !$last_updated) { + $sql_extra4 = " AND `item`.`unseen`"; + } else { + $gmupdate = gmdate(DateTimeFormat::MYSQL, $last_updated); + $sql_extra4 = " AND `item`.`received` > '" . $gmupdate . "'"; + } + + $items_stmt = DBA::p( + "SELECT DISTINCT(`parent-uri`) AS `uri`, `item`.`created` + FROM `item` + INNER JOIN `contact` + ON `contact`.`id` = `item`.`contact-id` + AND NOT `contact`.`blocked` + AND NOT `contact`.`pending` + WHERE `item`.`uid` = ? + AND `item`.`visible` + AND (NOT `item`.`deleted` OR `item`.`gravity` = ?) + AND NOT `item`.`moderated` + AND `item`.`wall` + $sql_extra4 + $sql_extra + ORDER BY `item`.`received` DESC", + $a->profile['profile_uid'], + GRAVITY_ACTIVITY + ); + + if (!DBA::isResult($items_stmt)) { + return ''; + } + + $pager = new Pager(DI::args()->getQueryString()); + + // Set a time stamp for this page. We will make use of it when we + // search for new items (update routine) + $last_updated_array[$last_updated_key] = time(); + Session::set('last_updated', $last_updated_array); + + if ($is_owner && !$profile_uid && !DI::config()->get('theme', 'hide_eventlist')) { + $o .= ProfileModel::getBirthdays(); + $o .= ProfileModel::getEventsReminderHTML(); + } + + if ($is_owner) { + $unseen = Item::exists(['wall' => true, 'unseen' => true, 'uid' => local_user()]); + if ($unseen) { + Item::update(['unseen' => false], ['wall' => true, 'unseen' => true, 'uid' => local_user()]); + } + } + + $items = DBA::toArray($items_stmt); + + $o .= conversation($a, $items, $pager, 'profile', $profile_uid, false, 'received', $a->profile['profile_uid']); + + header("Content-type: text/html"); + echo "\r\n"; + // We can remove this hack once Internet Explorer recognises HTML5 natively + echo "
"; + echo $o; + if (DI::pConfig()->get(local_user(), "system", "bandwidth_saver")) { + $replace = "
".DI::l10n()->t("[Embedded content - reload page to view]")."
"; + $pattern = "/<\s*audio[^>]*>(.*?)<\s*\/\s*audio>/i"; + $o = preg_replace($pattern, $replace, $o); + $pattern = "/<\s*video[^>]*>(.*?)<\s*\/\s*video>/i"; + $o = preg_replace($pattern, $replace, $o); + $pattern = "/<\s*embed[^>]*>(.*?)<\s*\/\s*embed>/i"; + $o = preg_replace($pattern, $replace, $o); + $pattern = "/<\s*iframe[^>]*>(.*?)<\s*\/\s*iframe>/i"; + $o = preg_replace($pattern, $replace, $o); + } + + // reportedly some versions of MSIE don't handle tabs in XMLHttpRequest documents very well + echo str_replace("\t", " ", $o); + echo "
"; + echo "\r\n"; + exit(); + } +} diff --git a/static/routes.config.php b/static/routes.config.php index 05f259956..ca1f9bf2f 100644 --- a/static/routes.config.php +++ b/static/routes.config.php @@ -195,10 +195,9 @@ return [ '/probe' => [Module\Debug\Probe::class, [R::GET]], '/profile' => [ - '/{nickname}' => [Module\Profile::class, [R::GET]], - '/{nickname}/{to:\d{4}-\d{2}-\d{2}}/{from:\d{4}-\d{2}-\d{2}}' => [Module\Profile::class, [R::GET]], - '/{nickname}/contacts[/{type}]' => [Module\Profile\Contacts::class, [R::GET]], - '/{profile:\d+}/view' => [Module\Profile::class, [R::GET]], + '/{nickname}' => [Module\Profile\Index::class, [R::GET]], + '/{nickname}/contacts[/{type}]' => [Module\Profile\Contacts::class, [R::GET]], + '/{nickname}/status[/{category}[/{date1}[/{date2}]]]' => [Module\Profile\Status::class, [R::GET]], ], '/proxy' => [ @@ -229,6 +228,7 @@ return [ ], '/delegation[/{action}/{user_id}]' => [Module\Settings\Delegation::class, [R::GET, R::POST]], '/profile' => [ + '[/]' => [Module\Settings\Profile\Index::class, [R::GET, R::POST]], '/photo[/new]' => [Module\Settings\Profile\Photo\Index::class, [R::GET, R::POST]], '/photo/crop/{guid}' => [Module\Settings\Profile\Photo\Crop::class, [R::GET, R::POST]], ], @@ -244,6 +244,9 @@ return [ '/starred/{item:\d+}' => [Module\Starred::class, [R::GET]], '/toggle_mobile' => [Module\ToggleMobile::class, [R::GET]], '/tos' => [Module\Tos::class, [R::GET]], + + '/update_profile' => [Module\Update\Profile::class, [R::GET]], + '/view/theme/{theme}/style.pcss' => [Module\Theme::class, [R::GET]], '/viewsrc/{item:\d+}' => [Module\Debug\ItemBody::class, [R::GET]], '/webfinger' => [Module\Debug\WebFinger::class, [R::GET]], diff --git a/tests/src/App/RouterTest.php b/tests/src/App/RouterTest.php index fc34df957..6d4b8770d 100644 --- a/tests/src/App/RouterTest.php +++ b/tests/src/App/RouterTest.php @@ -159,7 +159,7 @@ class RouterTest extends TestCase '/post' => [ '/it' => [Module\NodeInfo::class, [Router::POST]], ], - '/double' => [Module\Profile::class, [Router::GET, Router::POST]] + '/double' => [Module\Profile\Index::class, [Router::GET, Router::POST]] ], ], ]; @@ -177,7 +177,7 @@ class RouterTest extends TestCase $this->assertEquals(Module\Home::class, $router->getModuleClass('/')); $this->assertEquals(Module\Friendica::class, $router->getModuleClass('/group/route')); $this->assertEquals(Module\Xrd::class, $router->getModuleClass('/group2/group3/route')); - $this->assertEquals(Module\Profile::class, $router->getModuleClass('/double')); + $this->assertEquals(Module\Profile\Index::class, $router->getModuleClass('/double')); } /** @@ -191,6 +191,6 @@ class RouterTest extends TestCase // Don't find GET $this->assertEquals(Module\NodeInfo::class, $router->getModuleClass('/post/it')); - $this->assertEquals(Module\Profile::class, $router->getModuleClass('/double')); + $this->assertEquals(Module\Profile\Index::class, $router->getModuleClass('/double')); } } diff --git a/view/templates/profile-hide-wall.tpl b/view/templates/profile-hide-wall.tpl deleted file mode 100644 index ff106fa29..000000000 --- a/view/templates/profile-hide-wall.tpl +++ /dev/null @@ -1,17 +0,0 @@ - -

-{{$desc nofilter}} -

- -
- - - -
-
-
- - - -
-
diff --git a/view/templates/profile-in-directory.tpl b/view/templates/profile-in-directory.tpl deleted file mode 100644 index bfc13e85e..000000000 --- a/view/templates/profile-in-directory.tpl +++ /dev/null @@ -1,17 +0,0 @@ - -

-{{$desc nofilter}} -

- -
- - - -
-
-
- - - -
-
diff --git a/view/templates/profile-in-netdir.tpl b/view/templates/profile-in-netdir.tpl deleted file mode 100644 index c91601bce..000000000 --- a/view/templates/profile-in-netdir.tpl +++ /dev/null @@ -1,17 +0,0 @@ - -

-{{$desc nofilter}} -

- -
- - - -
-
-
- - - -
-
diff --git a/view/templates/profile_edlink.tpl b/view/templates/profile_edlink.tpl deleted file mode 100644 index 54d7a34dd..000000000 --- a/view/templates/profile_edlink.tpl +++ /dev/null @@ -1,3 +0,0 @@ - -
-
\ No newline at end of file diff --git a/view/templates/profile-hide-friends.tpl b/view/templates/settings/profile/hide-friends.tpl similarity index 100% rename from view/templates/profile-hide-friends.tpl rename to view/templates/settings/profile/hide-friends.tpl diff --git a/view/templates/profile_edit.tpl b/view/templates/settings/profile/index.tpl similarity index 91% rename from view/templates/profile_edit.tpl rename to view/templates/settings/profile/index.tpl index 08e833f10..b02ee175e 100644 --- a/view/templates/profile_edit.tpl +++ b/view/templates/settings/profile/index.tpl @@ -1,16 +1,12 @@ -{{$default nofilter}} -

{{$banner}}

+{{$default nofilter}} + @@ -18,17 +14,11 @@
-
- + + -{{if $detailled_profile}} +{{if $detailed_profile}} {{include file="field_yesno.tpl" field=$details}} -
- -
*
-
-
-
@@ -328,12 +318,6 @@ {{if $personal_account}} {{include file="field_yesno.tpl" field=$details}} {{/if}} -
- -
*
-
-
-
diff --git a/view/templates/profed_head.tpl b/view/templates/settings/profile/index_head.tpl similarity index 100% rename from view/templates/profed_head.tpl rename to view/templates/settings/profile/index_head.tpl diff --git a/view/templates/settings/profile/link.tpl b/view/templates/settings/profile/link.tpl new file mode 100644 index 000000000..d387ea683 --- /dev/null +++ b/view/templates/settings/profile/link.tpl @@ -0,0 +1,2 @@ +
+
diff --git a/view/templates/welcome.tpl b/view/templates/welcome.tpl index 4e0ae7754..0ac061b47 100644 --- a/view/templates/welcome.tpl +++ b/view/templates/welcome.tpl @@ -26,11 +26,11 @@ {{$profile_photo_txt nofilter}}
  • - {{$profiles_link}}
    + {{$profiles_link}}
    {{$profiles_txt nofilter}}
  • - {{$profiles_keywords_link}}
    + {{$profiles_keywords_link}}
    {{$profiles_keywords_txt nofilter}}
  • diff --git a/view/theme/frio/templates/profile_edit.tpl b/view/theme/frio/templates/settings/profile/index.tpl similarity index 76% rename from view/theme/frio/templates/profile_edit.tpl rename to view/theme/frio/templates/settings/profile/index.tpl index 946443cf1..b88a6f1a8 100644 --- a/view/theme/frio/templates/profile_edit.tpl +++ b/view/theme/frio/templates/settings/profile/index.tpl @@ -7,37 +7,22 @@
    - - - + +
    @@ -46,8 +31,8 @@
    -
    - +
    +
    @@ -60,39 +45,37 @@ 3 => The additional help text (if available) *}} - - + + {{* Some hints to characteristics of the current profile (if available) *}} - {{if $is_default}} - {{/if}} + + {{if $personal_account}} +

    {{include file="field_yesno.tpl" field=$details}}

    + {{/if}} {{* friendica differs in $detailled_profile (all fields available and a short Version if this is variable false *}} - {{if $detailled_profile}} + {{if $detailed_profile}}
    {{* The personal settings *}}
    - {{* for the $detailled_profile we use bootstraps collapsable panel-groups to have expandable groups *}} -
    + {{* for the $detailed_profile we use bootstraps collapsable panel-groups to have expandable groups *}} +
    - {{include file="field_yesno.tpl" field=$details}} - - {{include file="field_input.tpl" field=$profile_name}} - {{include file="field_input.tpl" field=$name}} {{include file="field_input.tpl" field=$pdesc}}
    - + {{$gender nofilter}}
    @@ -101,7 +84,7 @@ {{$hide_friends nofilter}} -
    +
    @@ -128,18 +111,18 @@ {{include file="field_input.tpl" field=$postal_code}}
    - +
    - - +
    @@ -147,7 +130,7 @@ {{include file="field_input.tpl" field=$hometown}} -
    +
    @@ -166,8 +149,8 @@
    -
    - +
    + {{$marital.selector nofilter}}
    @@ -176,13 +159,13 @@ {{include file="field_input.tpl" field=$howlong}} -
    - +
    + {{$sexual.selector nofilter}}
    -
    +
    @@ -238,7 +221,7 @@ {{include file="field_textarea.tpl" field=$education}} -
    +
    @@ -247,19 +230,13 @@
    - {{else}} - {{* if $detailled_profile not available a short version of the setting page is displayed *}} - {{if $personal_account}} - {{include file="field_yesno.tpl" field=$details}} - {{/if}} - - {{include file="field_input.tpl" field=$profile_name}} - + {{else}} + {{* if $detailed_profile not available a short version of the setting page is displayed *}} {{include file="field_input.tpl" field=$name}} {{if $personal_account}}
    - + {{$gender nofilter}}
    @@ -278,22 +255,21 @@ {{include file="field_input.tpl" field=$locality}} - {{include file="field_input.tpl" field=$postal_code}}
    - +
    - - +
    @@ -305,7 +281,7 @@ {{include file="field_textarea.tpl" field=$about}} -
    +
    diff --git a/view/theme/vier/templates/profile_edit.tpl b/view/theme/vier/templates/settings/profile/index.tpl similarity index 93% rename from view/theme/vier/templates/profile_edit.tpl rename to view/theme/vier/templates/settings/profile/index.tpl index 066b2566f..841c6d17e 100644 --- a/view/theme/vier/templates/profile_edit.tpl +++ b/view/theme/vier/templates/settings/profile/index.tpl @@ -1,7 +1,7 @@ diff --git a/view/theme/vier/templates/profile_edlink.tpl b/view/theme/vier/templates/settings/profile/link.tpl similarity index 100% rename from view/theme/vier/templates/profile_edlink.tpl rename to view/theme/vier/templates/settings/profile/link.tpl