Initial commit: CloudOps infrastructure platform

This commit is contained in:
root
2026-04-09 19:58:57 +02:00
commit 1166a52f26
7762 changed files with 839452 additions and 0 deletions

View File

@@ -0,0 +1,97 @@
<?php
namespace Mautic\DashboardBundle\Controller;
use Doctrine\ORM\EntityManagerInterface;
use Mautic\CoreBundle\Controller\AjaxController as CommonAjaxController;
use Mautic\DashboardBundle\Entity\Widget;
use Mautic\DashboardBundle\Form\Type\WidgetType;
use Mautic\DashboardBundle\Model\DashboardModel;
use Mautic\PageBundle\Entity\Hit;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
class AjaxController extends CommonAjaxController
{
/**
* Count how many visitors are currently viewing a page.
*/
public function viewingVisitorsAction(EntityManagerInterface $entityManager): JsonResponse
{
$dataArray = ['success' => 0];
/** @var \Mautic\PageBundle\Entity\PageRepository $pageRepository */
$pageRepository = $entityManager->getRepository(Hit::class);
$dataArray['viewingVisitors'] = $pageRepository->countVisitors(60, true);
$dataArray['success'] = 1;
return $this->sendJsonResponse($dataArray);
}
/**
* Returns HTML of a new widget based on its values.
*/
public function updateWidgetFormAction(Request $request, FormFactoryInterface $formFactory): JsonResponse
{
$data = $request->request->all()['widget'] ?? [];
$dataArray = ['success' => 0];
// Clear params if type is not selected
if (empty($data['type'])) {
unset($data['params']);
}
$widget = new Widget();
$form = $formFactory->create(WidgetType::class, $widget);
$formHtml = $this->render('@MauticDashboard/Widget/form.html.twig',
['form' => $form->submit($data)->createView()]
)->getContent();
$dataArray['formHtml'] = $formHtml;
$dataArray['success'] = 1;
return $this->sendJsonResponse($dataArray);
}
/**
* Saves the new ordering of dashboard widgets.
*/
public function updateWidgetOrderingAction(Request $request): JsonResponse
{
$data = $request->request->all()['ordering'] ?? [];
$dashboardModel = $this->getModel('dashboard');
\assert($dashboardModel instanceof DashboardModel);
$repo = $dashboardModel->getRepository();
$repo->updateOrdering(array_flip($data), $this->user->getId());
$dataArray = ['success' => 1];
return $this->sendJsonResponse($dataArray);
}
/**
* Deletes the entity.
*/
public function deleteAction(Request $request): JsonResponse
{
$objectId = $request->request->get('widget');
$dataArray = ['success' => 0];
// @todo: build permissions
// if (!$this->security->isGranted('dashobard:widgets:delete')) {
// return $this->accessDenied();
// }
/** @var DashboardModel $model */
$model = $this->getModel('dashboard');
$entity = $model->getEntity($objectId);
if ($entity) {
$model->deleteEntity($entity);
$name = $entity->getName();
$dataArray['success'] = 1;
}
return $this->sendJsonResponse($dataArray);
}
}

View File

