Commit 95fd4136 authored by Thomas SECHER's avatar Thomas SECHER
Browse files

Add CKEditor plugin

parent fcad5cfc
/**
* @file
* TriggerApiCKeditorPlugin CKEditor plugin.
*
* Basic plugin inserting abbreviation elements into the CKEditor editing area.
*
* @DCG The code is based on an example from CKEditor Plugin SDK tutorial.
*
* @see http://docs.ckeditor.com/#!/guide/plugin_sdk_sample_1
*/
(function (Drupal) {
'use strict';
CKEDITOR.plugins.add('trigger_api_ckeditor_plugin', {
// Register the icons.
icons: 'trigger-api-plugin',
// The plugin initialization logic goes inside this method.
init: function (editor) {
// Add custom command.
// @todo
// editor.addCommand('triggerApiPlugin', {
// allowedContent:
// 'a',
// modes: { wysiwyg: 1 },
// canUndo: true,
// exec(editor, data) {
// const dialogSettings = {
// title: data.dialogTitle,
// dialogClass: 'editor-image-dialog',
// };
// Drupal.ckeditor.openDialog(
// editor,
// Drupal.url("/trigger-api/trigger-api-ckeditor"),
// data.existingValues,
// data.saveCallback,
// dialogSettings,
// );
// },
// });
// Create a toolbar button that executes the above command.
editor.ui.addButton('trigger-api-plugin', {
// The text part of the button (if available) and the tooltip.
label: Drupal.t('Insert Trigger API data'),
// The command to execute on click.
command: 'triggerApiPlugin',
// The button placement in the toolbar (toolbar group name).
toolbar: 'insert'
});
}
});
} (Drupal));
/**
* @file
* Defines dialog for TriggerApiCKeditorPlugin CKEditor plugin.
* Defines dialog for CKEditor CKEditor plugin.
*/
(function (Drupal) {
......@@ -8,7 +8,7 @@
'use strict';
// Dialog definition.
CKEDITOR.dialog.add('ckeditorPluginDialog', function (editor) {
CKEDITOR.dialog.add('ckeditorDialog', function (editor) {
return {
......@@ -26,15 +26,6 @@
// The tab content.
elements: [
{
// Text input field for the abbreviation text.
type: 'text',
id: 'abbr',
label: Drupal.t('Abbreviation'),
// Validation checking whether the field is not empty.
validate: CKEDITOR.dialog.validate.notEmpty(Drupal.t('Abbreviation field cannot be empty.'))
},
{
// Text input field for the abbreviation text.
type: 'text',
......
/**
* @file
* CKEditor CKEditor plugin.
*
* Basic plugin inserting abbreviation elements into the CKEditor editing area.
*
* @DCG The code is based on an example from CKEditor Plugin SDK tutorial.
*
* @see http://docs.ckeditor.com/#!/guide/plugin_sdk_sample_1
*/
(function (Drupal) {
'use strict';
CKEDITOR.plugins.add('trigger_api_ckeditor', {
// Register the icons.
icons: 'ckeditor',
// The plugin initialization logic goes inside this method.
init: function (editor) {
// Define an editor command that opens our dialog window.
// editor.addCommand('ckeditor', new CKEDITOR.dialogCommand('ckeditorDialog'));
// Create a toolbar button that executes the above command.
editor.ui.addButton('ckeditor', {
// The text part of the button (if available) and the tooltip.
label: Drupal.t('Insert abbreviation'),
// The command to execute on click.
command: 'ckeditor',
// The button placement in the toolbar (toolbar group name).
toolbar: 'insert'
});
// Register our dialog file, this.path is the plugin folder path.
// CKEDITOR.dialog.add('ckeditorDialog', this.path + 'dialogs/ckeditor.js');
editor.addCommand('ckeditor', new TriggerAPICKEditorCommandClass('ckeditorDialog'));
}
});
}(Drupal));
class TriggerAPICKEditorCommandClass {
constructor(id) {
this.id = id;
// this.allowedContent = this.getAllowedContent()
// this.requiredContent = this.getRequiredContent()
this.modes = {wysiwyg: 1}
this.canUndo = true;
}
/**
* On click on ckeditor plugin button.
* @param editor
*/
exec(editor) {
const dialogSettings = {
title: 'Trigger API',
dialogClass: 'editor-trigger-api-dialog'
};
const parentItem = editor.getSelection().getStartElement();
/**
* Save callback.
*
* @param returnValues
*/
const saveCallback = function (returnValues) {
editor.fire('saveSnapshot');
parentItem.setAttribute('data-trigger-api-on', returnValues['data-trigger-api-on'])
parentItem.setAttribute('data-trigger-api-type', returnValues['data-trigger-api-type'])
parentItem.setAttribute('data-trigger-api-data', returnValues['data-trigger-api-data'])
// Save snapshot for undo support.
editor.fire('saveSnapshot');
}
/**
* Existing values.
* @type {{"data-trigger-api-on": string, "data-trigger-api-type": string, "data-trigger-api-data": string}}
*/
if (parentItem) {
const existingValue = {
'data-trigger-api-on': parentItem.getAttribute('data-trigger-api-on') || '',
'data-trigger-api-type': parentItem.getAttribute('data-trigger-api-type') || '',
'data-trigger-api-data': parentItem.getAttribute('data-trigger-api-data') || '',
}
// Open the dialog for the edit form.
Drupal.ckeditor.openDialog(editor,
Drupal.url('trigger-api/trigger-api-ckeditor/' + editor.config.drupal.format),
existingValue,
saveCallback,
dialogSettings
);
} else {
alert("You should select an item");
}
}
}
......@@ -5,6 +5,9 @@ namespace Drupal\trigger_api\Ajax;
use Drupal\Core\Ajax\CommandInterface;
use Drupal\trigger_api\Data\TriggerQueueDataInterface;
/**
* Ajax Commands that dispatch trigger api events.
*/
class TriggerApiCommand implements CommandInterface {
/**
......@@ -15,7 +18,10 @@ class TriggerApiCommand implements CommandInterface {
protected TriggerQueueDataInterface $triggerQueueData;
/**
* Constructor.
*
* @param \Drupal\trigger_api\Data\TriggerQueueDataInterface $triggerQueueData
* The trigger queue data.
*/
public function __construct(TriggerQueueDataInterface $triggerQueueData) {
$this->triggerQueueData = $triggerQueueData;
......
......@@ -24,6 +24,8 @@ class TriggerQueueData implements TriggerQueueDataInterface {
protected array $data;
/**
* Constructor.
*
* @param string $type
* The type of trigger.
* @param array $data
......
......@@ -26,7 +26,8 @@ interface TriggerQueueDataInterface {
/**
* Return the serialized data.
*
* @return string
* @return array
* The json serializable data.
*/
public function getSerializableData(): array;
......
......@@ -4,21 +4,26 @@ namespace Drupal\trigger_api\Element;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Render\Element\Hidden;
use Drupal\trigger_api\Plugin\WebformElement\WebformTriggerApi;
/**
* Candidature selector field
* Candidature selector field.
*
* @FormElement("webform_trigger_api")
*/
class TriggerApiElement extends Element\Hidden {
class TriggerApiElement extends Hidden {
/**
* The global formstate.
*
* @var \Drupal\Core\Form\FormStateInterface
*/
protected FormStateInterface $globalFormState;
/**
* {@inheritdoc}
*/
public function getInfo() {
$values = parent::getInfo();
$values['#process'][] = [$this, 'process'];
......@@ -49,7 +54,7 @@ class TriggerApiElement extends Element\Hidden {
* @return mixed|string
* THe value.
*/
public function postRender($content, $element) {
public function postRender($content, array $element) {
$hasErrors = count($this->globalFormState->getErrors()) > 0;
$eventTypes = array_filter($element['#' . WebformTriggerApi::FIELD_EVENT_TYPE]);
......
......@@ -10,15 +10,11 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
/**
* Event subscriber that automatically add TriggerApoCommand in Ajax response.
*/
class ResponseEventSubscriber implements EventSubscriberInterface, TrustedCallbackInterface {
/**
* Nom du service.
*
* @const string
*/
const SERVICE_NAME = '';
/**
* Trigger Queue.
*
......@@ -27,28 +23,21 @@ class ResponseEventSubscriber implements EventSubscriberInterface, TrustedCallba
protected $triggerQueue;
/**
* Constructor.
*
* @param \Drupal\trigger_api\Services\TriggerQueueInterface $triggerQueue
* The trigger queue manager.
*/
public function __construct(TriggerQueueInterface $triggerQueue) {
$this->triggerQueue = $triggerQueue;
}
/**
* Retourne le singleton.
*
* @return static
* Le singleton.
*/
public static function me() {
return \Drupal::service(static::SERVICE_NAME);
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
return [
KernelEvents::RESPONSE => ['onResponse', 255]
KernelEvents::RESPONSE => ['onResponse', 255],
];
}
......
......@@ -2,14 +2,15 @@
namespace Drupal\trigger_api\Exceptions;
use Throwable;
/**
* Exception triggered when item is added into an already sent queue.
*/
class AlreadyTriggeredException extends \Exception {
/**
* {@inheritdoc}
*/
public function __construct($message = "", $code = 0, Throwable $previous = NULL) {
public function __construct($message = "", $code = 0, \Throwable $previous = NULL) {
$message = empty($message) ? "Trigger queue has already been triggered. You cannot add queue items anymore." : $message;
parent::__construct($message, $code, $previous);
}
......
<?php
namespace Drupal\trigger_api\Form\Traits;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Form\FormStateInterface;
/**
* Validation for API Data format.
*/
trait TriggerAPIFormValidatorTrait {
/**
* Validate JSON.
*
* @param array $element
* The element.
* @param \Drupal\Core\Form\FormStateInterface $formState
* The formstate.
*/
public function validateJson(array &$element, FormStateInterface $formState) {
try {
if (!Json::decode($element['#value'])) {
$noJson = TRUE;
}
}
catch (\Exception $e) {
$noJson = TRUE;
}
if ($noJson) {
$formState->setError($element, $this->t('The value is not json valid'));
}
}
}
......@@ -2,41 +2,135 @@
namespace Drupal\trigger_api\Form;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\CloseModalDialogCommand;
use Drupal\Core\Ajax\HtmlCommand;
use Drupal\Core\Form\BaseFormIdInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\editor\Ajax\EditorDialogSave;
use Drupal\filter\Entity\FilterFormat;
use Drupal\trigger_api\Form\Traits\TriggerAPIFormValidatorTrait;
/**
* Provides a Analytics trigger form.
*/
class TriggerApiCKEditorForm extends FormBase {
class TriggerApiCKEditorForm extends FormBase implements BaseFormIdInterface {
use TriggerAPIFormValidatorTrait;
/**
* Field Trigger Type.
*
* @const string
*/
public const FIELD_TRIGGER_TYPE = 'data-trigger-api-on';
/**
* Field Type.
*
* @const string
*/
public const FIELD_TYPE = 'data-trigger-api-type';
/**
* FIELD data.
*
* @const string
*/
public const FIELD_DATA = 'data-trigger-api-data';
/**
* TRIGGER_TYPE APPEAR.
*
* @const string
*/
public const TRIGGER_TYPE_APPEAR = 'appear';
/**
* TRIGGER TYPE CLICK.
*
* @const string
*/
public const TRIGGER_TYPE_CLICK = 'click';
/**
* TRIGGER TYPE OTHER.
*
* @const string
*/
public const TRIGGER_TYPE_OTHER = 'other';
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'trigger_api_trigger_api_c_k_editor';
return 'trigger_api_trigger_api_ck_editor';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
public function buildForm(array $form, FormStateInterface $form_state, FilterFormat $filterFormat = NULL) {
$inputValues = $form_state->getUserInput()['editor_object'] ?? [];
$form['message'] = [
'#type' => 'textarea',
'#title' => $this->t('Message'),
'#required' => TRUE,
$form['#attached']['library'][] = 'editor/drupal.editor.dialog';
$form['#prefix'] = '<div id="' . $this->getBaseFormId() . '">';
$form['#suffix'] = '</div>';
// FIeld trigger type.
$form[static::FIELD_TRIGGER_TYPE] = [
'#type' => 'select',
'#title' => $this->t('Trigger type'),
'#options' => $this->getTriggerTypeOptions(),
'#default_value' => $this->getTriggerTypeDefaultValue($inputValues),
'#required' => TRUE,
];
// Field trigger type other.
$state = [
'select[name="' . static::FIELD_TRIGGER_TYPE . '"]' => ['value' => 'other'],
];
$form[static::FIELD_TRIGGER_TYPE . '_other'] = [
'#type' => 'textfield',
'#title' => $this->t('Other'),
'#states' => [
'visible' => $state,
'required' => $state,
],
'#default_value' => $this->getTriggerTypeOtherDefaultValue($inputValues),
];
// TYpe.
$form[static::FIELD_TYPE] = [
'#type' => 'textfield',
'#title' => $this->t('Event type'),
'#default_value' => isset($inputValues[static::FIELD_TYPE]) ? $inputValues[static::FIELD_TYPE] : '',
'#required' => TRUE,
];
// Data.
$form[static::FIELD_DATA] = [
'#type' => 'textarea',
'#title' => $this->t('Data'),
'#element_validate' => [[$this, 'validateJson']],
'#default_value' => isset($inputValues[static::FIELD_DATA]) ? $inputValues[static::FIELD_DATA] : '',
'#required' => TRUE,
];
// Validate.
$form['actions'] = [
'#type' => 'actions',
];
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Send'),
'#type' => 'submit',
'#value' => $this->t('Send'),
'#submit' => [],
'#ajax' => [
'callback' => '::submitForm',
'event' => 'click',
],
];
return $form;
......@@ -45,21 +139,97 @@ class TriggerApiCKEditorForm extends FormBase {
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
if (mb_strlen($form_state->getValue('message')) < 10) {
$form_state->setErrorByName('name', $this->t('Message should be at least 10 characters.'));
public function submitForm(array &$form, FormStateInterface $form_state) {
$response = new AjaxResponse();
if (count($form_state->getErrors()) === 0) {
$response->addCommand(new EditorDialogSave($this->getCleanValues($form_state)));
$response->addCommand(new CloseModalDialogCommand());
}
else {
$response->addCommand(new HtmlCommand('#' . $this->getBaseFormId(), $form));
}
return $response;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$response = new AjaxResponse();
$response->addCommand(new EditorDialogSave($form_state->getValues()));
$response->addCommand(new CloseModalDialogCommand());
public function getBaseFormId() {
return 'editor_trigger_api_dialog';
}
return $response;
/**
* Return the list of available trigger types.
*
* @return array
* The available trigger types.
*/
protected function getTriggerTypeOptions() {
return [
static::TRIGGER_TYPE_APPEAR => $this->t('Appear'),
static::TRIGGER_TYPE_CLICK => $this->t('Click'),
static::TRIGGER_TYPE_OTHER => $this->t('Other'),
];
}
/**
* Return the trigger type default value.
*
* @param array $inputs
* The inputs.
*
* @return mixed|string
* The default trigger type.
*/
protected function getTriggerTypeDefaultValue(array $inputs = []) {
$otherValue = $this->getTriggerTypeOtherDefaultValue($inputs);
if ($otherValue) {
return static::TRIGGER_TYPE_OTHER;
}
return isset($inputs[static::FIELD_TRIGGER_TYPE]) ? $inputs[static::FIELD_TRIGGER_TYPE] : static::TRIGGER_TYPE_APPEAR;
}
/**
* Return the value of other trigger type if not in options list.
*
* @param array $inputs
* The input values.
*
* @return mixed|null
* The vaue of other field.
*/
protected function getTriggerTypeOtherDefaultValue(array $inputs = []) {