465 lines
19 KiB
PHP
Executable File
465 lines
19 KiB
PHP
Executable File
<?php
|
|
|
|
namespace Mautic\PluginBundle\Controller;
|
|
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use Mautic\CoreBundle\Controller\FormController;
|
|
use Mautic\CoreBundle\Helper\InputHelper;
|
|
use Mautic\PluginBundle\Event\PluginIntegrationAuthRedirectEvent;
|
|
use Mautic\PluginBundle\Event\PluginIntegrationEvent;
|
|
use Mautic\PluginBundle\Facade\ReloadFacade;
|
|
use Mautic\PluginBundle\Form\Type\DetailsType;
|
|
use Mautic\PluginBundle\Helper\IntegrationHelper;
|
|
use Mautic\PluginBundle\Integration\AbstractIntegration;
|
|
use Mautic\PluginBundle\Model\PluginModel;
|
|
use Mautic\PluginBundle\PluginEvents;
|
|
use Psr\Log\LoggerInterface;
|
|
use Symfony\Component\Form\FormError;
|
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
|
use Symfony\Component\HttpFoundation\RedirectResponse;
|
|
use Symfony\Component\HttpFoundation\Request;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
|
|
class PluginController extends FormController
|
|
{
|
|
/**
|
|
* @return JsonResponse|Response
|
|
*/
|
|
public function indexAction(Request $request, IntegrationHelper $integrationHelper)
|
|
{
|
|
if (!$this->security->isGranted('plugin:plugins:manage')) {
|
|
return $this->accessDenied();
|
|
}
|
|
|
|
/** @var PluginModel $pluginModel */
|
|
$pluginModel = $this->getModel('plugin');
|
|
|
|
// List of plugins for filter and to show as a single integration
|
|
$plugins = $pluginModel->getEntities(
|
|
[
|
|
'filter' => [
|
|
'force' => [
|
|
[
|
|
'column' => 'p.isMissing',
|
|
'expr' => 'eq',
|
|
'value' => 0,
|
|
],
|
|
],
|
|
],
|
|
'hydration_mode' => 'hydrate_array',
|
|
]
|
|
);
|
|
|
|
$session = $request->getSession();
|
|
$pluginFilter = $request->get('plugin', $session->get('mautic.integrations.filter', ''));
|
|
|
|
$session->set('mautic.integrations.filter', $pluginFilter);
|
|
|
|
$integrationObjects = $integrationHelper->getIntegrationObjects(null, null, true);
|
|
$integrations = $foundPlugins = [];
|
|
|
|
foreach ($integrationObjects as $name => $object) {
|
|
$settings = $object->getIntegrationSettings();
|
|
$plugin = $settings->getPlugin();
|
|
$pluginId = $plugin ? $plugin->getId() : $name;
|
|
if (isset($plugins[$pluginId]) || $pluginId === $name) {
|
|
$integrations[$name] = [
|
|
'name' => $object->getName(),
|
|
'display' => $object->getDisplayName(),
|
|
'icon' => $integrationHelper->getIconPath($object),
|
|
'enabled' => $settings->isPublished(),
|
|
'plugin' => $pluginId,
|
|
'isBundle' => false,
|
|
];
|
|
}
|
|
|
|
$foundPlugins[$pluginId] = true;
|
|
}
|
|
|
|
$nonIntegrationPlugins = array_diff_key($plugins, $foundPlugins);
|
|
foreach ($nonIntegrationPlugins as $plugin) {
|
|
$integrations[$plugin['name']] = [
|
|
'name' => $plugin['bundle'],
|
|
'display' => $plugin['name'],
|
|
'icon' => $integrationHelper->getIconPath($plugin),
|
|
'enabled' => true,
|
|
'plugin' => $plugin['id'],
|
|
'description' => $plugin['description'],
|
|
'isBundle' => true,
|
|
];
|
|
}
|
|
|
|
// sort by name
|
|
uksort(
|
|
$integrations,
|
|
fn ($a, $b): int => strnatcasecmp($a, $b)
|
|
);
|
|
|
|
$tmpl = $request->isXmlHttpRequest() ? $request->get('tmpl', 'index') : 'index';
|
|
|
|
if (!empty($pluginFilter)) {
|
|
foreach ($plugins as $plugin) {
|
|
if ($plugin['id'] == $pluginFilter) {
|
|
$pluginName = $plugin['name'];
|
|
$pluginId = $plugin['id'];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $this->delegateView(
|
|
[
|
|
'viewParameters' => [
|
|
'items' => $integrations,
|
|
'tmpl' => $tmpl,
|
|
'pluginFilter' => ($pluginFilter) ? ['id' => $pluginId, 'name' => $pluginName] : false,
|
|
'plugins' => $plugins,
|
|
],
|
|
'contentTemplate' => '@MauticPlugin/Integration/grid.html.twig',
|
|
'passthroughVars' => [
|
|
'activeLink' => '#mautic_plugin_index',
|
|
'mauticContent' => 'integration',
|
|
'route' => $this->generateUrl('mautic_plugin_index'),
|
|
],
|
|
]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param string $name
|
|
*
|
|
* @return JsonResponse|Response
|
|
*/
|
|
public function configAction(Request $request, EntityManagerInterface $em, IntegrationHelper $integrationHelper, LoggerInterface $mauticLogger, $name, $activeTab = 'details-container', $page = 1)
|
|
{
|
|
if (!$this->security->isGranted('plugin:plugins:manage')) {
|
|
return $this->accessDenied();
|
|
}
|
|
if (!empty($request->get('activeTab'))) {
|
|
$activeTab = $request->get('activeTab');
|
|
}
|
|
|
|
$session = $request->getSession();
|
|
|
|
$integrationDetailsPost = $request->request->all()['integration_details'] ?? [];
|
|
$authorize = empty($integrationDetailsPost['in_auth']) ? false : true;
|
|
|
|
/** @var AbstractIntegration $integrationObject */
|
|
$integrationObject = $integrationHelper->getIntegrationObject($name);
|
|
|
|
// Verify that the requested integration exists
|
|
if (empty($integrationObject)) {
|
|
throw $this->createNotFoundException($this->translator->trans('mautic.core.url.error.404'));
|
|
}
|
|
|
|
$object = ('leadFieldsContainer' === $activeTab) ? 'lead' : 'company';
|
|
$limit = $this->coreParametersHelper->get('default_pagelimit');
|
|
$start = (1 === $page) ? 0 : (($page - 1) * $limit);
|
|
if ($start < 0) {
|
|
$start = 0;
|
|
}
|
|
$session->set('mautic.plugin.'.$name.'.'.$object.'.start', $start);
|
|
$session->set('mautic.plugin.'.$name.'.'.$object.'.page', $page);
|
|
|
|
/** @var PluginModel $pluginModel */
|
|
$pluginModel = $this->getModel('plugin');
|
|
$leadFields = $pluginModel->getLeadFields();
|
|
$companyFields = $pluginModel->getCompanyFields();
|
|
/** @var AbstractIntegration $integrationObject */
|
|
$entity = $integrationObject->getIntegrationSettings();
|
|
$existingPublishedState = $entity->getIsPublished();
|
|
$form = $this->createForm(
|
|
DetailsType::class,
|
|
$entity,
|
|
[
|
|
'integration' => $entity->getName(),
|
|
'lead_fields' => $leadFields,
|
|
'company_fields' => $companyFields,
|
|
'integration_object' => $integrationObject,
|
|
'action' => $this->generateUrl('mautic_plugin_config', ['name' => $name]),
|
|
]
|
|
);
|
|
|
|
if ('POST' == $request->getMethod()) {
|
|
$valid = false;
|
|
if (!$cancelled = $this->isFormCancelled($form)) {
|
|
$currentKeys = $integrationObject->getDecryptedApiKeys($entity);
|
|
$currentFeatureSettings = $entity->getFeatureSettings();
|
|
$valid = $this->isFormValid($form);
|
|
|
|
if ($authorize || $valid) {
|
|
$integration = $entity->getName();
|
|
|
|
if (isset($form['apiKeys'])) {
|
|
$keys = $form['apiKeys']->getData();
|
|
|
|
// Prevent merged keys
|
|
$secretKeys = $integrationObject->getSecretKeys();
|
|
foreach ($secretKeys as $secretKey) {
|
|
if (empty($keys[$secretKey]) && !empty($currentKeys[$secretKey])) {
|
|
$keys[$secretKey] = $currentKeys[$secretKey];
|
|
}
|
|
}
|
|
$keys = $this->removeAuthData($keys, $currentKeys, $integrationObject);
|
|
$integrationObject->encryptAndSetApiKeys($keys, $entity);
|
|
|
|
$integrationObject->encryptAndSetApiKeys($keys, $entity);
|
|
}
|
|
|
|
if (!$authorize) {
|
|
$features = $entity->getSupportedFeatures();
|
|
if (in_array('public_profile', $features) || in_array('push_lead', $features)) {
|
|
// Ungroup the fields
|
|
$mauticLeadFields = [];
|
|
foreach ($leadFields as $groupFields) {
|
|
$mauticLeadFields = array_merge($mauticLeadFields, $groupFields);
|
|
}
|
|
$mauticCompanyFields = [];
|
|
foreach ($companyFields as $groupFields) {
|
|
$mauticCompanyFields = array_merge($mauticCompanyFields, $groupFields);
|
|
}
|
|
|
|
if ($missing = $integrationObject->cleanUpFields($entity, $mauticLeadFields, $mauticCompanyFields)) {
|
|
if ($entity->getIsPublished()) {
|
|
// Only fail validation if the integration is enabled
|
|
if (!empty($missing['leadFields'])) {
|
|
$valid = false;
|
|
|
|
$form->get('featureSettings')->get('leadFields')->addError(
|
|
new FormError(
|
|
$this->translator->trans('mautic.plugin.field.required_mapping_missing', [], 'validators')
|
|
)
|
|
);
|
|
}
|
|
|
|
if (!empty($missing['companyFields'])) {
|
|
$valid = false;
|
|
|
|
$form->get('featureSettings')->get('companyFields')->addError(
|
|
new FormError(
|
|
$this->translator->trans('mautic.plugin.field.required_mapping_missing', [], 'validators')
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// make sure they aren't overwritten because of API connection issues
|
|
$entity->setFeatureSettings($currentFeatureSettings);
|
|
}
|
|
|
|
if ($valid || $authorize) {
|
|
$dispatcher = $this->dispatcher;
|
|
$mauticLogger->info('Dispatching integration config save event.');
|
|
if ($dispatcher->hasListeners(PluginEvents::PLUGIN_ON_INTEGRATION_CONFIG_SAVE)) {
|
|
$mauticLogger->info('Event dispatcher has integration config save listeners.');
|
|
if (!$valid && !$existingPublishedState) {
|
|
$integrationObject->getIntegrationSettings()->setIsPublished(false);
|
|
}
|
|
$event = new PluginIntegrationEvent($integrationObject);
|
|
|
|
$dispatcher->dispatch($event, PluginEvents::PLUGIN_ON_INTEGRATION_CONFIG_SAVE);
|
|
|
|
$entity = $event->getEntity();
|
|
}
|
|
|
|
$em->persist($entity);
|
|
$em->flush();
|
|
}
|
|
|
|
if ($authorize) {
|
|
// redirect to the oauth URL
|
|
/** @var AbstractIntegration $integrationObject */
|
|
$event = $this->dispatcher->dispatch(
|
|
new PluginIntegrationAuthRedirectEvent(
|
|
$integrationObject,
|
|
$integrationObject->getAuthLoginUrl()
|
|
),
|
|
PluginEvents::PLUGIN_ON_INTEGRATION_AUTH_REDIRECT
|
|
);
|
|
$oauthUrl = $event->getAuthUrl();
|
|
|
|
return new JsonResponse(
|
|
[
|
|
'integration' => $integration,
|
|
'authUrl' => $oauthUrl,
|
|
'authorize' => 1,
|
|
'popupBlockerMessage' => $this->translator->trans('mautic.core.popupblocked'),
|
|
]
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (($cancelled || ($valid && !$this->isFormApplied($form))) && !$authorize) {
|
|
// Close the modal and return back to the list view
|
|
return new JsonResponse(
|
|
[
|
|
'closeModal' => 1,
|
|
'enabled' => $entity->getIsPublished(),
|
|
'name' => $integrationObject->getName(),
|
|
'mauticContent' => 'integrationConfig',
|
|
'sidebar' => $this->renderView('@MauticCore/LeftPanel/index.html.twig'),
|
|
]
|
|
);
|
|
}
|
|
}
|
|
|
|
$template = $integrationObject->getFormTemplate();
|
|
$objectTheme = $integrationObject->getFormTheme();
|
|
$themes = [
|
|
'@MauticPlugin/FormTheme/Integration/layout.html.twig',
|
|
];
|
|
if (is_array($objectTheme)) {
|
|
$themes = array_merge($themes, $objectTheme);
|
|
} elseif (is_string($objectTheme)) {
|
|
$themes[] = $objectTheme;
|
|
}
|
|
$themes = array_unique($themes);
|
|
|
|
$formSettings = $integrationObject->getFormSettings();
|
|
$callbackUrl = !empty($formSettings['requires_callback']) ? $integrationObject->getAuthCallbackUrl() : '';
|
|
|
|
$formNotes = [];
|
|
$noteSections = ['authorization', 'features', 'feature_settings', 'custom'];
|
|
foreach ($noteSections as $section) {
|
|
if ('custom' === $section) {
|
|
$formNotes[$section] = $integrationObject->getFormNotes($section);
|
|
} else {
|
|
[$specialInstructions, $alertType] = $integrationObject->getFormNotes($section);
|
|
|
|
if (!empty($specialInstructions)) {
|
|
$formNotes[$section] = [
|
|
'note' => $specialInstructions,
|
|
'type' => $alertType,
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
return $this->delegateView(
|
|
[
|
|
'viewParameters' => [
|
|
'form' => $form->createView(),
|
|
'description' => $integrationObject->getDescription(),
|
|
'formSettings' => $formSettings,
|
|
'formNotes' => $formNotes,
|
|
'callbackUrl' => $callbackUrl,
|
|
'activeTab' => $activeTab,
|
|
'formThemes' => $themes,
|
|
],
|
|
'contentTemplate' => $template,
|
|
'passthroughVars' => [
|
|
'activeLink' => '#mautic_plugin_index',
|
|
'mauticContent' => 'integrationConfig',
|
|
'route' => false,
|
|
'sidebar' => $this->renderView('@MauticCore/LeftPanel/index.html.twig'),
|
|
],
|
|
]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @return array|JsonResponse|RedirectResponse|Response
|
|
*/
|
|
public function infoAction(IntegrationHelper $integrationHelper, $name)
|
|
{
|
|
if (!$this->security->isGranted('plugin:plugins:manage')) {
|
|
return $this->accessDenied();
|
|
}
|
|
|
|
/** @var PluginModel $pluginModel */
|
|
$pluginModel = $this->getModel('plugin');
|
|
|
|
$bundle = $pluginModel->getRepository()->findOneBy(
|
|
[
|
|
'bundle' => InputHelper::clean($name),
|
|
]
|
|
);
|
|
|
|
if (!$bundle) {
|
|
return $this->accessDenied();
|
|
}
|
|
|
|
$bundle->splitDescriptions();
|
|
|
|
return $this->delegateView(
|
|
[
|
|
'viewParameters' => [
|
|
'bundle' => $bundle,
|
|
'icon' => $integrationHelper->getIconPath($bundle),
|
|
],
|
|
'contentTemplate' => '@MauticPlugin/Integration/info.html.twig',
|
|
'passthroughVars' => [
|
|
'activeLink' => '#mautic_plugin_index',
|
|
'mauticContent' => 'integration',
|
|
'route' => false,
|
|
],
|
|
]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Scans the addon bundles directly and loads bundles which are not registered to the database.
|
|
*
|
|
* @return Response
|
|
*/
|
|
public function reloadAction(Request $request, ReloadFacade $reloadFacade)
|
|
{
|
|
if (!$this->security->isGranted('plugin:plugins:manage')) {
|
|
return $this->accessDenied();
|
|
}
|
|
|
|
$this->addFlashMessage(
|
|
$reloadFacade->reloadPlugins()
|
|
);
|
|
|
|
$viewParameters = [
|
|
'page' => $request->getSession()->get('mautic.plugin.page'),
|
|
];
|
|
|
|
// Refresh the index contents
|
|
return $this->postActionRedirect(
|
|
[
|
|
'returnUrl' => $this->generateUrl('mautic_plugin_index', $viewParameters),
|
|
'viewParameters' => $viewParameters,
|
|
'contentTemplate' => 'Mautic\PluginBundle\Controller\PluginController::indexAction',
|
|
'passthroughVars' => [
|
|
'activeLink' => '#mautic_plugin_index',
|
|
'mauticContent' => 'plugin',
|
|
],
|
|
]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param array <string,mixed> $keys
|
|
* @param array <string,mixed> $currentKeys
|
|
*
|
|
* @return array <string,mixed>
|
|
*
|
|
* @phpstan-ignore-next-line Ignore as AbstractIntegration is deprecated
|
|
*/
|
|
private function removeAuthData(array $keys, array $currentKeys, AbstractIntegration $integrationObject): array
|
|
{
|
|
$resetTokens = false;
|
|
$secretKeys = array_unique(array_merge($integrationObject->getSecretKeys(), [$integrationObject->getClientIdKey()]));
|
|
|
|
foreach ($secretKeys as $secretKey) {
|
|
if (($keys[$secretKey] ?? null) !== ($currentKeys[$secretKey] ?? null)) {
|
|
$resetTokens = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!$resetTokens) {
|
|
return $keys;
|
|
}
|
|
|
|
$keysToRemove = array_unique(array_merge($integrationObject->getRefreshTokenKeys(), [$integrationObject->getAuthTokenKey()]));
|
|
|
|
return array_diff_key($keys, array_flip($keysToRemove));
|
|
}
|
|
}
|