Commit 1e591eff authored by jrockowitz's avatar jrockowitz Committed by jrockowitz

Issue #2920241 by jrockowitz: Add 'Action' handler

parent 8c815b4a
......@@ -869,6 +869,29 @@ webform.handler.*:
type: mapping
label: 'Handler settings'
webform.handler.action:
type: mapping
label: 'Action'
mapping:
states:
type: sequence
label: 'States'
sequence:
type: string
label: 'State'
notes:
label: 'Notes'
type: text
sticky:
label: 'Flag'
type: boolean
data:
label: 'Data'
type: text
debug:
type: boolean
label: 'Enable debugging'
webform.handler.log:
type: mapping
label: 'Log'
......
......@@ -31,6 +31,9 @@ function webform_theme() {
'webform_actions' => [
'render element' => 'element',
],
'webform_handler_action_summary' => [
'variables' => ['settings' => NULL, 'handler' => NULL],
],
'webform_handler_debug_summary' => [
'variables' => ['settings' => NULL, 'handler' => NULL],
],
......
......@@ -706,6 +706,13 @@ class WebformSubmission extends ContentEntityBase implements WebformSubmissionIn
return parent::save();
}
/**
* {@inheritdoc}
*/
public function resave() {
return $this->entityManager()->getStorage($this->entityTypeId)->resave($this);
}
/**
* {@inheritdoc}
*/
......@@ -757,7 +764,6 @@ class WebformSubmission extends ContentEntityBase implements WebformSubmissionIn
}
}
/**
* Default value callback for 'uid' base field definition.
*
......
This diff is collapsed.
......@@ -594,6 +594,9 @@ class EmailWebformHandler extends WebformHandlerBase implements WebformHandlerMe
}
}
}
// Cast debug.
$this->configuration['debug'] = (bool) $this->configuration['debug'];
}
/**
......@@ -861,8 +864,8 @@ class EmailWebformHandler extends WebformHandlerBase implements WebformHandlerMe
$build = [
'#theme' => 'webform_email_message_' . (($this->configuration['html']) ? 'html' : 'text'),
'#message' => [
'body' => is_string($message['body']) ? Markup::create($message['body']) : $message['body'],
] + $message,
'body' => is_string($message['body']) ? Markup::create($message['body']) : $message['body'],
] + $message,
'#webform_submission' => $webform_submission,
'#handler' => $this,
];
......
......@@ -302,6 +302,9 @@ class RemotePostWebformHandler extends WebformHandlerBase {
if ($this->configuration['method'] === 'GET') {
$this->configuration['type'] = '';
}
// Cast debug.
$this->configuration['debug'] = (bool) $this->configuration['debug'];
}
/**
......@@ -386,10 +389,10 @@ class RemotePostWebformHandler extends WebformHandlerBase {
$token_data = ['webform_handler' => [$this->getHandlerId() => [$state => $response_data]]];
$submission_data = $this->tokenManager->replace($submission_data, $webform_submission, $token_data);
$webform_submission->setData($submission_data);
// Save changes to the submission data without invoking any hooks
// Resave changes to the submission data without invoking any hooks
// or handlers.
if ($this->isResultsEnabled()) {
$this->submissionStorage->saveData($webform_submission);
$webform_submission->resave();
}
}
}
......
......@@ -7,6 +7,7 @@ use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Plugin\PluginBase;
use Drupal\Core\Render\Element;
use Drupal\webform\WebformInterface;
use Drupal\webform\WebformSubmissionConditionsValidatorInterface;
use Drupal\webform\WebformSubmissionInterface;
......@@ -411,7 +412,7 @@ abstract class WebformHandlerBase extends PluginBase implements WebformHandlerIn
protected function applyFormStateToConfiguration(FormStateInterface $form_state) {
$values = $form_state->getValues();
foreach ($values as $key => $value) {
if (isset($this->configuration[$key])) {
if (array_key_exists($key, $this->configuration)) {
$this->configuration[$key] = $value;
}
}
......@@ -537,7 +538,48 @@ abstract class WebformHandlerBase extends PluginBase implements WebformHandlerIn
public function deleteElement($key, array $element) {}
/****************************************************************************/
// Loggin methods.
// Form helper methods.
/****************************************************************************/
/**
* Set configuration settings parents.
*
* This helper method looks looks for the handler default configuration keys
* within a form and set a matching element's #parents property to
* ['settings', '{element_kye}']
*
* @param array $elements
* An array of form elements.
*
* @return array
* Form element with #parents set.
*/
protected function setSettingsParentsRecursively(array &$elements) {
$default_configuration = $this->defaultConfiguration();
foreach ($elements as $element_key => &$element) {
// Only a form element can have #parents.
if (Element::property($element_key) || !is_array($element)) {
continue;
}
// If the element has #parents property assume that it has also been
// defined for all sub-elements.
if (isset($element['#parents'])) {
continue;
}
if (isset($default_configuration[$element_key]) && isset($element['#type'])) {
$element['#parents'] = ['settings', $element_key];
}
else {
$this->setSettingsParentsRecursively($element);
}
}
return $elements;
}
/****************************************************************************/
// Logging methods.
/****************************************************************************/
/**
......
......@@ -42,7 +42,7 @@ class WebformHandlerExcludedTest extends WebformTestBase {
$this->assertResponse(403);
// Exclude the email handler.
\Drupal::configFactory()->getEditable('webform.settings')->set('handler.excluded_handlers', ['broken' => 'broken', 'debug' => 'debug', 'email' => 'email', 'remote_post' => 'remote_post'])->save();
\Drupal::configFactory()->getEditable('webform.settings')->set('handler.excluded_handlers', ['action' => 'action', 'broken' => 'broken', 'debug' => 'debug', 'email' => 'email', 'remote_post' => 'remote_post'])->save();
// Check add mail and handler hidden.
$this->drupalGet('admin/structure/webform/manage/contact/handlers');
......
......@@ -248,7 +248,7 @@ interface WebformSubmissionInterface extends ContentEntityInterface, EntityOwner
* Set a webform submission element's data.
*
* @param string $key
* An webform submission element's key
* An webform submission element's key.
* @param mixed $value
* A value.
*
......@@ -356,6 +356,11 @@ interface WebformSubmissionInterface extends ContentEntityInterface, EntityOwner
*/
public function convert(UserInterface $account);
/**
* Resave a webform submission without trigger any hooks or handlers.
*/
public function resave();
/**
* Gets an array of all property values.
*
......
......@@ -6,6 +6,7 @@ use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Database\Query\AlterableInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityStorageException;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\Query\QueryInterface;
use Drupal\Core\Language\LanguageManagerInterface;
......@@ -715,33 +716,6 @@ class WebformSubmissionStorage extends SqlContentEntityStorage implements Webfor
$this->setAnonymousSubmission($entity);
}
// DEBUG: dsm($entity->getState());
// Log transaction.
$webform = $entity->getWebform();
$context = [
'@id' => $entity->id(),
'@form' => $webform->label(),
'link' => $entity->toLink($this->t('Edit'), 'edit-form')->toString(),
];
switch ($entity->getState()) {
case WebformSubmissionInterface::STATE_DRAFT:
\Drupal::logger('webform')->notice('@form: Submission #@id draft saved.', $context);
break;
case WebformSubmissionInterface::STATE_UPDATED:
\Drupal::logger('webform')->notice('@form: Submission #@id updated.', $context);
break;
case WebformSubmissionInterface::STATE_COMPLETED:
if ($result === SAVED_NEW) {
\Drupal::logger('webform')->notice('@form: Submission #@id created.', $context);
}
else {
\Drupal::logger('webform')->notice('@form: Submission #@id completed.', $context);
}
break;
}
return $result;
}
......@@ -752,6 +726,34 @@ class WebformSubmissionStorage extends SqlContentEntityStorage implements Webfor
/** @var \Drupal\webform\WebformSubmissionInterface $entity */
parent::doPostSave($entity, $update);
// Log transaction.
$webform = $entity->getWebform();
if (!$entity->getWebform()->getSetting('results_disabled')) {
$context = [
'@id' => $entity->id(),
'@form' => $webform->label(),
'link' => $entity->toLink($this->t('Edit'), 'edit-form')->toString(),
];
switch ($entity->getState()) {
case WebformSubmissionInterface::STATE_DRAFT:
\Drupal::logger('webform')->notice('@form: Submission #@id draft saved.', $context);
break;
case WebformSubmissionInterface::STATE_UPDATED:
\Drupal::logger('webform')->notice('@form: Submission #@id updated.', $context);
break;
case WebformSubmissionInterface::STATE_COMPLETED:
if ($update) {
\Drupal::logger('webform')->notice('@form: Submission #@id completed.', $context);
}
else {
\Drupal::logger('webform')->notice('@form: Submission #@id created.', $context);
}
break;
}
}
// Log submission events.
if ($entity->getWebform()->hasSubmissionLog()) {
$t_args = ['@title' => $entity->label()];
......@@ -808,6 +810,27 @@ class WebformSubmissionStorage extends SqlContentEntityStorage implements Webfor
$this->invokeWebformHandlers('postSave', $entity, $update);
}
/**
* {@inheritdoc}
*/
public function resave(EntityInterface $entity) {
/** @var \Drupal\webform\WebformSubmissionInterface $entity */
$transaction = $this->database->startTransaction();
try {
$return = $this->doSave($entity->id(), $entity);
// Ignore replica server temporarily.
db_ignore_replica();
return $return;
}
catch (\Exception $e) {
$transaction->rollBack();
watchdog_exception($this->entityTypeId, $e);
throw new EntityStorageException($e->getMessage(), $e->getCode(), $e);
}
}
/**
* {@inheritdoc}
*/
......
......@@ -367,6 +367,25 @@ interface WebformSubmissionStorageInterface extends ContentEntityStorageInterfac
*/
public function getCustomSetting($name, $default, WebformInterface $webform = NULL, EntityInterface $source_entity = NULL);
/****************************************************************************/
// Custom CRUD methods.
/****************************************************************************/
/**
* Resaves the entity without triggering any hooks or handlers.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity to save.
*
* @return
* SAVED_NEW or SAVED_UPDATED is returned depending on the operation
* performed.
*
* @throws \Drupal\Core\Entity\EntityStorageException
* In case of failures, an exception is thrown.
*/
public function resave(EntityInterface $entity);
/****************************************************************************/
// Invoke methods.
/****************************************************************************/
......
{#
/**
* @file
* Default theme implementation for a summary of a webform action handler.
*
* Available variables:
* - settings: The current configuration for this debug handler.
* - handler: The action handler.
*
* @ingroup themeable
*/
#}
{% if settings.debug %}<b class="color-error">{{ 'Debugging is enabled'|t }}</b><br />{% endif %}
{% if settings.sticky is not null %}<b>{{ 'Status:'|t }}</b> {{ settings.sticky ? 'Flag/Star'|t : 'Unflag/Unstar'|t }}<br />{% endif %}
{% if settings.notes %}<b>{{ 'Notes:'|t }}</b> {{ settings.notes }}<br />{% endif %}
{% if settings.data %}<b>{{ 'Data (keys):'|t }}</b> {{ settings.data|join('; ') }}<br />{% endif %}
<b>{{ 'Execute when:'|t }}</b> {{ settings.states|join('; ') }}<br />
......@@ -16,5 +16,5 @@
{% if settings.bcc_mail %}<b>{{ 'BCC:'|t }}</b> {{ settings.bcc_mail }}<br />{% endif %}
<b>{{ 'From:'|t }}</b> {% if settings.from_name %}{{ settings.from_name }}{% endif %} &lt;{{ settings.from_mail }}&gt;<br />
<b>{{ 'Subject:'|t }}</b> {{ settings.subject }}<br />
<b>{{ 'Settings:'|t }}</b> {{ settings.html ? 'HTML' : 'Plain text'|t }}{{ settings.html and settings.attachments ? '/' : '' }}{{ settings.attachments ? 'Attachments '|t : '' }}<br />
<b>{{ 'Settings:'|t }}</b> {{ settings.html ? 'HTML' : 'Plain text'|t }}{{ settings.html and settings.attachments ? '/' : '' }}{{ settings.attachments ? 'Attachments'|t : '' }}<br />
<b>{{ 'Sent when:'|t }}</b> {{ settings.states|join('; ') }}<br />
......@@ -47,7 +47,9 @@
<div><b>{{ 'Flagged'|t }}:</b> {{ sticky }}</div>
{% endif %}
{% if notes %}
<div><b>{{ 'Notes'|t }}:</b> {{ notes }}</div>
<div><b>{{ 'Notes'|t }}:</b><br/>
<pre>{{ notes }}</pre>
</div>
{% endif %}
{% endif %}
......
uuid: 76ccaf9f-f2c5-4fda-9939-316ce3bdc8f4
langcode: en
status: open
dependencies:
enforced:
module:
- webform_test
_core:
default_config_hash: HXJTQ6yD8Ij1iWN_h9J_dG9hxJzLDyHhxn-GHiFwfWY
open: null
close: null
uid: null
template: false
id: test_handler_action
title: 'Test: Handler: Action'
description: 'Test action handler.'
category: 'Test: Handler'
elements: |
status:
'#type': fieldset
'#title': Status
sticky:
'#type': radios
'#title': Status
'#title_display': invisible
'#options':
flag: Flag/Star
unflag: Unflag/Unstar
notes:
'#type': fieldset
'#title': Notes
notes_add:
'#title': 'Add note'
'#type': textarea
notes_last:
'#title': 'Last note'
'#type': textarea
'#attributes':
readonly: readonly
style: 'background-color: #eee'
css: ''
javascript: ''
settings:
ajax: false
ajax_scroll_top: form
page: true
page_submit_path: ''
page_confirm_path: ''
form_submit_once: false
form_exception_message: ''
form_open_message: ''
form_close_message: ''
form_previous_submissions: true
form_confidential: false
form_confidential_message: ''
form_convert_anonymous: false
form_prepopulate: false
form_prepopulate_source_entity: false
form_prepopulate_source_entity_required: false
form_prepopulate_source_entity_type: ''
form_reset: false
form_disable_autocomplete: false
form_novalidate: false
form_unsaved: false
form_disable_back: false
form_autofocus: false
form_details_toggle: false
submission_label: ''
submission_log: false
submission_user_columns: { }
wizard_progress_bar: true
wizard_progress_pages: false
wizard_progress_percentage: false
wizard_start_label: ''
wizard_confirmation: true
wizard_confirmation_label: ''
preview: 1
preview_label: ''
preview_title: ''
preview_message: ''
preview_attributes: { }
preview_excluded_elements: { }
preview_exclude_empty: true
draft: authenticated
draft_multiple: false
draft_auto_save: false
draft_saved_message: ''
draft_loaded_message: ''
confirmation_type: page
confirmation_title: ''
confirmation_message: ''
confirmation_url: ''
confirmation_attributes: { }
confirmation_back: true
confirmation_back_label: ''
confirmation_back_attributes: { }
limit_total: null
limit_total_message: ''
limit_user: null
limit_user_message: ''
purge: none
purge_days: null
entity_limit_total: null
entity_limit_user: null
results_disabled: false
results_disabled_ignore: false
token_update: false
access:
create:
roles:
- anonymous
- authenticated
users: { }
permissions: { }
view_any:
roles: { }
users: { }
permissions: { }
update_any:
roles: { }
users: { }
permissions: { }
delete_any:
roles: { }
users: { }
permissions: { }
purge_any:
roles: { }
users: { }
permissions: { }
view_own:
roles: { }
users: { }
permissions: { }
update_own:
roles: { }
users: { }
permissions: { }
delete_own:
roles: { }
users: { }
permissions: { }
handlers:
notes:
id: action
label: 'Notes'
handler_id: notes
status: true
conditions:
enabled:
':input[name="notes_add"]':
filled: true
weight: -48
settings:
states:
- draft
- completed
- updated
notes: '[webform_submission:values:notes_add]'
sticky: null
data: |
notes_add: ''
notes_last: '[webform_submission:values:notes_add]'
debug: true
flag:
id: action
label: Flag
handler_id: flag
status: true
conditions:
enabled:
':input[name="sticky"]':
value: flag
weight: -50
settings:
states:
- draft
- converted
- completed
- updated
notes: ''
sticky: true
data: ''
debug: true
unflag:
id: action
label: Unflag
handler_id: unflag
status: true
conditions:
enabled:
':input[name="sticky"]':
value: unflag
weight: -49
settings:
states:
- draft
- converted
- completed
- updated
notes: ''
sticky: false
data: ''
debug: true
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment