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,131 @@
<?php
namespace MauticPlugin\MauticFocusBundle\Controller;
use Mautic\CacheBundle\Cache\CacheProviderTagAwareInterface;
use Mautic\CoreBundle\Controller\AjaxController as CommonAjaxController;
use Mautic\CoreBundle\Helper\InputHelper;
use MauticPlugin\MauticFocusBundle\Helper\IframeAvailabilityChecker;
use MauticPlugin\MauticFocusBundle\Model\FocusModel;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
class AjaxController extends CommonAjaxController
{
/**
* This method produces HTTP request checking headers which are blocking availability for iframe inheritance for other pages.
*/
public function checkIframeAvailabilityAction(Request $request, IframeAvailabilityChecker $availabilityChecker): JsonResponse
{
$url = $request->query->get('website');
return $availabilityChecker->check($url, $request->getScheme());
}
public function generatePreviewAction(Request $request): JsonResponse
{
$responseContent = ['html' => '', 'style' => ''];
$focus = $request->request->all();
if (isset($focus['focus'])) {
$focusArray = InputHelper::_($focus['focus']);
if (!empty($focusArray['style']) && !empty($focusArray['type'])) {
/** @var FocusModel $model */
$model = $this->getModel('focus');
$focusArray['id'] = 'preview';
$responseContent['html'] = $model->getContent($focusArray, true);
$responseContent['style'] = $focusArray['style']; // Required by JS in response
}
}
return $this->sendJsonResponse($responseContent);
}
public function getViewsCountAction(Request $request, CacheProviderTagAwareInterface $cacheProvider): JsonResponse
{
$focusId = (int) InputHelper::clean($request->query->get('focusId'));
if (0 === $focusId) {
return $this->sendJsonResponse([
'success' => 0,
'message' => $this->translator->trans('mautic.core.error.badrequest'),
], 400);
}
$cacheTimeout = (int) $this->coreParametersHelper->get('cached_data_timeout');
$cacheItem = $cacheProvider->getItem('focus.viewsCount.'.$focusId);
if ($cacheItem->isHit()) {
$cacheItemValue = $cacheItem->get();
$viewsCount = $cacheItemValue['views'];
$uniqueViewsCount = $cacheItemValue['uniqueViews'];
} else {
/** @var FocusModel $model */
$model = $this->getModel('focus');
$focus = $model->getEntity($focusId);
if (null === $focus) {
return $this->sendJsonResponse([
'success' => 0,
'message' => $this->translator->trans('mautic.api.call.notfound'),
], 404);
}
$viewsCount = $model->getViewsCount($focus);
$uniqueViewsCount = $model->getUniqueViewsCount($focus);
$cacheItem->set([
'views' => $viewsCount,
'uniqueViews' => $uniqueViewsCount,
]);
$cacheItem->tag("focus.{$focusId}");
$cacheItem->expiresAfter($cacheTimeout * 60);
$cacheProvider->save($cacheItem);
}
return $this->sendJsonResponse([
'success' => 1,
'views' => $viewsCount,
'uniqueViews' => $uniqueViewsCount,
]);
}
public function getClickThroughCountAction(Request $request, CacheProviderTagAwareInterface $cacheProvider): JsonResponse
{
$focusId = (int) InputHelper::clean($request->query->get('focusId'));
if (0 === $focusId) {
return $this->sendJsonResponse([
'success' => 0,
'message' => $this->translator->trans('mautic.core.error.badrequest'),
], 400);
}
$cacheTimeout = (int) $this->coreParametersHelper->get('cached_data_timeout');
$cacheItem = $cacheProvider->getItem('focus.clickThroughCount.'.$focusId);
if ($cacheItem->isHit()) {
$clickThroughCount = $cacheItem->get();
} else {
/** @var FocusModel $model */
$model = $this->getModel('focus');
$focus = $model->getEntity($focusId);
if (null === $focus) {
return $this->sendJsonResponse([
'success' => 0,
'message' => $this->translator->trans('mautic.api.call.notfound'),
], 404);
}
$clickThroughCount = $model->getClickThroughCount($focus);
$cacheItem->set($clickThroughCount);
$cacheItem->tag("focus.{$focusId}");
$cacheItem->expiresAfter($cacheTimeout * 60);
$cacheProvider->save($cacheItem);
}
return $this->sendJsonResponse([
'success' => 1,
'clickThrough' => $clickThroughCount,
]);
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace MauticPlugin\MauticFocusBundle\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\Security\Permissions\CorePermissions;
use Mautic\CoreBundle\Translation\Translator;
use MauticPlugin\MauticFocusBundle\Entity\Focus;
use MauticPlugin\MauticFocusBundle\Model\FocusModel;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\RouterInterface;
/**
* @extends CommonApiController<Focus>
*/
class FocusApiController extends CommonApiController
{
/**
* @var FocusModel|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)
{
$focusModel = $modelFactory->getModel('focus');
\assert($focusModel instanceof FocusModel);
$this->model = $focusModel;
$this->entityClass = Focus::class;
$this->entityNameOne = 'focus';
$this->entityNameMulti = 'focus';
$this->permissionBase = 'focus:items';
$this->dataInputMasks = [
'html' => 'html',
'editor' => 'html',
];
$this->serializerGroups = [
'focusDetails',
'categoryList',
'publishDetails',
];
parent::__construct($security, $translator, $entityResultHelper, $router, $formFactory, $appVersion, $requestStack, $doctrine, $modelFactory, $dispatcher, $coreParametersHelper);
}
public function generateJsAction($id)
{
$focus = $this->model->getEntity($id);
$view = $this->view(['js' => $this->model->generateJavascript($focus)], Response::HTTP_OK);
return $this->handleView($view);
}
}

View File

@@ -0,0 +1,266 @@
<?php
namespace MauticPlugin\MauticFocusBundle\Controller;
use Doctrine\Persistence\ManagerRegistry;
use Mautic\CacheBundle\Cache\CacheProviderTagAwareInterface;
use Mautic\CoreBundle\Controller\AbstractStandardFormController;
use Mautic\CoreBundle\Factory\ModelFactory;
use Mautic\CoreBundle\Form\Type\DateRangeType;
use Mautic\CoreBundle\Helper\CoreParametersHelper;
use Mautic\CoreBundle\Helper\UserHelper;
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
use Mautic\CoreBundle\Service\FlashBag;
use Mautic\CoreBundle\Translation\Translator;
use Mautic\FormBundle\Helper\FormFieldHelper;
use Mautic\PageBundle\Model\TrackableModel;
use MauticPlugin\MauticFocusBundle\Entity\Focus;
use MauticPlugin\MauticFocusBundle\Model\FocusModel;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
class FocusController extends AbstractStandardFormController
{
/**
* @phpstan-ignore-next-line
*/
public function __construct(
private CacheProviderTagAwareInterface $cacheProvider,
FormFactoryInterface $formFactory,
FormFieldHelper $fieldHelper,
ManagerRegistry $doctrine,
ModelFactory $modelFactory,
UserHelper $userHelper,
CoreParametersHelper $coreParametersHelper,
EventDispatcherInterface $dispatcher,
Translator $translator,
FlashBag $flashBag,
RequestStack $requestStack,
CorePermissions $security,
) {
parent::__construct($formFactory, $fieldHelper, $doctrine, $modelFactory, $userHelper, $coreParametersHelper, $dispatcher, $translator, $flashBag, $requestStack, $security);
}
protected function getTemplateBase(): string
{
return '@MauticFocus/Focus';
}
protected function getModelName(): string
{
return 'focus';
}
/**
* @param int $page
*/
public function indexAction(Request $request, $page = 1): Response
{
return parent::indexStandard($request, $page);
}
/**
* Generates new form and processes post data.
*
* @return JsonResponse|Response
*/
public function newAction(Request $request)
{
return parent::newStandard($request);
}
/**
* Generates edit form and processes post data.
*
* @param int $objectId
* @param bool $ignorePost
*
* @return JsonResponse|Response
*/
public function editAction(Request $request, $objectId, $ignorePost = false)
{
return parent::editStandard($request, $objectId, $ignorePost);
}
/**
* Displays details on a Focus.
*
* @return array|JsonResponse|RedirectResponse|Response
*/
public function viewAction(Request $request, $objectId)
{
return parent::viewStandard($request, $objectId, 'focus', 'focus');
}
/**
* Clone an entity.
*
* @param int $objectId
*
* @return JsonResponse|RedirectResponse|Response
*/
public function cloneAction(Request $request, $objectId)
{
return parent::cloneStandard($request, $objectId);
}
/**
* Deletes the entity.
*
* @param int $objectId
*
* @return JsonResponse|RedirectResponse
*/
public function deleteAction(Request $request, $objectId)
{
return parent::deleteStandard($request, $objectId);
}
/**
* Deletes a group of entities.
*
* @return JsonResponse|RedirectResponse
*/
public function batchDeleteAction(Request $request)
{
return parent::batchDeleteStandard($request);
}
/**
* @throws \Exception
*/
public function getViewArguments(array $args, $action): array
{
$cacheTimeout = (int) $this->coreParametersHelper->get('cached_data_timeout');
if ('view' == $action) {
/** @var Focus $item */
$item = $args['viewParameters']['item'];
// For line graphs in the view
$dateRangeValues = $this->getCurrentRequest()->get('daterange', []);
$dateRangeForm = $this->formFactory->create(
DateRangeType::class,
$dateRangeValues,
[
'action' => $this->generateUrl(
'mautic_focus_action',
[
'objectAction' => 'view',
'objectId' => $item->getId(),
]
),
]
);
$statsDateFrom = new \DateTime($dateRangeForm->get('date_from')->getData());
$statsDateTo = new \DateTime($dateRangeForm->get('date_to')->getData());
$cacheKey = "focus.viewArguments.{$item->getId()}.{$statsDateFrom->getTimestamp()}.{$statsDateTo->getTimestamp()}";
$cacheItem = $this->cacheProvider->getItem($cacheKey);
if ($cacheItem->isHit()) {
[$stats, $trackables] = $cacheItem->get();
} else {
// invalidate cache for entire focus item to keep AJAX loaded data consistent
$this->cacheProvider->invalidateTags(["focus.{$item->getId()}"]);
/** @var FocusModel $model */
$model = $this->getModel('focus');
$stats = $model->getStats(
$item,
null,
$statsDateFrom,
$statsDateTo
);
if ('link' === $item->getType()) {
$trackableModel = $this->getModel('page.trackable');
\assert($trackableModel instanceof TrackableModel);
$trackables = $trackableModel->getTrackableList('focus', $item->getId());
$cacheItem->set([$stats, $trackables]);
$cacheItem->expiresAfter($cacheTimeout * 60);
$cacheItem->tag("focus.{$item->getId()}");
$this->cacheProvider->save($cacheItem);
}
}
$args['viewParameters']['stats'] = $stats;
$args['viewParameters']['dateRangeForm'] = $dateRangeForm->createView();
$args['viewParameters']['showConversionRate'] = true;
if (isset($trackables)) {
$args['viewParameters']['trackables'] = $trackables;
}
}
return $args;
}
/**
* @return mixed[]
*/
protected function getPostActionRedirectArguments(array $args, $action): array
{
$focus = $this->getCurrentRequest()->request->all()['focus'] ?? [];
$updateSelect = 'POST' === $this->getCurrentRequest()->getMethod()
? ($focus['updateSelect'] ?? false)
: $this->getCurrentRequest()->get('updateSelect', false);
if ($updateSelect) {
switch ($action) {
case 'new':
case 'edit':
$passthrough = $args['passthroughVars'];
$passthrough = array_merge(
$passthrough,
[
'updateSelect' => $updateSelect,
'id' => $args['entity']->getId(),
'name' => $args['entity']->getName(),
]
);
$args['passthroughVars'] = $passthrough;
break;
}
}
return $args;
}
/**
* @return array
*/
protected function getEntityFormOptions()
{
$focus = $this->getCurrentRequest()->request->all()['focus'] ?? [];
$updateSelect = 'POST' === $this->getCurrentRequest()->getMethod()
? ($focus['updateSelect'] ?? false)
: $this->getCurrentRequest()->get('updateSelect', false);
if ($updateSelect) {
return ['update_select' => $updateSelect];
}
}
/**
* 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
*/
protected function getUpdateSelectParams($updateSelect, $entity, $nameMethod = 'getName', $groupMethod = 'getLanguage'): array
{
return [
'updateSelect' => $updateSelect,
'id' => $entity->getId(),
'name' => $entity->$nameMethod(),
];
}
}

View File

@@ -0,0 +1,66 @@
<?php
namespace MauticPlugin\MauticFocusBundle\Controller;
use Mautic\CoreBundle\Controller\CommonController;
use Mautic\CoreBundle\Helper\TrackingPixelHelper;
use Mautic\LeadBundle\Tracker\ContactTracker;
use MauticPlugin\MauticFocusBundle\Entity\Stat;
use MauticPlugin\MauticFocusBundle\Event\FocusViewEvent;
use MauticPlugin\MauticFocusBundle\FocusEvents;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class PublicController extends CommonController
{
/**
* @return array|\Symfony\Component\HttpFoundation\JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse|Response
*/
public function generateAction($id)
{
// Don't store a visitor with this request
defined('MAUTIC_NON_TRACKABLE_REQUEST') || define('MAUTIC_NON_TRACKABLE_REQUEST', 1);
/** @var \MauticPlugin\MauticFocusBundle\Model\FocusModel $model */
$model = $this->getModel('focus');
$focus = $model->getEntity($id);
if ($focus) {
if (!$focus->isPublished()) {
return new Response('', Response::HTTP_NOT_FOUND);
}
$content = $model->generateJavascript($focus);
return new Response($content, 200, ['Content-Type' => 'application/javascript']);
} else {
return new Response('', Response::HTTP_NOT_FOUND);
}
}
/**
* @return Response
*/
public function viewPixelAction(Request $request, ContactTracker $contactTracker)
{
$id = $request->get('id', false);
if ($id) {
/** @var \MauticPlugin\MauticFocusBundle\Model\FocusModel $model */
$model = $this->getModel('focus');
$focus = $model->getEntity($id);
$lead = $contactTracker->getContact();
if ($focus && $focus->isPublished() && $lead) {
$stat = $model->addStat($focus, Stat::TYPE_NOTIFICATION, $request, $lead);
if ($stat && $this->dispatcher->hasListeners(FocusEvents::FOCUS_ON_VIEW)) {
$event = new FocusViewEvent($stat);
$this->dispatcher->dispatch($event, FocusEvents::FOCUS_ON_VIEW);
unset($event);
}
}
}
return TrackingPixelHelper::getResponse($request);
}
}