Initial commit: CloudOps infrastructure platform
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Controller;
|
||||
|
||||
use Mautic\CoreBundle\Controller\AjaxController as CommonAjaxController;
|
||||
use Mautic\ReportBundle\Model\ReportModel;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class AjaxController extends CommonAjaxController
|
||||
{
|
||||
/**
|
||||
* Get updated data for context.
|
||||
*/
|
||||
public function getSourceDataAction(Request $request): \Symfony\Component\HttpFoundation\JsonResponse
|
||||
{
|
||||
$model = $this->getModel('report');
|
||||
\assert($model instanceof ReportModel);
|
||||
$context = $request->get('context');
|
||||
|
||||
$graphs = $model->getGraphList($context);
|
||||
$columns = $model->getColumnList($context);
|
||||
$filters = $model->getFilterList($context);
|
||||
|
||||
return $this->sendJsonResponse(
|
||||
[
|
||||
'columns' => $columns->choiceHtml,
|
||||
'columnDefinitions' => $columns->definitions,
|
||||
'filters' => $filters->choiceHtml,
|
||||
'filterDefinitions' => $filters->definitions,
|
||||
'filterOperators' => $filters->operatorHtml,
|
||||
'graphs' => $graphs->choiceHtml,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Controller\Api;
|
||||
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use Mautic\ApiBundle\Controller\CommonApiController;
|
||||
use Mautic\ApiBundle\Helper\EntityResultHelper;
|
||||
use Mautic\CoreBundle\Factory\ModelFactory;
|
||||
use Mautic\CoreBundle\Helper\AppVersion;
|
||||
use Mautic\CoreBundle\Helper\CoreParametersHelper;
|
||||
use Mautic\CoreBundle\Helper\DateTimeHelper;
|
||||
use Mautic\CoreBundle\Helper\UserHelper;
|
||||
use Mautic\CoreBundle\Security\Exception\PermissionException;
|
||||
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
|
||||
use Mautic\CoreBundle\Translation\Translator;
|
||||
use Mautic\ReportBundle\Entity\Report;
|
||||
use Mautic\ReportBundle\Model\ReportModel;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\Form\FormFactoryInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\RouterInterface;
|
||||
|
||||
/**
|
||||
* @extends CommonApiController<Report>
|
||||
*/
|
||||
class ReportApiController extends CommonApiController
|
||||
{
|
||||
/**
|
||||
* @var ReportModel|null
|
||||
*/
|
||||
protected $model;
|
||||
|
||||
public function __construct(CorePermissions $security, Translator $translator, EntityResultHelper $entityResultHelper, RouterInterface $router, FormFactoryInterface $formFactory, AppVersion $appVersion, RequestStack $requestStack, ManagerRegistry $doctrine, ModelFactory $modelFactory, EventDispatcherInterface $dispatcher, CoreParametersHelper $coreParametersHelper, protected UserHelper $userHelper)
|
||||
{
|
||||
$reportModel = $modelFactory->getModel('report');
|
||||
\assert($reportModel instanceof ReportModel);
|
||||
|
||||
$this->model = $reportModel;
|
||||
$this->entityClass = Report::class;
|
||||
$this->entityNameOne = 'report';
|
||||
$this->entityNameMulti = 'reports';
|
||||
$this->serializerGroups = ['reportList', 'reportDetails'];
|
||||
|
||||
parent::__construct($security, $translator, $entityResultHelper, $router, $formFactory, $appVersion, $requestStack, $doctrine, $modelFactory, $dispatcher, $coreParametersHelper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains a compiled report.
|
||||
*
|
||||
* @param int $id Report ID
|
||||
*/
|
||||
public function getEntityAction(Request $request, $id): Response
|
||||
{
|
||||
try {
|
||||
if (!$this->security->isGranted($this->permissionBase.':view')) {
|
||||
return $this->accessDenied();
|
||||
}
|
||||
} catch (PermissionException $e) {
|
||||
return $this->accessDenied($e->getMessage());
|
||||
}
|
||||
|
||||
$entity = $this->model->getEntity($id);
|
||||
|
||||
if (!$entity instanceof $this->entityClass) {
|
||||
return $this->notFound();
|
||||
}
|
||||
|
||||
if (
|
||||
$this->security->checkPermissionExists($this->permissionBase.':viewother')
|
||||
&& !$this->security->isGranted($this->permissionBase.':viewother')
|
||||
&& $entity->getCreatedBy() !== $this->userHelper->getUser()->getId()
|
||||
) {
|
||||
return $this->accessDenied();
|
||||
}
|
||||
|
||||
$reportData = $this->model->getReportData($entity, $this->formFactory, $this->getOptionsFromRequest($request));
|
||||
|
||||
// Unset keys that we don't need to send back
|
||||
foreach (['graphs', 'contentTemplate', 'columns'] as $key) {
|
||||
unset($reportData[$key]);
|
||||
}
|
||||
|
||||
// Include report metadata
|
||||
$reportData[$this->entityNameOne] = $entity;
|
||||
|
||||
return $this->handleView(
|
||||
$this->view($reportData, Response::HTTP_OK)
|
||||
);
|
||||
}
|
||||
|
||||
public function getReportAction(Request $request, int $id): Response
|
||||
{
|
||||
return $this->getEntityAction($request, $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is careful to add new options from the request to keep BC.
|
||||
* It originally loaded all rows without any filter or pagination applied.
|
||||
*/
|
||||
private function getOptionsFromRequest(Request $request): array
|
||||
{
|
||||
$options = ['paginate'=> false, 'ignoreGraphData' => true];
|
||||
|
||||
if ($request->query->has('dateFrom')) {
|
||||
$options['dateFrom'] = DateTimeHelper::setTimeIfMissing($request->query->get('dateFrom'), '00:00:00');
|
||||
}
|
||||
|
||||
if ($request->query->has('dateTo')) {
|
||||
$options['dateTo'] = DateTimeHelper::setTimeIfMissing($request->query->get('dateTo'), '23:59:59');
|
||||
}
|
||||
|
||||
if ($request->query->has('page')) {
|
||||
$options['page'] = $request->query->getInt('page');
|
||||
$options['paginate'] = true;
|
||||
}
|
||||
|
||||
if ($request->query->has('limit')) {
|
||||
$options['limit'] = $request->query->getInt('limit');
|
||||
$options['paginate'] = true;
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,925 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Controller;
|
||||
|
||||
use Mautic\CoreBundle\Controller\FormController;
|
||||
use Mautic\CoreBundle\EventListener\ReportSubscriber;
|
||||
use Mautic\CoreBundle\Factory\PageHelperFactoryInterface;
|
||||
use Mautic\CoreBundle\Form\Type\DateRangeType;
|
||||
use Mautic\CoreBundle\Helper\DateTimeHelper;
|
||||
use Mautic\CoreBundle\Helper\InputHelper;
|
||||
use Mautic\ReportBundle\Crate\ReportDataResult;
|
||||
use Mautic\ReportBundle\Entity\Report;
|
||||
use Mautic\ReportBundle\Form\Type\DynamicFiltersType;
|
||||
use Mautic\ReportBundle\Model\ExportResponse;
|
||||
use Mautic\ReportBundle\Model\ReportModel;
|
||||
use Mautic\ReportBundle\Scheduler\Model\FileHandler;
|
||||
use Symfony\Component\HttpFoundation;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
|
||||
|
||||
class ReportController extends FormController
|
||||
{
|
||||
/**
|
||||
* @param int $page
|
||||
*
|
||||
* @return HttpFoundation\JsonResponse|HttpFoundation\RedirectResponse|Response
|
||||
*/
|
||||
public function indexAction(Request $request, PageHelperFactoryInterface $pageHelperFactory, $page = 1)
|
||||
{
|
||||
/* @type \Mautic\ReportBundle\Model\ReportModel $model */
|
||||
$model = $this->getModel('report');
|
||||
|
||||
// set some permissions
|
||||
$permissions = $this->security->isGranted(
|
||||
[
|
||||
'report:reports:viewown',
|
||||
'report:reports:viewother',
|
||||
'report:reports:create',
|
||||
'report:reports:editown',
|
||||
'report:reports:editother',
|
||||
'report:reports:deleteown',
|
||||
'report:reports:deleteother',
|
||||
'report:reports:publishown',
|
||||
'report:reports:publishother',
|
||||
],
|
||||
'RETURN_ARRAY'
|
||||
);
|
||||
|
||||
if (!$permissions['report:reports:viewown'] && !$permissions['report:reports:viewother']) {
|
||||
return $this->accessDenied();
|
||||
}
|
||||
|
||||
$this->setListFilters();
|
||||
|
||||
$pageHelper = $pageHelperFactory->make('mautic.report', $page);
|
||||
|
||||
$limit = $pageHelper->getLimit();
|
||||
$start = $pageHelper->getStart();
|
||||
$search = $request->get('search', $request->getSession()->get('mautic.report.filter', ''));
|
||||
$filter = ['string' => $search, 'force' => []];
|
||||
$request->getSession()->set('mautic.report.filter', $search);
|
||||
|
||||
if (!$permissions['report:reports:viewother']) {
|
||||
$filter['force'][] = ['column' => 'r.createdBy', 'expr' => 'eq', 'value' => $this->user->getId()];
|
||||
}
|
||||
|
||||
if (!$this->security->isAdmin()) {
|
||||
$filter['force'][] = ['column' => 'r.source', 'expr' => 'neq', 'value' => ReportSubscriber::CONTEXT_AUDIT_LOG];
|
||||
}
|
||||
|
||||
$orderBy = $request->getSession()->get('mautic.report.orderby', 'r.dateModified');
|
||||
$orderByDir = $request->getSession()->get('mautic.report.orderbydir', $this->getDefaultOrderDirection());
|
||||
|
||||
$reports = $model->getEntities(
|
||||
[
|
||||
'start' => $start,
|
||||
'limit' => $limit,
|
||||
'filter' => $filter,
|
||||
'orderBy' => $orderBy,
|
||||
'orderByDir' => $orderByDir,
|
||||
]
|
||||
);
|
||||
|
||||
$count = count($reports);
|
||||
if ($count && $count < ($start + 1)) {
|
||||
$lastPage = $pageHelper->countPage($count);
|
||||
$returnUrl = $this->generateUrl('mautic_report_index', ['page' => $lastPage]);
|
||||
$pageHelper->rememberPage($lastPage);
|
||||
|
||||
return $this->postActionRedirect(
|
||||
[
|
||||
'returnUrl' => $returnUrl,
|
||||
'viewParameters' => ['page' => $lastPage],
|
||||
'contentTemplate' => 'Mautic\ReportBundle\Controller\ReportController::indexAction',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_report_index',
|
||||
'mauticContent' => 'report',
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$pageHelper->rememberPage($page);
|
||||
|
||||
return $this->delegateView(
|
||||
[
|
||||
'viewParameters' => [
|
||||
'searchValue' => $search,
|
||||
'items' => $reports,
|
||||
'totalItems' => $count,
|
||||
'page' => $page,
|
||||
'limit' => $limit,
|
||||
'permissions' => $permissions,
|
||||
'model' => $model,
|
||||
'tmpl' => $request->isXmlHttpRequest() ? $request->get('tmpl', 'index') : 'index',
|
||||
'security' => $this->security,
|
||||
],
|
||||
'contentTemplate' => '@MauticReport/Report/list.html.twig',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_report_index',
|
||||
'mauticContent' => 'report',
|
||||
'route' => $this->generateUrl('mautic_report_index', ['page' => $page]),
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $objectId
|
||||
*/
|
||||
public function cloneAction(ReportModel $model, Request $request, $objectId): Response
|
||||
{
|
||||
$entity = $model->getEntity($objectId);
|
||||
|
||||
if ($entity) {
|
||||
if (!$this->security->isGranted('report:reports:create')
|
||||
|| !$this->security->hasEntityAccess(
|
||||
'report:reports:viewown',
|
||||
'report:reports:viewother',
|
||||
$entity->getCreatedBy()
|
||||
)
|
||||
) {
|
||||
return $this->accessDenied();
|
||||
}
|
||||
|
||||
$entity = clone $entity;
|
||||
$entity->setId(null);
|
||||
$entity->setIsPublished(false);
|
||||
}
|
||||
|
||||
return $this->newAction($model, $request, $entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the entity.
|
||||
*
|
||||
* @return array<string, string|array<string, string>>|bool|HttpFoundation\JsonResponse|HttpFoundation\RedirectResponse|Response
|
||||
*/
|
||||
public function deleteAction(Request $request, $objectId)
|
||||
{
|
||||
$page = $request->getSession()->get('mautic.report.page', 1);
|
||||
$returnUrl = $this->generateUrl('mautic_report_index', ['page' => $page]);
|
||||
$flashes = [];
|
||||
|
||||
$postActionVars = [
|
||||
'returnUrl' => $returnUrl,
|
||||
'viewParameters' => ['page' => $page],
|
||||
'contentTemplate' => 'Mautic\ReportBundle\Controller\ReportController::indexAction',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_report_index',
|
||||
'mauticContent' => 'report',
|
||||
],
|
||||
];
|
||||
|
||||
if (Request::METHOD_POST === $request->getMethod()) {
|
||||
$model = $this->getModel('report');
|
||||
\assert($model instanceof ReportModel);
|
||||
$entity = $model->getEntity($objectId);
|
||||
|
||||
$check = $this->checkEntityAccess(
|
||||
$postActionVars,
|
||||
$entity,
|
||||
$objectId,
|
||||
['report:reports:deleteown', 'report:reports:deleteother'],
|
||||
$model,
|
||||
'report'
|
||||
);
|
||||
if (true !== $check) {
|
||||
return $check;
|
||||
}
|
||||
|
||||
$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(
|
||||
array_merge(
|
||||
$postActionVars,
|
||||
[
|
||||
'flashes' => $flashes,
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a group of entities.
|
||||
*/
|
||||
public function batchDeleteAction(Request $request): Response
|
||||
{
|
||||
$page = $request->getSession()->get('mautic.report.page', 1);
|
||||
$returnUrl = $this->generateUrl('mautic_report_index', ['page' => $page]);
|
||||
$flashes = [];
|
||||
|
||||
$postActionVars = [
|
||||
'returnUrl' => $returnUrl,
|
||||
'viewParameters' => ['page' => $page],
|
||||
'contentTemplate' => 'Mautic\ReportBundle\Controller\ReportController::indexAction',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_report_index',
|
||||
'mauticContent' => 'report',
|
||||
],
|
||||
];
|
||||
|
||||
if (Request::METHOD_POST === $request->getMethod()) {
|
||||
$model = $this->getModel('report');
|
||||
\assert($model instanceof ReportModel);
|
||||
$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' => 'mautic.report.report.error.notfound',
|
||||
'msgVars' => ['%id%' => $objectId],
|
||||
];
|
||||
} elseif (!$this->security->hasEntityAccess(
|
||||
'report:reports:deleteown',
|
||||
'report:reports:deleteother',
|
||||
$entity->getCreatedBy()
|
||||
)
|
||||
) {
|
||||
$flashes[] = $this->accessDenied(true);
|
||||
} elseif ($model->isLocked($entity)) {
|
||||
$flashes[] = $this->isLocked($postActionVars, $entity, 'report', true);
|
||||
} else {
|
||||
$deleteIds[] = $objectId;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete everything we are able to
|
||||
if (!empty($deleteIds)) {
|
||||
$entities = $model->deleteEntities($deleteIds);
|
||||
|
||||
$flashes[] = [
|
||||
'type' => 'notice',
|
||||
'msg' => 'mautic.report.report.notice.batch_deleted',
|
||||
'msgVars' => [
|
||||
'%count%' => count($entities),
|
||||
],
|
||||
];
|
||||
}
|
||||
} // else don't do anything
|
||||
|
||||
return $this->postActionRedirect(
|
||||
array_merge(
|
||||
$postActionVars,
|
||||
[
|
||||
'flashes' => $flashes,
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates edit form and processes post data.
|
||||
*
|
||||
* @param int $objectId Item ID
|
||||
* @param bool $ignorePost Flag to ignore POST data
|
||||
*
|
||||
* @return HttpFoundation\JsonResponse|HttpFoundation\RedirectResponse|Response
|
||||
*/
|
||||
public function editAction(Request $request, $objectId, $ignorePost = false)
|
||||
{
|
||||
$model = $this->getModel('report');
|
||||
\assert($model instanceof ReportModel);
|
||||
$entity = $model->getEntity($objectId);
|
||||
$session = $request->getSession();
|
||||
$page = $session->get('mautic.report.page', 1);
|
||||
|
||||
// set the return URL
|
||||
$returnUrl = $this->generateUrl('mautic_report_index', ['page' => $page]);
|
||||
|
||||
$postActionVars = [
|
||||
'returnUrl' => $returnUrl,
|
||||
'viewParameters' => ['page' => $page],
|
||||
'contentTemplate' => 'Mautic\ReportBundle\Controller\ReportController::indexAction',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => 'mautic_report_index',
|
||||
'mauticContent' => 'report',
|
||||
],
|
||||
];
|
||||
|
||||
// not found
|
||||
$check = $this->checkEntityAccess(
|
||||
$postActionVars,
|
||||
$entity,
|
||||
$objectId,
|
||||
['report:reports:viewown', 'report:reports:viewother'],
|
||||
$model,
|
||||
'report'
|
||||
);
|
||||
if (true !== $check) {
|
||||
return $check;
|
||||
}
|
||||
|
||||
// Create the form
|
||||
$action = $this->generateUrl('mautic_report_action', ['objectAction' => 'edit', 'objectId' => $objectId]);
|
||||
$form = $model->createForm($entity, $this->formFactory, $action);
|
||||
|
||||
// /Check for a submitted form and process it
|
||||
if (!$ignorePost && 'POST' == $request->getMethod()) {
|
||||
$valid = false;
|
||||
if (!$cancelled = $this->isFormCancelled($form)) {
|
||||
// Columns have to be reset in order for Symfony to honor the new submitted order
|
||||
$oldColumns = $entity->getColumns();
|
||||
$entity->setColumns([]);
|
||||
$oldSchedule = $entity->isScheduled() ? $entity->getSchedule() : null;
|
||||
|
||||
$newSchedule['schedule_unit'] = $request->request->all()['report']['scheduleUnit'];
|
||||
$newSchedule['schedule_day'] = $request->request->all()['report']['scheduleDay'];
|
||||
$newSchedule['schedule_month_frequency'] = $request->request->all()['report']['scheduleMonthFrequency'];
|
||||
|
||||
if ($oldSchedule != $newSchedule) {
|
||||
$entity->setHasScheduleChanged(true);
|
||||
}
|
||||
|
||||
$oldGraphs = $entity->getGraphs();
|
||||
$entity->setGraphs([]);
|
||||
if ($valid = $this->isFormValid($form)) {
|
||||
// form is valid so process the data
|
||||
$model->saveEntity($entity, $this->getFormButton($form, ['buttons', 'save'])->isClicked());
|
||||
|
||||
$this->addFlashMessage(
|
||||
'mautic.core.notice.updated',
|
||||
[
|
||||
'%name%' => $entity->getName(),
|
||||
'%menu_link%' => 'mautic_report_index',
|
||||
'%url%' => $this->generateUrl(
|
||||
'mautic_report_action',
|
||||
[
|
||||
'objectAction' => 'edit',
|
||||
'objectId' => $entity->getId(),
|
||||
]
|
||||
),
|
||||
]
|
||||
);
|
||||
|
||||
$returnUrl = $this->generateUrl(
|
||||
'mautic_report_view',
|
||||
[
|
||||
'objectId' => $entity->getId(),
|
||||
]
|
||||
);
|
||||
$viewParams = ['objectId' => $entity->getId()];
|
||||
$template = 'Mautic\ReportBundle\Controller\ReportController::viewAction';
|
||||
} else {
|
||||
// reset old columns
|
||||
$entity->setColumns($oldColumns);
|
||||
$entity->setGraphs($oldGraphs);
|
||||
}
|
||||
} else {
|
||||
// unlock the entity
|
||||
$model->unlockEntity($entity);
|
||||
|
||||
$returnUrl = $this->generateUrl('mautic_report_index', ['page' => $page]);
|
||||
$viewParams = ['report' => $page];
|
||||
$template = 'Mautic\ReportBundle\Controller\ReportController::indexAction';
|
||||
}
|
||||
|
||||
if ($cancelled || ($valid && $this->getFormButton($form, ['buttons', 'save'])->isClicked())) {
|
||||
// Clear session items in case columns changed
|
||||
$session->remove('mautic.report.'.$entity->getId().'.orderby');
|
||||
$session->remove('mautic.report.'.$entity->getId().'.orderbydir');
|
||||
|
||||
return $this->postActionRedirect(
|
||||
array_merge(
|
||||
$postActionVars,
|
||||
[
|
||||
'returnUrl' => $returnUrl,
|
||||
'viewParameters' => $viewParams,
|
||||
'contentTemplate' => $template,
|
||||
]
|
||||
)
|
||||
);
|
||||
} elseif ($valid) {
|
||||
// Rebuild the form for updated columns
|
||||
$form = $model->createForm($entity, $this->formFactory, $action);
|
||||
}
|
||||
} else {
|
||||
// lock the entity
|
||||
$model->lockEntity($entity);
|
||||
}
|
||||
|
||||
return $this->delegateView(
|
||||
[
|
||||
'viewParameters' => [
|
||||
'report' => $entity,
|
||||
'form' => $form->createView(),
|
||||
],
|
||||
'contentTemplate' => '@MauticReport/Report/form.html.twig',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_report_index',
|
||||
'mauticContent' => 'report',
|
||||
'route' => $this->generateUrl(
|
||||
'mautic_report_action',
|
||||
[
|
||||
'objectAction' => 'edit',
|
||||
'objectId' => $entity->getId(),
|
||||
]
|
||||
),
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function newAction(ReportModel $model, Request $request, ?Report $entity = null): Response
|
||||
{
|
||||
if (!$this->security->isGranted('report:reports:create')) {
|
||||
return $this->accessDenied();
|
||||
}
|
||||
|
||||
if (!($entity instanceof Report)) {
|
||||
$entity = $model->getEntity();
|
||||
}
|
||||
|
||||
$session = $request->getSession();
|
||||
$page = $session->get('mautic.report.page', 1);
|
||||
|
||||
$action = $this->generateUrl('mautic_report_action', ['objectAction' => 'new']);
|
||||
$form = $model->createForm($entity, $this->formFactory, $action);
|
||||
|
||||
// /Check for a submitted form and process it
|
||||
if (Request::METHOD_POST === $request->getMethod()) {
|
||||
$valid = false;
|
||||
if (!$cancelled = $this->isFormCancelled($form)) {
|
||||
if ($valid = $this->isFormValid($form)) {
|
||||
// form is valid so process the data
|
||||
$model->saveEntity($entity);
|
||||
|
||||
$this->addFlashMessage(
|
||||
'mautic.core.notice.created',
|
||||
[
|
||||
'%name%' => $entity->getName(),
|
||||
'%menu_link%' => 'mautic_report_index',
|
||||
'%url%' => $this->generateUrl(
|
||||
'mautic_report_action',
|
||||
[
|
||||
'objectAction' => 'edit',
|
||||
'objectId' => $entity->getId(),
|
||||
]
|
||||
),
|
||||
]
|
||||
);
|
||||
|
||||
if (!$this->getFormButton($form, ['buttons', 'save'])->isClicked()) {
|
||||
// return edit view so that all the session stuff is loaded
|
||||
return $this->editAction($request, $entity->getId(), true);
|
||||
}
|
||||
|
||||
$viewParameters = ['objectId' => $entity->getId()];
|
||||
$returnUrl = $this->generateUrl('mautic_report_view', $viewParameters);
|
||||
$template = 'Mautic\ReportBundle\Controller\ReportController::viewAction';
|
||||
}
|
||||
} else {
|
||||
$viewParameters = ['page' => $page];
|
||||
$returnUrl = $this->generateUrl('mautic_report_index', $viewParameters);
|
||||
$template = 'Mautic\ReportBundle\Controller\ReportController::indexAction';
|
||||
}
|
||||
|
||||
if ($cancelled || ($valid && $this->getFormButton($form, ['buttons', 'save'])->isClicked())) {
|
||||
return $this->postActionRedirect(
|
||||
[
|
||||
'returnUrl' => $returnUrl,
|
||||
'viewParameters' => $viewParameters,
|
||||
'contentTemplate' => $template,
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_report_index',
|
||||
'mauticContent' => 'report',
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->delegateView(
|
||||
[
|
||||
'viewParameters' => [
|
||||
'report' => $entity,
|
||||
'form' => $form->createView(),
|
||||
],
|
||||
'contentTemplate' => '@MauticReport/Report/form.html.twig',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_report_index',
|
||||
'mauticContent' => 'report',
|
||||
'route' => $this->generateUrl(
|
||||
'mautic_report_action',
|
||||
[
|
||||
'objectAction' => 'new',
|
||||
]
|
||||
),
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a report.
|
||||
*
|
||||
* @param int $objectId Report ID
|
||||
* @param int $reportPage
|
||||
*
|
||||
* @return HttpFoundation\JsonResponse|Response
|
||||
*/
|
||||
public function viewAction(Request $request, $objectId, $reportPage = 1)
|
||||
{
|
||||
$model = $this->getModel('report');
|
||||
\assert($model instanceof ReportModel);
|
||||
$entity = $model->getEntity($objectId);
|
||||
$security = $this->security;
|
||||
|
||||
if (null === $entity) {
|
||||
$page = $request->getSession()->get('mautic.report.page', 1);
|
||||
|
||||
return $this->postActionRedirect(
|
||||
[
|
||||
'returnUrl' => $this->generateUrl('mautic_report_index', ['page' => $page]),
|
||||
'viewParameters' => ['page' => $page],
|
||||
'contentTemplate' => 'Mautic\ReportBundle\Controller\ReportController::indexAction',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_report_index',
|
||||
'mauticContent' => 'report',
|
||||
],
|
||||
'flashes' => [
|
||||
[
|
||||
'type' => 'error',
|
||||
'msg' => 'mautic.report.report.error.notfound',
|
||||
'msgVars' => ['%id%' => $objectId],
|
||||
],
|
||||
],
|
||||
]
|
||||
);
|
||||
} elseif (!$security->hasEntityAccess('report:reports:viewown', 'report:reports:viewother', $entity->getCreatedBy())) {
|
||||
return $this->accessDenied();
|
||||
}
|
||||
|
||||
$this->setListFilters();
|
||||
|
||||
$mysqlFormat = 'Y-m-d';
|
||||
$session = $request->getSession();
|
||||
|
||||
// Init the forms
|
||||
$action = $this->generateUrl('mautic_report_action', ['objectAction' => 'view', 'objectId' => $objectId]);
|
||||
|
||||
// Get the date range filter values from the request of from the session
|
||||
$dateRangeValues = $request->query->all()['daterange'] ?? $request->request->all()['daterange'] ?? [];
|
||||
|
||||
if (!empty($dateRangeValues['date_from'])) {
|
||||
$from = new \DateTime($dateRangeValues['date_from']);
|
||||
$session->set('mautic.report.date.from', $from->format($mysqlFormat));
|
||||
} elseif ($fromDate = $session->get('mautic.report.date.from')) {
|
||||
$dateRangeValues['date_from'] = $fromDate;
|
||||
}
|
||||
if (!empty($dateRangeValues['date_to'])) {
|
||||
$to = new \DateTime($dateRangeValues['date_to']);
|
||||
$session->set('mautic.report.date.to', $to->format($mysqlFormat));
|
||||
} elseif ($toDate = $session->get('mautic.report.date.to')) {
|
||||
$dateRangeValues['date_to'] = $toDate;
|
||||
}
|
||||
|
||||
$dateRangeForm = $this->formFactory->create(DateRangeType::class, $dateRangeValues, ['action' => $action]);
|
||||
if ('POST' === $request->getMethod() && $request->request->has('daterange')) {
|
||||
if ($this->isFormValid($dateRangeForm)) {
|
||||
$to = new \DateTime($dateRangeForm['date_to']->getData());
|
||||
$dateRangeValues['date_to'] = $to->format($mysqlFormat);
|
||||
$session->set('mautic.report.date.to', $dateRangeValues['date_to']);
|
||||
|
||||
$from = new \DateTime($dateRangeForm['date_from']->getData());
|
||||
$dateRangeValues['date_from'] = $from->format($mysqlFormat);
|
||||
$session->set('mautic.report.date.from', $dateRangeValues['date_from']);
|
||||
}
|
||||
}
|
||||
|
||||
// Setup dynamic filters
|
||||
$filterDefinitions = $model->getFilterList($entity->getSource());
|
||||
/** @var array $dynamicFilters */
|
||||
$dynamicFilters = $session->get('mautic.report.'.$objectId.'.filters', []);
|
||||
$filterSettings = [];
|
||||
|
||||
if (count($dynamicFilters) > 0 && count($entity->getFilters()) > 0) {
|
||||
foreach ($entity->getFilters() as $filter) {
|
||||
foreach ($dynamicFilters as $dfcol => $dfval) {
|
||||
if (1 === $filter['dynamic'] && $filter['column'] === $dfcol) {
|
||||
$dynamicFilters[$dfcol]['expr'] = $filter['condition'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($dynamicFilters as $filter) {
|
||||
$filterSettings[$filterDefinitions->definitions[$filter['column']]['alias']] = $filter['value'];
|
||||
}
|
||||
|
||||
foreach ($entity->getFilters() as $filter) {
|
||||
if (!isset($filter['dynamic']) || 1 !== $filter['dynamic']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$column = $filter['column'] ?? null;
|
||||
$definition = (null !== $column) ? ($filterDefinitions->definitions[$column] ?? []) : [];
|
||||
$alias = $definition['alias'] ?? null;
|
||||
|
||||
if (null === $alias || isset($filterSettings[$alias])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$value = $filter['value'] ?? null;
|
||||
if ('' === $value || null === $value) {
|
||||
$default = $definition['defaultValue'] ?? null;
|
||||
if ('' !== $default && null !== $default) {
|
||||
$value = $default;
|
||||
}
|
||||
}
|
||||
|
||||
if ('' !== $value && null !== $value) {
|
||||
$filterSettings[$alias] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$dynamicFilterForm = $this->formFactory->create(
|
||||
DynamicFiltersType::class,
|
||||
$filterSettings,
|
||||
[
|
||||
'action' => $action,
|
||||
'report' => $entity,
|
||||
'filterDefinitions' => $filterDefinitions,
|
||||
]
|
||||
);
|
||||
|
||||
$reportData = $model->getReportData(
|
||||
$entity,
|
||||
$this->formFactory,
|
||||
[
|
||||
'dynamicFilters' => $dynamicFilters,
|
||||
'paginate' => true,
|
||||
'reportPage' => $reportPage,
|
||||
'dateFrom' => new \DateTime($dateRangeForm->get('date_from')->getData()),
|
||||
'dateTo' => new \DateTime($dateRangeForm->get('date_to')->getData()),
|
||||
]
|
||||
);
|
||||
|
||||
$reportDataResult = new ReportDataResult($reportData);
|
||||
|
||||
return $this->delegateView(
|
||||
[
|
||||
'viewParameters' => [
|
||||
'data' => $reportData['data'],
|
||||
'columns' => $reportData['columns'],
|
||||
'dataColumns' => $reportData['dataColumns'],
|
||||
'totalResults' => $reportData['totalResults'],
|
||||
'debug' => $reportData['debug'],
|
||||
'report' => $entity,
|
||||
'reportPage' => $reportPage,
|
||||
'graphs' => $reportData['graphs'],
|
||||
'reportDataResult' => $reportDataResult,
|
||||
'tmpl' => $request->isXmlHttpRequest() ? $request->get('tmpl', 'index') : 'index',
|
||||
'limit' => $reportData['limit'],
|
||||
'permissions' => $security->isGranted(
|
||||
[
|
||||
'report:reports:viewown',
|
||||
'report:reports:viewother',
|
||||
'report:reports:create',
|
||||
'report:reports:editown',
|
||||
'report:reports:editother',
|
||||
'report:reports:deleteown',
|
||||
'report:reports:deleteother',
|
||||
],
|
||||
'RETURN_ARRAY'
|
||||
),
|
||||
'dateRangeForm' => $dateRangeForm->createView(),
|
||||
'dynamicFilterForm' => $dynamicFilterForm->createView(),
|
||||
'enableExportPermission' => $this->security->isAdmin() || $this->security->isGranted('report:export:enable', 'MATCH_ONE'),
|
||||
'baseUrl' => $this->generateUrl(
|
||||
'mautic_report_view',
|
||||
[
|
||||
'objectId' => $objectId,
|
||||
'reportPage' => $reportPage,
|
||||
]
|
||||
),
|
||||
],
|
||||
'contentTemplate' => $reportData['contentTemplate'],
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_report_index',
|
||||
'mauticContent' => 'report',
|
||||
'route' => $this->generateUrl(
|
||||
'mautic_report_view',
|
||||
[
|
||||
'objectId' => $entity->getId(),
|
||||
'reportPage' => $reportPage,
|
||||
]
|
||||
),
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks access to an entity.
|
||||
*
|
||||
* @param array<mixed> $postActionVars
|
||||
* @param array<mixed> $permissions
|
||||
*
|
||||
* @return array<string, string|array<string, string>>|bool|HttpFoundation\JsonResponse|HttpFoundation\RedirectResponse|Response
|
||||
*/
|
||||
private function checkEntityAccess(array $postActionVars, ?Report $entity, int $objectId, array $permissions, ReportModel $model, string $modelName)
|
||||
{
|
||||
if (null === $entity) {
|
||||
return $this->postActionRedirect(
|
||||
array_merge(
|
||||
$postActionVars,
|
||||
[
|
||||
'flashes' => [
|
||||
[
|
||||
'type' => 'error',
|
||||
'msg' => 'mautic.report.report.error.notfound',
|
||||
'msgVars' => ['%id%' => $objectId],
|
||||
],
|
||||
],
|
||||
]
|
||||
)
|
||||
);
|
||||
} elseif (!$this->security->hasEntityAccess($permissions[0], $permissions[1], $entity->getCreatedBy())) {
|
||||
return $this->accessDenied();
|
||||
} elseif ($model->isLocked($entity)) {
|
||||
// deny access if the entity is locked
|
||||
return $this->isLocked($postActionVars, $entity, $modelName);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $objectId
|
||||
* @param string $format
|
||||
*
|
||||
* @return Response
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function exportAction(Request $request, $objectId, $format = 'csv')
|
||||
{
|
||||
/** @var ReportModel $model */
|
||||
$model = $this->getModel('report');
|
||||
$entity = $model->getEntity($objectId);
|
||||
$security = $this->security;
|
||||
|
||||
if (null === $entity) {
|
||||
$page = $request->getSession()->get('mautic.report.page', 1);
|
||||
|
||||
return $this->postActionRedirect(
|
||||
[
|
||||
'returnUrl' => $this->generateUrl('mautic_report_index', ['page' => $page]),
|
||||
'viewParameters' => ['page' => $page],
|
||||
'contentTemplate' => 'Mautic\ReportBundle\Controller\ReportController::indexAction',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_report_index',
|
||||
'mauticContent' => 'report',
|
||||
],
|
||||
'flashes' => [
|
||||
[
|
||||
'type' => 'error',
|
||||
'msg' => 'mautic.report.report.error.notfound',
|
||||
'msgVars' => ['%id%' => $objectId],
|
||||
],
|
||||
],
|
||||
]
|
||||
);
|
||||
} elseif (!$security->hasEntityAccess('report:reports:viewown', 'report:reports:viewother', $entity->getCreatedBy())) {
|
||||
return $this->accessDenied();
|
||||
} elseif (!$this->security->isAdmin() && !$this->security->isGranted('report:export:enable', 'MATCH_ONE')) {
|
||||
return $this->accessDenied();
|
||||
}
|
||||
|
||||
$session = $request->getSession();
|
||||
$fromDate = $session->get('mautic.report.date.from', (new \DateTime('-30 days'))->format('Y-m-d'));
|
||||
$toDate = $session->get('mautic.report.date.to', (new \DateTime())->format('Y-m-d'));
|
||||
|
||||
$date = (new DateTimeHelper())->toLocalString();
|
||||
$name = str_replace(' ', '_', $date).'_'.InputHelper::alphanum($entity->getName(), false, '-');
|
||||
$options = ['dateFrom' => new \DateTime($fromDate), 'dateTo' => new \DateTime($toDate)];
|
||||
|
||||
$dynamicFilters = $session->get('mautic.report.'.$objectId.'.filters', []);
|
||||
$options['dynamicFilters'] = $dynamicFilters;
|
||||
|
||||
if ('csv' === $format) {
|
||||
$response = new HttpFoundation\StreamedResponse(
|
||||
function () use ($model, $entity, $format, $options): void {
|
||||
$options['paginate'] = true;
|
||||
$options['ignoreGraphData'] = true;
|
||||
$options['limit'] = (int) $this->coreParametersHelper->get('report_export_batch_size', 1000);
|
||||
$options['page'] = 1;
|
||||
$handle = fopen('php://output', 'r+');
|
||||
$batchTotals = [];
|
||||
$batchDataSize = 0;
|
||||
do {
|
||||
$reportData = $model->getReportData($entity, null, $options);
|
||||
|
||||
// Calculate number of pages only once
|
||||
if (1 === $options['page']) {
|
||||
$totalPages = (int) ceil($reportData['totalResults'] / $options['limit']);
|
||||
}
|
||||
|
||||
// Build the data rows
|
||||
$isLastBatch = (isset($totalPages) && $totalPages === $options['page']);
|
||||
$reportDataResult = new ReportDataResult($reportData, $batchTotals, $batchDataSize, $isLastBatch);
|
||||
|
||||
// Store batch totals and size
|
||||
$batchTotals = $reportDataResult->getTotals();
|
||||
$batchDataSize += $reportDataResult->getDataCount();
|
||||
|
||||
// Note this so that it's not recalculated on each batch
|
||||
$options['totalResults'] = $reportData['totalResults'];
|
||||
|
||||
$model->exportResults($format, $entity, $reportDataResult, $handle, $options['page']);
|
||||
++$options['page'];
|
||||
} while (!empty($reportData['data']));
|
||||
|
||||
fclose($handle);
|
||||
}
|
||||
);
|
||||
$fileName = $name.'.'.$format;
|
||||
ExportResponse::setResponseHeaders($response, $fileName);
|
||||
} else {
|
||||
if ('xlsx' === $format) {
|
||||
$options['ignoreGraphData'] = true;
|
||||
}
|
||||
$reportData = $model->getReportData($entity, null, $options);
|
||||
$reportDataResult = new ReportDataResult($reportData);
|
||||
$response = $model->exportResults($format, $entity, $reportDataResult);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $reportId
|
||||
* @param string $format
|
||||
*
|
||||
* @return BinaryFileResponse
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function downloadAction(FileHandler $fileHandler, $reportId, $format = 'csv')
|
||||
{
|
||||
if ('csv' !== $format) {
|
||||
throw new \Exception($this->translator->trans('mautic.format.invalid', ['%format%' => $format, '%validFormats%' => 'csv']));
|
||||
}
|
||||
|
||||
/** @var ReportModel $model */
|
||||
$model = $this->getModel('report');
|
||||
|
||||
/** @var Report $report */
|
||||
$report = $model->getEntity($reportId);
|
||||
|
||||
/** @var \Mautic\CoreBundle\Security\Permissions\CorePermissions $security */
|
||||
$security = $this->security;
|
||||
|
||||
if (empty($report)) {
|
||||
return $this->notFound($this->translator->trans('mautic.report.notfound', ['%id%' => $reportId]));
|
||||
}
|
||||
|
||||
if (!$security->hasEntityAccess('report:reports:viewown', 'report:reports:viewother', $report->getCreatedBy())) {
|
||||
return $this->accessDenied();
|
||||
}
|
||||
|
||||
if (!$fileHandler->compressedCsvFileForReportExists($report)) {
|
||||
if ($report->isScheduled()) {
|
||||
$message = 'mautic.report.download.missing';
|
||||
} else {
|
||||
$message = 'mautic.report.download.missing.but.scheduled';
|
||||
$report->setAsScheduledNow($this->user->getEmail());
|
||||
$model->saveEntity($report);
|
||||
}
|
||||
|
||||
return $this->notFound($this->translator->trans($message, ['%id%' => $reportId]));
|
||||
}
|
||||
|
||||
$response = new BinaryFileResponse($fileHandler->getPathToCompressedCsvFileForReport($report));
|
||||
|
||||
$response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, "report-{$report->getId()}.zip");
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
protected function getDefaultOrderDirection(): string
|
||||
{
|
||||
return 'DESC';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ReportBundle\Controller;
|
||||
|
||||
use Mautic\CoreBundle\Controller\AjaxController as CommonAjaxController;
|
||||
use Mautic\CoreBundle\Service\FlashBag;
|
||||
use Mautic\ReportBundle\Scheduler\Date\DateBuilder;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
|
||||
class ScheduleController extends CommonAjaxController
|
||||
{
|
||||
public function indexAction(DateBuilder $dateBuilder, $isScheduled, $scheduleUnit, $scheduleDay, $scheduleMonthFrequency): JsonResponse
|
||||
{
|
||||
$dates = $dateBuilder->getPreviewDays($isScheduled, $scheduleUnit, $scheduleDay, $scheduleMonthFrequency);
|
||||
|
||||
$html = $this->render(
|
||||
'@MauticReport/Schedule/index.html.twig',
|
||||
[
|
||||
'dates' => $dates,
|
||||
]
|
||||
)->getContent();
|
||||
|
||||
return $this->sendJsonResponse(
|
||||
[
|
||||
'html' => $html,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets report to schedule NOW if possible.
|
||||
*
|
||||
* @param int $reportId
|
||||
*/
|
||||
public function nowAction($reportId): JsonResponse
|
||||
{
|
||||
/** @var \Mautic\ReportBundle\Model\ReportModel $model */
|
||||
$model = $this->getModel('report');
|
||||
|
||||
/** @var \Mautic\ReportBundle\Entity\Report $report */
|
||||
$report = $model->getEntity($reportId);
|
||||
|
||||
/** @var \Mautic\CoreBundle\Security\Permissions\CorePermissions $security */
|
||||
$security = $this->security;
|
||||
|
||||
if (empty($report)) {
|
||||
$this->addFlashMessage('mautic.report.notfound', ['%id%' => $reportId], FlashBag::LEVEL_ERROR, 'messages');
|
||||
|
||||
return $this->flushFlash();
|
||||
}
|
||||
|
||||
if (!$security->hasEntityAccess('report:reports:viewown', 'report:reports:viewother', $report->getCreatedBy())) {
|
||||
$this->addFlashMessage('mautic.core.error.accessdenied', [], FlashBag::LEVEL_ERROR);
|
||||
|
||||
return $this->flushFlash();
|
||||
}
|
||||
|
||||
if ($report->isScheduled()) {
|
||||
$this->addFlashMessage('mautic.report.scheduled.already', ['%id%' => $reportId], FlashBag::LEVEL_ERROR);
|
||||
|
||||
return $this->flushFlash();
|
||||
}
|
||||
|
||||
$report->setAsScheduledNow($this->user->getEmail());
|
||||
$model->saveEntity($report);
|
||||
|
||||
$this->addFlashMessage(
|
||||
'mautic.report.scheduled.to.now',
|
||||
['%id%' => $reportId, '%email%' => $this->user->getEmail()]
|
||||
);
|
||||
|
||||
return $this->flushFlash();
|
||||
}
|
||||
|
||||
private function flushFlash(): JsonResponse
|
||||
{
|
||||
return new JsonResponse(['flashes' => $this->getFlashContent()]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user