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,114 @@
<?php
namespace Mautic\PointBundle\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\InputHelper;
use Mautic\CoreBundle\Helper\IpLookupHelper;
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
use Mautic\CoreBundle\Translation\Translator;
use Mautic\LeadBundle\Controller\LeadAccessTrait;
use Mautic\LeadBundle\Model\LeadModel;
use Mautic\PointBundle\Entity\Point;
use Mautic\PointBundle\Model\PointModel;
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<Point>
*/
class PointApiController extends CommonApiController
{
use LeadAccessTrait;
protected LeadModel $leadModel;
/**
* @var PointModel|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)
{
$leadModel = $modelFactory->getModel('lead');
\assert($leadModel instanceof LeadModel);
$pointModel = $modelFactory->getModel('point');
\assert($pointModel instanceof PointModel);
$this->model = $pointModel;
$this->leadModel = $leadModel;
$this->entityClass = Point::class;
$this->entityNameOne = 'point';
$this->entityNameMulti = 'points';
$this->serializerGroups = ['pointDetails', 'categoryList', 'publishDetails'];
parent::__construct($security, $translator, $entityResultHelper, $router, $formFactory, $appVersion, $requestStack, $doctrine, $modelFactory, $dispatcher, $coreParametersHelper);
}
/**
* Return array of available point action types.
*/
public function getPointActionTypesAction()
{
if (!$this->security->isGranted([$this->permissionBase.':view', $this->permissionBase.':viewown'])) {
return $this->accessDenied();
}
$actionTypes = $this->model->getPointActions();
$view = $this->view(['pointActionTypes' => $actionTypes['list']]);
return $this->handleView($view);
}
/**
* Subtract points from a lead.
*
* @param int $leadId
* @param string $operator
* @param int $delta
*
* @return Response
*/
public function adjustPointsAction(Request $request, IpLookupHelper $ipLookupHelper, $leadId, $operator, $delta)
{
$lead = $this->checkLeadAccess($leadId, 'edit');
if ($lead instanceof Response) {
return $lead;
}
try {
$this->logApiPointChange($request, $ipLookupHelper, $lead, $delta, $operator);
} catch (\Exception $e) {
return $this->returnError($e->getMessage(), Response::HTTP_BAD_REQUEST);
}
return $this->handleView($this->view(['success' => 1], Response::HTTP_OK));
}
/**
* Log the lead points change.
*
* @param int $delta
*/
protected function logApiPointChange(Request $request, IpLookupHelper $ipLookupHelper, $lead, $delta, $operator)
{
$trans = $this->translator;
$ip = $ipLookupHelper->getIpAddress();
$eventName = InputHelper::clean($request->request->get('eventName', $trans->trans('mautic.lead.lead.submitaction.operator_'.$operator)));
$actionName = InputHelper::clean($request->request->get('actionName', $trans->trans('mautic.lead.event.api')));
$lead->adjustPoints($delta, $operator);
$lead->addPointsChangeLogEntry('API', $eventName, $actionName, $delta, $ip);
$this->leadModel->saveEntity($lead, false);
}
}

View File