@@ -0,0 +1,155 @@
<?php
namespace Mautic\DashboardBundle\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\InputHelper;
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
use Mautic\CoreBundle\Translation\Translator;
use Mautic\DashboardBundle\DashboardEvents;
use Mautic\DashboardBundle\Entity\Widget;
use Mautic\DashboardBundle\Event\WidgetTypeListEvent;
use Mautic\DashboardBundle\Model\DashboardModel;
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<Widget>
*/
class WidgetApiController extends CommonApiController
{
/**
* @var DashboardModel|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)
{
$dashboardModel = $modelFactory->getModel('dashboard');
\assert($dashboardModel instanceof DashboardModel);
$this->model = $dashboardModel;
$this->entityClass = Widget::class;
$this->entityNameOne = 'widget';
$this->entityNameMulti = 'widgets';
$this->serializerGroups = [];
parent::__construct($security, $translator, $entityResultHelper, $router, $formFactory, $appVersion, $requestStack, $doctrine, $modelFactory, $dispatcher, $coreParametersHelper);
}
/**
* Obtains a list of available widget types.
*
* @return Response
*/
public function getTypesAction()
{
$dispatcher = $this->dispatcher;
$event = new WidgetTypeListEvent();
$event->setTranslator($this->translator);
$dispatcher->dispatch($event, DashboardEvents::DASHBOARD_ON_MODULE_LIST_GENERATE);
$view = $this->view(['success' => 1, 'types' => $event->getTypes()], Response::HTTP_OK);
return $this->handleView($view);
}
/**
* Obtains a list of available widget types.
*
* @param string $type of the widget
*
* @return Response
*/
public function getDataAction(Request $request, $type)
{
$start = microtime(true);
$timezone = InputHelper::clean($request->get('timezone', null));
$from = InputHelper::clean($request->get('dateFrom', null));
$to = InputHelper::clean($request->get('dateTo', null));
$dataFormat = InputHelper::clean($request->get('dataFormat', null));
$unit = InputHelper::clean($request->get('timeUnit', 'Y'));
$dataset = InputHelper::clean($request->query->all()['dataset'] ?? $request->request->all()['dataset'] ?? []);
$response = ['success' => 0];
try {
DateTimeHelper::validateMysqlDateTimeUnit($unit);
} catch (\InvalidArgumentException $e) {
return $this->returnError($e->getMessage(), Response::HTTP_BAD_REQUEST);
}
if ($timezone) {
$fromDate = new \DateTime($from, new \DateTimeZone($timezone));
$toDate = new \DateTime($to, new \DateTimeZone($timezone));
} else {
$fromDate = new \DateTime($from);
$toDate = new \DateTime($to);
}
$params = [
'timeUnit' => InputHelper::clean($request->get('timeUnit', 'Y')),
'dateFormat' => InputHelper::clean($request->get('dateFormat', null)),
'dateFrom' => $fromDate,
'dateTo' => $toDate,
'limit' => (int) $request->get('limit', null),
'filter' => InputHelper::clean($request->query->all()['filter'] ?? $request->request->all()['filter'] ?? []),
'dataset' => $dataset,
];
// Merge filters into the root array as well as that's how widget edit forms send them.
$params = array_merge($params, $params['filter']);
$cacheTimeout = (int) $request->get('cacheTimeout', 0);
$widgetHeight = (int) $request->get('height', 300);
$widget = new Widget();
$widget->setParams($params);
$widget->setType($type);
$widget->setHeight($widgetHeight);
$widget->setCacheTimeout($cacheTimeout);
$this->model->populateWidgetContent($widget);
$data = $widget->getTemplateData();
if (!$data) {
return $this->notFound();
}
if ('raw' == $dataFormat) {
if (isset($data['chartData']['labels']) && isset($data['chartData']['datasets'])) {
$rawData = [];
foreach ($data['chartData']['datasets'] as $dataset) {
$rawData[$dataset['label']] = [];
foreach ($dataset['data'] as $key => $value) {
$rawData[$dataset['label']][$data['chartData']['labels'][$key]] = $value;
}
}
$data = $rawData;
} elseif (isset($data['raw'])) {
$data = $data['raw'];
}
} else {
if (isset($data['raw'])) {
unset($data['raw']);
}
}
$response['cached'] = $widget->isCached();
$response['execution_time'] = microtime(true) - $start;
$response['success'] = 1;
$response['data'] = $data;
$view = $this->view($response, Response::HTTP_OK);
return $this->handleView($view);
}
}

View File

