<?php
/*
* @since 1.0.0
* @copyright Copyright (C) 2020 ArtMedia. All rights reserved.
* @website http://artmedia.biz.pl
* @author Arkadiusz Tobiasz
* @email kontakt@artmedia.biz.pl
*/
namespace App\Controller\Profile;
use App\Controller\AbstractController;
use App\Entity\Profile;
use App\Entity\ProfileType as ProfileTypeEntity;
use App\Entity\Profile\Follow;
use App\Entity\Profile\Note;
use App\Entity\User\Notification;
use App\Entity\Profile\Mute;
use App\Form\Profile\ProfileType;
use App\Repository\ProfileRepository;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use App\Settings\SettingsManager;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
use App\Form\Profile\SearchType;
use App\Events\Core\MetaInfoAddEvent;
use App\Form\Profile\ContactType;
use App\Events\Core\MailAddEvent;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use App\Events\User\ProfileCreatedEvent;
use App\Events\User\ProfileBeforeDeleteEvent;
use App\Events\User\ProfileWidgetAddEvent;
use App\Events\Core\NotificationEvent;
use App\Entity\Media\Credit;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
class ProfileController extends AbstractController
{
/*
// translations from Twig/Profile
$this->translator->trans('Good news! {profile} is going to be in your area ({city}) from {start} to {end}', [], 'profile_feed');
$this->translator->trans('{profile} added a new image to their Portfolio', [], 'profile_feed');
$this->translator->trans('{profile} credited you on this image. Why don’t you add it to your Portfolio as well?', [], 'profile_feed');
$this->translator->trans('{profile} was credited in this image', [], 'profile_feed');
$this->translator->trans('(Directions)', [], 'profile_view');
$this->translator->trans('Manage links', [], 'profile_view');
$this->translator->trans('Send me email', [], 'profile_view');
$this->translator->trans('I\'m currently in {city}', [], 'profile_view');
*/
/**
* @Route("/{slug}", name="profile", priority="-1", methods={"GET"})
*/
public function profile(
Profile $profile,
SettingsManager $settingsManager,
EventDispatcherInterface $eventDispatcher
): Response
{
$sendMoney = false;
$receiveTips = false;
if ($profile->getCreatedBy() !== $this->getUser()) {
$configs = $settingsManager->get([
SettingsManager::MONEY_ENABLED,
SettingsManager::MONEY_SEND_BETWEEN_USERS,
SettingsManager::MONEY_RECEIVE_TIPS,
SettingsManager::MONEY_GROUP_RECEIVE_TIPS,
], true);
$sendMoney = ((bool) $configs[SettingsManager::MONEY_ENABLED] && (bool) $configs[SettingsManager::MONEY_SEND_BETWEEN_USERS]);
$receiveTips = ((bool) $configs[SettingsManager::MONEY_ENABLED] && (bool) $configs[SettingsManager::MONEY_RECEIVE_TIPS] && $configs[SettingsManager::MONEY_GROUP_RECEIVE_TIPS] && $profile->getUser()->getGroups([$configs[SettingsManager::MONEY_GROUP_RECEIVE_TIPS]])->count());
}
$params = [
'sendMoney' => $sendMoney,
'receiveTips' => $receiveTips,
];
$profileWidgetAddEvent = new ProfileWidgetAddEvent($profile, $this->getUser());
$eventDispatcher->dispatch($profileWidgetAddEvent, ProfileWidgetAddEvent::NAME);
return $this->renderTemplate('profile/view.html.twig', [
'profile' => $profile,
'params' => $params,
'widgets' => $profileWidgetAddEvent->getWidgets(),
], [
'%name%' => $profile->getName(),
'%type%' => $profile->getProfileType()->getName(),
]);
}
/**
* @Route("/{slug}/credits", name="profile_credits", priority="-1", methods={"GET", "POST"})
*/
public function credits(
Request $request,
Profile $profile,
EventDispatcherInterface $eventDispatcher,
SettingsManager $settingsManager
): Response
{
$user = $this->getUser();
$owner = $user && $user == $profile->getUser();
$profiles = [];
$viewersProfiles = [];
$commonProfiles = [];
$moreLink = false;
$creditedProfiles = $this->getDoctrine()->getRepository(Credit::class)->getWorkProfiles($profile);
if ($creditedProfiles) {
foreach ($creditedProfiles as $creditedProfile) {
$profiles[$creditedProfile->getId()] = $creditedProfile;
}
}
if ($profiles) {
if ($user && !$owner) {
if ($user->getProfiles()) {
foreach ($user->getProfiles() as $viewerProfile) {
if (isset($profiles[$viewerProfile->getId()])) {
$viewersProfiles[] = $profiles[$viewerProfile->getId()];
unset($profiles[$viewerProfile->getId()]);
}
}
}
$viewersCredits = $this->getDoctrine()->getRepository(Profile::class)->getUserWorkProfiles($user);
if ($viewersCredits) {
foreach ($viewersCredits as $credit) {
if (isset($profiles[$credit->getId()])) {
$commonProfiles[] = $profiles[$credit->getId()];
unset($profiles[$credit->getId()]);
}
}
}
}
}
return $this->renderTemplate('profile/credits.html.twig', [
'profile' => $profile,
'profiles' => $profiles,
'viewersProfiles' => $viewersProfiles,
'commonProfiles' => $commonProfiles,
], [
'%name%' => $profile->getName(),
'%type%' => $profile->getProfileType()->getName(),
]);
}
/**
* @Route("/{slug}/contact", name="profile_contact", priority="-1", methods={"GET", "POST"})
*/
public function contact(
Request $request,
Profile $profile,
EventDispatcherInterface $eventDispatcher,
SettingsManager $settingsManager
): Response
{
if (!$profile->isEnableContact()) {
throw new AccessDeniedException($this->translator->trans("Profile don't allow contact via site", [], 'profile_contact'));
}
$configs = $settingsManager->get([
SettingsManager::CORE_RECAPTCHA_PUBLIC_KEY,
SettingsManager::CORE_SITE_MAIL,
], true);
$form = $this->createForm(
ContactType::class,
null,
[
'site_key' => $configs[SettingsManager::CORE_RECAPTCHA_PUBLIC_KEY],
]
);
if ($this->getUser()) {
$form->get('email')->setData($this->getUser()->getEmail());
}
$form->handleRequest($request);
if($form->isSubmitted()) {
if ($form->isValid()) {
$data = $form->getData();
$email = $profile->getEmail() ? $profile->getEmail() : $profile->getUser()->getEmail();
$mailAddEvent = new MailAddEvent(
$configs[SettingsManager::CORE_SITE_MAIL],
$email,
$data['subject'],
'profile/email/contact.html.twig',
'profile/email/contact.txt.twig',
[
'email' => $data['email'],
'user' => $this->getUser(),
'message' => $data['text'],
'profile' => $profile,
],
[],
true,
1
);
$eventDispatcher->dispatch($mailAddEvent, MailAddEvent::NAME);
$this->addFlash('success', $this->translator->trans('Message was successfully sended!', [], 'profile_contact'));
return $this->redirectToRoute('profile', ['slug' => $profile->getSlug()]);
} else {
$this->addFlash('error', $this->translator->trans('Could not send email because the form is invalid.', [], 'profile_contact'));
}
}
return $this->renderTemplate('profile/contact.html.twig', [
'profile' => $profile,
'form' => $form->createView(),
], [
'%name%' => $profile->getName(),
'%type%' => $profile->getProfileType()->getName(),
]);
}
/**
* @Route("/profiles", name="profile_index", methods={"GET"})
*/
public function index(): Response
{
return $this->renderTemplate('profile/index.html.twig', [
'profiles' => $this->getDoctrine()->getRepository(Profile::class)->findByUser($this->getUser())
]);
}
/**
* @Route("/profiles/new", name="profile_new", methods={"POST", "GET"})
* @Route("/profiles/new/{type}", name="profile_new_type", methods={"POST", "GET"})
* @IsGranted("ROLE_USER")
*/
public function new(
Request $request,
?ProfileTypeEntity $type = null,
SettingsManager $settingsManager,
EventDispatcherInterface $eventDispatcher
)
{
$configs = $settingsManager->get([
SettingsManager::PROFILE_ENABLE_FOR,
SettingsManager::PROFILE_ENABLE_GROUPS,
SettingsManager::PROFILE_MAX_COUNT,
], true);
$user = $this->getUser();
if ($configs[SettingsManager::PROFILE_ENABLE_FOR] == 'groups') {
$groups = [];
foreach ($user->getGroups() as $group) {
$groups[] = $group->getId();
}
if (!count(array_intersect($groups, json_decode($configs[SettingsManager::PROFILE_ENABLE_GROUPS], true)))) {
throw new AccessDeniedException($this->translator->trans('You can\'t create profiles', [], 'core'));
}
}
if ($configs[SettingsManager::PROFILE_MAX_COUNT]) {
$count = $this->getDoctrine()->getRepository(Profile::class)->count(['user' => $user]);
if ($count >= (int)$configs[SettingsManager::PROFILE_MAX_COUNT]) {
throw new AccessDeniedException($this->translator->trans('You can\'t create more than %count% profiles', [
'%count%' => $count
], 'core'));
}
}
if (!$type) {
return $this->renderTemplate('profile/types.html.twig', [
'types' => $this->getDoctrine()->getRepository(ProfileTypeEntity::class)->findAvailableForUser($user),
]);
}
$class = '\App\Entity';
if ($type->getEntity() != 'profile') {
$class .= '\Profile\Type';
}
$class .= '\\' . ucfirst($type->getEntity());
$profile = new $class();
$profile->setProfileType($type);
$profile->setUser($this->getUser());
$profile->setEmail($this->getUser()->getEmail());
$form = $this->createForm(ProfileType::class, $profile);
$form->handleRequest($request);
if ($form->isSubmitted()) {
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($profile);
$em->flush();
$profileCreatedEvent = new ProfileCreatedEvent($profile);
$eventDispatcher->dispatch($profileCreatedEvent, ProfileCreatedEvent::NAME);
$this->addFlash('success', $this->translator->trans('New profile was successfully added!', [], 'profile'));
return $this->redirectToRoute('profile_index');
} else {
$this->addFlash('error', $this->translator->trans('Could not save data because the form is invalid.', [], 'profile'));
}
}
return $this->renderTemplate('profile/new.html.twig', [
'profile' => $profile,
'form' => $form->createView(),
]);
}
/**
* @Route("/profiles/{id}", name="profile_edit", methods={"POST", "GET"})
*/
public function edit(Request $request, Profile $profile)
{
$this->denyAccessUnlessGranted('ROLE_CREATOR', $profile);
$class = '\App\Form\Profile\Edit' . ucfirst($profile->getType()) . 'Type';
$form = $this->createForm($class, $profile);
if (!$profile->getEmail()) {
$form->get('email')->setData($this->getUser()->getEmail());
}
$form->handleRequest($request);
if ($form->isSubmitted()) {
if ($form->isValid()) {
$this->getDoctrine()->getManager()->flush();
$this->addFlash('success', $this->translator->trans('Profile was successfully updated!', [], 'profile'));
return $this->redirectToRoute('profile_index');
} else {
$this->addFlash('error', $this->translator->trans('Could not save data because the form is invalid.', [], 'profile'));
}
}
return $this->renderTemplate('profile/edit_' . $profile->getType() . '.html.twig', [
'profile' => $profile,
'form' => $form->createView(),
]);
}
/**
* @Route("/profiles/{id}", name="profile_delete", methods={"DELETE"})
* @param EntityManagerInterface $entityManager
* @param Profile $profile
* @return RedirectResponse
*/
public function delete(
Profile $profile,
EventDispatcherInterface $eventDispatcher
): RedirectResponse
{
$this->denyAccessUnlessGranted('ROLE_CREATOR', $profile);
$profileBeforeDeleteEvent = new ProfileBeforeDeleteEvent($profile);
$eventDispatcher->dispatch($profileBeforeDeleteEvent, ProfileBeforeDeleteEvent::NAME);
$em = $this->getDoctrine()->getManager();
$em->remove($profile);
$em->flush();
$this->addFlash('success', $this->translator->trans('Profile was successfully deleted!', [], 'profile'));
return $this->redirectToRoute('profile_index');
}
/**
* @Route("profiles-responder", methods={"POST", "GET"}, name="profile_responder")
*/
public function responder(
Request $request
): Response {
$type = $request->get('type');
$result = [];
switch ($type) {
case 'profiles':
$term = $request->get('q');
$profiles = $this->getDoctrine()->getRepository(Profile::class)->findByTerm($term);
if ($profiles) {
foreach ($profiles as $profile) {
$result[] = [
'value' => $profile['id'],
'text' => $profile['name'],
'url' => $this->generateUrl('profile', ['slug' => $profile['slug']]),
];
}
}
break;
case 'note':
$profile = (int)$request->request->get('profile');
$content = $request->request->get('content');
$profile = $this->getDoctrine()->getRepository(Profile::class)->find($profile);
if ($profile) {
$user = $this->getUser();
$note = $this->getDoctrine()->getRepository(Note::class)->findOneBy([
'profile' => $profile,
'user' => $user,
]);
if ($note) {
$note->setContent($content);
$this->getDoctrine()->getManager()->flush();
} else {
$note = new Note();
$note->setProfile($profile);
$note->setUser($user);
$note->setContent($content);
$em = $this->getDoctrine()->getManager();
$em->persist($note);
$em->flush();
}
$result = [
'response' => 'success',
'message' => $this->translator->trans('Private note was saved!', [], 'profile_note')
];
} else {
$result = [
'response' => 'error',
'message' => $this->translator->trans('Private note wasn\'t saved!', [], 'profile_note')
];
}
break;
}
return new JsonResponse($result);
}
/**
* @Route("/search/profiles", methods="GET|POST", name="profile_search")
*/
public function search(
Request $request,
SettingsManager $settingsManager
): Response {
$processed = false;
$results = [];
$configs = $settingsManager->get([
SettingsManager::PROFILE_SEARCH,
SettingsManager::PROFILE_SEARCH_GROUPS,
SettingsManager::PROFILE_SEARCH_RADIUS,
SettingsManager::PROFILE_SEARCH_RADIUS_GROUPS,
], true);
$query = $request->get('q', null);
if ($configs[SettingsManager::PROFILE_SEARCH] == 'groups') {
$groups = [];
if ($this->getUser()) {
foreach ($this->getUser()->getGroups() as $group) {
$groups[] = $group->getId();
}
}
if (!count(array_intersect($groups, json_decode($configs[SettingsManager::PROFILE_SEARCH_GROUPS], true)))) {
throw new AccessDeniedException($this->translator->trans('You can\'t use advanced search', [], 'profile_search'));
}
}
$radius = true;
if ($configs[SettingsManager::PROFILE_SEARCH_RADIUS] == 'groups') {
$groups = [];
if ($this->getUser()) {
foreach ($this->getUser()->getGroups() as $group) {
$groups[] = $group->getId();
}
}
if (!count(array_intersect($groups, json_decode($configs[SettingsManager::PROFILE_SEARCH_RADIUS_GROUPS], true)))) {
$radius = false;
}
}
$form = $this->createForm(SearchType::class, null, [
'radius' => $radius,
]);
if ($query) {
$form->get('name')->setData($query);
}
$form->handleRequest($request);
$location = null;
$user = $this->getUser();
if ($user && $user->getLocation()) {
// we get location from profile
$location = $user->getLocation();
}
if ($form->isSubmitted() && $form->isValid()) {
$data = $form->getData();
$data['coords'] = $location ?: false;
$results = $this->getDoctrine()->getRepository(Profile::class)->search($data);
$processed = true;
} elseif ($query) {
$results = $this->getDoctrine()->getRepository(Profile::class)->search([
'name' => $query,
]);
$processed = true;
}
return $this->renderTemplate('profile/search.html.twig', [
'form' => $form->createView(),
'radius' => $radius,
'results' => $results,
'processed' => $processed,
'location' => $location ? true : false,
]);
}
/**
* @Route("/action/{profile}/{type}", name="profile_action", methods={"POST", "GET"})
* @IsGranted("ROLE_USER")
*/
public function action(Profile $profile, string $type, EventDispatcherInterface $eventDispatcher)
{
$user = $this->getUser();
$em = $this->getDoctrine()->getManager();
switch ($type) {
case Follow::ACTION:
$em = $this->getDoctrine()->getManager();
$follow = $this->getDoctrine()->getRepository(Follow::class)->findOneBy([
'profile' => $profile,
'user' => $user,
]);
if ($follow) {
$em->remove($follow);
$this->addFlash('success', $this->translator->trans('You unfollowed this profile', [], 'profile'));
} else {
$follow = new Follow($profile, $user);
$em->persist($follow);
$this->addFlash('success', $this->translator->trans('You now follow this profile', [], 'profile'));
if ($user != $profile->getUser()) {
$notification = $this->getDoctrine()->getRepository(Notification::class)->findOneBy([
'name' => Follow::NOTIFICATION,
'user' => $profile->getUser(),
]);
if (!$notification || ($notification && $notification->isActive())) {
$notificationEvent = new NotificationEvent(
Follow::NOTIFICATION,
$profile->getUser(),
$this->translator->trans('{name} now follow your profile {profile}', [
'{name}' => $user->getDisplayName(),
'{profile}' => $profile->getName(),
], 'email_notification'),
'profile/email/follow_notification',
[
'name' => $user->getDisplayName(),
'profile' => $profile->getName(),
'user' => $profile->getUser()->getDisplayName(),
]
);
$eventDispatcher->dispatch($notificationEvent, NotificationEvent::NAME);
}
}
}
break;
case Mute::ACTION:
$em = $this->getDoctrine()->getManager();
$mute = $this->getDoctrine()->getRepository(Mute::class)->findOneBy([
'profile' => $profile,
'user' => $user,
]);
if ($mute) {
$em->remove($mute);
$this->addFlash('success', $this->translator->trans('You unmuted this profile', [], 'profile'));
} else {
$mute = new Mute($profile, $user);
$em->persist($mute);
$this->addFlash('success', $this->translator->trans('You now muted this profile', [], 'profile'));
}
break;
default:
$this->addFlash('danger', $this->translator->trans('Error: Unrecognized action!', [], 'profile'));
break;
}
$em->flush();
return $this->redirectToRoute('profile', ['slug' => $profile->getSlug()]);
}
}