@@ -0,0 +1,150 @@
<?php
declare(strict_types=1);
namespace Mautic\PointBundle\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\InputHelper;
use Mautic\CoreBundle\Helper\IpLookupHelper;
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
use Mautic\CoreBundle\Translation\Translator;
use Mautic\LeadBundle\Model\LeadModel;
use Mautic\PointBundle\Entity\Group;
use Mautic\PointBundle\Model\PointGroupModel;
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<Group>
*/
class PointGroupsApiController extends CommonApiController
{
/**
* @var PointGroupModel
*/
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, PointGroupModel $pointGroupModel, private LeadModel $leadModel)
{
$this->model = $pointGroupModel;
$this->entityClass = Group::class;
$this->entityNameOne = 'pointGroup';
$this->entityNameMulti = 'pointGroups';
$this->serializerGroups = ['pointGroupDetails', 'pointGroupList', 'publishDetails'];
parent::__construct($security, $translator, $entityResultHelper, $router, $formFactory, $appVersion, $requestStack, $doctrine, $modelFactory, $dispatcher, $coreParametersHelper);
}
public function getContactPointGroupsAction(int $contactId): Response
{
$contact = $this->leadModel->getEntity($contactId);
if (null === $contact) {
return $this->notFound($this->translator->trans('mautic.lead.event.api.lead.not.found'));
}
if (!$this->checkEntityAccess($contact)) {
return $this->accessDenied();
}
$groupScores = $contact->getGroupScores();
$view = $this->view(
[
'total' => count($groupScores),
'groupScores' => $groupScores,
],
Response::HTTP_OK
);
$context = $view->getContext()->setGroups(['groupContactScoreDetails', 'pointGroupDetails']);
$view->setContext($context);
return $this->handleView($view);
}
public function getContactPointGroupAction(int $contactId, int $groupId): Response
{
$contact = $this->leadModel->getEntity($contactId);
if (null === $contact) {
return $this->notFound($this->translator->trans('mautic.lead.event.api.lead.not.found'));
}
if (!$this->checkEntityAccess($contact)) {
return $this->accessDenied();
}
$pointGroup = $this->model->getEntity($groupId);
if (null === $pointGroup) {
return $this->notFound($this->translator->trans('mautic.lead.event.api.point.group.not.found'));
}
$groupScore = $contact->getGroupScore($pointGroup);
$view = $this->view(
[
'groupScore' => $groupScore,
],
Response::HTTP_OK
);
$context = $view->getContext()->setGroups(['groupContactScoreDetails', 'pointGroupDetails']);
$view->setContext($context);
return $this->handleView($view);
}
public function adjustGroupPointsAction(Request $request, IpLookupHelper $ipLookupHelper, int $contactId, int $groupId, string $operator, int $value): Response
{
$contact = $this->leadModel->getEntity($contactId);
if (null === $contact) {
return $this->notFound($this->translator->trans('mautic.lead.event.api.lead.not.found'));
}
if (!$this->checkEntityAccess($contact)) {
return $this->accessDenied();
}
$pointGroup = $this->model->getEntity($groupId);
if (null === $pointGroup) {
return $this->notFound($this->translator->trans('mautic.lead.event.api.point.group.not.found'));
}
if (!PointGroupModel::isAllowedPointOperation($operator)) {
return $this->badRequest($this->translator->trans('mautic.lead.event.api.operation.not.allowed'));
}
$oldScore = $contact->getGroupScore($pointGroup)?->getScore();
$contact = $this->model->adjustPoints($contact, $pointGroup, $value, $operator);
$newScore = $contact->getGroupScore($pointGroup)->getScore();
$delta = $newScore - ($oldScore ?? 0);
$eventName = InputHelper::clean($request->request->get('eventName', $this->translator->trans('mautic.point.event.manual_change')));
$actionName = InputHelper::clean($request->request->get('actionName', $this->translator->trans('mautic.lead.event.api')));
$contact->addPointsChangeLogEntry(
type: 'API',
name: $eventName,
action: $actionName,
pointChanges: $delta,
ip: $ipLookupHelper->getIpAddress(),
group: $pointGroup
);
$this->leadModel->saveEntity($contact, false);
$view = $this->view(['groupScore' => $contact->getGroupScore($pointGroup)], Response::HTTP_OK);
$context = $view->getContext()->setGroups(['groupContactScoreDetails', 'pointGroupDetails']);
$view->setContext($context);
return $this->handleView($view);
}
}

View File

