getSession()->get('mautic.'.$this->getSessionBase().'.page', 1); $returnUrl = $this->generateUrl($this->getIndexRoute(), ['page' => $page]); $flashes = []; $postActionVars = [ 'returnUrl' => $returnUrl, 'viewParameters' => ['page' => $page], 'contentTemplate' => $this->getControllerBase().'::'.$this->getPostActionControllerAction('batchDelete').'Action', 'passthroughVars' => [ 'mauticContent' => $this->getJsLoadMethodPrefix(), ], ]; if ('POST' == $request->getMethod()) { $model = $this->getModel($this->getModelName()); $ids = json_decode($request->query->get('ids', '')); $deleteIds = []; // Loop over the IDs to perform access checks pre-delete foreach ($ids as $objectId) { $entity = $model->getEntity($objectId); if (null === $entity) { $flashes[] = [ 'type' => 'error', 'msg' => $this->getTranslatedString('error.notfound'), 'msgVars' => ['%id%' => $objectId], ]; } elseif (!$this->checkActionPermission('batchDelete', $entity)) { $flashes[] = $this->accessDenied(true); } elseif ($model->isLocked($entity)) { $flashes[] = $this->isLocked($postActionVars, $entity, $this->getModelName(), true); } else { $deleteIds[] = $objectId; } } // Delete everything we are able to if (!empty($deleteIds)) { $entities = $model->deleteEntities($deleteIds); $flashes[] = [ 'type' => 'notice', 'msg' => $this->getTranslatedString('notice.batch_deleted'), 'msgVars' => [ '%count%' => count($entities), ], ]; } } // else don't do anything return $this->postActionRedirect( $this->getPostActionRedirectArguments( array_merge( $postActionVars, [ 'flashes' => $flashes, ] ), 'batchDelete' ) ); } /** * Modify entity prior to persisting or perform custom validation on the form. * * @return mixed Whatever is returned will be passed into afterEntitySave; pass false to fail validation */ protected function beforeEntitySave($entity, Form $form, $action, $objectId = null, $isClone = false): bool { return true; } /** * Do anything necessary before the form is checked for POST and processed. */ protected function beforeFormProcessed($entity, Form $form, $action, $isPost, $objectId = null, $isClone = false) { } /** * @return bool|mixed */ protected function checkActionPermission($action, $entity = null, $objectId = null) { $permissionUser = 0; if ($entity) { $permissionUser = method_exists($entity, 'getPermissionUser') ? $entity->getPermissionUser() : $entity->getCreatedBy(); } if ($entity) { return match ($action) { 'new' => $this->security->isGranted($this->getPermissionBase().':create'), 'view', 'index' => ($entity) ? $this->security->hasEntityAccess( $this->getPermissionBase().':viewown', $this->getPermissionBase().':viewother', $permissionUser ) : $this->security->isGranted($this->getPermissionBase().':view'), 'clone' => $this->security->isGranted($this->getPermissionBase().':create') && $this->security->hasEntityAccess( $this->getPermissionBase().':viewown', $this->getPermissionBase().':viewother', $permissionUser ), 'delete', 'batchDelete' => $this->security->hasEntityAccess( $this->getPermissionBase().':deleteown', $this->getPermissionBase().':deleteother', $permissionUser ), default => $this->security->hasEntityAccess( $this->getPermissionBase().':'.$action.'own', $this->getPermissionBase().':'.$action.'other', $permissionUser ), }; } else { return match ($action) { 'new' => $this->security->isGranted($this->getPermissionBase().':create'), 'view', 'index' => $this->security->isGranted($this->getPermissionBase().':view'), 'clone' => $this->security->isGranted($this->getPermissionBase().':create') && $this->security->isGranted($this->getPermissionBase().':view'), 'delete', 'batchDelete' => $this->security->isGranted($this->getPermissionBase().':delete'), default => $this->security->isGranted($this->getPermissionBase().':'.$action), }; } } /** * Clone an entity. * * @return array|\Symfony\Component\HttpFoundation\JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse */ protected function cloneStandard(Request $request, $objectId) { $model = $this->getModel($this->getModelName()); $entity = $model->getEntity($objectId); if (null != $entity) { if (!$this->checkActionPermission('clone', $entity)) { return $this->accessDenied(); } $newEntity = clone $entity; if ($arguments = $this->afterEntityClone($newEntity, $entity)) { array_unshift($arguments, $request); return call_user_func_array([$this, 'editAction'], $arguments); } else { return $this->editAction($request, $newEntity, true); } } return $this->newAction($request); } /** * Deletes the entity. * * @param int $objectId * * @return \Symfony\Component\HttpFoundation\JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse */ protected function deleteStandard(Request $request, $objectId) { $page = $request->getSession()->get('mautic.'.$this->getSessionBase().'.page', 1); $returnUrl = $this->generateUrl($this->getIndexRoute(), ['page' => $page]); $flashes = []; $model = $this->getModel($this->getModelName()); $entity = $model->getEntity($objectId); $postActionVars = [ 'returnUrl' => $returnUrl, 'viewParameters' => ['page' => $page], 'contentTemplate' => $this->getControllerBase().'::'.$this->getPostActionControllerAction('delete').'Action', 'passthroughVars' => [ 'mauticContent' => $this->getJsLoadMethodPrefix(), ], 'entity' => $entity, ]; if ('POST' == $request->getMethod()) { if (null === $entity) { $flashes[] = [ 'type' => 'error', 'msg' => $this->getTranslatedString('error.notfound'), 'msgVars' => ['%id%' => $objectId], ]; } elseif (!$this->checkActionPermission('delete', $entity)) { return $this->accessDenied(); } elseif ($model->isLocked($entity)) { return $this->isLocked($postActionVars, $entity, $this->getModelName()); } $model->deleteEntity($entity); $identifier = $this->translator->trans($entity->getName()); $flashes[] = [ 'type' => 'notice', 'msg' => 'mautic.core.notice.deleted', 'msgVars' => [ '%name%' => $identifier, '%id%' => $objectId, ], ]; } // else don't do anything return $this->postActionRedirect( $this->getPostActionRedirectArguments( array_merge( $postActionVars, [ 'flashes' => $flashes, ] ), 'delete' ) ); } /** * @param bool $ignorePost * * @return \Symfony\Component\HttpFoundation\JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse|Response * * @throws \Exception */ protected function editStandard(Request $request, $objectId, $ignorePost = false) { $isClone = false; $model = $this->getModel($this->getModelName()); if (!$model instanceof FormModel) { throw new \Exception($model::class.' must extend '.FormModel::class); } $entity = $this->getFormEntity('edit', $objectId, $isClone); // set the return URL $returnUrl = $this->generateUrl($this->getIndexRoute()); $page = $request->getSession()->get('mautic.'.$this->getSessionBase().'.page', 1); $viewParameters = ['page' => $page]; $template = $this->getControllerBase().'::'.$this->getPostActionControllerAction('edit').'Action'; $postActionVars = [ 'returnUrl' => $returnUrl, 'viewParameters' => $viewParameters, 'contentTemplate' => $template, 'passthroughVars' => [ 'mauticContent' => $this->getJsLoadMethodPrefix(), ], 'entity' => $entity, ]; // form not found if (null === $entity) { return $this->postActionRedirect( $this->getPostActionRedirectArguments( array_merge( $postActionVars, [ 'flashes' => [ [ 'type' => 'error', 'msg' => $this->getTranslatedString('error.notfound'), 'msgVars' => ['%id%' => $objectId], ], ], ] ), 'edit' ) ); } elseif ((!$isClone && !$this->checkActionPermission('edit', $entity)) || ($isClone && !$this->checkActionPermission('create'))) { // deny access if the entity is not a clone and don't have permission to edit or is a clone and don't have permission to create return $this->accessDenied(); } elseif (!$isClone && $model->isLocked($entity)) { // deny access if the entity is locked return $this->isLocked($postActionVars, $entity, $this->getModelName()); } $options = $this->getEntityFormOptions(); $action = $this->generateUrl($this->getActionRoute(), ['objectAction' => 'edit', 'objectId' => $objectId]); $form = $model->createForm($entity, $this->formFactory, $action, $options); $isPost = !$ignorePost && 'POST' == $request->getMethod(); $this->beforeFormProcessed($entity, $form, 'edit', $isPost, $objectId, $isClone); $this->setOptimisticLockVersion($entity, $form); // /Check for a submitted form and process it if ($isPost) { $valid = false; if (!$cancelled = $this->isFormCancelled($form)) { if ($valid = $this->isFormValid($form)) { if ($valid = $this->checkOptimisticLockVersion($entity, $form, $isClone) && $this->beforeEntitySave($entity, $form, 'edit', $objectId, $isClone)) { $model->saveEntity($entity, $this->getFormButton($form, ['buttons', 'save'])->isClicked()); $this->afterEntitySave($entity, $form, 'edit', $valid); $this->addFlashMessage( 'mautic.core.notice.updated', [ '%name%' => $entity->getName(), '%menu_link%' => $this->getIndexRoute(), '%url%' => $this->generateUrl( $this->getActionRoute(), [ 'objectAction' => 'edit', 'objectId' => $entity->getId(), ] ), ] ); if ($entity->getId() !== $objectId) { // No longer a clone - this is important for Apply $objectId = $entity->getId(); } if (!$this->isFormApplied($form) && method_exists($this, 'viewAction')) { $viewParameters = ['objectId' => $objectId, 'objectAction' => 'view']; $returnUrl = $this->generateUrl($this->getActionRoute(), $viewParameters); $postActionVars['contentTemplate'] = $this->getControllerBase().'::viewAction'; } } $this->afterFormProcessed($valid, $entity, $form, 'edit', $isClone); } } else { if (!$isClone) { // unlock the entity $model->unlockEntity($entity); } $returnUrl = $this->generateUrl($this->getIndexRoute(), $viewParameters); } if ($cancelled || ($valid && !$this->isFormApplied($form))) { return $this->postActionRedirect( $this->getPostActionRedirectArguments( array_merge( $postActionVars, [ 'returnUrl' => $returnUrl, 'viewParameters' => $viewParameters, ] ), 'edit' ) ); } elseif ($valid) { // Rebuild the form with new action so that apply doesn't keep creating a clone $action = $this->generateUrl($this->getActionRoute(), ['objectAction' => 'edit', 'objectId' => $entity->getId()]); $form = $model->createForm($entity, $this->formFactory, $action); $this->beforeFormProcessed($entity, $form, 'edit', false, $isClone); $this->setOptimisticLockVersion($entity, $form); } } elseif (!$isClone) { $model->lockEntity($entity); } $delegateArgs = [ 'viewParameters' => [ 'permissionBase' => $this->getPermissionBase(), 'mauticContent' => $this->getJsLoadMethodPrefix(), 'actionRoute' => $this->getActionRoute(), 'indexRoute' => $this->getIndexRoute(), 'tablePrefix' => $model->getRepository()->getTableAlias(), 'modelName' => $this->getModelName(), 'translationBase' => $this->getTranslationBase(), 'tmpl' => $request->isXmlHttpRequest() ? $request->get('tmpl', 'index') : 'index', 'entity' => $entity, 'form' => $this->getFormView($form, 'edit'), ], 'contentTemplate' => $this->getTemplateName('form.html.twig'), 'passthroughVars' => [ 'mauticContent' => $this->getJsLoadMethodPrefix(), 'route' => $this->generateUrl( $this->getActionRoute(), [ 'objectAction' => 'edit', 'objectId' => $entity->getId(), ] ), 'validationError' => $this->getFormErrorForBuilder($form), ], 'objectId' => $objectId, 'entity' => $entity, ]; return $this->delegateView( $this->getViewArguments($delegateArgs, 'edit') ); } /** * Get action route. * * @return string */ protected function getActionRoute() { return 'mautic_'.str_replace('mautic_', '', $this->getRouteBase().'_action'); } /** * Get controller base if different than MauticCoreBundle:Standard. * * @return string */ protected function getControllerBase() { return static::class; } /** * @param bool $isClone */ protected function getFormEntity($action, &$objectId = null, &$isClone = false) { $model = $this->getModel($this->getModelName()); switch ($action) { case 'new': $entity = $model->getEntity(); break; case 'edit': /* @var $entity FormEntity */ if (is_object($objectId)) { $entity = $objectId; $isClone = true; $objectId = (!empty($this->sessionId)) ? $this->sessionId : 'mautic_'.sha1(uniqid(mt_rand(), true)); } elseif (str_contains($objectId, 'mautic_')) { $isClone = true; $entity = $model->getEntity(); } else { $entity = $model->getEntity($objectId); } break; } return $entity; } /** * Set custom form themes, etc. */ protected function getFormView(FormInterface $form, $action): FormView { return $form->createView(); } /** * Get items for index list. */ protected function getIndexItems($start, $limit, $filter, $orderBy, $orderByDir, array $args = []) { $items = $this->getModel($this->getModelName())->getEntities( array_merge( [ 'start' => $start, 'limit' => $limit, 'filter' => $filter, 'orderBy' => $orderBy, 'orderByDir' => $orderByDir, ], $args ) ); $count = count($items); return [$count, $items]; } /** * Get index route. * * @return string */ protected function getIndexRoute() { return 'mautic_'.str_replace('mautic_', '', $this->getRouteBase().'_index'); } /** * Get the name of the JS onLoad and onUnload methods for ajax. * * @return mixed */ protected function getJsLoadMethodPrefix() { return $this->getModelName(); } /** * Get the permission base from the model. * * @return string */ protected function getPermissionBase() { return $this->getModel($this->getModelName())->getPermissionBase(); } /** * Amend the parameters sent through postActionRedirect. */ protected function getPostActionRedirectArguments(array $args, $action): array { return $args; } /** * @return string */ protected function getPostActionControllerAction($action) { return 'index'; } /** * Get the route base for getIndexRoute() and getActionRoute() if they do not meet the mautic_*_index and mautic_*_action standards. * * @return mixed */ protected function getRouteBase() { return $this->getModelName(); } /** * Provide the name of the column which is used for default ordering. * * @return string */ protected function getDefaultOrderColumn() { return 'name'; } /** * Provide the direction for default ordering. * * @return string */ protected function getDefaultOrderDirection() { return 'ASC'; } /** * @return mixed */ protected function getSessionBase($objectId = null) { $base = $this->getModelName(); if (null !== $objectId) { $base .= '.'.$objectId; } return $base; } /** * Get the template file. * * @return string */ protected function getTemplateName($file) { $namespaces = [ $this->getTemplateBase(), '@MauticCore/Standard', ]; foreach ($namespaces as $namespace) { if ($this->get('twig')->getLoader()->exists($namespace.'/'.$file)) { return $namespace.'/'.$file; } } throw new \Exception("Template {$file} not found in any of the following places: ".implode(', ', $namespaces).'.'); } /** * Get template base different than @MauticCore/Standard. * * @return string */ protected function getTemplateBase() { return '@MauticCore/Standard'; } /** * Get custom or core translation. * * @return string */ protected function getTranslatedString($string) { return $this->translator->hasId($this->getTranslationBase().'.'.$string) ? $this->getTranslationBase() .'.'.$string : 'mautic.core.'.$string; } /** * Get the base to override core translation keys. * * @return string */ protected function getTranslationBase() { return 'mautic.'.$this->getModelName(); } /** * Amend the parameters sent through delegateView. */ protected function getViewArguments(array $args, $action): array { return $args; } /** * Return array of options for the form when it's being created. * * @return array */ protected function getEntityFormOptions() { return []; } /** * Return array of options update select response. * * @param string $updateSelect HTML id of the select * @param object $entity * @param string $nameMethod name of the entity method holding the name * @param string $groupMethod name of the entity method holding the select group * * @return array */ protected function getUpdateSelectParams($updateSelect, $entity, $nameMethod = 'getName', $groupMethod = 'getLanguage') { $options = [ 'updateSelect' => $updateSelect, 'id' => $entity->getId(), 'name' => $entity->$nameMethod(), ]; if ($groupMethod) { $options['group'] = $entity->$groupMethod(); } return $options; } /** * @param string $timezone * * @return array */ protected function getViewDateRange(Request $request, $objectId, $returnUrl, $timezone = 'local', &$dateRangeForm = null) { $name = $this->getSessionBase($objectId).'.view.daterange'; $method = ('POST' === $request->getMethod()) ? 'request' : 'query'; $dateRangeValues = $request->$method->get('daterange', $request->getSession()->get($name, [])); $request->getSession()->set($name, $dateRangeValues); $dateRangeForm = $this->formFactory->create(DateRangeType::class, $dateRangeValues, ['action' => $returnUrl]); $dateFrom = new \DateTime($dateRangeForm['date_from']->getData()); $dateFrom->setTime(0, 0, 0); $dateTo = new \DateTime($dateRangeForm['date_to']->getData()); $dateTo->setTime(24, 59, 59); if ('utc' === $timezone) { $dateFrom = clone $dateFrom; $dateFrom->setTimezone(new \DateTimeZone('utc')); $dateTo = clone $dateTo; $dateTo->setTimezone(new \DateTimeZone('utc')); } return [$dateFrom, $dateTo]; } /** * @param int $page */ protected function indexStandard(Request $request, $page = null): Response { // set some permissions $permissions = $this->security->isGranted( [ $this->getPermissionBase().':view', $this->getPermissionBase().':viewown', $this->getPermissionBase().':viewother', $this->getPermissionBase().':create', $this->getPermissionBase().':edit', $this->getPermissionBase().':editown', $this->getPermissionBase().':editother', $this->getPermissionBase().':delete', $this->getPermissionBase().':deleteown', $this->getPermissionBase().':deleteother', $this->getPermissionBase().':publish', $this->getPermissionBase().':publishown', $this->getPermissionBase().':publishother', ], 'RETURN_ARRAY', null, true ); if (!$this->checkActionPermission('index')) { return $this->accessDenied(); } $this->setListFilters(); $session = $request->getSession(); if (empty($page)) { $page = $session->get('mautic.'.$this->getSessionBase().'.page', 1); } // set limits $limit = $session->get('mautic.'.$this->getSessionBase().'.limit', $this->coreParametersHelper->get('default_pagelimit')); $start = (1 === $page) ? 0 : (($page - 1) * $limit); if ($start < 0) { $start = 0; } $search = $request->get('search', $session->get('mautic.'.$this->getSessionBase().'.filter', '')); $session->set('mautic.'.$this->getSessionBase().'.filter', $search); $filter = ['string' => $search, 'force' => []]; $model = $this->getModel($this->getModelName()); $repo = $model->getRepository(); if (!$permissions[$this->getPermissionBase().':viewother']) { $filter['force'][] = ['column' => $repo->getTableAlias().'.createdBy', 'expr' => 'eq', 'value' => $this->user->getId()]; } $orderBy = $session->get('mautic.'.$this->getSessionBase().'.orderby', $repo->getTableAlias().'.'.$this->getDefaultOrderColumn()); $orderByDir = $session->get('mautic.'.$this->getSessionBase().'.orderbydir', $this->getDefaultOrderDirection()); [$count, $items] = $this->getIndexItems($start, $limit, $filter, $orderBy, $orderByDir); if ($count && $count < ($start + 1)) { // the number of entities are now less then the current page so redirect to the last page $lastPage = (1 === $count) ? 1 : (((ceil($count / $limit)) ?: 1) ?: 1); $session->set('mautic.'.$this->getSessionBase().'.page', $lastPage); $returnUrl = $this->generateUrl($this->getIndexRoute(), ['page' => $lastPage]); return $this->postActionRedirect( $this->getPostActionRedirectArguments( [ 'returnUrl' => $returnUrl, 'viewParameters' => ['page' => $lastPage], 'contentTemplate' => $this->getControllerBase().'::'.$this->getPostActionControllerAction('index').'Action', 'passthroughVars' => [ 'mauticContent' => $this->getJsLoadMethodPrefix(), ], ], 'index' ) ); } // set what page currently on so that we can return here after form submission/cancellation $session->set('mautic.'.$this->getSessionBase().'.page', $page); $viewParameters = [ 'permissionBase' => $this->getPermissionBase(), 'mauticContent' => $this->getJsLoadMethodPrefix(), 'sessionVar' => $this->getSessionBase(), 'actionRoute' => $this->getActionRoute(), 'indexRoute' => $this->getIndexRoute(), 'tablePrefix' => $model->getRepository()->getTableAlias(), 'modelName' => $this->getModelName(), 'translationBase' => $this->getTranslationBase(), 'searchValue' => $search, 'items' => $items, 'totalItems' => $count, 'page' => $page, 'limit' => $limit, 'permissions' => $permissions, 'tmpl' => $request->get('tmpl', 'index'), ]; return $this->delegateView( $this->getViewArguments( [ 'viewParameters' => $viewParameters, 'contentTemplate' => $this->getTemplateName('list.html.twig'), 'passthroughVars' => [ 'mauticContent' => $this->getJsLoadMethodPrefix(), 'route' => $this->generateUrl($this->getIndexRoute(), ['page' => $page]), ], ], 'index' ) ); } /** * @return array|\Symfony\Component\HttpFoundation\JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse|Response * * @throws \Exception */ protected function newStandard(Request $request) { $entity = $this->getFormEntity('new'); if (!$this->checkActionPermission('new')) { return $this->accessDenied(); } $model = $this->getModel($this->getModelName()); if (!$model instanceof FormModel) { throw new \Exception($model::class.' must extend '.FormModel::class); } // set the page we came from $page = $request->getSession()->get('mautic.'.$this->getSessionBase().'.page', 1); $options = $this->getEntityFormOptions(); $action = $this->generateUrl($this->getActionRoute(), ['objectAction' => 'new']); $form = $model->createForm($entity, $this->formFactory, $action, $options); // /Check for a submitted form and process it $isPost = 'POST' === $request->getMethod(); $this->beforeFormProcessed($entity, $form, 'new', $isPost); if ($isPost) { $valid = false; if (!$cancelled = $this->isFormCancelled($form)) { if ($valid = $this->isFormValid($form)) { if ($valid = $this->beforeEntitySave($entity, $form, 'new')) { $model->saveEntity($entity); $this->afterEntitySave($entity, $form, 'new', $valid); if (method_exists($this, 'viewAction')) { $viewParameters = ['objectId' => $entity->getId(), 'objectAction' => 'view']; $returnUrl = $this->generateUrl($this->getActionRoute(), $viewParameters); $template = $this->getControllerBase().'::viewAction'; } else { $viewParameters = ['page' => $page]; $returnUrl = $this->generateUrl($this->getIndexRoute(), $viewParameters); $template = $this->getControllerBase().'::'.$this->getPostActionControllerAction('new').'Action'; } } } $this->afterFormProcessed($valid, $entity, $form, 'new'); } else { $viewParameters = ['page' => $page]; $returnUrl = $this->generateUrl($this->getIndexRoute(), $viewParameters); $template = $this->getControllerBase().'::'.$this->getPostActionControllerAction('new').'Action'; } $passthrough = [ 'mauticContent' => $this->getJsLoadMethodPrefix(), ]; if ($isInPopup = isset($form['updateSelect'])) { $template = false; $passthrough = array_merge( $passthrough, $this->getUpdateSelectParams($form['updateSelect']->getData(), $entity) ); } if ($cancelled || ($valid && !$this->isFormApplied($form))) { if ($isInPopup) { $passthrough['closeModal'] = true; } return $this->postActionRedirect( $this->getPostActionRedirectArguments( [ 'returnUrl' => $returnUrl, 'viewParameters' => $viewParameters, 'contentTemplate' => $template, 'passthroughVars' => $passthrough, 'entity' => $entity, ], 'new' ) ); } elseif ($valid && $this->isFormApplied($form)) { return $this->editAction($request, $entity->getId(), true); } } $delegateArgs = [ 'viewParameters' => [ 'permissionBase' => $this->getPermissionBase(), 'mauticContent' => $this->getJsLoadMethodPrefix(), 'actionRoute' => $this->getActionRoute(), 'indexRoute' => $this->getIndexRoute(), 'tablePrefix' => $model->getRepository()->getTableAlias(), 'modelName' => $this->getModelName(), 'translationBase' => $this->getTranslationBase(), 'tmpl' => $request->isXmlHttpRequest() ? $request->get('tmpl', 'index') : 'index', 'entity' => $entity, 'form' => $this->getFormView($form, 'new'), ], 'contentTemplate' => $this->getTemplateName('form.html.twig'), 'passthroughVars' => [ 'mauticContent' => $this->getJsLoadMethodPrefix(), 'route' => $this->generateUrl( $this->getActionRoute(), [ 'objectAction' => (!empty($valid) ? 'edit' : 'new'), // valid means a new form was applied 'objectId' => ($entity) ? $entity->getId() : 0, ] ), 'validationError' => $this->getFormErrorForBuilder($form), ], 'entity' => $entity, 'form' => $form, ]; return $this->delegateView( $this->getViewArguments($delegateArgs, 'new') ); } /** * @param string|null $name */ protected function setListFilters($name = null) { return parent::setListFilters($name ?: $this->getSessionBase()); } /** * @param string|null $logObject * @param string|null $logBundle * @param string|null $listPage * @param string $itemName * * @return array|\Symfony\Component\HttpFoundation\JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse|Response */ protected function viewStandard(Request $request, $objectId, $logObject = null, $logBundle = null, $listPage = null, $itemName = 'item') { $model = $this->getModel($this->getModelName()); $entity = $model->getEntity($objectId); if (null === $entity) { $page = $request->getSession()->get('mautic.'.$this->getSessionBase().'.page', 1); return $this->postActionRedirect( $this->getPostActionRedirectArguments( [ 'returnUrl' => $this->generateUrl($this->getIndexRoute(), ['page' => $page]), 'viewParameters' => ['page' => $page], 'contentTemplate' => $this->getControllerBase().'::'.$this->getPostActionControllerAction('view').'Action', 'passthroughVars' => [ 'mauticContent' => $this->getJsLoadMethodPrefix(), ], 'flashes' => [ [ 'type' => 'error', 'msg' => $this->getTranslatedString('error.notfound'), 'msgVars' => ['%id%' => $objectId], ], ], ], 'view' ) ); } elseif (!$this->checkActionPermission('view', $entity)) { return $this->accessDenied(); } $this->setListFilters(); // Audit log entries $logs = ($logObject) ? $this->getModel('core.auditlog')->getLogForObject($logObject, $objectId, $entity->getDateAdded(), 10, $logBundle) : []; // Generate route $routeVars = [ 'objectAction' => 'view', 'objectId' => $entity->getId(), ]; if (null !== $listPage) { $routeVars['listPage'] = $listPage; } $route = $this->generateUrl($this->getActionRoute(), $routeVars); $delegateArgs = [ 'viewParameters' => [ $itemName => $entity, 'logs' => $logs, 'tmpl' => $request->isXmlHttpRequest() ? $request->get('tmpl', 'index') : 'index', 'permissions' => $this->security->isGranted( [ $this->getPermissionBase().':view', $this->getPermissionBase().':viewown', $this->getPermissionBase().':viewother', $this->getPermissionBase().':create', $this->getPermissionBase().':edit', $this->getPermissionBase().':editown', $this->getPermissionBase().':editother', $this->getPermissionBase().':delete', $this->getPermissionBase().':deleteown', $this->getPermissionBase().':deleteother', $this->getPermissionBase().':publish', $this->getPermissionBase().':publishown', $this->getPermissionBase().':publishother', ], 'RETURN_ARRAY', null, true ), ], 'contentTemplate' => $this->getTemplateName('details.html.twig'), 'passthroughVars' => [ 'mauticContent' => $this->getJsLoadMethodPrefix(), 'route' => $route, ], 'objectId' => $objectId, 'entity' => $entity, ]; return $this->delegateView( $this->getViewArguments($delegateArgs, 'view') ); } protected function getDataForExport(AbstractCommonModel $model, array $args, callable $resultsCallback = null, ?int $start = 0) { return parent::getDataForExport($model, $args, $resultsCallback, $start); } /** * If the $entity supports optimistic locking, entity's current version is set to the $form for a later comparison. * * @param mixed $entity */ protected function setOptimisticLockVersion($entity, FormInterface $form): void { if (!$entity instanceof OptimisticLockInterface) { return; } $form->get($entity->getVersionField()) ->setData($entity->getVersion()); } /** * If the $entity supports optimistic locking, entity's current version is compared to the value in the $form. * * @param mixed $entity */ protected function checkOptimisticLockVersion($entity, FormInterface $form, bool $isClone): bool { if ($isClone || !$entity instanceof OptimisticLockInterface) { return true; } $version = (int) $form->get($entity->getVersionField()) ->getData(); if (!$version) { throw new \LogicException(sprintf('A version is required for entities that implement "%s"', OptimisticLockInterface::class)); } if ($version !== $entity->getVersion()) { $form->addError( new FormError( $this->translator->trans('mautic.core.optimistic_lock.changed_by_someone_else_error') ) ); return false; } $entity->markForVersionIncrement(); return true; } }