<?php
namespace Products\SchoolNowBundle\Controller\Dashboard;
use App\Component\ViewLayer\Views\AbstractHtmlView;
use App\Component\ViewLayer\Views\AjaxHtmlView;
use App\Component\ViewLayer\Views\DocHtmlView;
use App\Component\ViewLayer\Views\JsonView;
use App\Controller\PaginationTrait;
use App\Doctrine\Repository\Feed\Entry\ContentEventSearch;
use App\Doctrine\Repository\Feed\FeedSearch;
use App\Entity\Content\AbstractObject;
use App\Entity\Content\Events\Event\EventObject;
use App\Entity\Feed\AbstractEntry;
use App\Entity\Feed\Entry\AbstractContentEntry;
use App\Entity\Feed\Entry\ContentEventEntry;
use App\Entity\Feed\Entry\ContentGalleryEntry;
use App\Entity\System\School;
use App\Enum\ChannelEnum;
use App\Entity\System\SocialAccount;
use App\Form\Forms\Content\ObjectForm;
use App\Form\Forms\DummyForm;
use App\Model\Content\ObjectInterface;
use App\Service\Content\ContentManager;
use App\Service\Content\ContentSchoolProvider;
use App\Service\Feed\FeedManager;
use App\Util\Pagination;
use Cms\CoreBundle\Util\DateTimeUtils;
use Cms\Modules\AlertBundle\Model\Alert\AlertData;
use Cms\Modules\CalendarBundle\Entity\Event\EventProxy;
use Cms\SystemBundle\Entity\Announcement\SystemAnnouncement;
use Doctrine\Common\Util\ClassUtils;
use Platform\SecurityBundle\Entity\Identity\Account;
use Products\NotificationsBundle\Entity\Broadcast;
use Products\NotificationsBundle\Entity\Profile;
use Products\NotificationsBundle\Entity\Recipients\EmailRecipient;
use Products\SchoolNowBundle\Controller\AbstractDashboardController;
use Products\SchoolNowBundle\Form\Forms\FeedSearchForm;
use Products\SchoolNowBundle\Form\Forms\Posts\PostForm;
use Products\SchoolNowBundle\Form\Forms\Posts\PostReviewForm;
use Products\SchoolNowBundle\Service\PostLogic;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class DefaultController
* @package Products\SchoolNowBundle\Controller\Dashboard
*/
final class DefaultController extends AbstractDashboardController
{
use PaginationTrait;
public const ROUTES__MAIN = 'app.schoolnow.dashboard.default.main';
public const ROUTES__FEEDS__NOTIFICATIONS = 'app.schoolnow.dashboard.default.feeds.notifications';
public const ROUTES__FEEDS__CALENDAR = 'app.schoolnow.dashboard.default.feeds.calendar';
public const ROUTES__FEEDS__PHOTOS = 'app.schoolnow.dashboard.default.feeds.photos';
public const ROUTES__FEEDS__CONTENT = 'app.schoolnow.dashboard.default.feeds.content';
public const ROUTES__CREATE = 'app.schoolnow.dashboard.default.create';
public const ROUTES__PREVIEW = 'app.schoolnow.dashboard.default.preview';
public const ROUTES__UPDATE = 'app.schoolnow.dashboard.default.update';
public const ROUTES__SOCIAL_ACCOUNTS = 'app.schoolnow.dashboard.default.social_accounts';
public const ROUTES__DELETE = 'app.schoolnow.dashboard.default.delete';
public const ROUTES__DELETE_PREVIEW = 'app.schoolnow.dashboard.default.delete_preview';
public const ROUTES__DRAFTS__LIST = 'app.schoolnow.dashboard.default.drafts.list';
public const ROUTES__DRAFTS__UPDATE = 'app.schoolnow.dashboard.default.drafts.update';
public const ROUTES__DRAFTS__DELETE = 'app.schoolnow.dashboard.default.drafts.delete';
public const ROUTES__PIN = 'app.schoolnow.dashboard.default.pin';
public const ROUTES__CREATE_REVIEW = 'app.schoolnow.dashboard.default.create_review';
public const ROUTES__UPDATE_REVIEW = 'app.schoolnow.dashboard.default.update_review';
public const SEARCH_PAGING = Pagination::PAGE_LIMIT;
private ContentSchoolProvider $contentSchoolProvider;
/**
* @param ContentSchoolProvider $contentSchoolProvider
*/
public function __construct(
ContentSchoolProvider $contentSchoolProvider
) {
$this->contentSchoolProvider = $contentSchoolProvider;
}
/**
* @return Account
*/
protected function getCurrentUser(): Account
{
$user = $this->getUser();
if ( ! $user instanceof Account) {
throw new \LogicException();
}
return $user;
}
/**
* @param int $pagination
* @return DocHtmlView|RedirectResponse
*
* @Route(
* "{pagination}",
* name = self::ROUTES__MAIN,
* requirements = {
* "pagination" = "[1-9]\d*",
* },
* defaults = {
* "pagination" = 0,
* },
* )
*/
public function mainAction(int $pagination)
{
// search form logic
$form = $this->createForm(
FeedSearchForm::class,
$search = (new FeedSearch())
->setUser($this->getCurrentUser())
->setPast(true)
->setVisibility(ObjectInterface::VISIBILITIES__PUBLISHED)
->setBoosted(true)
->setDeep(true)
);
$result = $this->doSearch(
AbstractContentEntry::class,
'feed',
$search,
$form,
$pagination,
);
if ($result instanceof Response) {
return $result;
}
// handle searching
// if not searching, inject the notifications hackery into the top of the feed...
// try to grab notifications if any when on the first page
$notifications = [];
if ($pagination === 0 && ! (($form->isSubmitted() && $form->isValid())) && $profile = $this->getProfile()) {
$notifications = $this->getEntityManager()->getRepository(Broadcast::class)->findByProfileAfterTimestamp(
$profile,
DateTimeUtils::beforeNow('PT24H')
);
}
$result['notifications'] = $notifications;
$result['isGrantedShare'] = ! empty($this->contentSchoolProvider->getSchools());
$result['announcements'] = $this->getEntityManager()->getRepository(SystemAnnouncement::class)->findTenantAnnouncements(
$this->getGlobalContext()->getTenant()
);
return $this->html($result);
}
/**
* @return Profile|null
*/
protected function getProfile(): ?Profile
{
// first attempt to find a profile based on one-roster id
// this is the "safest" way to poll the info...
$profile = null;
if ($this->getCurrentUser()->getOneRosterId()) {
$profile = $this->getEntityManager()->getRepository(Profile::class)->findOneBy([
'onerosterId' => $this->getCurrentUser()->getOneRosterId(),
]);
}
// if that lookup does not find anything, search based on user email
// TOOD: is this 100% safe? could somebody change their email to obtain info they shouldn't have?
if ( ! $profile) {
$recipient = $this->getEntityManager()->getRepository(EmailRecipient::class)->findOneBy([
'contact' => $this->getCurrentUser()->getEmail(),
]);
if ($recipient) {
$profiles = $recipient->getProfilesAsArray();
if ($profiles) {
$profile = $profiles[0];
}
}
}
return $profile;
}
/**
* @param Request $request
* @param int $pagination
* @return AbstractHtmlView|RedirectResponse
*
* @Route(
* "/feeds/notifications",
* name = self::ROUTES__FEEDS__NOTIFICATIONS,
* requirements = {
* "pagination" = "[1-9]\d*",
* },
* defaults = {
* "pagination" = 0,
* },
* )
*/
public function feedsNotificationsAction(Request $request, int $pagination)
{
// only search for things if there is a profile
$entries = [];
if ($profile = $this->getProfile()) {
// querying
$entries = $this->getEntityManager()->getRepository(Broadcast::class)->findByProfile(
$profile,
true,
self::SEARCH_PAGING,
Pagination::offset($pagination, self::SEARCH_PAGING)
);
// determine if we are out of bounds on the pagination
if (Pagination::outOfBounds($entries, $pagination, self::SEARCH_PAGING)) {
return $this->redirectToRoute(self::ROUTES__FEEDS__NOTIFICATIONS, array_merge(
$request->query->all(),
['pagination' => Pagination::maxPage($entries, self::SEARCH_PAGING)]
));
}
}
return $this->html([
'pagination' => $profile ? Pagination::controller(
$entries,
count($entries),
$pagination,
self::SEARCH_PAGING
) : null,
'profile' => $profile,
'feed' => $entries,
'isGrantedShare' => ! empty($this->contentSchoolProvider->getSchools()),
]);
}
/**
* @param Request $request
* @param int $pagination
* @return AbstractHtmlView|RedirectResponse
*
* @Route(
* "/feeds/calendar/{pagination}",
* name = self::ROUTES__FEEDS__CALENDAR,
* requirements = {
* "pagination" = "[1-9]\d*",
* },
* defaults = {
* "pagination" = 0,
* },
* )
*/
public function feedsCalendarAction(Request $request, int $pagination)
{
// querying
$entries = $this->getEntityManager()->getRepository(ContentEventEntry::class)->findBySearch(
(new ContentEventSearch())
->setUser($this->getCurrentUser())
->setPast(true)
->setVisibility(ObjectInterface::VISIBILITIES__PUBLISHED)
->setDeep(false),
self::SEARCH_PAGING,
Pagination::offset($pagination, self::SEARCH_PAGING)
);
// determine if we are out of bounds on the pagination
if (Pagination::outOfBounds($entries, $pagination, self::SEARCH_PAGING)) {
return $this->redirectToRoute(self::ROUTES__FEEDS__CALENDAR, array_merge(
$request->query->all(),
['pagination' => Pagination::maxPage($entries, self::SEARCH_PAGING)]
));
}
return $this->html([
'pagination' => Pagination::controller(
$entries,
count($entries),
$pagination,
self::SEARCH_PAGING
),
'feed' => $entries,
'isGrantedShare' => ! empty($this->contentSchoolProvider->getSchools()),
]);
}
/**
* @param Request $request
* @param int $pagination
* @return AbstractHtmlView|RedirectResponse
*
* @Route(
* "/feeds/photos/{pagination}",
* name = self::ROUTES__FEEDS__PHOTOS,
* requirements = {
* "pagination" = "[1-9]\d*",
* },
* defaults = {
* "pagination" = 0,
* },
* )
*/
public function feedsPhotosAction(Request $request, int $pagination)
{
// querying
$entries = $this->getEntityManager()->getRepository(ContentGalleryEntry::class)->findBySearch(
(new FeedSearch())
->setUser($this->getCurrentUser())
->setPast(true)
->setVisibility(ObjectInterface::VISIBILITIES__PUBLISHED)
->setBoosted(true)
->setDeep(true),
self::SEARCH_PAGING,
Pagination::offset($pagination, self::SEARCH_PAGING)
);
// determine if we are out of bounds on the pagination
if (Pagination::outOfBounds($entries, $pagination, self::SEARCH_PAGING)) {
return $this->redirectToRoute(self::ROUTES__FEEDS__CALENDAR, array_merge(
$request->query->all(),
['pagination' => Pagination::maxPage($entries, self::SEARCH_PAGING)]
));
}
return $this->html([
'pagination' => Pagination::controller(
$entries,
count($entries),
$pagination,
self::SEARCH_PAGING
),
'feed' => $entries,
'isGrantedShare' => ! empty($this->contentSchoolProvider->getSchools()),
]);
}
/**
* @param int $pagination
* @return JsonView|Response
*
* @Route(
* "/feeds/content/{pagination}",
* name = self::ROUTES__FEEDS__CONTENT,
* requirements = {
* "pagination" = "[1-9]\d*",
* },
* defaults = {
* "pagination" = 0,
* },
* )
*/
public function feedsContentAjaxAction(int $pagination)
{
$result = $this->doSearch(
AbstractContentEntry::class,
'entries',
(new FeedSearch())
->setUser($this->getCurrentUser())
->setPast(true)
->setVisibility(ObjectInterface::VISIBILITIES__PUBLISHED)
->setBoosted(true)
->setDeep(true),
FeedSearchForm::class,
$pagination,
);
if ($result instanceof Response) {
return $result;
}
return $this->jsonView([
'pagination' => $result['pagination'],
'results' => array_map(
function (object $item) {
return $this->getMobileApiSerializer()->serialize(
$item
);
},
$result['entries']->getIterator()->getArrayCopy()
),
]);
}
/**
* @param Request $request
* @return AbstractHtmlView|RedirectResponse
*
* @Route(
* "/create",
* name = self::ROUTES__CREATE,
* )
*/
public function createAction(Request $request)
{
$schools = $this->contentSchoolProvider->getSchools();
if ( ! $schools) {
throw new \RuntimeException();
}
// HACK: try to find a passed in cms event
$event = null;
if ($request->query->has('event')) {
$event = $this->getEntityManager()->getRepository(EventProxy::class)->find(
$request->query->get('event')
);
if ( ! $event) {
throw new \RuntimeException();
}
$sch = $schools[0];
foreach ($schools as $school) {
if ($school->getDepartment() === $event->getContainer()) {
$sch = $school;
break;
}
}
$event = [
'school' => $sch,
'type' => 'event',
'headline' => $event->getData()->getTitle(),
'startsAt' => $event->getData()->getStartDate(),
'stopsAt' => $event->getData()->getEndDate(),
'html' => $event->getData()->getDescription(),
];
}
// create the form
$form = $this->createForm(
PostForm::class,
$event ?: [
'school' => $schools[0],
'startsAt' => $startsAt = DateTimeUtils::now(),
'stopsAt' => (clone $startsAt)->add(new \DateInterval('PT1H')),
],
[
'schools' => $schools,
]
);
// if this is a post, we might have review data coming in
// attach that form so the submission handling deals with that too
if ($request->isMethod('POST')) {
$form->add('review', PostReviewForm::class);
}
// handle submission
if ($this->handleForm($form)) {
// we are always creating a new item, publishing is optional and is checked later
$object = $this->getPostLogic()->init(
$form->getData()['type'],
$form->getData()
);
$pinned = false;
if ($this->isGranted('app.feed.pin'))
{
$pinned = (bool) $form->get('pinned')->getData();
}
// create the object and feed entry
$this->getContentManager()->createObject(
$object,
FeedManager::BOOSTING[ClassUtils::getClass($object)] ? FeedManager::FLAGS__BOOST__SET : FeedManager::FLAGS__BOOST__UNSET,
$pinned ? FeedManager::FLAGS__PIN__SET : FeedManager::FLAGS__PIN__UNSET
);
// determine if we are publishing and handle it if so
if ($object->getScheduledAt() === null && $form->get('action')->getData() === 'publish') {
$this->getContentManager()->publishObject(
$object,
FeedManager::BOOSTING[ClassUtils::getClass($object)] ? FeedManager::FLAGS__BOOST__SET : FeedManager::FLAGS__BOOST__UNSET,
$pinned ? FeedManager::FLAGS__PIN__SET : FeedManager::FLAGS__PIN__UNSET
);
}
$this->getLoggingService()->createLog($object);
return $this->jumpOrRedirectToRoute(self::ROUTES__MAIN);
}
return $this->html([
'form' => $form->createView(),
]);
}
/**
* @param Request $request
* @param AbstractContentEntry $entry
* @return AbstractHtmlView|RedirectResponse
*
* @Route(
* "/{entry}/update",
* name = self::ROUTES__UPDATE,
* requirements = {
* "entry" = "%app.routing.regexes.ulid%",
* },
* )
* @ParamConverter(
* "entry",
* class = AbstractContentEntry::class,
* )
*/
public function updateAction(Request $request, AbstractContentEntry $entry)
{
// AUDIT
$this->denyAccessUnlessGranted(
[
sprintf('campussuite.cms.container.%s.manage', $entry->getDepartment()->getType()),
'campussuite.cms.module.manage',
],
[$entry, $entry->getDepartment()],
);
// create the form
$form = $this->createForm(
ObjectForm::map($object = $entry->getObject()),
$object,
[
'boosting' => false,
'pinning' => false,
]
);
if ($request->isMethod('POST')) {
$form->add('review', PostReviewForm::class);
}
$pinned = $entry->isPinned();
// handle submission
if ($this->handleForm($form)) {
if ($this->isGranted('app.feed.pin')) {
$pinned = (bool) $form->get('pinned')->getData();
}
// TODO: PERMS: this was removed in the perms branch, was it supposed to be?
$object->setReview($form->get('review')->getData() ?? []);
// update the object
$this->getContentManager()->updateObject(
$object,
FeedManager::FLAGS__BOOST__PRESERVE,
$pinned ? FeedManager::FLAGS__PIN__SET : FeedManager::FLAGS__PIN__UNSET,
);
// determine if we are publishing and handle it if so
if ($object->getScheduledAt() === null && $form->get('action')->getData() === 'publish') {
$this->getContentManager()->publishObject(
$object,
FeedManager::FLAGS__BOOST__PRESERVE,
FeedManager::FLAGS__PIN__PRESERVE
);
}
$this->getLoggingService()->createLog($object);
return $this->jumpOrRedirectToRoute(self::ROUTES__MAIN);
}
return $this->html([
'entry' => $entry,
'object' => $object,
'form' => $form->createView(),
]);
}
/**
* @param Request $request
* @param AbstractContentEntry $entry
* @return AjaxHtmlView
*
* @Route(
* "/{entry}/social-accounts",
* name = self::ROUTES__SOCIAL_ACCOUNTS,
* requirements = {
* "entry" = "%app.routing.regexes.ulid%",
* },
* )
* @ParamConverter(
* "entry",
* class = AbstractContentEntry::class,
* )
*/
public function socialPostsAction(Request $request, AbstractContentEntry $entry): AjaxHtmlView
{
// AUDIT
$this->denyAccessUnlessGranted(
[
sprintf('campussuite.cms.container.%s.manage', $entry->getDepartment()->getType()),
'campussuite.cms.module.manage',
],
[$entry, $entry->getDepartment()],
);
// make sure it is ajax
if ( ! $request->isXmlHttpRequest()) {
throw new NotFoundHttpException();
}
// make sure we have an object to be working with from the feed entry
// this is where the social posting information is held
$object = $entry->getObject();
if ( ! $object instanceof AbstractObject) {
throw new \LogicException();
}
// get a set of all the social accounts for the customer
// optimizing the array to be associative with the social identifier as the key
// NOTE: may be technically possible for collisions of identifiers between social services, but highly unlikely...
$socialAccounts = $this->getEntityManager()->getRepository(SocialAccount::class)->findAll();
$socialAccounts = array_combine(
array_map(
static function (SocialAccount $socialAccount) {
return $socialAccount->getIdentifier();
},
$socialAccounts,
),
$socialAccounts,
);
return $this->ajax([
'socialAccounts' => $socialAccounts,
'socialPosts' => $object->getSocialPosts(),
]);
}
/**
* @param Request $request
* @param AbstractEntry $entry
* @return AjaxHtmlView|JsonView
*
* @Route(
* "/{entry}/delete",
* name = self::ROUTES__DELETE,
* requirements = {
* "entry" = "%app.routing.regexes.ulid%",
* },
* )
* @Route(
* "/{entry}/delete-preview",
* name = self::ROUTES__DELETE_PREVIEW,
* requirements = {
* "entry" = "%app.routing.regexes.ulid%",
* },
* )
* @ParamConverter(
* "entry",
* class = AbstractContentEntry::class,
* )
*/
public function deleteAction(Request $request, AbstractContentEntry $entry)
{
// AUDIT
$this->denyAccessUnlessGranted(
[
sprintf('campussuite.cms.container.%s.manage', $entry->getDepartment()->getType()),
'campussuite.cms.module.manage',
],
[$entry, $entry->getDepartment()],
);
// make sure it is ajax
if ( ! $request->isXmlHttpRequest()) {
throw new NotFoundHttpException();
}
// make a form for submission purposes
$form = $this->createForm(DummyForm::class);
// handle submission
if ($this->handleForm($form)) {
// delete the entry
$object = $entry->getObject();
$id = $entry->getId();
$this->getEntityManager()->transactional(
function () use ($entry, $object) {
$this->getFeedManager()->delete($entry);
if ($object) {
$this->getContentManager()->deleteObject($object);
}
}
);
$this->getLoggingService()->createLog($object, $id);
// send back redirect
return $this->jsonView([
'redirect' => true,
]);
}
return $this->ajax([
'entry' => $entry,
'form' => $form->createView(),
]);
}
/**
* @param string $entry
* @return AjaxHtmlView
*
* @Route(
* "/{entry}/preview",
* name = self::ROUTES__PREVIEW,
* requirements = {
* "entry" = "%app.routing.regexes.ulid%",
* },
* )
*/
public function previewAction(string $entry): AjaxHtmlView
{
$entity = $this->getEntityManager()->getRepository(AbstractContentEntry::class)->find($entry);
if ( ! $entity) {
$entity = $this->getEntityManager()->getRepository(Broadcast::class)->find($entry);
}
if ( ! $entity) {
throw new NotFoundHttpException();
}
return $this->ajax([
'entry' => $entity,
]);
}
/**
* @param Request $request
* @param int $pagination
* @return AbstractHtmlView|RedirectResponse
*
* @Route(
* "/drafts/list",
* name = self::ROUTES__DRAFTS__LIST,
* )
*
* @Route(
* "/drafts/list/{pagination}",
* name = self::ROUTES__DRAFTS__LIST,
* requirements = {
* "pagination" = "[1-9]\d*",
* },
* defaults = {
* "pagination" = 0,
* },
* )
*/
public function draftsListAction(Request $request, int $pagination)
{
// search form logic
$this->handleSearch(
$form = $this->createForm(
FeedSearchForm::class,
$search = (new FeedSearch())
->setUser($this->getCurrentUser())
->setPast(true)
->setCutoff(DateTimeUtils::max())
->setVisibility(ObjectInterface::VISIBILITIES__UNPUBLISHED)
),
);
// querying
$drafts = $this->getEntityManager()->getRepository(AbstractContentEntry::class)->findBySearch(
$search,
self::SEARCH_PAGING.
Pagination::offset($pagination, self::SEARCH_PAGING),
);
// determine if we are out of bounds on the pagination
if (Pagination::outOfBounds($drafts, $pagination, self::SEARCH_PAGING)) {
return $this->redirectToRoute(self::ROUTES__FEEDS__CALENDAR, array_merge(
$request->query->all(),
['pagination' => Pagination::maxPage($drafts, self::SEARCH_PAGING)]
));
}
return $this->html([
'pagination' => Pagination::controller(
$drafts,
count($drafts),
$pagination,
self::SEARCH_PAGING
),
'form' => $form->createView(),
'drafts' => $drafts,
'isGrantedShare' => ! empty($this->contentSchoolProvider->getSchools()),
]);
}
/**
* @return AbstractHtmlView|RedirectResponse
*
* @Route(
* "/drafts/{draft}/update",
* name = self::ROUTES__DRAFTS__UPDATE,
* requirements = {
* "draft" = "%app.routing.regexes.ulid%",
* },
* )
* @ParamConverter(
* "draft",
* class = AbstractContentEntry::class,
* )
*/
public function draftsUpdateAction(AbstractContentEntry $draft)
{
// AUDIT
$this->denyAccessUnlessGranted(
[
sprintf('campussuite.cms.container.%s.manage', $draft->getDepartment()->getType()),
'campussuite.cms.module.manage',
],
[$draft, $draft->getDepartment()],
);
// make sure the entry is not published
if ($draft->isPublished()) {
throw new \RuntimeException();
}
// create the form
$form = $this->createForm(PostForm::class);
// handle submission
if ($this->handleForm($form)) {
// we are always creating a new item, publishing is optional and is checked later
$object = $this->getPostLogic()->init(
$form->getData()['type'],
$form->getData()
);
// determine if we are publishing and handle it if so
if ($form->get('action')->getData() === 'publish') {
$this->getContentManager()->publishDraft($object);
}
$this->getLoggingService()->createLog($object);
return $this->jumpOrRedirectToRoute(self::ROUTES__MAIN);
}
return $this->html([
'form' => $form->createView(),
]);
}
/**
* @param Request $request
* @param AbstractContentEntry $draft
* @return AjaxHtmlView|JsonView
*
* @Route(
* "/drafts/{draft}/delete",
* name = self::ROUTES__DRAFTS__DELETE,
* requirements = {
* "draft" = "%app.routing.regexes.ulid%",
* },
* )
* @ParamConverter(
* "draft",
* class = AbstractContentEntry::class,
* )
*/
public function draftDeleteAction(Request $request, AbstractContentEntry $draft)
{
// AUDIT
$this->denyAccessUnlessGranted(
[
sprintf('campussuite.cms.container.%s.manage', $draft->getDepartment()->getType()),
'campussuite.cms.module.manage',
],
[$draft, $draft->getDepartment()],
);
// make sure it is ajax
if ( ! $request->isXmlHttpRequest()) {
throw new NotFoundHttpException();
}
// make sure the entry is not published
if ($draft->isPublished()) {
throw new \RuntimeException();
}
// make a form for submission purposes
$form = $this->createForm(DummyForm::class);
// handle submission
if ($this->handleForm($form)) {
// delete the entry
$object = $draft->getObject();
$id = $object ? $object->getId() : null;
$this->getEntityManager()->transactional(
function () use ($draft, $object) {
$this->getFeedManager()->delete($draft);
if ($object) {
$this->getContentManager()->deleteObject($object);
}
}
);
if ($id) {
$this->getLoggingService()->createLog($object, $id);
}
// send back redirect
return $this->jsonView([
'redirect' => true,
]);
}
return $this->ajax([
'draft' => $draft,
'form' => $form->createView(),
]);
}
/**
* @param Request $request
* @param AbstractContentEntry $entry
* @return JsonView
*
* @Route(
* "/pin/{entry}",
* name = self::ROUTES__PIN,
* methods = {"POST"},
* requirements = {
* "entry" = "%app.routing.regexes.ulid%",
* },
* )
* @ParamConverter(
* "entry",
* class = AbstractContentEntry::class,
* )
*/
public function pinAction(Request $request, AbstractContentEntry $entry): JsonView
{
// AUDIT
// $this->denyAccessUnlessGranted(
// [
// sprintf('campussuite.cms.container.%s.manage', $entry->getDepartment()->getType()),
// 'campussuite.cms.module.manage',
// ],
// [$entry, $entry->getDepartment()],
// );
$this->denyAccessUnlessGranted(
'app.feed.pin',
[$entry, $entry->getDepartment()]
);
// make sure it is ajax
if ( ! $request->isXmlHttpRequest()) {
throw new NotFoundHttpException();
}
// flip the pinned state
$this->getEntityManager()->save(
$entry
->setPinnedAt(
$entry->isPinned()
? null
: DateTimeUtils::now()
)
->setBoosted($entry->isPinned() || ! $entry instanceof ContentEventEntry)
);
$this->getLoggingService()->createLog($entry->getObject());
return $this->jsonView([
'entry' => $entry->getId(),
'pinned' => $entry->isPinned(),
]);
}
/**
* @param Request $request
* @param AbstractContentEntry|null $entry
* @return AjaxHtmlView
*
* @Route(
* "/create/review",
* name = self::ROUTES__CREATE_REVIEW,
* methods = {"POST"},
* )
* @Route(
* "/{entry}/update/review",
* name = self::ROUTES__UPDATE_REVIEW,
* methods = {"POST"},
* requirements = {
* "entry" = "%app.routing.regexes.ulid%",
* },
* )
* @ParamConverter(
* "entry",
* class = AbstractContentEntry::class,
* )
*/
public function reviewAction(Request $request, ?AbstractContentEntry $entry = null): AjaxHtmlView
{
// make sure it is ajax
if ( ! $request->isXmlHttpRequest()) {
throw new NotFoundHttpException();
}
$formData = $request->request->get('post_form');
// resolve the school
if ($entry && ($object = $entry->getObject())) {
// AUDIT
$this->denyAccessUnlessGranted(
[
sprintf('campussuite.cms.container.%s.manage', $entry->getDepartment()->getType()),
'campussuite.cms.module.manage',
],
[$entry, $entry->getDepartment()],
);
$school = $this->getResolverManager()->getSchoolResolver()->resolveSchoolByDepartment(
$object->getDepartment(),
);
$mainForm = $this->createForm(
ObjectForm::map($object),
$object,
[
'boosting' => false,
'pinning' => false,
]
);
$formName = $mainForm->getName();
// submit "new" form data
$mainForm->submit($request->request->get($formName));
$reviewData = [
'review' => $object->getReview()
];
$isEventType = $object instanceof EventObject;
} else if ( ! empty($formData['school'])) {
$school = $this->getEntityManager()->getRepository(School::class)->find(
(int)$formData['school']
);
if ( ! $school) {
throw new \RuntimeException();
}
// AUDIT
$cls = AbstractEntry::TYPE_CLASSES[$formData['type']];
$this->denyAccessUnlessGranted(
[
sprintf('campussuite.cms.container.%s.manage', $school->getDepartment()->getType()),
'campussuite.cms.module.manage',
],
[new $cls(), $school->getDepartment()],
);
$formName = 'post_form';
$mainForm = $this->createForm(
PostForm::class,
null,
[
'schools' => [$school],
]
);
$mainForm->submit($formData);
$reviewData = [
'review' => [
'scheduled' => false,
'channels' => ChannelEnum::WEBSITE | ChannelEnum::APP,
'websiteLevel' => AlertData::LEVELS__INFORMATIVE,
'websiteBehavior' => AlertData::BEHAVIORS__NONE
],
];
$isEventType = isset($formData['type']) && $formData['type'] === AbstractEntry::TYPES__EVENT;
} else {
throw new \LogicException();
}
if ( ! $school) {
throw new \RuntimeException();
}
$form = $this
->createNamedBuilder($formName, $reviewData)
->add('review', PostReviewForm::class)
->getForm();
return $this->ajax([
'school' => $school,
'entry' => $entry,
'isEventType' => $isEventType,
'form' => $form->createView(),
'mainForm' => $mainForm->createView(),
'mainFormData' => $mainForm->getData(),
]);
}
/**
* @return PostLogic|object
*/
private function getPostLogic(): PostLogic
{
return $this->get(__METHOD__);
}
}