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,37 @@
<?php
declare(strict_types=1);
namespace Mautic\IntegrationsBundle\Controller;
use Mautic\CoreBundle\Controller\CommonController;
use Mautic\IntegrationsBundle\Exception\IntegrationNotFoundException;
use Mautic\IntegrationsBundle\Exception\UnauthorizedException;
use Mautic\IntegrationsBundle\Helper\AuthIntegrationsHelper;
use Symfony\Component\HttpFoundation\Request;
class AuthController extends CommonController
{
public function callbackAction(AuthIntegrationsHelper $authIntegrationsHelper, string $integration, Request $request)
{
$authenticationError = false;
try {
$authIntegration = $authIntegrationsHelper->getIntegration($integration);
$message = $authIntegration->authenticateIntegration($request);
} catch (UnauthorizedException $exception) {
$message = $exception->getMessage();
$authenticationError = true;
} catch (IntegrationNotFoundException) {
return $this->notFound();
}
return $this->render(
'@Integrations/Auth/authenticated.html.twig',
[
'message' => $message,
'authenticationError' => $authenticationError,
]
);
}
}

View File

@@ -0,0 +1,272 @@
<?php
declare(strict_types=1);
namespace Mautic\IntegrationsBundle\Controller;
use Mautic\CoreBundle\Controller\AbstractFormController;
use Mautic\CoreBundle\Twig\Extension\FormExtension;
use Mautic\IntegrationsBundle\Event\ConfigAuthUrlEvent;
use Mautic\IntegrationsBundle\Event\ConfigSaveEvent;
use Mautic\IntegrationsBundle\Event\FormLoadEvent;
use Mautic\IntegrationsBundle\Event\KeysSaveEvent;
use Mautic\IntegrationsBundle\Exception\IntegrationNotFoundException;
use Mautic\IntegrationsBundle\Form\Type\IntegrationConfigType;
use Mautic\IntegrationsBundle\Helper\ConfigIntegrationsHelper;
use Mautic\IntegrationsBundle\Helper\FieldMergerHelper;
use Mautic\IntegrationsBundle\Helper\FieldValidationHelper;
use Mautic\IntegrationsBundle\Integration\BasicIntegration;
use Mautic\IntegrationsBundle\Integration\Interfaces\ConfigFormAuthInterface;
use Mautic\IntegrationsBundle\Integration\Interfaces\ConfigFormAuthorizeButtonInterface;
use Mautic\IntegrationsBundle\Integration\Interfaces\ConfigFormCallbackInterface;
use Mautic\IntegrationsBundle\Integration\Interfaces\ConfigFormFeatureSettingsInterface;
use Mautic\IntegrationsBundle\Integration\Interfaces\ConfigFormFeaturesInterface;
use Mautic\IntegrationsBundle\Integration\Interfaces\ConfigFormInterface;
use Mautic\IntegrationsBundle\Integration\Interfaces\ConfigFormNotesInterface;
use Mautic\IntegrationsBundle\Integration\Interfaces\ConfigFormSyncInterface;
use Mautic\IntegrationsBundle\IntegrationEvents;
use Mautic\PluginBundle\Entity\Integration;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Session;
class ConfigController extends AbstractFormController
{
/**
* @var BasicIntegration|ConfigFormInterface
*/
private $integrationObject;
/**
* @var Integration
*/
private $integrationConfiguration;
/**
* @return array|JsonResponse|RedirectResponse|Response
*/
public function editAction(
Request $request,
ConfigIntegrationsHelper $integrationsHelper,
EventDispatcherInterface $dispatcher,
FieldValidationHelper $fieldValidator,
FormFactoryInterface $formFactory,
FormExtension $formExtension,
string $integration,
) {
// Check ACL
if (!$this->security->isGranted('plugin:plugins:manage')) {
return $this->accessDenied();
}
try {
$this->integrationObject = $integrationsHelper->getIntegration($integration);
$this->integrationConfiguration = $this->integrationObject->getIntegrationConfiguration();
} catch (IntegrationNotFoundException) {
return $this->notFound();
}
$event = new FormLoadEvent($this->integrationConfiguration);
$dispatcher->dispatch($event, IntegrationEvents::INTEGRATION_CONFIG_FORM_LOAD);
// Create the form
$form = $this->getForm($formFactory);
if (Request::METHOD_POST === $request->getMethod()) {
return $this->submitForm($request, $integrationsHelper, $fieldValidator, $dispatcher, $formFactory, $formExtension, $form);
}
// Clear the session of previously stored fields in case it got stuck
/** @var Session $session */
$session = $request->getSession();
$session->remove("$integration-fields");
return $this->showForm($request, $form, $formExtension);
}
/**
* @param FormInterface<mixed> $form
*/
private function submitForm(
Request $request,
ConfigIntegrationsHelper $integrationsHelper,
FieldValidationHelper $fieldValidator,
EventDispatcherInterface $eventDispatcher,
FormFactoryInterface $formFactory,
FormExtension $formExtension,
FormInterface $form,
): JsonResponse|Response {
if ($this->isFormCancelled($form)) {
return $this->closeForm($request);
}
// Get the fields before the form binds partial data due to pagination
$settings = $this->integrationConfiguration->getFeatureSettings();
$fieldMappings = $settings['sync']['fieldMappings'] ?? [];
$oldApiKeys = $this->integrationConfiguration->getApiKeys();
// Submit the form
$form->handleRequest($request);
$configEvent = new KeysSaveEvent($this->integrationConfiguration, $oldApiKeys);
$this->dispatcher->dispatch($configEvent, IntegrationEvents::INTEGRATION_API_KEYS_BEFORE_SAVE);
if ($this->integrationObject instanceof ConfigFormSyncInterface) {
$integration = $this->integrationObject->getName();
$settings = $this->integrationConfiguration->getFeatureSettings();
$session = $request->getSession();
$updatedFields = $session->get("$integration-fields", []);
$fieldMerger = new FieldMergerHelper($this->integrationObject, $fieldMappings);
foreach ($updatedFields as $object => $fields) {
$fieldMerger->mergeSyncFieldMapping($object, $fields);
}
$settings['sync']['fieldMappings'] = $fieldMerger->getFieldMappings();
$fieldValidator->validateRequiredFields($form, $this->integrationObject, $settings['sync']['fieldMappings']);
$this->integrationConfiguration->setFeatureSettings($settings);
}
// Dispatch event prior to saving the Integration. Bundles/plugins may need to modify some field values before save
$configEvent = new ConfigSaveEvent($this->integrationConfiguration);
$eventDispatcher->dispatch($configEvent, IntegrationEvents::INTEGRATION_CONFIG_BEFORE_SAVE);
// Show the form if there are errors and the plugin is published or the authorized button was clicked
$integrationDetailsPost = $request->request->all()['integration_details'] ?? [];
$authorize = !empty($integrationDetailsPost['in_auth']);
if ($form->isSubmitted() && !$form->isValid() && ($this->integrationConfiguration->getIsPublished() || $authorize)) {
return $this->showForm($request, $form, $formExtension);
}
// Save the integration configuration
$integrationsHelper->saveIntegrationConfiguration($this->integrationConfiguration);
// Dispatch after save event
$eventDispatcher->dispatch($configEvent, IntegrationEvents::INTEGRATION_CONFIG_AFTER_SAVE);
// Show the form if the apply button was clicked
if ($this->isFormApplied($form)) {
// Regenerate the form
$this->resetFieldsInSession($request);
$form = $this->getForm($formFactory);
return $this->showForm($request, $form, $formExtension);
}
// Otherwise close the modal
return $this->closeForm($request);
}
/**
* @return FormInterface<mixed>
*/
private function getForm(FormFactoryInterface $formFactory): FormInterface
{
return $formFactory->create(
$this->integrationObject->getConfigFormName() ?: IntegrationConfigType::class,
$this->integrationConfiguration,
[
'action' => $this->generateUrl('mautic_integration_config', ['integration' => $this->integrationObject->getName()]),
'integration' => $this->integrationObject->getName(),
]
);
}
/**
* @param FormInterface<mixed> $form
*/
private function showForm(Request $request, FormInterface $form, FormExtension $formExtension): Response
{
$integrationObject = $this->integrationObject;
$formView = $form->createView();
$showFeaturesTab = $integrationObject instanceof ConfigFormFeaturesInterface
|| $integrationObject instanceof ConfigFormSyncInterface
|| $integrationObject instanceof ConfigFormFeatureSettingsInterface;
$hasFeatureErrors = (
$integrationObject instanceof ConfigFormFeatureSettingsInterface
&& $formExtension->containsErrors($formView['featureSettings']['integration'])
) || (
isset($formView['featureSettings']['sync']['integration'])
&& $formExtension->containsErrors($formView['featureSettings']['sync']['integration'])
);
$hasAuthErrors = $integrationObject instanceof ConfigFormAuthInterface && $formExtension->containsErrors($formView['apiKeys']);
$useSyncFeatures = $integrationObject instanceof ConfigFormSyncInterface;
$useFeatureSettings = $integrationObject instanceof ConfigFormFeatureSettingsInterface;
$useAuthorizationUrl = $integrationObject instanceof ConfigFormAuthorizeButtonInterface;
$callbackUrl = $integrationObject instanceof ConfigFormCallbackInterface ?
$integrationObject->getRedirectUri()
: false;
$useConfigFormNotes = $integrationObject instanceof ConfigFormNotesInterface;
return $this->delegateView(
[
'viewParameters' => [
'integrationObject' => $integrationObject,
'form' => $formView,
'activeTab' => $request->get('activeTab'),
'showFeaturesTab' => $showFeaturesTab,
'hasFeatureErrors' => $hasFeatureErrors,
'hasAuthErrors' => $hasAuthErrors,
'useSyncFeatures' => $useSyncFeatures,
'useFeatureSettings' => $useFeatureSettings,
'useAuthorizationUrl' => $useAuthorizationUrl,
'callbackUrl' => $callbackUrl,
'useConfigFormNotes' => $useConfigFormNotes,
],
'contentTemplate' => $integrationObject->getConfigFormContentTemplate()
?: '@Integrations/Config/form.html.twig',
'passthroughVars' => [
'activeLink' => '#mautic_plugin_index',
'mauticContent' => 'integrationsConfig',
'route' => false,
],
]
);
}
private function closeForm(Request $request): JsonResponse
{
$this->resetFieldsInSession($request);
$response = [
'closeModal' => 1,
'enabled' => $this->integrationConfiguration->getIsPublished(),
'name' => $this->integrationConfiguration->getName(),
'mauticContent' => 'integrationsConfig',
'flashes' => $this->getFlashContent(),
];
if ($this->integrationObject instanceof ConfigFormAuthorizeButtonInterface) {
// Dispatch event to allow listeners to extract information and/or manipulate the URL
$authUrl = $this->integrationObject->getAuthorizationUrl();
$authUrlEvent = new ConfigAuthUrlEvent($this->integrationConfiguration, $authUrl);
$this->dispatcher->dispatch($authUrlEvent, IntegrationEvents::INTEGRATION_CONFIG_ON_GENERATE_AUTH_URL);
$response['authUrl'] = $authUrlEvent->getAuthUrl();
}
return new JsonResponse($response);
}
private function resetFieldsInSession(Request $request): void
{
$session = $request->getSession();
$session->remove("{$this->integrationObject->getName()}-fields");
}
}