@@ -0,0 +1,562 @@
<?php
namespace Mautic\DashboardBundle\Controller;
use Mautic\CoreBundle\Controller\AbstractFormController;
use Mautic\CoreBundle\Form\Type\DateRangeType;
use Mautic\CoreBundle\Helper\DateTimeHelper;
use Mautic\CoreBundle\Helper\InputHelper;
use Mautic\CoreBundle\Helper\PathsHelper;
use Mautic\CoreBundle\Helper\PhpVersionHelper;
use Mautic\CoreBundle\Release\ThisRelease;
use Mautic\DashboardBundle\Dashboard\Widget as WidgetService;
use Mautic\DashboardBundle\Entity\Widget;
use Mautic\DashboardBundle\Form\Type\UploadType;
use Mautic\DashboardBundle\Model\DashboardModel;
use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\RouterInterface;
use Twig\Environment;
class DashboardController extends AbstractFormController
{
/**
* Generates the default view.
*/
public function indexAction(Request $request, WidgetService $widget, FormFactoryInterface $formFactory, PathsHelper $pathsHelper, RouterInterface $urlGenerator): Response
{
$model = $this->getModel('dashboard');
\assert($model instanceof DashboardModel);
$widgets = $model->getWidgets();
// Apply the default dashboard if no widget exists
if (!count($widgets) && $this->user->getId()) {
return $this->applyDashboardFileAction($request, $pathsHelper, $urlGenerator, 'global.default');
}
$action = $this->generateUrl('mautic_dashboard_index');
$dateRangeFilter = $request->query->all()['daterange'] ?? $request->request->all()['daterange'] ?? [];
// Set new date range to the session
if ($request->isMethod(Request::METHOD_POST)) {
if (!empty($dateRangeFilter['date_from'])) {
$from = new \DateTime($dateRangeFilter['date_from']);
$request->getSession()->set('mautic.daterange.form.from', $from->format(DateTimeHelper::FORMAT_DB_DATE_ONLY));
}
if (!empty($dateRangeFilter['date_to'])) {
$to = new \DateTime($dateRangeFilter['date_to']);
$request->getSession()->set('mautic.daterange.form.to', $to->format(DateTimeHelper::FORMAT_DB_DATE_ONLY.' 23:59:59'));
}
$model->clearDashboardCache();
}
// Set new date range to the session, if present in POST
$widget->setFilter($request);
// Load date range from session
$filter = $model->getDefaultFilter();
// Set the final date range to the form
$dateRangeFilter['date_from'] = $filter['dateFrom']->format(WidgetService::FORMAT_HUMAN);
$dateRangeFilter['date_to'] = $filter['dateTo']->format(WidgetService::FORMAT_HUMAN);
$dateRangeForm = $formFactory->create(DateRangeType::class, $dateRangeFilter, ['action' => $action]);
$model->populateWidgetsContent($widgets, $filter);
$releaseMetadata = ThisRelease::getMetadata();
$model->populateWidgetPreviews($widgets);
return $this->delegateView([
'viewParameters' => [
'security' => $this->security,
'widgets' => $widgets,
'dateRangeForm' => $dateRangeForm->createView(),
'phpVersion' => [
'isOutdated' => version_compare(PHP_VERSION, $releaseMetadata->getShowPHPVersionWarningIfUnder(), 'lt'),
'version' => PhpVersionHelper::getCurrentSemver(),
],
],
'contentTemplate' => '@MauticDashboard/Dashboard/index.html.twig',
'passthroughVars' => [
'activeLink' => '#mautic_dashboard_index',
'mauticContent' => 'dashboard',
'route' => $this->generateUrl('mautic_dashboard_index'),
],
]);
}
public function widgetAction(Request $request, WidgetService $widgetService, Environment $twig, $widgetId): JsonResponse
{
if (!$request->isXmlHttpRequest()) {
throw new NotFoundHttpException('Not found.');
}
$widgetService->setFilter($request);
$widget = $widgetService->get((int) $widgetId);
if (!$widget) {
throw new NotFoundHttpException('Not found.');
}
$content = $twig->render(
'@MauticDashboard/Dashboard/widget.html.twig',
['widget' => $widget]
);
return new JsonResponse([
'success' => 1,
'widgetId' => $widgetId,
'widgetHtml' => $content,
'widgetWidth' => $widget->getWidth(),
'widgetHeight' => $widget->getHeight(),
]);
}
/**
* Generate new dashboard widget and processes post data.
*
* @return JsonResponse|RedirectResponse|Response
*/
public function newAction(Request $request, FormFactoryInterface $formFactory)
{
// retrieve the entity
$widget = new Widget();
$model = $this->getModel('dashboard');
\assert($model instanceof DashboardModel);
$action = $this->generateUrl('mautic_dashboard_action', ['objectAction' => 'new']);
// get the user form factory
$form = $model->createForm($widget, $formFactory, $action);
$closeModal = false;
$valid = false;
// /Check for a submitted form and process it
if ($request->isMethod(Request::METHOD_POST)) {
if (!$cancelled = $this->isFormCancelled($form)) {
if ($valid = $this->isFormValid($form)) {
$closeModal = true;
// form is valid so process the data
$model->saveEntity($widget);
}
} else {
$closeModal = true;
}
}
if ($closeModal) {
// just close the modal
$passthroughVars = [
'closeModal' => 1,
'mauticContent' => 'widget',
];
$filter = $model->getDefaultFilter();
$model->populateWidgetContent($widget, $filter);
if ($valid && !$cancelled) {
$passthroughVars['upWidgetCount'] = 1;
$passthroughVars['widgetHtml'] = $this->renderView('@MauticDashboard/Widget/detail.html.twig', [
'widget' => $widget,
]);
$passthroughVars['widgetId'] = $widget->getId();
$passthroughVars['widgetWidth'] = $widget->getWidth();
$passthroughVars['widgetHeight'] = $widget->getHeight();
}
return new JsonResponse($passthroughVars);
} else {
return $this->delegateView([
'viewParameters' => [
'form' => $form->createView(),
],
'contentTemplate' => '@MauticDashboard/Widget/form.html.twig',
]);
}
}
/**
* edit widget and processes post data.
*
* @return JsonResponse|RedirectResponse|Response
*/
public function editAction(Request $request, FormFactoryInterface $formFactory, $objectId)
{
$model = $this->getModel('dashboard');
\assert($model instanceof DashboardModel);
$widget = $model->getEntity($objectId);
$action = $this->generateUrl('mautic_dashboard_action', ['objectAction' => 'edit', 'objectId' => $objectId]);
// get the user form factory
$form = $model->createForm($widget, $formFactory, $action);
$closeModal = false;
$valid = false;
// /Check for a submitted form and process it
if ($request->isMethod(Request::METHOD_POST)) {
if (!$cancelled = $this->isFormCancelled($form)) {
if ($valid = $this->isFormValid($form)) {
$closeModal = true;
// form is valid so process the data
$model->saveEntity($widget);
}
} else {
$closeModal = true;
}
}
if ($closeModal) {
// just close the modal
$passthroughVars = [
'closeModal' => 1,
'mauticContent' => 'widget',
];
$filter = $model->getDefaultFilter();
$model->populateWidgetContent($widget, $filter);
if ($valid && !$cancelled) {
$passthroughVars['upWidgetCount'] = 1;
$passthroughVars['widgetHtml'] = $this->renderView('@MauticDashboard/Widget/detail.html.twig', [
'widget' => $widget,
]);
$passthroughVars['widgetId'] = $widget->getId();
$passthroughVars['widgetWidth'] = $widget->getWidth();
$passthroughVars['widgetHeight'] = $widget->getHeight();
}
return new JsonResponse($passthroughVars);
} else {
return $this->delegateView([
'viewParameters' => [
'form' => $form->createView(),
],
'contentTemplate' => '@MauticDashboard/Widget/form.html.twig',
]);
}
}
/**
* Deletes entity if exists.
*
* @param int $objectId
*/
public function deleteAction(Request $request, $objectId): Response
{
if (!$request->isXmlHttpRequest()) {
throw new BadRequestHttpException();
}
$flashes = [];
$success = 0;
/** @var DashboardModel $model */
$model = $this->getModel('dashboard');
$entity = $model->getEntity($objectId);
if ($entity) {
$model->deleteEntity($entity);
$name = $entity->getName();
$flashes[] = [
'type' => 'notice',
'msg' => 'mautic.core.notice.deleted',
'msgVars' => [
'%name%' => $name,
'%id%' => $objectId,
],
];
$success = 1;
} else {
$flashes[] = [
'type' => 'error',
'msg' => 'mautic.api.client.error.notfound',
'msgVars' => ['%id%' => $objectId],
];
}
return $this->postActionRedirect(
[
'success' => $success,
'flashes' => $flashes,
]
);
}
/**
* Saves the widgets of current user into a json and stores it for later as a file.
*
* @return Response
*/
public function saveAction(Request $request)
{
// Accept only AJAX POST requests because those are check for CSRF tokens
if (!$request->isMethod(Request::METHOD_POST) || !$request->isXmlHttpRequest()) {
return $this->accessDenied();
}
$name = $this->getNameFromRequest($request);
/** @var DashboardModel $dashboardModel */
$dashboardModel = $this->getModel('dashboard');
try {
$dashboardModel->saveSnapshot($name);
$type = 'notice';
$msg = $this->translator->trans('mautic.dashboard.notice.save', [
'%name%' => $name,
'%viewUrl%' => $this->generateUrl(
'mautic_dashboard_action',
[
'objectAction' => 'import',
]
),
], 'flashes');
} catch (IOException $e) {
$type = 'error';
$msg = $this->translator->trans('mautic.dashboard.error.save', [
'%msg%' => $e->getMessage(),
], 'flashes');
}
return $this->postActionRedirect(
[
'flashes' => [
[
'type' => $type,
'msg' => $msg,
],
],
]
);
}
/**
* Exports the widgets of current user into a json file and downloads it.
*/
public function exportAction(Request $request): JsonResponse
{
$dashboardModel = $this->getModel('dashboard');
\assert($dashboardModel instanceof DashboardModel);
$filename = InputHelper::filename($this->getNameFromRequest($request), 'json');
$response = new JsonResponse($dashboardModel->toArray($filename));
$response->setEncodingOptions($response->getEncodingOptions() | JSON_PRETTY_PRINT);
$response->headers->set('Content-Type', 'application/force-download');
$response->headers->set('Content-Type', 'application/octet-stream');
$response->headers->set('Content-Disposition', 'attachment; filename="'.$filename.'"');
$response->headers->set('Expires', '0');
$response->headers->set('Cache-Control', 'must-revalidate');
$response->headers->set('Pragma', 'public');
return $response;
}
/**
* Exports the widgets of current user into a json file.
*/
public function deleteDashboardFileAction(Request $request, PathsHelper $pathsHelper): RedirectResponse
{
$file = $request->get('file');
$parts = explode('.', $file);
$type = array_shift($parts);
$name = implode('.', $parts);
$dir = $pathsHelper->getSystemPath("dashboard.$type");
$path = $dir.'/'.$name.'.json';
if (file_exists($path) && is_writable($path)) {
unlink($path);
}
return $this->redirectToRoute('mautic_dashboard_action', ['objectAction' => 'import']);
}
/**
* Applies dashboard layout.
*
* @param string|null $file
*/
public function applyDashboardFileAction(Request $request, PathsHelper $pathsHelper, RouterInterface $urlGenerator, $file = null): RedirectResponse
{
if (!$file) {
$file = $request->get('file');
}
$parts = explode('.', $file);
$type = array_shift($parts);
$name = implode('.', $parts);
$dir = $pathsHelper->getSystemPath("dashboard.$type");
$path = $dir.'/'.$name.'.json';
if (!file_exists($path) || !is_readable($path)) {
$this->addFlashMessage('mautic.dashboard.upload.filenotfound', [], 'error', 'validators');
return $this->redirectToRoute('mautic_dashboard_action', ['objectAction' => 'import']);
}
$widgets = json_decode(file_get_contents($path), true);
if (isset($widgets['widgets'])) {
$widgets = $widgets['widgets'];
}
if ($widgets) {
/** @var DashboardModel $model */
$model = $this->getModel('dashboard');
$model->clearDashboardCache();
$currentWidgets = $model->getWidgets();
if (count($currentWidgets)) {
foreach ($currentWidgets as $widget) {
$model->deleteEntity($widget);
}
}
$filter = $model->getDefaultFilter();
foreach ($widgets as $widget) {
$widget = $model->populateWidgetEntity($widget);
$model->saveEntity($widget);
}
}
return $this->redirect($urlGenerator->generate('mautic_dashboard_index'));
}
public function importAction(Request $request, FormFactoryInterface $formFactory, PathsHelper $pathsHelper): Response
{
$preview = $request->get('preview');
/** @var DashboardModel $model */
$model = $this->getModel('dashboard');
$directories = [
'user' => $pathsHelper->getSystemPath('dashboard.user'),
'global' => $pathsHelper->getSystemPath('dashboard.global'),
];
$action = $this->generateUrl('mautic_dashboard_action', ['objectAction' => 'import']);
$form = $formFactory->create(UploadType::class, [], ['action' => $action]);
if ($request->isMethod(Request::METHOD_POST)) {
if (!$this->isFormCancelled($form)) {
if ($this->isFormValid($form)) {
$fileData = $form['file']->getData();
if (!empty($fileData)) {
$extension = pathinfo($fileData->getClientOriginalName(), PATHINFO_EXTENSION);
if ('json' === $extension) {
$fileData->move($directories['user'], $fileData->getClientOriginalName());
} else {
$form->addError(
new FormError(
$this->translator->trans('mautic.core.not.allowed.file.extension', ['%extension%' => $extension], 'validators')
)
);
}
} else {
$form->addError(
new FormError(
$this->translator->trans('mautic.dashboard.upload.filenotfound', [], 'validators')
)
);
}
}
}
}
$dashboardFiles = ['user' => [], 'gobal' => []];
$dashboards = [];
if (is_readable($directories['user'])) {
// User specific layouts
chdir($directories['user']);
$dashboardFiles['user'] = glob('*.json');
}
if (is_readable($directories['global'])) {
// Global dashboards
chdir($directories['global']);
$dashboardFiles['global'] = glob('*.json');
}
foreach ($dashboardFiles as $type => $dirDashboardFiles) {
$tempDashboard = [];
foreach ($dirDashboardFiles as $dashId => $dashboard) {
$dashboard = str_replace('.json', '', $dashboard);
$config = json_decode(
file_get_contents($directories[$type].'/'.$dirDashboardFiles[$dashId]),
true
);
// Check for name, description, etc
$tempDashboard[$dashboard] = [
'type' => $type,
'name' => $config['name'] ?? $dashboard,
'description' => $config['description'] ?? '',
'widgets' => $config['widgets'] ?? $config,
];
}
// Sort by name
uasort($tempDashboard,
fn ($a, $b): int => strnatcasecmp($a['name'], $b['name'])
);
$dashboards = array_merge(
$dashboards,
$tempDashboard
);
}
if ($preview && isset($dashboards[$preview])) {
// @todo check is_writable
$widgets = $dashboards[$preview]['widgets'];
$filter = $model->getDefaultFilter();
$model->populateWidgetsContent($widgets, $filter);
} else {
$widgets = [];
}
return $this->delegateView(
[
'viewParameters' => [
'form' => $form->createView(),
'dashboards' => $dashboards,
'widgets' => $widgets,
'preview' => $preview,
],
'contentTemplate' => '@MauticDashboard/Dashboard/import.html.twig',
'passthroughVars' => [
'activeLink' => '#mautic_dashboard_index',
'mauticContent' => 'dashboardImport',
'route' => $this->generateUrl(
'mautic_dashboard_action',
[
'objectAction' => 'import',
]
),
],
]
);
}
/**
* Gets name from request and defaults it to the timestamp if not provided.
*
* @throws \Exception
*/
private function getNameFromRequest(Request $request): string
{
return $request->get('name', (new \DateTime())->format('Y-m-dTH:i:s'));
}
}