Merge pull request #12227 from matthiasmoritz/public_calendar

Public calendar
This commit is contained in:
Philipp 2022-12-01 17:27:36 +01:00 committed by GitHub
commit 5ea22197ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 559 additions and 497 deletions

View File

@ -45,7 +45,7 @@ function notes_content(App $a, bool $update = false)
return; return;
} }
$o = BaseProfile::getTabsHTML($a, 'notes', true, $a->getLoggedInUserNickname(), false); $o = BaseProfile::getTabsHTML('notes', true, $a->getLoggedInUserNickname(), false);
if (!$update) { if (!$update) {
$o .= '<h3>' . DI::l10n()->t('Personal Notes') . '</h3>'; $o .= '<h3>' . DI::l10n()->t('Personal Notes') . '</h3>';

View File

@ -865,9 +865,8 @@ function photos_content(App $a)
$contact = DBA::selectFirst('contact', [], ['id' => $contact_id, 'uid' => $owner_uid, 'blocked' => false, 'pending' => false]); $contact = DBA::selectFirst('contact', [], ['id' => $contact_id, 'uid' => $owner_uid, 'blocked' => false, 'pending' => false]);
} }
if ($user['hidewall'] && (DI::userSession()->getLocalUserId() != $owner_uid) && !$remote_contact) { if ($user['hidewall'] && !DI::userSession()->isAuthenticated()) {
DI::sysmsg()->addNotice(DI::l10n()->t('Access to this item is restricted.')); DI::baseUrl()->redirect('profile/' . $user['nickname'] . '/restricted');
return;
} }
$sql_extra = Security::getPermissionsSQLByUserId($owner_uid); $sql_extra = Security::getPermissionsSQLByUserId($owner_uid);
@ -876,7 +875,7 @@ function photos_content(App $a)
// tabs // tabs
$is_owner = (DI::userSession()->getLocalUserId() && (DI::userSession()->getLocalUserId() == $owner_uid)); $is_owner = (DI::userSession()->getLocalUserId() && (DI::userSession()->getLocalUserId() == $owner_uid));
$o .= BaseProfile::getTabsHTML($a, 'photos', $is_owner, $user['nickname'], $profile['hide-friends']); $o .= BaseProfile::getTabsHTML('photos', $is_owner, $user['nickname'], $profile['hide-friends']);
// Display upload form // Display upload form
if ($datatype === 'upload') { if ($datatype === 'upload') {

View File

@ -69,7 +69,6 @@ class App
private $theme_info = [ private $theme_info = [
'videowidth' => 425, 'videowidth' => 425,
'videoheight' => 350, 'videoheight' => 350,
'events_in_profile' => true
]; ];
private $user_id = 0; private $user_id = 0;

View File

@ -120,6 +120,12 @@ class Feature
['tagadelic', DI::l10n()->t('Tag Cloud'), DI::l10n()->t('Provide a personal tag cloud on your profile page'), false, DI::config()->get('feature_lock', 'tagadelic', false)], ['tagadelic', DI::l10n()->t('Tag Cloud'), DI::l10n()->t('Provide a personal tag cloud on your profile page'), false, DI::config()->get('feature_lock', 'tagadelic', false)],
['profile_membersince', DI::l10n()->t('Display Membership Date'), DI::l10n()->t('Display membership date in profile'), false, DI::config()->get('feature_lock', 'profile_membersince', false)], ['profile_membersince', DI::l10n()->t('Display Membership Date'), DI::l10n()->t('Display membership date in profile'), false, DI::config()->get('feature_lock', 'profile_membersince', false)],
], ],
//Advanced Calendar Settings
'advanced_calendar' => [
DI::l10n()->t('Advanced Calendar Settings'),
['public_calendar', DI::l10n()->t('Allow anonymous access to your calendar'), DI::l10n()->t('Allows anonymous visitors to consult your calendar and your public events. Contact birthday events are private to you.'), false, DI::config()->get('feature_lock', 'public_calendar', false)],
]
]; ];
// removed any locked features and remove the entire category if this makes it empty // removed any locked features and remove the entire category if this makes it empty

View File

