<?php
namespace XF\Pub\Controller;
use XF\Entity\User;
use XF\Entity\UserProfile;
use XF\Mvc\FormAction;
use XF\Mvc\ParameterBag;
use function array_slice;
class Member extends AbstractController
{
public function actionIndex(ParameterBag $params)
{
if ($params->user_id)
{
return $this->rerouteController(__CLASS__, 'view', $params);
}
$this->assertNotEmbeddedImageRequest();
if (!\XF::visitor()->canViewMemberList())
{
return $this->noPermission();
}
$username = $this->filter('username', 'str');
if ($username === '')
{
// workaround for find member widget and browsers that
// insist on trying to trigger autofill/autocomplete
$username = $this->filter('member_name', 'str');
}
if ($username !== '')
{
$user = $this->em()->findOne('XF:User', ['username' => $username]);
if ($user)
{
return $this->redirect($this->buildLink('members', $user));
}
else
{
$userNotFound = true;
}
}
else
{
$userNotFound = false;
}
$this->assertCanonicalUrl($this->buildLink('members'));
/** @var \XF\Repository\MemberStat $memberStatRepo */
$memberStatRepo = $this->repository('XF:MemberStat');
/** @var \XF\Entity\MemberStat[] $memberStats */
$memberStats = $memberStatRepo
->findMemberStatsForDisplay()
->fetch()
->filterViewable();
$active = null;
$key = $this->filter('key', 'str');
if (isset($memberStats[$key]))
{
$active = $memberStats[$key];
}
$userFinder = $this->finder('XF:User');
$resultsData = [];
if ($active)
{
if (!$active->canView())
{
return $this->noPermission();
}
$results = $active->getResults();
$users = $userFinder
->with('Option', true)
->with('Profile', true)
->where('user_id', array_unique(array_keys($results)))
->isValidUser()
->fetch();
foreach ($results AS $userId => $value)
{
if (!isset($users[$userId]))
{
// no valid user record found
continue;
}
$resultsData[$active->member_stat_key][$userId] = [
'user' => $users[$userId],
'value' => $value
];
}
if (isset($resultsData[$active->member_stat_key]))
{
if ($active->user_limit > 0)
{
$resultsData[$active->member_stat_key] = array_slice(
$resultsData[$active->member_stat_key], 0, $active->user_limit, true
);
}
}
else
{
$resultsData[$active->member_stat_key] = [];
}
}
else
{
$keyedResults = [];
$userIds = [];
foreach ($memberStats AS $key => $memberStat)
{
$results = $memberStat->getResults(true);
$keyedResults[$key] = $results;
$userIds = array_merge(array_keys($results), $userIds);
}
$users = $userFinder
->with('Option', true)
->with('Profile', true)
->where('user_id', array_unique($userIds))
->isValidUser()
->fetch();
foreach ($memberStats AS $key => $memberStat)
{
$results = $keyedResults[$key];
$count = 0;
foreach ($results AS $userId => $value)
{
if ($count == 5)
{
// we have enough for this stat
break;
}
if (!isset($users[$userId]))
{
// no valid user record found
continue;
}
$resultsData[$key][$userId] = [
'user' => $users[$userId],
'value' => $value
];
$count++;
}
}
}
$viewParams = [
'userNotFound' => $userNotFound,
'memberStats' => $memberStats,
'resultsData' => $resultsData,
'active' => $active,
'users' => $users
];
return $this->view('XF:Member\Notable', 'member_notable', $viewParams);
}
public function actionList()
{
$this->assertNotEmbeddedImageRequest();
if (!$this->options()->enableMemberList || !\XF::visitor()->canViewMemberList())
{
return $this->noPermission();
}
$this->assertCanonicalUrl($this->buildLink('members/list'));
/** @var \XF\Repository\MemberStat $memberStatRepo */
$memberStatRepo = $this->repository('XF:MemberStat');
/** @var \XF\Entity\MemberStat[] $memberStats */
$memberStats = $memberStatRepo
->findMemberStatsForDisplay()
->fetch()
->filterViewable();
$page = $this->filterPage();
$perPage = $this->options()->membersPerPage;
$searcher = $this->searcher('XF:User');
$finder = $searcher->getFinder()
->isValidUser()
->with(['Profile', 'Option'])
->limitByPage($page, $perPage);
$total = $finder->total();
$this->assertValidPage($page, $perPage, $total, 'members/list');
$viewParams = [
'users' => $finder->fetch(),
'memberStats' => $memberStats,
'total' => $total,
'page' => $page,
'perPage' => $perPage
];
return $this->view('XF:Member\Listing', 'member_list', $viewParams);
}
public function actionView(ParameterBag $params)
{
if ($this->filter('tooltip', 'bool'))
{
return $this->rerouteController(__CLASS__, 'tooltip', $params);
}
$this->assertNotEmbeddedImageRequest();
$user = $this->assertViewableUser($params->user_id);
$page = $params->page;
$perPage = $this->options()->messagesPerPage;
$this->assertCanonicalUrl($this->buildLink('members', $user, ['page' => $page]));
/** @var \XF\Repository\UserAlert $userAlertRepo */
$userAlertRepo = $this->repository('XF:UserAlert');
/** @var \XF\Repository\Attachment $attachmentRepo */
$attachmentRepo = $this->repository('XF:Attachment');
if ($user->canViewPostsOnProfile())
{
$profilePostRepo = $this->getProfilePostRepo();
$profilePostFinder = $profilePostRepo->findProfilePostsOnProfile($user, [
'allowOwnPending' => $this->hasContentPendingApproval()
]);
$profilePosts = $profilePostFinder->limitByPage($page, $perPage)->fetch();
$attachmentRepo->addAttachmentsToContent($profilePosts, 'profile_post');
$total = $profilePostFinder->total();
$isRobot = $this->isRobot();
$profilePosts = $profilePostRepo->addCommentsToProfilePosts($profilePosts, $isRobot);
/** @var \XF\Repository\Unfurl $unfurlRepo */
$unfurlRepo = $this->repository('XF:Unfurl');
$unfurlRepo->addUnfurlsToContent($profilePosts, $isRobot);
$commentIds = [];
foreach ($profilePosts AS $profilePost)
{
if ($profilePost->LatestComments)
{
$commentIds = array_merge($commentIds, $profilePost->LatestComments->keys());
}
}
$userAlertRepo->markUserAlertsReadForContent('profile_post', $profilePosts->keys());
$userAlertRepo->markUserAlertsReadForContent('profile_post_comment', $commentIds);
}
else
{
$total = 0;
$profilePosts = $this->em()->getEmptyCollection();
}
$this->assertValidPage($page, $perPage, $total, 'members', $user);
$visitor = \XF::visitor();
if ($user->user_id != $visitor->user_id)
{
$userAlertRepo->markUserAlertsReadForContent('user', $visitor->user_id, 'following');
}
$canInlineMod = false;
$canViewAttachments = false;
$profilePostAttachData = [];
foreach ($profilePosts AS $profilePost)
{
if (!$canInlineMod && $profilePost->canUseInlineModeration())
{
$canInlineMod = true;
}
if (!$canViewAttachments && $profilePost->canViewAttachments())
{
$canViewAttachments = true;
}
if ($profilePost->canUploadAndManageAttachments())
{
$profilePostAttachData[$profilePost->profile_post_id] = $attachmentRepo->getEditorData('profile_post_comment', $profilePost);
}
}
if ($user->canUploadAndManageAttachmentsOnProfile())
{
$attachmentData = $attachmentRepo->getEditorData('profile_post', $user);
}
else
{
$attachmentData = null;
}
if ($user->canViewLatestActivity() && !$user->canViewPostsOnProfile())
{
$maxItems = $this->options()->newsFeedMaxItems;
$newsFeedRepo = $this->repository('XF:NewsFeed');
$newsFeedFinder = $newsFeedRepo->findMembersActivity($user);
$newsFeed = $newsFeedFinder->fetch($maxItems * 2);
$newsFeedRepo->addContentToNewsFeedItems($newsFeed);
$newsFeed = $newsFeed->filterViewable();
$newsFeed = $newsFeed->slice(0, $maxItems);
$newsFeedItems = $newsFeed;
$newsFeedOldestItemId = $newsFeed->count() ? min(array_keys($newsFeed->toArray())) : 0;
}
else
{
$newsFeedItems = [];
$newsFeedOldestItemId = 0;
}
$viewParams = [
'user' => $user,
'profilePosts' => $profilePosts,
'canInlineMod' => $canInlineMod,
'page' => $page,
'perPage' => $perPage,
'total' => $total,
'attachmentData' => $attachmentData,
'canViewAttachments' => $canViewAttachments,
'profilePostAttachData' => $profilePostAttachData,
'newsFeedItems' => $newsFeedItems,
'newsFeedOldestItemId' => $newsFeedOldestItemId,
];
return $this->view('XF:Member\View', 'member_view', $viewParams);
}
public function actionAbout(ParameterBag $params)
{
$user = $this->assertViewableUser($params->user_id);
/** @var \XF\Repository\UserFollow $userFollowRepo */
$userFollowRepo = $this->repository('XF:UserFollow');
$following = [];
$followingCount = 0;
if ($user->Profile->following)
{
$userFollowingFinder = $userFollowRepo->findFollowingForProfile($user);
$userFollowingFinder->order($userFollowingFinder->expression('RAND()'));
$following = $userFollowingFinder->fetch(12)->pluckNamed('FollowUser');
$followingCount = $userFollowingFinder->total();
}
$userFollowersFinder = $userFollowRepo->findFollowersForProfile($user);
$userFollowersFinder->order($userFollowersFinder->expression('RAND()'));
$followers = $userFollowersFinder->fetch(12)->pluckNamed('User');
$followersCount = $userFollowersFinder->total();
if ($this->options()->enableTrophies)
{
/** @var \XF\Repository\Trophy $trophyRepo */
$trophyRepo = $this->repository('XF:Trophy');
$trophies = $trophyRepo->findUserTrophies($user->user_id)
->with('Trophy')
->fetch();
}
else
{
$trophies = null;
}
$viewParams = [
'user' => $user,
'following' => $following,
'followingCount' => $followingCount,
'followers' => $followers,
'followersCount' => $followersCount,
'trophies' => $trophies
];
return $this->view('XF:Member\About', 'member_about', $viewParams);
}
public function actionFollowing(ParameterBag $params)
{
$user = $this->assertViewableUser($params->user_id);
$page = $this->filterPage($params->page);
$perPage = $this->options()->membersPerPage;
$userFollowRepo = $this->repository('XF:UserFollow');
$userFollowingFinder = $userFollowRepo->findFollowingForProfile($user)
->order('FollowUser.username')
->limitByPage($page, $perPage, 1);
$following = $userFollowingFinder->fetch()->pluckNamed('FollowUser');
$hasMore = ($following->count() > $perPage);
$following = $following->slice(0, $perPage);
$followingCount = $userFollowingFinder->total();
$viewParams = [
'user' => $user,
'following' => $following,
'page' => $page,
'perPage' => $perPage,
'total' => $followingCount,
'hasMore' => $hasMore
];
return $this->view('XF:Member\Following', 'member_following', $viewParams);
}
public function actionTooltip(ParameterBag $params)
{
$this->assertNotEmbeddedImageRequest();
$user = $this->assertViewableUser($params->user_id, [], true);
$viewParams = [
'user' => $user
];
return $this->view('XF:Member\Tooltip', 'member_tooltip', $viewParams);
}
public function actionUsernameHistory(ParameterBag $params)
{
$user = $this->assertViewableUser($params->user_id, [], true);
if (!$user->hasViewableUsernameHistory())
{
return $this->noPermission();
}
/** @var \XF\Repository\UsernameChange $usernameChangeRepo */
$usernameChangeRepo = $this->repository('XF:UsernameChange');
$changeFinder = $usernameChangeRepo->findUsernameChangeHistoryForUser($user->user_id);
if (!$user->canViewFullUsernameHistory())
{
$changeFinder->visibleOnly()->recentOnly();
}
if ($this->filter('menu', 'bool'))
{
$viewParams = [
'user' => $user,
'changes' => $changeFinder->fetch(5),
'seeMore' => $user->canViewFullUsernameHistory()
];
return $this->view('XF:Member\UsernameHistoryMenu', 'member_username_history_menu', $viewParams);
}
else if ($user->canViewFullUsernameHistory())
{
$changeFinder->with('User');
$viewParams = [
'user' => $user,
'changes' => $changeFinder->fetch()
];
return $this->view('XF:Member\UsernameHistory', 'member_username_history', $viewParams);
}
else
{
return $this->noPermission();
}
}
public function actionFollowers(ParameterBag $params)
{
$user = $this->assertViewableUser($params->user_id);
$page = $this->filterPage($params->page);
$perPage = $this->options()->membersPerPage;
$userFollowRepo = $this->repository('XF:UserFollow');
$userFollowersFinder = $userFollowRepo->findFollowersForProfile($user)
->order('User.username')
->limitByPage($page, $perPage, 1);
$followers = $userFollowersFinder->fetch()->pluckNamed('User');
$hasMore = ($followers->count() > $perPage);
$followers = $followers->slice(0, $perPage);
$followersCount = $userFollowersFinder->total();
$viewParams = [
'user' => $user,
'followers' => $followers,
'page' => $page,
'perPage' => $perPage,
'total' => $followersCount,
'hasMore' => $hasMore
];
return $this->view('XF:Member\Followers', 'member_followers', $viewParams);
}
/**
* @param User $followUser
*
* @return \XF\Service\User\Follow
*/
protected function setupFollowService(\XF\Entity\User $followUser)
{
return $this->service('XF:User\Follow', $followUser);
}
public function actionFollow(ParameterBag $params)
{
$user = $this->assertViewableUser($params->user_id, [], true);
$visitor = \XF::visitor();
$wasFollowing = $visitor->isFollowing($user);
if (!$wasFollowing && !$visitor->canFollowUser($user))
{
return $this->error(\XF::phrase('you_not_currently_able_to_follow_this_user'));
}
$redirect = $this->getDynamicRedirect(null, false);
if ($this->isPost())
{
$followService = $this->setupFollowService($user);
if ($wasFollowing)
{
$userFollow = $followService->unfollow();
}
else
{
$userFollow = $followService->follow();
}
if ($userFollow && $userFollow->hasErrors())
{
return $this->error($userFollow->getErrors());
}
$reply = $this->redirect($redirect);
$reply->setJsonParam('switchKey', $wasFollowing ? 'follow' : 'unfollow');
return $reply;
}
else
{
$viewParams = [
'user' => $user,
'redirect' => $redirect,
'isFollowing' => $wasFollowing
];
return $this->view('XF:Member\Follow', 'member_follow', $viewParams);
}
}
/**
* @param User $ignoreUser
*
* @return \XF\Service\User\Ignore
*/
protected function setupIgnoreService(\XF\Entity\User $ignoreUser)
{
return $this->service('XF:User\Ignore', $ignoreUser);
}
public function actionIgnore(ParameterBag $params)
{
$user = $this->assertViewableUser($params->user_id, [], true);
$visitor = \XF::visitor();
$wasIgnoring = $visitor->isIgnoring($user);
if (!$wasIgnoring && !$visitor->canIgnoreUser($user, $error))
{
return $this->noPermission($error);
}
$redirect = $this->getDynamicRedirect(null, false);
if ($this->isPost())
{
$ignoreService = $this->setupIgnoreService($user);
if ($wasIgnoring)
{
$userIgnored = $ignoreService->unignore();
}
else
{
$userIgnored = $ignoreService->ignore();
}
if ($userIgnored && $userIgnored->hasErrors())
{
return $this->error($userIgnored->getErrors());
}
$reply = $this->redirect($redirect);
$reply->setJsonParam('switchKey', $wasIgnoring ? 'ignore' : 'unignore');
return $reply;
}
else
{
$viewParams = [
'user' => $user,
'redirect' => $redirect,
'isIgnoring' => $wasIgnoring
];
return $this->view('XF:Member\Ignore', 'member_ignore', $viewParams);
}
}
public function actionLatestActivity(ParameterBag $params)
{
$user = $this->assertViewableUser($params->user_id);
if (!$user->canViewLatestActivity())
{
$viewParams = [
'user' => $user,
'restricted' => true
];
return $this->view('XF:Member\LatestActivityRestricted', 'member_latest_activity', $viewParams);
}
$maxItems = $this->options()->newsFeedMaxItems;
$newsFeedRepo = $this->repository('XF:NewsFeed');
$beforeId = $this->filter('before_id', 'uint');
$newsFeedFinder = $newsFeedRepo->findMembersActivity($user);
$newsFeedFinder->beforeFeedId($beforeId);
$newsFeed = $newsFeedFinder->fetch($maxItems * 2);
$newsFeedRepo->addContentToNewsFeedItems($newsFeed);
$newsFeed = $newsFeed->filterViewable();
$newsFeed = $newsFeed->slice(0, $maxItems);
$viewParams = [
'user' => $user,
'newsFeedItems' => $newsFeed,
'oldestItemId' => $newsFeed->count() ? min(array_keys($newsFeed->toArray())) : 0,
'beforeId' => $beforeId
];
return $this->view('XF:Member\LatestActivity', 'member_latest_activity', $viewParams);
}
public function actionRecentContent(ParameterBag $params)
{
$user = $this->assertViewableUser($params->user_id);
$searcher = $this->app->search();
$query = $searcher->getQuery();
$query->byUserId($user->user_id)
->orderedBy('date');
$resultSet = $searcher->getResultSet($searcher->search($query));
$resultSet->limitResults(15);
$results = $searcher->wrapResultsForRender($resultSet);
$resultCount = $resultSet->countResults();
$viewParams = [
'user' => $user,
'results' => $results,
'resultCount' => $resultCount
];
return $this->view('XF:Member\RecentContent', 'member_recent_content', $viewParams);
}
protected function moderatorCustomFieldsSaveProcess(FormAction $form, \XF\Entity\UserProfile $userProfile)
{
/** @var \XF\CustomField\Set $fieldSet */
$fieldSet = $userProfile->custom_fields;
$fieldDefinition = $fieldSet->getDefinitionSet()
->filterEditable($fieldSet, 'moderator');
$customFields = $this->filter('custom_fields', 'array');
$customFieldsShown = array_keys($fieldDefinition->getFieldDefinitions());
if ($customFieldsShown)
{
$form->setup(function() use ($fieldSet, $customFields, $customFieldsShown)
{
$fieldSet->bulkSet($customFields, $customFieldsShown, 'moderator');
});
}
}
protected function memberSaveProcess(\XF\Entity\User $user)
{
$form = $this->formAction();
// TODO: double check editability restrictions (particularly when the user can't edit elements)
$input = $this->filter([
'delete_avatar' => 'bool',
'delete_banner' => 'bool',
'user' => [
'custom_title' => 'str'
],
'profile' => [
'location' => 'str',
'website' => 'str',
'about' => 'str',
'signature' => 'str'
]
]);
$form->basicEntitySave($user, $input['user']);
/** @var \XF\Entity\UserProfile $userProfile */
$userProfile = $user->getRelationOrDefault('Profile');
$form->setupEntityInput($userProfile, $input['profile']);
$this->moderatorCustomFieldsSaveProcess($form, $userProfile);
$form->validateEntity($userProfile)->saveEntity($userProfile);
if ($input['delete_avatar'])
{
/** @var \XF\Service\User\Avatar $avatarService */
$avatarService = $this->service('XF:User\Avatar', $user);
$form->apply(function() use ($avatarService)
{
$avatarService->deleteAvatar();
});
}
if ($input['delete_banner'])
{
/** @var \XF\Service\User\ProfileBanner $bannerService */
$bannerService = $this->service('XF:User\ProfileBanner', $user);
$form->apply(function() use ($bannerService)
{
$bannerService->deleteBanner();
});
}
return $form;
}
public function actionEdit(ParameterBag $params)
{
$user = $this->assertViewableUser($params->user_id, [], true);
if (!$user->canEdit())
{
return $this->noPermission();
}
$notAdmin = $this->filter('not_admin', 'bool');
if ($this->isPost())
{
$this->memberSaveProcess($user)->run();
return $this->redirect($this->buildLink('members/edit', $user, $notAdmin ? ['not_admin' => 1] : []));
}
else
{
if (\XF::visitor()->hasAdminPermission('user') && !$notAdmin)
{
/** @var \XF\Mvc\Router $router */
$router = $this->app->container('router.admin');
return $this->redirect($router->buildLink('users/edit', $user));
}
$viewParams = [
'user' => $user,
'notAdmin' => $notAdmin
];
return $this->view('XF:Member\Edit', 'member_edit', $viewParams);
}
}
public function actionUserIps(ParameterBag $params)
{
$user = $this->assertViewableUser($params->user_id, [], true);
if (!\XF::visitor()->canViewIps())
{
return $this->noPermission();
}
/** @var \XF\Repository\Ip $ipRepo */
$ipRepo = $this->repository('XF:Ip');
$ips = $ipRepo->getIpsByUser($user);
if (!$ips)
{
return $this->message(\XF::phrase('no_ip_logs_for_requested_user'));
}
$viewParams = [
'user' => $user,
'ips' => $ips
];
return $this->view('XF:Member\UserIps', 'member_ip_list', $viewParams);
}
public function actionIpUsers()
{
if (!\XF::visitor()->canViewIps())
{
return $this->noPermission();
}
/** @var \XF\Repository\Ip $ipRepo */
$ipRepo = $this->repository('XF:Ip');
$ip = $this->filter('ip', 'str');
$parsed = \XF\Util\Ip::parseIpRangeString($ip);
if (!$parsed)
{
return $this->message(\XF::phrase('please_enter_valid_ip_or_ip_range'));
}
else if ($parsed['isRange'])
{
$ips = $ipRepo->getUsersByIpRange($parsed['startRange'], $parsed['endRange']);
}
else
{
$ips = $ipRepo->getUsersByIp($parsed['startRange']);
}
if ($ips)
{
$viewParams = [
'ip' => $ip,
'ipParsed' => $parsed,
'ipPrintable' => $parsed['printable'],
'ips' => $ips
];
return $this->view('XF:Member\IpUsers', 'member_ip_users_list', $viewParams);
}
else
{
return $this->message(\XF::phrase('no_users_logged_at_ip'));
}
}
public function actionSharedIps(ParameterBag $params)
{
$user = $this->assertViewableUser($params->user_id, [], true);
if (!\XF::visitor()->canViewIps())
{
return $this->noPermission();
}
$shared = $user->getSharedIpUsers($this->options()->sharedIpsCheckLimit);
$viewParams = [
'user' => $user,
'shared' => $shared
];
return $this->view('XF:Member\SharedIps', 'member_shared_ips_list', $viewParams);
}
public function actionReport(ParameterBag $params)
{
$user = $this->assertViewableUser($params->user_id, [], true);
if (!$user->canBeReported($error))
{
return $this->noPermission($error);
}
/** @var \XF\ControllerPlugin\Report $reportPlugin */
$reportPlugin = $this->plugin('XF:Report');
return $reportPlugin->actionReport(
'user', $user,
$this->buildLink('members/report', $user),
$this->buildLink('members', $user)
);
}
/**
* @param User $user
*
* @return \XF\Service\ProfilePost\Creator
*/
protected function setupProfilePostCreate(UserProfile $userProfile)
{
$message = $this->plugin('XF:Editor')->fromInput('message');
/** @var \XF\Service\ProfilePost\Creator $creator */
$creator = $this->service('XF:ProfilePost\Creator', $userProfile);
$creator->setContent($message);
$profilePost = $creator->getProfilePost();
if ($profilePost->ProfileUser->canUploadAndManageAttachmentsOnProfile())
{
$creator->setAttachmentHash($this->filter('attachment_hash', 'str'));
}
return $creator;
}
protected function finalizeProfilePostCreate(\XF\Service\ProfilePost\Creator $creator)
{
$creator->sendNotifications();
$profilePost = $creator->getProfilePost();
if (\XF::visitor()->user_id)
{
if ($profilePost->message_state == 'moderated')
{
$this->session()->setHasContentPendingApproval();
}
}
}
public function actionPost(ParameterBag $params)
{
$this->assertPostOnly();
$user = $this->assertViewableUser($params->user_id);
if (!$user->canPostOnProfile())
{
return $this->noPermission();
}
$creator = $this->setupProfilePostCreate($user->Profile);
$creator->checkForSpam();
if (!$creator->validate($errors))
{
return $this->error($errors);
}
$this->assertNotFlooding('post');
$profilePost = $creator->save();
$this->finalizeProfilePostCreate($creator);
if ($this->filter('_xfWithData', 'bool') && $this->request->exists('last_date') && $profilePost->canView())
{
$profilePostRepo = $this->getProfilePostRepo();
$limit = 3;
$lastDate = $this->filter('last_date', 'uint');
$style = $this->filter('style', 'str');
$context = $this->filter('context', 'str');
$firstUnshownProfilePost = null;
if ($context == 'all')
{
/** @var \XF\Mvc\Entity\Finder $profilePostList */
$profilePostList = $profilePostRepo->findNewestProfilePosts($lastDate)->with('fullProfile');
$profilePosts = $profilePostList->fetch($limit)->filterViewable();
}
else
{
/** @var \XF\Mvc\Entity\Finder $profilePostList */
$profilePostList = $profilePostRepo->findNewestProfilePostsOnProfile($user, $lastDate)->with('fullProfile');
$profilePosts = $profilePostList->fetch($limit + 1)->filterViewable();
// We fetched one more post than needed, if more than $limit posts were returned,
// we can show the 'there are more posts' notice
if ($profilePosts->count() > $limit)
{
$firstUnshownProfilePost = $profilePosts->last();
// Remove the extra post
$profilePosts = $profilePosts->pop();
}
}
// put the posts into oldest-first order as they will be (essentially prepended) in that order
$profilePosts = $profilePosts->reverse(true);
/** @var \XF\Repository\Attachment $attachmentRepo */
$attachmentRepo = $this->repository('XF:Attachment');
$profilePostAttachData = [];
foreach ($profilePosts AS $profilePost)
{
if ($profilePost->canUploadAndManageAttachments())
{
$profilePostAttachData[$profilePost->profile_post_id] = $attachmentRepo->getEditorData('profile_post_comment', $profilePost);
}
}
$viewParams = [
'user' => $user,
'style' => $style,
'profilePosts' => $profilePosts,
'firstUnshownProfilePost' => $firstUnshownProfilePost,
'profilePostAttachData' => $profilePostAttachData
];
$view = $this->view('XF:Member\NewProfilePosts', 'member_post_new_profile_posts', $viewParams);
$view->setJsonParam('lastDate', $profilePosts->last()->post_date);
return $view;
}
else
{
return $this->redirect($this->buildLink('profile-posts', $profilePost), \XF::phrase('your_profile_post_has_been_posted'));
}
}
public function userBanAddEdit(User $user)
{
if (!$user->canBan($error))
{
return $this->error($error);
}
$viewParams = [
'user' => $user,
'userBan' => $user->getRelationOrDefault('Ban')
];
return $this->view('XF:Member\Ban\Edit', 'member_ban_edit', $viewParams);
}
public function actionBanEdit(ParameterBag $params)
{
$user = $this->assertViewableUser($params->user_id);
if (!$user->is_banned)
{
return $this->notFound();
}
return $this->userBanAddEdit($user);
}
public function actionBan(ParameterBag $params)
{
$user = $this->assertViewableUser($params->user_id, [], true);
return $this->userBanAddEdit($user);
}
protected function userBanSaveProcess(User $user)
{
$form = $this->formAction();
$input = $this->filter([
'ban_length' => 'str',
'end_date' => 'datetime',
'user_reason' => 'str'
]);
$form->apply(function(FormAction $form) use ($input, $user)
{
if ($input['ban_length'] == 'permanent')
{
$input['end_date'] = 0;
}
/** @var \XF\Repository\Banning $banningRepo */
$banningRepo = $this->repository('XF:Banning');
if (!$banningRepo->banUser($user, $input['end_date'], $input['user_reason'], $error))
{
throw $this->exception($this->error($error));
}
});
return $form;
}
public function actionBanSave(ParameterBag $params)
{
$this->assertPostOnly();
$user = $this->assertViewableUser($params->user_id, [], true);
if (!$user->canBan($error))
{
return $this->error($error);
}
$this->userBanSaveProcess($user)->run();
return $this->redirect($this->getDynamicRedirect());
}
public function actionBanLift(ParameterBag $params)
{
$user = $this->assertViewableUser($params->user_id, [], true);
if (!$user->is_banned)
{
return $this->notFound();
}
if (!$user->canBan($error))
{
return $this->error($error);
}
if ($this->isPost())
{
$user->Ban->delete();
return $this->redirect($this->getDynamicRedirect());
}
else
{
$viewParams = [
'user' => $user,
'userBan' => $user->Ban
];
return $this->view('XF:Member\Ban\Lift', 'member_ban_lift', $viewParams);
}
}
public function actionTrophies(ParameterBag $params)
{
$this->assertNotEmbeddedImageRequest();
$user = $this->assertViewableUser($params->user_id);
if (!$this->options()->enableTrophies)
{
return $this->redirect($this->buildLink('members', $user));
}
/** @var \XF\Repository\Trophy $trophyRepo */
$trophyRepo = $this->repository('XF:Trophy');
$trophies = $trophyRepo->findUserTrophies($user->user_id)
->with('Trophy')
->fetch();
if ($user->user_id == \XF::visitor()->user_id)
{
$trophyIds = $trophies->pluckNamed('trophy_id');
/** @var \XF\Repository\UserAlert $userAlertRepo */
$userAlertRepo = $this->repository('XF:UserAlert');
$userAlertRepo->markUserAlertsReadForContent('trophy', $trophyIds);
}
$viewParams = [
'user' => $user,
'trophies' => $trophies
];
return $this->view('XF:Member\Trophy\Listing', 'member_trophies', $viewParams);
}
public function actionWarn(ParameterBag $params)
{
$user = $this->assertViewableUser($params->user_id, [], true);
if (!$user->canWarn())
{
return $this->noPermission();
}
/** @var \XF\ControllerPlugin\Warn $warnPlugin */
$warnPlugin = $this->plugin('XF:Warn');
return $warnPlugin->actionWarn(
'user', $user,
$this->buildLink('members/warn', $user)
);
}
public function actionWarnings(ParameterBag $params)
{
$user = $this->assertViewableUser($params->user_id, [], true);
if (!\XF::visitor()->canViewWarnings())
{
return $this->noPermission();
}
/** @var \XF\Repository\Warning $warningRepo */
$warningRepo = $this->repository('XF:Warning');
$warnings = $warningRepo->findUserWarningsForList($user->user_id)->fetch();
if (!$warnings->count())
{
return $this->message(\XF::phrase('this_member_has_not_been_warned'));
}
$viewParams = [
'user' => $user,
'warnings' => $warnings
];
return $this->view('XF:Member\Warnings', 'member_warnings', $viewParams);
}
public function actionFind()
{
$q = ltrim($this->filter('q', 'str', ['no-trim']));
if ($q !== '' && utf8_strlen($q) >= 2)
{
/** @var \XF\Finder\User $userFinder */
$userFinder = $this->finder('XF:User');
$users = $userFinder
->where('username', 'like', $userFinder->escapeLike($q, '?%'))
->isValidUser(true)
->fetch(10);
}
else
{
$users = [];
$q = '';
}
$viewParams = [
'q' => $q,
'users' => $users
];
return $this->view('XF:Member\Find', '', $viewParams);
}
/**
* @param int $userId
* @param array $extraWith
* @param bool $basicProfileOnly
*
* @return \XF\Entity\User
*
* @throws \XF\Mvc\Reply\Exception
*/
protected function assertViewableUser($userId, array $extraWith = [], $basicProfileOnly = false)
{
$extraWith[] = 'Option';
$extraWith[] = 'Privacy';
$extraWith[] = 'Profile';
array_unique($extraWith);
/** @var \XF\Entity\User $user */
$user = $this->em()->find('XF:User', $userId, $extraWith);
if (!$user)
{
throw $this->exception($this->notFound(\XF::phrase('requested_user_not_found')));
}
$canView = $basicProfileOnly ? $user->canViewBasicProfile($error) : $user->canViewFullProfile($error);
if (!$canView)
{
throw $this->exception($this->noPermission($error));
}
return $user;
}
/**
* @return \XF\Repository\ProfilePost
*/
protected function getProfilePostRepo()
{
return $this->repository('XF:ProfilePost');
}
public static function getActivityDetails(array $activities)
{
$userIds = [];
$userData = [];
$router = \XF::app()->router('public');
$defaultPhrase = \XF::phrase('viewing_members');
if (!\XF::visitor()->hasPermission('general', 'viewProfile'))
{
return $defaultPhrase;
}
foreach ($activities AS $activity)
{
$userId = $activity->pluckParam('user_id');
if ($userId)
{
$userIds[$userId] = $userId;
}
}
if ($userIds)
{
$users = \XF::em()->findByIds('XF:User', $userIds, 'Privacy');
foreach ($users AS $user)
{
$userData[$user->user_id] = [
'username' => $user->username,
'url' => $router->buildLink('members', $user),
];
}
}
$output = [];
foreach ($activities AS $key => $activity)
{
$userId = $activity->pluckParam('user_id');
$user = $userId && isset($userData[$userId]) ? $userData[$userId] : null;
if ($user)
{
$output[$key] = [
'description' => \XF::phrase('viewing_member_profile'),
'title' => $user['username'],
'url' => $user['url']
];
}
else
{
$output[$key] = $defaultPhrase;
}
}
return $output;
}
}