@@ -0,0 +1,190 @@
<?php
namespace Mautic\PointBundle\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 Mautic\PointBundle\Entity\Trigger;
use Mautic\PointBundle\Model\TriggerEventModel;
use Mautic\PointBundle\Model\TriggerModel;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\RouterInterface;
/**
* @extends CommonApiController<Trigger>
*/
class TriggerApiController extends CommonApiController
{
/**
* @var TriggerModel|null
*/
protected $model;
public function __construct(
CorePermissions $security,
Translator $translator,
EntityResultHelper $entityResultHelper,
RouterInterface $router,
FormFactoryInterface $formFactory,
AppVersion $appVersion,
private ?RequestStack $requestStack,
ManagerRegistry $doctrine,
ModelFactory $modelFactory,
EventDispatcherInterface $dispatcher,
CoreParametersHelper $coreParametersHelper,
) {
$triggerModel = $modelFactory->getModel('point.trigger');
\assert($triggerModel instanceof TriggerModel);
$this->model = $triggerModel;
$this->entityClass = Trigger::class;
$this->entityNameOne = 'trigger';
$this->entityNameMulti = 'triggers';
$this->serializerGroups = ['triggerDetails', 'categoryList', 'publishDetails'];
parent::__construct($security, $translator, $entityResultHelper, $router, $formFactory, $appVersion, $requestStack, $doctrine, $modelFactory, $dispatcher, $coreParametersHelper);
}
protected function preSaveEntity(&$entity, $form, $parameters, $action = 'edit')
{
$method = $this->requestStack->getCurrentRequest()->getMethod();
$triggerEventModel = $this->getModel('point.triggerevent');
$isNew = false;
// Set timestamps
$this->model->setTimestamps($entity, true, false);
if (!$entity->getId()) {
$isNew = true;
// Save the entitz first to get the ID.
// Using the repository function to not trigger the listeners twice.
$this->model->getRepository()->saveEntity($entity);
}
$requestTriggerIds = [];
$currentEvents = $entity->getEvents();
// Add events from the request
if (!empty($parameters['events']) && is_array($parameters['events'])) {
foreach ($parameters['events'] as &$eventParams) {
if (empty($eventParams['id'])) {
// Create an unique ID if not set - the following code requires one
$eventParams['id'] = 'new'.hash('sha1', uniqid(mt_rand()));
$triggerEventEntity = $triggerEventModel->getEntity();
} else {
$triggerEventEntity = $triggerEventModel->getEntity($eventParams['id']);
$requestTriggerIds[] = $eventParams['id'];
}
$triggerEventForm = $this->createTriggerEventEntityForm($triggerEventEntity);
$triggerEventForm->submit($eventParams, 'PATCH' !== $method);
if (!($triggerEventForm->isSubmitted() && $triggerEventForm->isValid())) {
$formErrors = $this->getFormErrorMessages($triggerEventForm);
$msg = $this->getFormErrorMessage($formErrors);
return $this->returnError('Trigger events: '.$msg, Response::HTTP_BAD_REQUEST);
}
}
$this->model->setEvents($entity, $parameters['events']);
}
// Remove events which weren't in the PUT request
if (!$isNew && 'PUT' === $method) {
foreach ($currentEvents as $currentEvent) {
if (!in_array($currentEvent->getId(), $requestTriggerIds)) {
$entity->removeTriggerEvent($currentEvent);
}
}
}
}
/**
* @return FormInterface<mixed>
*/
protected function createTriggerEventEntityForm($entity): FormInterface
{
$triggerEventModel = $this->getModel('point.triggerevent');
\assert($triggerEventModel instanceof TriggerEventModel);
return $triggerEventModel->createForm(
$entity,
$this->formFactory,
null,
[
'csrf_protection' => false,
'allow_extra_fields' => true,
]
);
}
/**
* Return array of available point trigger event types.
*/
public function getPointTriggerEventTypesAction()
{
if (!$this->security->isGranted([$this->permissionBase.':view', $this->permissionBase.':viewown'])) {
return $this->accessDenied();
}
$eventTypesRaw = $this->model->getEvents();
$eventTypes = [];
foreach ($eventTypesRaw as $key => $type) {
$eventTypes[$key] = $type['label'];
}
$view = $this->view(['eventTypes' => $eventTypes]);
return $this->handleView($view);
}
/**
* Delete events from a point trigger.
*
* @param int $triggerId
*
* @return Response
*/
public function deletePointTriggerEventsAction($triggerId)
{
if (!$this->security->isGranted([$this->permissionBase.':editown', $this->permissionBase.':editother'], 'MATCH_ONE')) {
return $this->accessDenied();
}
$entity = $this->model->getEntity($triggerId);
if (null === $entity) {
return $this->notFound();
}
$eventsToDelete = $this->requestStack->getCurrentRequest()->get('events');
$currentEvents = $entity->getEvents();
if (!is_array($eventsToDelete)) {
return $this->badRequest('The events attribute must be array.');
}
foreach ($currentEvents as $currentEvent) {
if (in_array($currentEvent->getId(), $eventsToDelete)) {
$entity->removeTriggerEvent($currentEvent);
}
}
$view = $this->view([$this->entityNameOne => $entity]);
return $this->handleView($view);
}
}