@ -21,6 +21,7 @@
namespace Friendica\Model; namespace Friendica\Model;
use Friendica\Content\Feature;
use Friendica\Content\Text\BBCode; use Friendica\Content\Text\BBCode;
use Friendica\Core\Hook; use Friendica\Core\Hook;
use Friendica\Core\Logger; use Friendica\Core\Logger;
@ -28,8 +29,7 @@ use Friendica\Core\Renderer;
use Friendica\Core\System; use Friendica\Core\System;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;
use Friendica\Network\HTTPException\NotFoundException; use Friendica\Network\HTTPException;
use Friendica\Network\HTTPException\UnauthorizedException;
use Friendica\Protocol\Activity; use Friendica\Protocol\Activity;
use Friendica\Util\DateTimeFormat; use Friendica\Util\DateTimeFormat;
use Friendica\Util\Map; use Friendica\Util\Map;
@ -281,7 +281,7 @@ class Event
if (!DBA::isResult($existing_event)) { if (!DBA::isResult($existing_event)) {
return 0; return 0;
} }
if ($existing_event['edited'] === $event['edited']) { if ($existing_event['edited'] === $event['edited']) {
return $event['id']; return $event['id'];
} }
@ -501,36 +501,32 @@ class Event
* Additionally, it can check if the owner array is selectable * Additionally, it can check if the owner array is selectable
* *
* @param string $nickname * @param string $nickname
* @param bool $check
* *
* @return array the owner array * @return array the owner array
* @throws NotFoundException The given nickname does not exist * @throws HTTPException\InternalServerErrorException
* @throws UnauthorizedException The access for the given nickname is restricted * @throws HTTPException\NotFoundException The given nickname does not exist
* @throws HTTPException\UnauthorizedException The access for the given nickname is restricted
*/ */
public static function getOwnerForNickname(string $nickname, bool $check = true): array public static function getOwnerForNickname(string $nickname): array
{ {
$owner = User::getOwnerDataByNick($nickname); $owner = User::getOwnerDataByNick($nickname);
if (empty($owner)) { if (empty($owner) || $owner['account_removed'] || $owner['account_expired']) {
throw new NotFoundException(DI::l10n()->t('User not found.')); throw new HTTPException\NotFoundException(DI::l10n()->t('User not found.'));
} }
if ($check) { if (!DI::userSession()->isAuthenticated() && $owner['hidewall']) {
$contact_id = DI::userSession()->getRemoteContactID($owner['uid']); throw new HTTPException\UnauthorizedException(DI::l10n()->t('Access to this profile has been restricted.'));
}
$remote_contact = $contact_id && DBA::exists('contact', ['id' => $contact_id, 'uid' => $owner['uid']]); if (!DI::userSession()->isAuthenticated() && !Feature::isEnabled($owner['uid'], 'public_calendar')) {
throw new HTTPException\UnauthorizedException(DI::l10n()->t('Permission denied.'));
$is_owner = DI::userSession()->getLocalUserId() == $owner['uid'];
if ($owner['hidewall'] && !$is_owner && !$remote_contact) {
throw new UnauthorizedException(DI::l10n()->t('Access to this profile has been restricted.'));
}
} }
return $owner; return $owner;
} }
/** /**
* Get an event by its event ID. * Get an event by its event ID. Checks permissions.
* *
* @param int $owner_uid The User ID of the owner of the event * @param int $owner_uid The User ID of the owner of the event
* @param int $event_id The ID of the event in the event table * @param int $event_id The ID of the event in the event table
@ -538,37 +534,32 @@ class Event
* @return array Query result * @return array Query result
* @throws \Exception * @throws \Exception
*/ */
public static function getByIdAndUid(int $owner_uid, int $event_id, string $nickname = null): array public static function getByIdAndUid(int $owner_uid, int $event_id): array
{ {
if (!empty($nickname)) {
$owner = static::getOwnerForNickname($nickname, true);
$owner_uid = $owner['uid'];
// get the permissions
$sql_perms = Item::getPermissionsSQLByUserId($owner_uid);
// we only want to have the events of the profile owner
$sql_extra = " AND `event`.`cid` = 0 " . $sql_perms;
} else {
$sql_extra = "";
}
// Only allow events if there is a valid owner_id. // Only allow events if there is a valid owner_id.
if ($owner_uid == 0) { if ($owner_uid == 0) {
return []; return [];
} }
// Query for the event by event id // get the permissions
$events = DBA::toArray(DBA::p("SELECT `event`.*, `post-user`.`id` AS `itemid` FROM `event` $sql_perms = Item::getPermissionsSQLByUserId($owner_uid);
LEFT JOIN `post-user` ON `post-user`.`event-id` = `event`.`id` AND `post-user`.`uid` = `event`.`uid`
WHERE `event`.`uid` = ? AND `event`.`id` = ? $sql_extra",
$owner_uid, $event_id));
// Query for the event by event id
$events = DBA::toArray(DBA::p(
"SELECT `event`.*, `post-user`.`id` AS `itemid` FROM `event`
LEFT JOIN `post-user`
ON `post-user`.`event-id` = `event`.`id`
AND `post-user`.`uid` = `event`.`uid`
WHERE `event`.`id` = ?
AND `event`.`uid` = ?
$sql_perms",
$event_id, $owner_uid
));
if (empty($events)) { if (empty($events)) {
throw new NotFoundException(DI::l10n()->t('Event not found.')); throw new HTTPException\NotFoundException(DI::l10n()->t('Event not found.'));
} else {
$events = self::removeDuplicates($events);
return $events[0];
} }
return $events[0];
} }
/** /**
@ -577,34 +568,23 @@ class Event
* @param int $owner_uid The User ID of the owner of the events. * @param int $owner_uid The User ID of the owner of the events.
* @param string|null $start Start time of the timeframe. * @param string|null $start Start time of the timeframe.
* @param string|null $finish Finish time of the timeframe. * @param string|null $finish Finish time of the timeframe.
* @param bool $ignore * @param bool|null $ignore Filters ignored events (false: unignored events, true: ignored events, null: all events)
* @param string|null $nickname
* *
* @return array Query results. * @return array Query results.
* @throws NotFoundException * @throws HTTPException\NotFoundException
* @throws UnauthorizedException * @throws HTTPException\UnauthorizedException
*/ */
public static function getListByDate(int $owner_uid, string $start = null, string $finish = null, bool $ignore = false, string $nickname = null): array public static function getListByDate(int $owner_uid, string $start = null, string $finish = null, ?bool $ignore = false): array
{ {
if (!empty($nickname)) {
$owner = static::getOwnerForNickname($nickname);
$owner_uid = $owner['uid'];
// get the permissions
$sql_perms = Item::getPermissionsSQLByUserId($owner_uid);
// we only want to have the events of the profile owner
$sql_extra = " AND `event`.`cid` = 0 " . $sql_perms;
} else {
$sql_extra = "";
}
// Only allow events if there is a valid owner_id. // Only allow events if there is a valid owner_id.
if ($owner_uid == 0) { if ($owner_uid == 0) {
return []; return [];
} }
if (empty($start) || empty($finish)) { // get the permissions
$sql_perms = Item::getPermissionsSQLByUserId($owner_uid);
if (empty($start) || empty($finish)) {
$y = intval(DateTimeFormat::localNow('Y')); $y = intval(DateTimeFormat::localNow('Y'));
$m = intval(DateTimeFormat::localNow('m')); $m = intval(DateTimeFormat::localNow('m'));
@ -616,41 +596,36 @@ class Event
} }
} }
if ($ignore === true) {
$sql_ignore = " AND `event`.`ignore` = 1";
} elseif ($ignore === false) {
$sql_ignore = " AND `event`.`ignore` = 0";
} else {
$sql_ignore = "";
}
// Query for the event by date. // Query for the event by date.
$events = DBA::toArray(DBA::p("SELECT `event`.*, `post-user`.`id` AS `itemid` FROM `event` $events = DBA::toArray(DBA::p(
LEFT JOIN `post-user` ON `post-user`.`event-id` = `event`.`id` AND `post-user`.`uid` = `event`.`uid` "SELECT `event`.*, `post-user`.`id` AS `itemid` FROM `event`
WHERE `event`.`uid` = ? AND `event`.`ignore` = ? LEFT JOIN `post-user`
AND (`finish` >= ? OR (`nofinish` AND `start` >= ?)) AND `start` <= ? ON `post-user`.`event-id` = `event`.`id`
" . $sql_extra, AND `post-user`.`uid` = `event`.`uid`
$owner_uid, $ignore, WHERE `event`.`uid` = ?
$start, $start, $finish $sql_ignore
AND (`finish` >= ? OR (`nofinish` AND `start` >= ?))
AND `start` <= ?
$sql_perms",
$owner_uid,
$start, $start,
$finish
)); ));
$events = self::removeDuplicates($events ?? []); $events = self::removeDuplicates($events);
return self::sortByDate($events); return self::sortByDate($events);
} }
/** /**
* Convert an array query results in an array which could be used by the events template. * Convert an event in an array which could be used by the event template.
*
* @param array $event_result Event query array.
* @return array Event array for the template.
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
public static function prepareListForTemplate(array $event_result): array
{
$event_list = [];
foreach ($event_result as $event) {
$event_list[] = static::prepareForItem($event);
}
return $event_list;
}
/**
* Convert an one event in an array which could be used by the events template.
* *
* @param array $event Event query array. * @param array $event Event query array.
* @return array Event array for the template. * @return array Event array for the template.
@ -696,29 +671,25 @@ class Event
[$title, $_trash] = explode("<br", BBCode::convertForUriId($event['uri-id'], Strings::escapeHtml($event['desc'])), BBCode::TWITTER_API); [$title, $_trash] = explode("<br", BBCode::convertForUriId($event['uri-id'], Strings::escapeHtml($event['desc'])), BBCode::TWITTER_API);
} }
$author_link = $event['author-link']; $event['author-link'] = Contact::magicLink($event['author-link']);
$event['summary'] = BBCode::convertForUriId($event['uri-id'], Strings::escapeHtml($event['summary']));
$event['author-link'] = Contact::magicLink($author_link); $event['desc'] = BBCode::convertForUriId($event['uri-id'], Strings::escapeHtml($event['desc']));
$event['location'] = BBCode::convertForUriId($event['uri-id'], Strings::escapeHtml($event['location']));
$html = self::getHTML($event);
$event['summary'] = BBCode::convertForUriId($event['uri-id'], Strings::escapeHtml($event['summary']));
$event['desc'] = BBCode::convertForUriId($event['uri-id'], Strings::escapeHtml($event['desc']));
$event['location'] = BBCode::convertForUriId($event['uri-id'], Strings::escapeHtml($event['location']));
return [ return [
'id' => $event['id'], 'id' => $event['id'],
'start' => $start, 'start' => $start,
'end' => $end, 'end' => $end,
'allDay' => false, 'allDay' => false,
'title' => $title, 'title' => $title,
'j' => $j, 'j' => $j,
'd' => $day, 'd' => $day,
'edit' => $edit, 'edit' => $edit,
'drop' => $drop, 'drop' => $drop,
'copy' => $copy, 'copy' => $copy,
'item' => $event, 'item' => $event,
'html' => $html, 'html' => self::getHTML($event),
'plink' => Item::getPlink($event), 'plink' => Item::getPlink($event),
]; ];
} }
@ -727,8 +698,6 @@ class Event
* *
* @param array $events Query result for events. * @param array $events Query result for events.
* @param string $format The output format (ical/csv). * @param string $format The output format (ical/csv).
*
* @param string $timezone Timezone (missing parameter!)
* @return string Content according to selected export format. * @return string Content according to selected export format.
* *
* @todo Implement timezone support * @todo Implement timezone support
@ -749,8 +718,8 @@ class Event
foreach ($events as $event) { foreach ($events as $event) {
/// @todo The time / date entries don't include any information about the /// @todo The time / date entries don't include any information about the
/// timezone the event is scheduled in :-/ /// timezone the event is scheduled in :-/
$tmp1 = strtotime($event['start']); $tmp1 = strtotime($event['start']);
$tmp2 = strtotime($event['finish']); $tmp2 = strtotime($event['finish']);
$time_format = "%H:%M:%S"; $time_format = "%H:%M:%S";
$date_format = "%Y-%m-%d"; $date_format = "%Y-%m-%d";
@ -792,21 +761,21 @@ class Event
$tmp = $event['summary']; $tmp = $event['summary'];
$tmp = str_replace(PHP_EOL, PHP_EOL . ' ', $tmp); $tmp = str_replace(PHP_EOL, PHP_EOL . ' ', $tmp);
$tmp = addcslashes($tmp, ',;'); $tmp = addcslashes($tmp, ',;');
$o .= 'SUMMARY:' . $tmp . PHP_EOL; $o .= 'SUMMARY:' . $tmp . PHP_EOL;
} }
if ($event['desc']) { if ($event['desc']) {
$tmp = $event['desc']; $tmp = $event['desc'];
$tmp = str_replace(PHP_EOL, PHP_EOL . ' ', $tmp); $tmp = str_replace(PHP_EOL, PHP_EOL . ' ', $tmp);
$tmp = addcslashes($tmp, ',;'); $tmp = addcslashes($tmp, ',;');
$o .= 'DESCRIPTION:' . $tmp . PHP_EOL; $o .= 'DESCRIPTION:' . $tmp . PHP_EOL;
} }
if ($event['location']) { if ($event['location']) {
$tmp = $event['location']; $tmp = $event['location'];
$tmp = str_replace(PHP_EOL, PHP_EOL . ' ', $tmp); $tmp = str_replace(PHP_EOL, PHP_EOL . ' ', $tmp);
$tmp = addcslashes($tmp, ',;'); $tmp = addcslashes($tmp, ',;');
$o .= 'LOCATION:' . $tmp . PHP_EOL; $o .= 'LOCATION:' . $tmp . PHP_EOL;
} }
$o .= 'END:VEVENT' . PHP_EOL; $o .= 'END:VEVENT' . PHP_EOL;
@ -932,42 +901,42 @@ class Event
$tformat = DI::l10n()->t('g:i A'); // 8:01 AM. $tformat = DI::l10n()->t('g:i A'); // 8:01 AM.
// Convert the time to different formats. // Convert the time to different formats.
$dtstart_dt = DI::l10n()->getDay(DateTimeFormat::local($item['event-start'], $dformat)); $dtstart_dt = DI::l10n()->getDay(DateTimeFormat::local($item['event-start'], $dformat));
$dtstart_title = DateTimeFormat::utc($item['event-start'], DateTimeFormat::ATOM); $dtstart_title = DateTimeFormat::utc($item['event-start'], DateTimeFormat::ATOM);
// Format: Jan till Dec. // Format: Jan till Dec.
$month_short = DI::l10n()->getDayShort(DateTimeFormat::local($item['event-start'], 'M')); $month_short = DI::l10n()->getDayShort(DateTimeFormat::local($item['event-start'], 'M'));
// Format: 1 till 31. // Format: 1 till 31.
$date_short = DateTimeFormat::local($item['event-start'], 'j'); $date_short = DateTimeFormat::local($item['event-start'], 'j');
$start_time = DateTimeFormat::local($item['event-start'], $tformat); $start_time = DateTimeFormat::local($item['event-start'], $tformat);
$start_short = DI::l10n()->getDayShort(DateTimeFormat::local($item['event-start'], $dformat_short)); $start_short = DI::l10n()->getDayShort(DateTimeFormat::local($item['event-start'], $dformat_short));
// If the option 'nofinisch' isn't set, we need to format the finish date/time. // If the option 'nofinisch' isn't set, we need to format the finish date/time.
if (!$item['event-nofinish']) { if (!$item['event-nofinish']) {
$finish = true; $finish = true;
$dtend_dt = DI::l10n()->getDay(DateTimeFormat::local($item['event-finish'], $dformat)); $dtend_dt = DI::l10n()->getDay(DateTimeFormat::local($item['event-finish'], $dformat));
$dtend_title = DateTimeFormat::utc($item['event-finish'], DateTimeFormat::ATOM); $dtend_title = DateTimeFormat::utc($item['event-finish'], DateTimeFormat::ATOM);
$end_short = DI::l10n()->getDayShort(DateTimeFormat::utc($item['event-finish'], $dformat_short)); $end_short = DI::l10n()->getDayShort(DateTimeFormat::utc($item['event-finish'], $dformat_short));
$end_time = DateTimeFormat::local($item['event-finish'], $tformat); $end_time = DateTimeFormat::local($item['event-finish'], $tformat);
// Check if start and finish time is at the same day. // Check if start and finish time is at the same day.
if (substr($dtstart_title, 0, 10) === substr($dtend_title, 0, 10)) { if (substr($dtstart_title, 0, 10) === substr($dtend_title, 0, 10)) {
$same_date = true; $same_date = true;
} }
} else { } else {
$dtend_title = ''; $dtend_title = '';
$dtend_dt = ''; $dtend_dt = '';
$end_time = ''; $end_time = '';
$end_short = ''; $end_short = '';
} }
// Format the event location. // Format the event location.
$location = self::locationToArray($item['event-location']); $location = self::locationToArray($item['event-location']);
// Construct the profile link (magic-auth). // Construct the profile link (magic-auth).
$author = ['uid' => 0, 'id' => $item['author-id'], $author = ['uid' => 0, 'id' => $item['author-id'],
'network' => $item['author-network'], 'url' => $item['author-link']]; 'network' => $item['author-network'], 'url' => $item['author-link']];
$profile_link = Contact::magicLinkByContact($author); $profile_link = Contact::magicLinkByContact($author);
$tpl = Renderer::getMarkupTemplate('event_stream_item.tpl'); $tpl = Renderer::getMarkupTemplate('event_stream_item.tpl');
$return = Renderer::replaceMacros($tpl, [ $return = Renderer::replaceMacros($tpl, [
'$id' => $item['event-id'], '$id' => $item['event-id'],
'$title' => BBCode::convertForUriId($item['uri-id'], $item['event-summary']), '$title' => BBCode::convertForUriId($item['uri-id'], $item['event-summary']),
@ -1025,15 +994,15 @@ class Event
if (strpos($s, '[/map]') !== false) { if (strpos($s, '[/map]') !== false) {
$found = preg_match("/\[map\](.*?)\[\/map\]/ism", $s, $match); $found = preg_match("/\[map\](.*?)\[\/map\]/ism", $s, $match);
if (intval($found) > 0 && array_key_exists(1, $match)) { if (intval($found) > 0 && array_key_exists(1, $match)) {
$location['address'] = $match[1]; $location['address'] = $match[1];
// Remove the map bbcode from the location name. // Remove the map bbcode from the location name.
$location['name'] = str_replace($match[0], "", $s); $location['name'] = str_replace($match[0], "", $s);
} }
// Map tag with coordinates - e.g. [map=48.864716,2.349014]. // Map tag with coordinates - e.g. [map=48.864716,2.349014].
} elseif (strpos($s, '[map=') !== false) { } elseif (strpos($s, '[map=') !== false) {
$found = preg_match("/\[map=(.*?)\]/ism", $s, $match); $found = preg_match("/\[map=(.*?)\]/ism", $s, $match);
if (intval($found) > 0 && array_key_exists(1, $match)) { if (intval($found) > 0 && array_key_exists(1, $match)) {
$location['coordinates'] = $match[1]; $location['coordinates'] = $match[1];
// Remove the map bbcode from the location name. // Remove the map bbcode from the location name.
$location['name'] = str_replace($match[0], "", $s); $location['name'] = str_replace($match[0], "", $s);
} }
@ -1063,10 +1032,10 @@ class Event
{ {
// Check for duplicates // Check for duplicates
$condition = [ $condition = [
'uid' => $contact['uid'], 'uid' => $contact['uid'],
'cid' => $contact['id'], 'cid' => $contact['id'],
'start' => DateTimeFormat::utc($birthday), 'start' => DateTimeFormat::utc($birthday),
'type' => 'birthday' 'type' => 'birthday'
]; ];
if (DBA::exists('event', $condition)) { if (DBA::exists('event', $condition)) {
return false; return false;

View File

@ -461,7 +461,7 @@ class Profile
'$unfollow' => DI::l10n()->t('Unfollow'), '$unfollow' => DI::l10n()->t('Unfollow'),
'$unfollow_link' => $unfollow_link, '$unfollow_link' => $unfollow_link,
'$subscribe_feed' => DI::l10n()->t('Atom feed'), '$subscribe_feed' => DI::l10n()->t('Atom feed'),
'$subscribe_feed_link' => $profile['poll'], '$subscribe_feed_link' => $profile['hidewall'] ? '' : $profile['poll'],
'$wallmessage' => DI::l10n()->t('Message'), '$wallmessage' => DI::l10n()->t('Message'),
'$wallmessage_link' => $wallmessage_link, '$wallmessage_link' => $wallmessage_link,
'$account_type' => $account_type, '$account_type' => $account_type,

View File

@ -29,6 +29,7 @@ use Friendica\DI;
use Friendica\Model\Contact; use Friendica\Model\Contact;
use Friendica\Model\Item; use Friendica\Model\Item;
use Friendica\Model\Post; use Friendica\Model\Post;
use Friendica\Model\User;
use Friendica\Network\HTTPException; use Friendica\Network\HTTPException;
use Friendica\Protocol\ActivityPub; use Friendica\Protocol\ActivityPub;
use Friendica\Util\HTTPSignature; use Friendica\Util\HTTPSignature;
@ -74,7 +75,9 @@ class Objects extends BaseModule
throw new HTTPException\NotFoundException(); throw new HTTPException\NotFoundException();
} }
$validated = in_array($item['private'], [Item::PUBLIC, Item::UNLISTED]); $owner = User::getById($item['uid'], ['hidewall']);
$validated = empty($owner['hidewall']) && in_array($item['private'], [Item::PUBLIC, Item::UNLISTED]);
if (!$validated) { if (!$validated) {
$requester = HTTPSignature::getSigner('', $_SERVER); $requester = HTTPSignature::getSigner('', $_SERVER);

View File

@ -23,23 +23,24 @@ namespace Friendica\Module;
use Friendica\App; use Friendica\App;
use Friendica\BaseModule; use Friendica\BaseModule;
use Friendica\Content\Feature;
use Friendica\Core\Hook; use Friendica\Core\Hook;
use Friendica\Core\Renderer; use Friendica\Core\Renderer;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\User;
class BaseProfile extends BaseModule class BaseProfile extends BaseModule
{ {
/** /**
* Returns the HTML for the profile pages tabs * Returns the HTML for the profile pages tabs
* *
* @param App $a
* @param string $current * @param string $current
* @param bool $is_owner * @param bool $is_owner
* @param string $nickname * @param string $nickname
* @return string * @return string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function getTabsHTML(App $a, string $current, bool $is_owner, string $nickname, bool $hide_friends) public static function getTabsHTML(string $current, bool $is_owner, string $nickname, bool $hide_friends)
{ {
$baseProfileUrl = DI::baseUrl() . '/profile/' . $nickname; $baseProfileUrl = DI::baseUrl() . '/profile/' . $nickname;
@ -79,7 +80,7 @@ class BaseProfile extends BaseModule
]; ];
// the calendar link for the full-featured events calendar // the calendar link for the full-featured events calendar
if ($is_owner && $a->getThemeInfoValue('events_in_profile')) { if ($is_owner) {
$tabs[] = [ $tabs[] = [
'label' => DI::l10n()->t('Calendar'), 'label' => DI::l10n()->t('Calendar'),
'url' => DI::baseUrl() . '/calendar', 'url' => DI::baseUrl() . '/calendar',
@ -88,17 +89,18 @@ class BaseProfile extends BaseModule
'id' => 'calendar-tab', 'id' => 'calendar-tab',
'accesskey' => 'c', 'accesskey' => 'c',
]; ];
// if the user is not the owner of the calendar we only show a calendar } else {
// with the public events of the calendar owner $owner = User::getByNickname($nickname, ['uid']);
} elseif (!$is_owner) { if(DI::userSession()->isAuthenticated() || $owner && Feature::isEnabled($owner['uid'], 'public_calendar')) {
$tabs[] = [ $tabs[] = [
'label' => DI::l10n()->t('Calendar'), 'label' => DI::l10n()->t('Calendar'),
'url' => DI::baseUrl() . '/calendar/show/' . $nickname, 'url' => DI::baseUrl() . '/calendar/show/' . $nickname,
'sel' => $current == 'calendar' ? 'active' : '', 'sel' => $current == 'calendar' ? 'active' : '',
'title' => DI::l10n()->t('Calendar'), 'title' => DI::l10n()->t('Calendar'),
'id' => 'calendar-tab', 'id' => 'calendar-tab',
'accesskey' => 'c', 'accesskey' => 'c',
]; ];
}
} }
if ($is_owner) { if ($is_owner) {

View File

@ -22,12 +22,14 @@
namespace Friendica\Module\Calendar\Event; namespace Friendica\Module\Calendar\Event;
use Friendica\App; use Friendica\App;
use Friendica\Content\Feature;
use Friendica\Core\L10n; use Friendica\Core\L10n;
use Friendica\Core\Session\Capability\IHandleUserSessions; use Friendica\Core\Session\Capability\IHandleUserSessions;
use Friendica\Core\System; use Friendica\Core\System;
use Friendica\Model\Event; use Friendica\Model\Event;
use Friendica\Model\Item; use Friendica\Model\Item;
use Friendica\Model\Post; use Friendica\Model\Post;
use Friendica\Model\User;
use Friendica\Module\Response; use Friendica\Module\Response;
use Friendica\Network\HTTPException; use Friendica\Network\HTTPException;
use Friendica\Util\DateTimeFormat; use Friendica\Util\DateTimeFormat;
@ -43,24 +45,27 @@ class Get extends \Friendica\BaseModule
/** @var IHandleUserSessions */ /** @var IHandleUserSessions */
protected $session; protected $session;
public function __construct(L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, IHandleUserSessions $session, array $server, array $parameters = []) public function __construct(App $app, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, IHandleUserSessions $session, array $server, array $parameters = [])
{ {
parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters); parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
$this->session = $session; $this->session = $session;
$this->app = $app;
} }
protected function rawContent(array $request = []) protected function rawContent(array $request = [])
{ {
if (!$this->session->getLocalUserId()) { $nickname = $this->parameters['nickname'] ?? $this->app->getLoggedInUserNickname();
if (!$nickname) {
throw new HTTPException\UnauthorizedException(); throw new HTTPException\UnauthorizedException();
} }
// get events by id or by date $owner = Event::getOwnerForNickname($nickname);
if (!empty($request['id'])) { if (!empty($request['id'])) {
$events = [Event::getByIdAndUid($this->session->getLocalUserId(), $request['id'], $this->parameters['nickname'] ?? null)]; $events = [Event::getByIdAndUid($owner['uid'], $request['id'])];
} else { } else {
$events = Event::getListByDate($this->session->getLocalUserId(), $request['start'] ?? '', $request['end'] ?? '', false, $this->parameters['nickname'] ?? null); $events = Event::getListByDate($owner['uid'], $request['start'] ?? '', $request['end'] ?? '');
} }
System::jsonExit($events ? self::map($events) : []); System::jsonExit($events ? self::map($events) : []);

View File

@ -23,11 +23,13 @@ namespace Friendica\Module\Calendar\Event;
use Friendica\App; use Friendica\App;
use Friendica\BaseModule; use Friendica\BaseModule;
use Friendica\Content\Feature;
use Friendica\Core\L10n; use Friendica\Core\L10n;
use Friendica\Core\Renderer; use Friendica\Core\Renderer;
use Friendica\Core\Session\Capability\IHandleUserSessions; use Friendica\Core\Session\Capability\IHandleUserSessions;
use Friendica\Core\System; use Friendica\Core\System;
use Friendica\Model\Event; use Friendica\Model\Event;
use Friendica\Model\User;
use Friendica\Module\Response; use Friendica\Module\Response;
use Friendica\Network\HTTPException; use Friendica\Network\HTTPException;
use Friendica\Util\Profiler; use Friendica\Util\Profiler;
@ -40,22 +42,27 @@ class Show extends BaseModule
{ {
/** @var IHandleUserSessions */ /** @var IHandleUserSessions */
protected $session; protected $session;
/** @var App */
private $app;
public function __construct(L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, IHandleUserSessions $session, array $server, array $parameters = []) public function __construct(App $app, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, IHandleUserSessions $session, array $server, array $parameters = [])
{ {
parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters); parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
$this->session = $session; $this->session = $session;
$this->app = $app;
} }
protected function rawContent(array $request = []) protected function rawContent(array $request = [])
{ {
if (!$this->session->getLocalUserId()) { $nickname = $this->parameters['nickname'] ?? $this->app->getLoggedInUserNickname();
throw new HTTPException\UnauthorizedException($this->t('Permission denied.')); if (!$nickname) {
throw new HTTPException\UnauthorizedException();
} }
$event = Event::getByIdAndUid($this->session->getLocalUserId(), (int)$this->parameters['id'] ?? 0, $this->parameters['nickname'] ?? ''); $owner = Event::getOwnerForNickname($nickname);
$event = Event::getByIdAndUid($owner['uid'], (int)$this->parameters['id'] ?? 0);
if (empty($event)) { if (empty($event)) {
throw new HTTPException\NotFoundException($this->t('Event not found.')); throw new HTTPException\NotFoundException($this->t('Event not found.'));
} }

View File

@ -23,6 +23,7 @@ namespace Friendica\Module\Calendar;
use Friendica\App; use Friendica\App;
use Friendica\BaseModule; use Friendica\BaseModule;
use Friendica\Content\Feature;
use Friendica\Content\Nav; use Friendica\Content\Nav;
use Friendica\Content\Widget; use Friendica\Content\Widget;
use Friendica\Core\L10n; use Friendica\Core\L10n;
@ -30,9 +31,11 @@ use Friendica\Core\Renderer;
use Friendica\Core\Session\Capability\IHandleUserSessions; use Friendica\Core\Session\Capability\IHandleUserSessions;
use Friendica\Core\Theme; use Friendica\Core\Theme;
use Friendica\Model\Event; use Friendica\Model\Event;
use Friendica\Model\User;
use Friendica\Module\BaseProfile; use Friendica\Module\BaseProfile;
use Friendica\Module\Response; use Friendica\Module\Response;
use Friendica\Module\Security\Login; use Friendica\Module\Security\Login;
use Friendica\Network\HTTPException;
use Friendica\Navigation\SystemMessages; use Friendica\Navigation\SystemMessages;
use Friendica\Util\Profiler; use Friendica\Util\Profiler;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
@ -60,7 +63,21 @@ class Show extends BaseModule
protected function content(array $request = []): string protected function content(array $request = []): string
{ {
if (!$this->session->getLocalUserId()) { $nickname = $this->parameters['nickname'] ?? $this->app->getLoggedInUserNickname();
if (!$nickname) {
throw new HTTPException\UnauthorizedException();
}
$owner = User::getOwnerDataByNick($nickname);
if (!$owner || $owner['account_expired'] || $owner['account_removed']) {
throw new HTTPException\NotFoundException($this->t('User not found.'));
}
if (!$this->session->isAuthenticated() && $owner['hidewall']) {
$this->baseUrl->redirect('profile/' . $nickname . '/restricted');
}
if (!$this->session->isAuthenticated() && !Feature::isEnabled($owner['uid'], 'public_calendar')) {
$this->sysMessages->addNotice($this->t('Permission denied.')); $this->sysMessages->addNotice($this->t('Permission denied.'));
return Login::form(); return Login::form();
} }
@ -73,42 +90,26 @@ class Show extends BaseModule
$this->page->registerFooterScript('view/asset/moment/min/moment-with-locales.min.js'); $this->page->registerFooterScript('view/asset/moment/min/moment-with-locales.min.js');
$this->page->registerFooterScript('view/asset/fullcalendar/dist/fullcalendar.min.js'); $this->page->registerFooterScript('view/asset/fullcalendar/dist/fullcalendar.min.js');
$htpl = Renderer::getMarkupTemplate('calendar/calendar_head.tpl'); $is_owner = $nickname == $this->app->getLoggedInUserNickname();
$htpl = Renderer::getMarkupTemplate('calendar/calendar_head.tpl');
$this->page['htmlhead'] .= Renderer::replaceMacros($htpl, [ $this->page['htmlhead'] .= Renderer::replaceMacros($htpl, [
'$calendar_api' => 'calendar/api/get' . (!empty($this->parameters['nickname']) ? '/' . $this->parameters['nickname'] : ''), '$calendar_api' => 'calendar/api/get' . ($is_owner ? '' : '/' . $nickname),
'$event_api' => 'calendar/event/show' . (!empty($this->parameters['nickname']) ? '/' . $this->parameters['nickname'] : ''), '$event_api' => 'calendar/event/show' . ($is_owner ? '' : '/' . $nickname),
'$modparams' => 2, '$modparams' => 2,
'$i18n' => $i18n, '$i18n' => $i18n,
]); ]);
$tabs = ''; Nav::setSelected($is_owner ? 'home' : 'calendar');
if (empty($this->parameters['nickname'])) {
if ($this->app->getThemeInfoValue('events_in_profile')) {
Nav::setSelected('home');
} else {
Nav::setSelected('calendar');
}
// tabs
if ($this->app->getThemeInfoValue('events_in_profile')) {
$tabs = BaseProfile::getTabsHTML($this->app, 'calendar', true, $this->app->getLoggedInUserNickname(), false);
}
$this->page['aside'] .= Widget\CalendarExport::getHTML($this->session->getLocalUserId());
} else {
$owner = Event::getOwnerForNickname($this->parameters['nickname'], true);
Nav::setSelected('calendar');
// get the tab navigation bar
$tabs = BaseProfile::getTabsHTML($this->app, 'calendar', false, $owner['nickname'], $owner['hide-friends']);
if (!$is_owner) {
$this->page['aside'] .= Widget\VCard::getHTML($owner); $this->page['aside'] .= Widget\VCard::getHTML($owner);
$this->page['aside'] .= Widget\CalendarExport::getHTML($owner['uid']);
} }
$this->page['aside'] .= Widget\CalendarExport::getHTML($owner['uid']);
$tabs = BaseProfile::getTabsHTML('calendar', $is_owner, $nickname, !$is_owner && $owner['hide-friends']);
// ACL blocks are loaded in modals in frio // ACL blocks are loaded in modals in frio
$this->page->registerFooterScript(Theme::getPathForFile('asset/typeahead.js/dist/typeahead.bundle.js')); $this->page->registerFooterScript(Theme::getPathForFile('asset/typeahead.js/dist/typeahead.bundle.js'));
$this->page->registerFooterScript(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.js')); $this->page->registerFooterScript(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.js'));

View File

@ -23,7 +23,9 @@ namespace Friendica\Module\DFRN;
use Friendica\BaseModule; use Friendica\BaseModule;
use Friendica\Core\System; use Friendica\Core\System;
use Friendica\Model\User;
use Friendica\Module\Response; use Friendica\Module\Response;
use Friendica\Network\HTTPException;
use Friendica\Protocol\OStatus; use Friendica\Protocol\OStatus;
/** /**
@ -33,7 +35,19 @@ class Poll extends BaseModule
{ {
protected function rawContent(array $request = []) protected function rawContent(array $request = [])
{ {
$owner = User::getByNickname(
$this->parameters['nickname'] ?? '',
['nickname', 'blocked', 'account_expired', 'account_removed', 'hidewall']
);
if (!$owner || $owner['account_expired'] || $owner['account_removed']) {
throw new HTTPException\NotFoundException($this->t('User not found.'));
}
if ($owner['blocked'] || $owner['hidewall']) {
throw new HTTPException\UnauthorizedException($this->t('Access to this profile has been restricted.'));
}
$last_update = $request['last_update'] ?? ''; $last_update = $request['last_update'] ?? '';
System::httpExit(OStatus::feed($this->parameters['nickname'], $last_update, 10) ?? '', Response::TYPE_ATOM); System::httpExit(OStatus::feed($owner['nickname'], $last_update, 10) ?? '', Response::TYPE_ATOM);
} }
} }

View File

@ -23,9 +23,9 @@ namespace Friendica\Module;
use Friendica\BaseModule; use Friendica\BaseModule;
use Friendica\Core\System; use Friendica\Core\System;
use Friendica\DI; use Friendica\Model\User;
use Friendica\Protocol\Feed as ProtocolFeed;
use Friendica\Network\HTTPException; use Friendica\Network\HTTPException;
use Friendica\Protocol\Feed as ProtocolFeed;
/** /**
* Provides public Atom feeds * Provides public Atom feeds
@ -37,23 +37,14 @@ use Friendica\Network\HTTPException;
* - /feed/[nickname]/replies => comments * - /feed/[nickname]/replies => comments
* - /feed/[nickname]/activity => activity * - /feed/[nickname]/activity => activity
* *
* The nocache GET parameter is provided mainly for debug purposes, requires auth
*
* @author Hypolite Petovan <hypolite@mrpetovan.com> * @author Hypolite Petovan <hypolite@mrpetovan.com>
*/ */
class Feed extends BaseModule class Feed extends BaseModule
{ {
protected function rawContent(array $request = []) protected function rawContent(array $request = [])
{ {
$last_update = $this->getRequestValue($request, 'last_update', ''); $nick = $this->parameters['nickname'] ?? '';
$nocache = !empty($request['nocache']) && DI::userSession()->getLocalUserId(); $type = $this->parameters['type'] ?? null;
$type = null;
// @TODO: Replace with parameter from router
if (DI::args()->getArgc() > 2) {
$type = DI::args()->getArgv()[2];
}
switch ($type) { switch ($type) {
case 'posts': case 'posts':
case 'comments': case 'comments':
@ -67,11 +58,19 @@ class Feed extends BaseModule
$type = 'posts'; $type = 'posts';
} }
$feed = ProtocolFeed::atom($this->parameters['nickname'], $last_update, 10, $type, $nocache, true); $last_update = $this->getRequestValue($request, 'last_update', '');
if (empty($feed)) {
throw new HTTPException\NotFoundException(DI::l10n()->t('User not found.')); $owner = User::getOwnerDataByNick($nick);
if (!$owner || $owner['account_expired'] || $owner['account_removed']) {
throw new HTTPException\NotFoundException($this->t('User not found.'));
} }
if ($owner['blocked'] || $owner['hidewall']) {
throw new HTTPException\UnauthorizedException($this->t('Access to this profile has been restricted.'));
}
$feed = ProtocolFeed::atom($owner, $last_update, 10, $type);
System::httpExit($feed, Response::TYPE_ATOM); System::httpExit($feed, Response::TYPE_ATOM);
} }
} }

View File

@ -196,8 +196,7 @@ class Display extends BaseModule
protected function getDisplayData(array $item, bool $update = false, int $updateUid = 0, bool $force = false): string protected function getDisplayData(array $item, bool $update = false, int $updateUid = 0, bool $force = false): string
{ {
$isRemoteContact = false; $itemUid = $this->session->getLocalUserId();
$itemUid = $this->session->getLocalUserId();
$parent = null; $parent = null;
if (!$this->session->getLocalUserId() && !empty($item['parent-uri-id'])) { if (!$this->session->getLocalUserId() && !empty($item['parent-uri-id'])) {
@ -206,8 +205,7 @@ class Display extends BaseModule
if (!empty($parent)) { if (!empty($parent)) {
$pageUid = $parent['uid']; $pageUid = $parent['uid'];
$isRemoteContact = $this->session->getRemoteContactID($pageUid); if ($this->session->getRemoteContactID($pageUid)) {
if ($isRemoteContact) {
$itemUid = $parent['uid']; $itemUid = $parent['uid'];
} }
} else { } else {
@ -215,13 +213,11 @@ class Display extends BaseModule
} }
if (!empty($pageUid) && ($pageUid != $this->session->getLocalUserId())) { if (!empty($pageUid) && ($pageUid != $this->session->getLocalUserId())) {
$page_user = User::getById($pageUid, ['hidewall']); $page_user = User::getById($pageUid, ['nickname', 'hidewall']);
} }
$is_owner = $this->session->getLocalUserId() && (in_array($pageUid, [$this->session->getLocalUserId(), 0])); if (!empty($page_user['hidewall']) && !$this->session->isAuthenticated()) {
$this->baseUrl->redirect('profile/' . $page_user['nickname'] . '/restricted');
if (!empty($page_user['hidewall']) && !$is_owner && !$isRemoteContact) {
throw new HTTPException\ForbiddenException($this->t('Access to this profile has been restricted.'));
} }
$sql_extra = Item::getPermissionsSQLByUserId($pageUid); $sql_extra = Item::getPermissionsSQLByUserId($pageUid);
@ -275,6 +271,8 @@ class Display extends BaseModule
$output = ''; $output = '';
$is_owner = $this->session->getLocalUserId() && (in_array($pageUid, [$this->session->getLocalUserId(), 0]));
// We need the editor here to be able to reshare an item. // We need the editor here to be able to reshare an item.
if ($is_owner && !$update) { if ($is_owner && !$update) {
$output .= $this->conversation->statusEditor([], 0, true); $output .= $this->conversation->statusEditor([], 0, true);

View File

@ -61,7 +61,7 @@ class Common extends BaseProfile
$a->redirect('profile/' . $nickname . '/contacts'); $a->redirect('profile/' . $nickname . '/contacts');
}; };
$o = self::getTabsHTML($a, 'contacts', false, $profile['nickname'], $profile['hide-friends']); $o = self::getTabsHTML('contacts', false, $profile['nickname'], $profile['hide-friends']);
$tabs = self::getContactFilterTabs('profile/' . $nickname, 'common', $displayCommonTab); $tabs = self::getContactFilterTabs('profile/' . $nickname, 'common', $displayCommonTab);

View File

@ -57,7 +57,7 @@ class Contacts extends Module\BaseProfile
Nav::setSelected('home'); Nav::setSelected('home');
$o = self::getTabsHTML($a, 'contacts', $is_owner, $profile['nickname'], $profile['hide-friends']); $o = self::getTabsHTML('contacts', $is_owner, $profile['nickname'], $profile['hide-friends']);
$tabs = self::getContactFilterTabs('profile/' . $nickname, $type, DI::userSession()->isAuthenticated() && $profile['uid'] != DI::userSession()->getLocalUserId()); $tabs = self::getContactFilterTabs('profile/' . $nickname, $type, DI::userSession()->isAuthenticated() && $profile['uid'] != DI::userSession()->getLocalUserId());

View File

@ -44,7 +44,7 @@ class Media extends BaseProfile
$is_owner = DI::userSession()->getLocalUserId() == $profile['uid']; $is_owner = DI::userSession()->getLocalUserId() == $profile['uid'];
$o = self::getTabsHTML($a, 'media', $is_owner, $profile['nickname'], $profile['hide-friends']); $o = self::getTabsHTML('media', $is_owner, $profile['nickname'], $profile['hide-friends']);
$o .= Contact::getPostsFromUrl($profile['url'], false, 0, 0, true); $o .= Contact::getPostsFromUrl($profile['url'], false, 0, 0, true);

View File

@ -72,8 +72,8 @@ class Photos extends \Friendica\Module\BaseProfile
throw new HttpException\ForbiddenException($this->t('Public access denied.')); throw new HttpException\ForbiddenException($this->t('Public access denied.'));
} }
$owner = User::getOwnerDataByNick($this->parameters['nickname']); $owner = Profile::load($this->app, $this->parameters['nickname'] ?? '');
if (!isset($owner['account_removed']) || $owner['account_removed']) { if (!$owner || $owner['account_removed'] || $owner['account_expired']) {
throw new HTTPException\NotFoundException($this->t('User not found.')); throw new HTTPException\NotFoundException($this->t('User not found.'));
} }
@ -88,8 +88,8 @@ class Photos extends \Friendica\Module\BaseProfile
$remote_contact = $contact && !$contact['blocked'] && !$contact['pending']; $remote_contact = $contact && !$contact['blocked'] && !$contact['pending'];
} }
if ($owner['hidewall'] && !$is_owner && !$remote_contact) { if ($owner['hidewall'] && !$this->session->isAuthenticated()) {
throw new HttpException\ForbiddenException($this->t('Access to this item is restricted.')); $this->baseUrl->redirect('profile/' . $owner['nickname'] . '/restricted');
} }
$this->session->set('photo_return', $this->args->getCommand()); $this->session->set('photo_return', $this->args->getCommand());
@ -174,13 +174,11 @@ class Photos extends \Friendica\Module\BaseProfile
]); ]);
} }
$this->page['aside'] .= Widget\VCard::getHTML($owner);
if (!empty($photo_albums_widget)) { if (!empty($photo_albums_widget)) {
$this->page['aside'] .= $photo_albums_widget; $this->page['aside'] .= $photo_albums_widget;
} }
$o = self::getTabsHTML($this->app, 'photos', $is_owner, $owner['nickname'], Profile::getByUID($owner['uid'])['hide-friends'] ?? false); $o = self::getTabsHTML('photos', $is_owner, $owner['nickname'], Profile::getByUID($owner['uid'])['hide-friends'] ?? false);
$tpl = Renderer::getMarkupTemplate('photos_recent.tpl'); $tpl = Renderer::getMarkupTemplate('photos_recent.tpl');
$o .= Renderer::replaceMacros($tpl, [ $o .= Renderer::replaceMacros($tpl, [

View File

@ -76,21 +76,19 @@ class Profile extends BaseProfile
{ {
$a = DI::app(); $a = DI::app();
$profile = ProfileModel::load($a, $this->parameters['nickname']); $profile = ProfileModel::load($a, $this->parameters['nickname'] ?? '');
if (!$profile) { if (!$profile) {
throw new HTTPException\NotFoundException(DI::l10n()->t('Profile not found.')); throw new HTTPException\NotFoundException(DI::l10n()->t('Profile not found.'));
} }
$remote_contact_id = DI::userSession()->getRemoteContactID($profile['uid']); $remote_contact_id = DI::userSession()->getRemoteContactID($profile['uid']);
if (DI::config()->get('system', 'block_public') && !DI::userSession()->getLocalUserId() && !$remote_contact_id) { if (DI::config()->get('system', 'block_public') && !DI::userSession()->isAuthenticated()) {
return Login::form(); return Login::form();
} }
$is_owner = DI::userSession()->getLocalUserId() == $profile['uid']; if (!empty($profile['hidewall']) && !DI::userSession()->isAuthenticated()) {
$this->baseUrl->redirect('profile/' . $profile['nickname'] . '/restricted');
if (!empty($profile['hidewall']) && !$is_owner && !$remote_contact_id) {
throw new HTTPException\ForbiddenException(DI::l10n()->t('Access to this profile has been restricted.'));
} }
if (!empty($profile['page-flags']) && $profile['page-flags'] == User::PAGE_FLAGS_COMMUNITY) { if (!empty($profile['page-flags']) && $profile['page-flags'] == User::PAGE_FLAGS_COMMUNITY) {
@ -102,12 +100,7 @@ class Profile extends BaseProfile
Nav::setSelected('home'); Nav::setSelected('home');
$is_owner = DI::userSession()->getLocalUserId() == $profile['uid']; $is_owner = DI::userSession()->getLocalUserId() == $profile['uid'];
$o = self::getTabsHTML($a, 'profile', $is_owner, $profile['nickname'], $profile['hide-friends']); $o = self::getTabsHTML('profile', $is_owner, $profile['nickname'], $profile['hide-friends']);
if (!empty($profile['hidewall']) && !$is_owner && !$remote_contact_id) {
DI::sysmsg()->addNotice(DI::l10n()->t('Access to this profile has been restricted.'));
return '';
}
$view_as_contacts = []; $view_as_contacts = [];
$view_as_contact_id = 0; $view_as_contact_id = 0;
@ -307,8 +300,8 @@ class Profile extends BaseProfile
} }
// site block // site block
$blocked = !DI::userSession()->getLocalUserId() && !$remote_contact_id && DI::config()->get('system', 'block_public'); $blocked = !DI::userSession()->isAuthenticated() && DI::config()->get('system', 'block_public');
$userblock = !DI::userSession()->getLocalUserId() && !$remote_contact_id && $profile['hidewall']; $userblock = !DI::userSession()->isAuthenticated() && $profile['hidewall'];
if (!$blocked && !$userblock) { if (!$blocked && !$userblock) {
$keywords = str_replace(['#', ',', ' ', ',,'], ['', ' ', ',', ','], $profile['pub_keywords'] ?? ''); $keywords = str_replace(['#', ',', ' ', ',,'], ['', ' ', ',', ','], $profile['pub_keywords'] ?? '');
if (strlen($keywords)) { if (strlen($keywords)) {

View File

@ -0,0 +1,63 @@
<?php
/**
* @copyright Copyright (C) 2010-2022, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Module\Profile;
use Friendica\App;
use Friendica\BaseModule;
use Friendica\Core\L10n;
use Friendica\Core\Renderer;
use Friendica\Model\Profile;
use Friendica\Module\Response;
use Friendica\Network\HTTPException;
use Friendica\Util\Profiler;
use Psr\Log\LoggerInterface;
class Restricted extends BaseModule
{
/** @var App */
private $app;
public function __construct(App $app, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, array $server, array $parameters = [])
{
parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
$this->app = $app;
}
protected function content(array $request = []): string
{
$profile = Profile::load($this->app, $this->parameters['nickname'] ?? '', false);
if (!$profile) {
throw new HTTPException\NotFoundException($this->t('Profile not found.'));
}
if (empty($profile['hidewall'])) {
$this->baseUrl->redirect('profile/' . $profile['nickname']);
}
$tpl = Renderer::getMarkupTemplate('exception.tpl');
return Renderer::replaceMacros($tpl, [
'$title' => $this->t('Restricted profile'),
'$message' => $this->t('This profile has been restricted which prevents access to their public content from anonymous visitors.'),
]);
}
}

View File

@ -58,7 +58,7 @@ class Schedule extends BaseProfile
$a = DI::app(); $a = DI::app();
$o = self::getTabsHTML($a, 'schedule', true, $a->getLoggedInUserNickname(), false); $o = self::getTabsHTML('schedule', true, $a->getLoggedInUserNickname(), false);
$schedule = []; $schedule = [];
$delayed = DBA::select('delayed-post', [], ['uid' => DI::userSession()->getLocalUserId()]); $delayed = DBA::select('delayed-post', [], ['uid' => DI::userSession()->getLocalUserId()]);

View File

@ -105,12 +105,11 @@ class Status extends BaseProfile
$is_owner = DI::userSession()->getLocalUserId() == $profile['uid']; $is_owner = DI::userSession()->getLocalUserId() == $profile['uid'];
$last_updated_key = "profile:" . $profile['uid'] . ":" . DI::userSession()->getLocalUserId() . ":" . $remote_contact; $last_updated_key = "profile:" . $profile['uid'] . ":" . DI::userSession()->getLocalUserId() . ":" . $remote_contact;
if (!empty($profile['hidewall']) && !$is_owner && !$remote_contact) { if (!empty($profile['hidewall']) && !DI::userSession()->isAuthenticated()) {
DI::sysmsg()->addNotice(DI::l10n()->t('Access to this profile has been restricted.')); $this->baseUrl->redirect('profile/' . $profile['nickname'] . '/restricted');
return '';
} }
$o .= self::getTabsHTML($a, 'status', $is_owner, $profile['nickname'], $profile['hide-friends']); $o .= self::getTabsHTML('status', $is_owner, $profile['nickname'], $profile['hide-friends']);
$o .= Widget::commonFriendsVisitor($profile['uid'], $profile['nickname']); $o .= Widget::commonFriendsVisitor($profile['uid'], $profile['nickname']);

View File

@ -586,7 +586,7 @@ class Account extends BaseSettings
'$profile_in_dir' => $profile_in_dir, '$profile_in_dir' => $profile_in_dir,
'$profile_in_net_dir' => ['profile_in_netdirectory', DI::l10n()->t('Allow your profile to be searchable globally?'), $profile['net-publish'], DI::l10n()->t("Activate this setting if you want others to easily find and follow you. Your profile will be searchable on remote systems. This setting also determines whether Friendica will inform search engines that your profile should be indexed or not.") . $net_pub_desc], '$profile_in_net_dir' => ['profile_in_netdirectory', DI::l10n()->t('Allow your profile to be searchable globally?'), $profile['net-publish'], DI::l10n()->t("Activate this setting if you want others to easily find and follow you. Your profile will be searchable on remote systems. This setting also determines whether Friendica will inform search engines that your profile should be indexed or not.") . $net_pub_desc],
'$hide_friends' => ['hide-friends', DI::l10n()->t('Hide your contact/friend list from viewers of your profile?'), $profile['hide-friends'], DI::l10n()->t('A list of your contacts is displayed on your profile page. Activate this option to disable the display of your contact list.')], '$hide_friends' => ['hide-friends', DI::l10n()->t('Hide your contact/friend list from viewers of your profile?'), $profile['hide-friends'], DI::l10n()->t('A list of your contacts is displayed on your profile page. Activate this option to disable the display of your contact list.')],
'$hide_wall' => ['hidewall', DI::l10n()->t('Hide your profile details from anonymous viewers?'), $user['hidewall'], DI::l10n()->t('Anonymous visitors will only see your profile picture, your display name and the nickname you are using on your profile page. Your public posts and replies will still be accessible by other means.')], '$hide_wall' => ['hidewall', $this->t('Hide your public content from anonymous viewers'), $user['hidewall'], $this->t('Anonymous visitors will only see your basic profile details. Your public posts and replies will still be freely accessible on the remote servers of your followers and through relays.')],
'$unlisted' => ['unlisted', DI::l10n()->t('Make public posts unlisted'), DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'unlisted'), DI::l10n()->t('Your public posts will not appear on the community pages or in search results, nor be sent to relay servers. However they can still appear on public feeds on remote servers.')], '$unlisted' => ['unlisted', DI::l10n()->t('Make public posts unlisted'), DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'unlisted'), DI::l10n()->t('Your public posts will not appear on the community pages or in search results, nor be sent to relay servers. However they can still appear on public feeds on remote servers.')],
'$accessiblephotos' => ['accessible-photos', DI::l10n()->t('Make all posted pictures accessible'), DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'accessible-photos'), DI::l10n()->t("This option makes every posted picture accessible via the direct link. This is a workaround for the problem that most other networks can't handle permissions on pictures. Non public pictures still won't be visible for the public on your photo albums though.")], '$accessiblephotos' => ['accessible-photos', DI::l10n()->t('Make all posted pictures accessible'), DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'accessible-photos'), DI::l10n()->t("This option makes every posted picture accessible via the direct link. This is a workaround for the problem that most other networks can't handle permissions on pictures. Non public pictures still won't be visible for the public on your photo albums though.")],
'$blockwall' => ['blockwall', DI::l10n()->t('Allow friends to post to your profile page?'), (intval($user['blockwall']) ? '0' : '1'), DI::l10n()->t('Your contacts may write posts on your profile wall. These posts will be distributed to your contacts')], '$blockwall' => ['blockwall', DI::l10n()->t('Allow friends to post to your profile page?'), (intval($user['blockwall']) ? '0' : '1'), DI::l10n()->t('Your contacts may write posts on your profile wall. These posts will be distributed to your contacts')],

View File

@ -49,7 +49,7 @@ class Profile extends BaseModule
$is_owner = DI::userSession()->getLocalUserId() == $a->getProfileOwner(); $is_owner = DI::userSession()->getLocalUserId() == $a->getProfileOwner();
$last_updated_key = "profile:" . $a->getProfileOwner() . ":" . DI::userSession()->getLocalUserId() . ":" . $remote_contact; $last_updated_key = "profile:" . $a->getProfileOwner() . ":" . DI::userSession()->getLocalUserId() . ":" . $remote_contact;
if (!$is_owner && !$remote_contact) { if (!DI::userSession()->isAuthenticated()) {
$user = User::getById($a->getProfileOwner(), ['hidewall']); $user = User::getById($a->getProfileOwner(), ['hidewall']);
if ($user['hidewall']) { if ($user['hidewall']) {
throw new ForbiddenException(DI::l10n()->t('Access to this profile has been restricted.')); throw new ForbiddenException(DI::l10n()->t('Access to this profile has been restricted.'));

View File

@ -40,6 +40,7 @@ use Friendica\Model\Item;
use Friendica\Model\Post; use Friendica\Model\Post;
use Friendica\Model\Tag; use Friendica\Model\Tag;
use Friendica\Model\User; use Friendica\Model\User;
use Friendica\Network\HTTPException;
use Friendica\Util\DateTimeFormat; use Friendica\Util\DateTimeFormat;
use Friendica\Util\Network; use Friendica\Util\Network;
use Friendica\Util\ParseUrl; use Friendica\Util\ParseUrl;
@ -915,28 +916,23 @@ class Feed
* Updates the provided last_update parameter if the result comes from the * Updates the provided last_update parameter if the result comes from the
* cache or it is empty * cache or it is empty
* *
* @param string $owner_nick Nickname of the feed owner * @param array $owner owner-view record of the feed owner
* @param string $last_update Date of the last update * @param string $last_update Date of the last update
* @param integer $max_items Number of maximum items to fetch * @param integer $max_items Number of maximum items to fetch
* @param string $filter Feed items filter (activity, posts or comments) * @param string $filter Feed items filter (activity, posts or comments)
* @param boolean $nocache Wether to bypass caching * @param boolean $nocache Wether to bypass caching
* *
* @return string Atom feed * @return string Atom feed
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws HTTPException\InternalServerErrorException
* @throws \ImagickException * @throws \ImagickException
*/ */
public static function atom(string $owner_nick, string $last_update, int $max_items = 300, string $filter = 'activity', bool $nocache = false) public static function atom(array $owner, string $last_update, int $max_items = 300, string $filter = 'activity', bool $nocache = false)
{ {
$stamp = microtime(true); $stamp = microtime(true);
$owner = User::getOwnerDataByNick($owner_nick); $cachekey = 'feed:feed:' . $owner['nickname'] . ':' . $filter . ':' . $last_update;
if (!$owner) {
return;
}
$cachekey = 'feed:feed:' . $owner_nick . ':' . $filter . ':' . $last_update; // Display events in the user's timezone
// Display events in the users's timezone
if (strlen($owner['timezone'])) { if (strlen($owner['timezone'])) {
DI::app()->setTimeZone($owner['timezone']); DI::app()->setTimeZone($owner['timezone']);
} }

View File

@ -38,6 +38,7 @@ $profileRoutes = [
'/photos' => [Module\Profile\Photos::class, [R::GET ]], '/photos' => [Module\Profile\Photos::class, [R::GET ]],
'/profile' => [Module\Profile\Profile::class, [R::GET]], '/profile' => [Module\Profile\Profile::class, [R::GET]],
'/remote_follow' => [Module\Profile\RemoteFollow::class, [R::GET, R::POST]], '/remote_follow' => [Module\Profile\RemoteFollow::class, [R::GET, R::POST]],
'/restricted' => [Module\Profile\Restricted::class, [R::GET ]],
'/schedule' => [Module\Profile\Schedule::class, [R::GET, R::POST]], '/schedule' => [Module\Profile\Schedule::class, [R::GET, R::POST]],
'/status[/{category}[/{date1}[/{date2}]]]' => [Module\Profile\Status::class, [R::GET]], '/status[/{category}[/{date1}[/{date2}]]]' => [Module\Profile\Status::class, [R::GET]],
'/unkmail' => [Module\Profile\UnkMail::class, [R::GET, R::POST]], '/unkmail' => [Module\Profile\UnkMail::class, [R::GET, R::POST]],
@ -416,13 +417,8 @@ return [
'/featured/{nickname}' => [Module\ActivityPub\Featured::class, [R::GET]], '/featured/{nickname}' => [Module\ActivityPub\Featured::class, [R::GET]],
'/feed' => [ '/feed/{nickname}[/{type:posts|comments|replies|activity}]' => [Module\Feed::class, [R::GET]],
'/{nickname}' => [Module\Feed::class, [R::GET]],
'/{nickname}/posts' => [Module\Feed::class, [R::GET]],
'/{nickname}/comments' => [Module\Feed::class, [R::GET]],
'/{nickname}/replies' => [Module\Feed::class, [R::GET]],
'/{nickname}/activity' => [Module\Feed::class, [R::GET]],
],
'/feedtest' => [Module\Debug\Feed::class, [R::GET]], '/feedtest' => [Module\Debug\Feed::class, [R::GET]],
'/fetch' => [ '/fetch' => [

File diff suppressed because it is too large Load Diff

View File

@ -53,8 +53,6 @@ function frio_init(App $a)
global $frio; global $frio;
$frio = 'view/theme/frio'; $frio = 'view/theme/frio';
// disable the events module link in the profile tab
$a->setThemeInfoValue('events_in_profile', false);
$a->setThemeInfoValue('videowidth', 622); $a->setThemeInfoValue('videowidth', 622);
Renderer::setActiveTemplateEngine('smarty3'); Renderer::setActiveTemplateEngine('smarty3');

View File

@ -42,8 +42,6 @@ use Friendica\Util\Strings;
function vier_init(App $a) function vier_init(App $a)
{ {
$a->setThemeInfoValue('events_in_profile', false);
Renderer::setActiveTemplateEngine('smarty3'); Renderer::setActiveTemplateEngine('smarty3');
$args = DI::args(); $args = DI::args();