View File

@@ -0,0 +1,121 @@
<?php
declare(strict_types=1);
namespace Mautic\IntegrationsBundle\Controller;
use Mautic\CoreBundle\Controller\CommonController;
use Mautic\IntegrationsBundle\Exception\IntegrationNotFoundException;
use Mautic\IntegrationsBundle\Form\Type\IntegrationSyncSettingsObjectFieldMappingType;
use Mautic\IntegrationsBundle\Helper\ConfigIntegrationsHelper;
use Mautic\IntegrationsBundle\Helper\FieldFilterHelper;
use Mautic\IntegrationsBundle\Helper\FieldMergerHelper;
use Mautic\IntegrationsBundle\Integration\Interfaces\ConfigFormSyncInterface;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class FieldPaginationController extends CommonController
{
/**
* @return Response
*/
public function paginateAction(
Request $request,
FormFactoryInterface $formFactory,
ConfigIntegrationsHelper $integrationsHelper,
string $integration,
string $object,
int $page,
) {
// Check ACL
if (!$this->security->isGranted('plugin:plugins:manage')) {
return $this->accessDenied();
}
// Find the integration
try {
/** @var ConfigFormSyncInterface $integrationObject */
$integrationObject = $integrationsHelper->getIntegration($integration);
$integrationConfiguration = $integrationObject->getIntegrationConfiguration();
} catch (IntegrationNotFoundException) {
return $this->notFound();
}
$keyword = $request->get('keyword');
$featureSettings = $integrationConfiguration->getFeatureSettings();
$currentFields = $this->getFields($request, $integrationObject, $featureSettings, $object);
$fieldFilterHelper = new FieldFilterHelper($integrationObject);
if ($keyword) {
$fieldFilterHelper->filterFieldsByKeyword($object, $keyword, $page);
} else {
$fieldFilterHelper->filterFieldsByPage($object, $page);
}
// Create the form
$form = $formFactory->create(
IntegrationSyncSettingsObjectFieldMappingType::class,
$currentFields,
[
'integrationFields' => $fieldFilterHelper->getFilteredFields(),
'page' => $page,
'keyword' => $keyword,
'totalFieldCount' => $fieldFilterHelper->getTotalFieldCount(),
'object' => $object,
'integrationObject' => $integrationObject,
'csrf_protection' => false,
]
);
$html = $this->render(
'@Integrations/Config/field_mapping.html.twig',
[
'form' => $form->createView(),
'integration' => $integration,
'object' => $object,
'page' => $page,
]
)->getContent();
$prefix = "integration_config[featureSettings][sync][fieldMappings][$object]";
$idPrefix = str_replace(['][', '[', ']'], '_', $prefix);
if (str_ends_with($idPrefix, '_')) {
$idPrefix = substr($idPrefix, 0, -1);
}
$formType = 'integration_sync_settings_object_field_mapping';
$html = preg_replace('/'.$formType.'\[(.*?)\]/', $prefix.'[$1]', $html);
$html = str_replace($formType, $idPrefix, $html);
return new JsonResponse(
[
'success' => 1,
'html' => $html,
]
);
}
private function getFields(Request $request, ConfigFormSyncInterface $integrationObject, array $featureSettings, string $object): array
{
$fields = $featureSettings['sync']['fieldMappings'] ?? [];
if (!isset($fields[$object])) {
$fields[$object] = [];
}
// Pull those changed from session
$session = $request->getSession();
$sessionFields = $session->get(sprintf('%s-fields', $integrationObject->getName()), []);
if (!isset($sessionFields[$object])) {
return $fields[$object];
}
$fieldMerger = new FieldMergerHelper($integrationObject, $fields);
$fieldMerger->mergeSyncFieldMapping($object, $sessionFields[$object]);
return $fieldMerger->getFieldMappings()[$object];
}
}

View File

@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace Mautic\IntegrationsBundle\Controller;
use Mautic\CoreBundle\Controller\CommonController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
class UpdateFieldController extends CommonController
{
public function updateAction(Request $request, string $integration, string $object, string $field): JsonResponse
{
// Clear the session of previously stored fields in case it got stuck
$session = $request->getSession();
$updatedFields = $session->get(sprintf('%s-fields', $integration), []);
if (!isset($updatedFields[$object])) {
$updatedFields[$object] = [];
}
if (!isset($updatedFields[$object][$field])) {
$updatedFields[$object][$field] = [];
}
if ($mappedField = $request->request->get('mappedField')) {
$updatedFields[$object][$field]['mappedField'] = $mappedField;
}
if ($syncDirection = $request->request->get('syncDirection')) {
$updatedFields[$object][$field]['syncDirection'] = $syncDirection;
}
$session->set(sprintf('%s-fields', $integration), $updatedFields);
return new JsonResponse([]);
}
}