diff --git a/config/install/system.action.webform_submission_make_lock_action.yml b/config/install/system.action.webform_submission_make_lock_action.yml
new file mode 100644
index 00000000..ee0a349b
--- /dev/null
+++ b/config/install/system.action.webform_submission_make_lock_action.yml
@@ -0,0 +1,10 @@
+langcode: en
+status: true
+dependencies:
+ module:
+ - webform
+id: webform_submission_make_lock_action
+label: 'Lock submission'
+type: webform_submission
+plugin: webform_submission_make_lock_action
+configuration: { }
diff --git a/config/install/system.action.webform_submission_make_unlock_action.yml b/config/install/system.action.webform_submission_make_unlock_action.yml
new file mode 100644
index 00000000..ac0b4685
--- /dev/null
+++ b/config/install/system.action.webform_submission_make_unlock_action.yml
@@ -0,0 +1,10 @@
+langcode: en
+status: true
+dependencies:
+ module:
+ - webform
+id: webform_submission_make_unlock_action
+label: 'Unlock submission'
+type: webform_submission
+plugin: webform_submission_make_unlock_action
+configuration: { }
diff --git a/config/install/webform.settings.yml b/config/install/webform.settings.yml
index caa19927..d260fceb 100644
--- a/config/install/webform.settings.yml
+++ b/config/install/webform.settings.yml
@@ -37,6 +37,7 @@ settings:
default_submission_label: '[webform_submission:submitted-to]: Submission #[webform_submission:serial]'
default_submission_login_message: 'Please login to access this submission.'
default_submission_exception_message: 'Unable to process this submission. Please contact the site administrator.'
+ default_submission_locked_message: 'This submission has been locked.'
default_submission_log: false
preview_classes: |
messages messages--error
diff --git a/config/schema/webform.action.schema.yml b/config/schema/webform.action.schema.yml
index 5fde3495..0cf20edb 100644
--- a/config/schema/webform.action.schema.yml
+++ b/config/schema/webform.action.schema.yml
@@ -9,3 +9,11 @@ action.configuration.webform_submission_make_sticky_action:
action.configuration.webform_submission_make_unsticky_action:
type: action_configuration_default
label: 'Unstar/Unflag selected submission configuration'
+
+action.configuration.webform_submission_make_lock_action:
+ type: action_configuration_default
+ label: 'Lock selected submission configuration'
+
+action.configuration.webform_submission_make_unlock_action:
+ type: action_configuration_default
+ label: 'Unlock selected submission configuration'
diff --git a/config/schema/webform.entity.webform.schema.yml b/config/schema/webform.entity.webform.schema.yml
index e54cf625..8442c249 100644
--- a/config/schema/webform.entity.webform.schema.yml
+++ b/config/schema/webform.entity.webform.schema.yml
@@ -131,6 +131,9 @@ webform.webform.*:
submission_exception_message:
type: text
label: 'Submission exception message'
+ submission_locked_message:
+ type: text
+ label: 'Submission locked message'
submission_log:
type: boolean
label: 'Submission logging'
diff --git a/config/schema/webform.plugin.handler.schema.yml b/config/schema/webform.plugin.handler.schema.yml
index b30f8673..735c24a4 100644
--- a/config/schema/webform.plugin.handler.schema.yml
+++ b/config/schema/webform.plugin.handler.schema.yml
@@ -18,6 +18,9 @@ webform.handler.action:
sticky:
label: 'Flag'
type: boolean
+ locked:
+ label: 'Locked'
+ type: boolean
data:
label: 'Data'
type: text
diff --git a/config/schema/webform.settings.schema.yml b/config/schema/webform.settings.schema.yml
index 71c6bf0c..ef20fd0c 100644
--- a/config/schema/webform.settings.schema.yml
+++ b/config/schema/webform.settings.schema.yml
@@ -108,6 +108,9 @@ webform.settings:
default_submission_exception_message:
type: text
label: 'Default submission exception message'
+ default_submission_locked_message:
+ type: text
+ label: 'Default submission locked message'
form_classes:
type: string
label: 'Form CSS classes '
diff --git a/css/webform.admin.css b/css/webform.admin.css
index ce0a8392..bc03bcee 100644
--- a/css/webform.admin.css
+++ b/css/webform.admin.css
@@ -91,7 +91,7 @@ a.webform-results__custom + .ajax-progress-throbber {
}
/**
- * Results icons (sticky & notes)
+ * Results icons (notes, sticky, and locked)
*/
.webform-icon {
display: inline-block;
@@ -105,6 +105,8 @@ a.webform-results__custom + .ajax-progress-throbber {
vertical-align: -2px;
}
+/* Notes */
+
.webform-icon-notes--on {
background-image: url(../images/icons/notes-on.svg);
}
@@ -124,6 +126,8 @@ a:focus .webform-icon-notes--off {
background-image: url(../images/icons/notes-link.svg);
}
+/* Sticky */
+
.webform-icon-sticky {
background: transparent url(../images/icons/sticky.svg) no-repeat left top;
display: inline-block;
@@ -146,6 +150,27 @@ a:focus .webform-icon-notes--off {
background: transparent url(../images/icons/sticky-link.svg) no-repeat left top;
}
+/* Locked */
+
+.webform-icon-locked--on {
+ background-image: url(../images/icons/locked-on.svg);
+}
+
+.webform-icon-locked--off {
+ background-image: url(../images/icons/locked-off.svg);
+}
+
+.webform-icon-locked--link {
+ background-image: url(../images/icons/locked-link.svg);
+}
+
+a:hover .webform-icon-locked--on,
+a:focus .webform-icon-locked--on,
+a:hover .webform-icon-locked--off,
+a:focus .webform-icon-locked--off {
+ background-image: url(../images/icons/locked-link.svg);
+}
+
/**
* Submission view table.
* @see /admin/structure/webform/manage/{webform_id}/submission/{webform_submission_id}/table
diff --git a/images/icons/locked-link.svg b/images/icons/locked-link.svg
new file mode 100644
index 00000000..90a510b4
--- /dev/null
+++ b/images/icons/locked-link.svg
@@ -0,0 +1,2 @@
+
+
diff --git a/images/icons/locked-off.svg b/images/icons/locked-off.svg
new file mode 100644
index 00000000..cfea31d3
--- /dev/null
+++ b/images/icons/locked-off.svg
@@ -0,0 +1,2 @@
+
+
diff --git a/images/icons/locked-on.svg b/images/icons/locked-on.svg
new file mode 100644
index 00000000..0f82e5b8
--- /dev/null
+++ b/images/icons/locked-on.svg
@@ -0,0 +1,2 @@
+
+
diff --git a/includes/webform.install.update.inc b/includes/webform.install.update.inc
index 1d564be4..2c1dde2e 100644
--- a/includes/webform.install.update.inc
+++ b/includes/webform.install.update.inc
@@ -11,7 +11,9 @@ use Drupal\Core\Serialization\Yaml;
use Drupal\Core\Render\Element;
use Drupal\webform\Entity\Webform;
use Drupal\webform\Entity\WebformOptions;
+use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\webform\Plugin\WebformHandler\EmailWebformHandler;
+use Drupal\webform\Plugin\WebformHandler\ActionWebformHandler;
use Drupal\webform\WebformInterface;
use Drupal\webform\Plugin\WebformHandler\RemotePostWebformHandler;
use Drupal\webform\Utility\WebformArrayHelper;
@@ -1817,3 +1819,65 @@ function webform_update_8096() {
function webform_update_8097() {
_webform_update_webform_handler_configuration(EmailWebformHandler::class);
}
+
+/**
+ * Issue #2888862: Provide a mechanism to lock a webform submission.
+ */
+function webform_update_8098() {
+ // Copied from: node_update_8001()
+ //
+ // Install the definition that this field had in
+ // \Drupal\webform\Entity\WebformSubmission::baseFieldDefinitions()
+ // at the time that this update function was written. If/when code is
+ // deployed that changes that definition, the corresponding module must
+ // implement an update function that invokes
+ // \Drupal::entityDefinitionUpdateManager()->updateFieldStorageDefinition()
+ // with the new definition.
+ $storage_definition = BaseFieldDefinition::create('boolean')
+ ->setLabel(t('Locked'))
+ ->setDescription(t('A flag that indicates a locked webform submission.'))
+ ->setDefaultValue(FALSE);
+
+ \Drupal::entityDefinitionUpdateManager()
+ ->installFieldStorageDefinition('locked', 'webform_submission', 'webform', $storage_definition);
+
+ // Set default value.
+ \Drupal::database()->update('webform_submission')
+ ->fields(['locked' => 0])
+ ->execute();
+
+ // Add submission locked message to admin and webform settings.
+ _webform_update_admin_settings();
+ _webform_update_webform_settings();
+}
+
+/**
+ * Issue #2888862: Provide a mechanism to lock a webform submission. Update handlers.
+ */
+function webform_update_8099() {
+ // Add locked to action handler.
+ _webform_update_webform_handler_configuration(ActionWebformHandler::class);
+
+ // Add locked to remote post handler's excluded data.
+ /** @var \Drupal\webform\WebformInterface[] $webforms */
+ $webforms = Webform::loadMultiple();
+ foreach ($webforms as $webform) {
+ $has_handler = FALSE;
+ $handlers = $webform->getHandlers();
+ foreach ($handlers as $handler) {
+ if ($handler instanceof RemotePostWebformHandler) {
+ $has_handler = TRUE;
+ $configuration = $handler->getConfiguration();
+ $settings = $configuration['settings'];
+ if ($settings['excluded_data']) {
+ $settings['excluded_data']['locked'] = 'locked';
+ $configuration['settings'] = $settings;
+ $handler->setConfiguration($configuration);
+ }
+ }
+ }
+ if ($has_handler) {
+ $webform->save();
+ }
+ }
+}
diff --git a/includes/webform.theme.template.inc b/includes/webform.theme.template.inc
index 08566ba3..ebd04bfe 100644
--- a/includes/webform.theme.template.inc
+++ b/includes/webform.theme.template.inc
@@ -307,6 +307,7 @@ function template_preprocess_webform_submission_information(array &$variables) {
$variables['completed'] = WebformDateHelper::format($webform_submission->getCompletedTime());
$variables['changed'] = WebformDateHelper::format($webform_submission->getChangedTime());
$variables['sticky'] = $webform_submission->isSticky() ? t('Yes') : '';
+ $variables['locked'] = $webform_submission->isLocked() ? t('Yes') : '';
$variables['notes'] = $webform_submission->getNotes();
// @see \Drupal\Core\Field\Plugin\Field\FieldFormatter\LanguageFormatter::viewValue()
diff --git a/src/Controller/WebformSubmissionController.php b/src/Controller/WebformSubmissionController.php
index 917e968f..7063d6f0 100644
--- a/src/Controller/WebformSubmissionController.php
+++ b/src/Controller/WebformSubmissionController.php
@@ -37,4 +37,27 @@ class WebformSubmissionController extends ControllerBase {
return $response;
}
+ /**
+ * Toggle webform submission locked.
+ *
+ * @param \Drupal\webform\WebformSubmissionInterface $webform_submission
+ * A webform submission.
+ *
+ * @return \Drupal\Core\Ajax\AjaxResponse
+ * An Ajax response that toggle the lock icon.
+ */
+ public function locked(WebformSubmissionInterface $webform_submission) {
+ // Toggle locked.
+ $webform_submission->setLocked(!$webform_submission->isLocked())->save();
+
+ // Get state.
+ $state = $webform_submission->isLocked() ? 'on' : 'off';
+
+ $response = new AjaxResponse();
+ $response->addCommand(new HtmlCommand(
+ '#webform-submission-' . $webform_submission->id() . '-locked',
+ new FormattableMarkup('', ['@state' => $state])
+ ));
+ return $response;
+ }
}
diff --git a/src/Entity/Webform.php b/src/Entity/Webform.php
index 28d9f736..54f36f47 100644
--- a/src/Entity/Webform.php
+++ b/src/Entity/Webform.php
@@ -826,6 +826,7 @@ class Webform extends ConfigEntityBundleBase implements WebformInterface {
'submission_login' => FALSE,
'submission_login_message' => '',
'submission_exception_message' => '',
+ 'submission_locked_message' => '',
'wizard_progress_bar' => TRUE,
'wizard_progress_pages' => FALSE,
'wizard_progress_percentage' => FALSE,
diff --git a/src/Entity/WebformSubmission.php b/src/Entity/WebformSubmission.php
index 193c2fcf..a1a7f149 100644
--- a/src/Entity/WebformSubmission.php
+++ b/src/Entity/WebformSubmission.php
@@ -192,6 +192,11 @@ class WebformSubmission extends ContentEntityBase implements WebformSubmissionIn
->setDescription(t('The ID of the entity of which this webform submission was submitted from.'))
->setSetting('max_length', 255);
+ $fields['locked'] = BaseFieldDefinition::create('boolean')
+ ->setLabel(t('Locked'))
+ ->setDescription(t('A flag that indicates a locked webform submission.'))
+ ->setDefaultValue(FALSE);
+
$fields['sticky'] = BaseFieldDefinition::create('boolean')
->setLabel(t('Sticky'))
->setDescription(t('A flag that indicate the status of the webform submission.'))
@@ -299,6 +304,14 @@ class WebformSubmission extends ContentEntityBase implements WebformSubmissionIn
return $this;
}
+ /**
+ * {@inheritdoc}
+ */
+ public function setLocked($locked) {
+ $this->set('locked', $locked);
+ return $this;
+ }
+
/**
* {@inheritdoc}
*/
@@ -516,6 +529,13 @@ class WebformSubmission extends ContentEntityBase implements WebformSubmissionIn
return $this->get('completed')->value ? TRUE : FALSE;
}
+ /**
+ * {@inheritdoc}
+ */
+ public function isLocked() {
+ return $this->get('locked')->value ? true : false;
+ }
+
/**
* {@inheritdoc}
*/
@@ -543,6 +563,9 @@ class WebformSubmission extends ContentEntityBase implements WebformSubmissionIn
elseif ($this->isDraft()) {
return self::STATE_DRAFT;
}
+ elseif ($this->isLocked()) {
+ return self::STATE_LOCKED;
+ }
elseif ($this->completed->value == $this->changed->value) {
return self::STATE_COMPLETED;
}
@@ -579,9 +602,10 @@ class WebformSubmission extends ContentEntityBase implements WebformSubmissionIn
$duplicate->set('changed', NULL);
$duplicate->set('completed', NULL);
- // Clear admin notes and sticky.
+ // Clear admin notes, sticky, and locked.
$duplicate->set('notes', '');
$duplicate->set('sticky', FALSE);
+ $duplicate->set('locked', FALSE);
return $duplicate;
}
diff --git a/src/EntitySettings/WebformEntitySettingsSubmissionsForm.php b/src/EntitySettings/WebformEntitySettingsSubmissionsForm.php
index 0d01ea53..2d7c3f20 100644
--- a/src/EntitySettings/WebformEntitySettingsSubmissionsForm.php
+++ b/src/EntitySettings/WebformEntitySettingsSubmissionsForm.php
@@ -94,6 +94,12 @@ class WebformEntitySettingsSubmissionsForm extends WebformEntitySettingsBaseForm
'#description' => $this->t('A message to be displayed if submission handling breaks.'),
'#default_value' => $settings['submission_exception_message'],
];
+ $form['submission_settings']['submission_locked_message'] = [
+ '#type' => 'webform_html_editor',
+ '#title' => $this->t('Submission locked message'),
+ '#description' => $this->t('A message to be displayed if submission is lockec.'),
+ '#default_value' => $settings['submission_locked_message'],
+ ];
$form['submission_settings']['next_serial'] = [
'#type' => 'number',
'#title' => $this->t('Next submission number'),
@@ -111,7 +117,7 @@ class WebformEntitySettingsSubmissionsForm extends WebformEntitySettingsBaseForm
// @see \Drupal\webform\Form\WebformResultsCustomForm::buildForm
$available_columns = $webform_submission_storage->getColumns($webform);
// Remove columns that should never be displayed to users.
- $available_columns = array_diff_key($available_columns, array_flip(['uuid', 'in_draft', 'entity', 'sticky', 'notes', 'uid', 'operations']));
+ $available_columns = array_diff_key($available_columns, array_flip(['uuid', 'in_draft', 'entity', 'sticky', 'locked', 'notes', 'uid', 'operations']));
$custom_columns = $webform_submission_storage->getUserColumns($webform);
// Change sid's # to an actual label.
$available_columns['sid']['title'] = $this->t('Submission ID');
diff --git a/src/Form/AdminConfig/WebformAdminConfigSubmissionsForm.php b/src/Form/AdminConfig/WebformAdminConfigSubmissionsForm.php
index 624225ef..6e5125d5 100644
--- a/src/Form/AdminConfig/WebformAdminConfigSubmissionsForm.php
+++ b/src/Form/AdminConfig/WebformAdminConfigSubmissionsForm.php
@@ -75,6 +75,12 @@ class WebformAdminConfigSubmissionsForm extends WebformAdminConfigBaseForm {
'#required' => TRUE,
'#default_value' => $settings['default_submission_exception_message'],
];
+ $form['submission_settings']['default_submission_locked_message'] = [
+ '#type' => 'webform_html_editor',
+ '#title' => $this->t('Default locked message'),
+ '#required' => TRUE,
+ '#default_value' => $settings['default_submission_locked_message'],
+ ];
$form['submission_settings']['default_submission_label'] = [
'#type' => 'textfield',
'#title' => $this->t('Default submission label'),
diff --git a/src/Plugin/Action/LockWebformSubmission.php b/src/Plugin/Action/LockWebformSubmission.php
new file mode 100644
index 00000000..eeaa76bb
--- /dev/null
+++ b/src/Plugin/Action/LockWebformSubmission.php
@@ -0,0 +1,38 @@
+setLocked(TRUE)->save();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
+ /** @var \Drupal\webform\WebformSubmissionInterface $object */
+ $result = $object->lock->access('edit', $account, TRUE)
+ ->andIf($object->access('update', $account, TRUE));
+
+ return $return_as_object ? $result : $result->isAllowed();
+ }
+
+}
diff --git a/src/Plugin/Action/UnlockWebformSubmission.php b/src/Plugin/Action/UnlockWebformSubmission.php
new file mode 100644
index 00000000..6f06e8d7
--- /dev/null
+++ b/src/Plugin/Action/UnlockWebformSubmission.php
@@ -0,0 +1,38 @@
+setLocked(FALSE)->save();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
+ /** @var \Drupal\webform\WebformSubmissionInterface $object */
+ $result = $object->lock->access('edit', $account, TRUE)
+ ->andIf($object->access('update', $account, TRUE));
+
+ return $return_as_object ? $result : $result->isAllowed();
+ }
+
+}
diff --git a/src/Plugin/WebformHandler/ActionWebformHandler.php b/src/Plugin/WebformHandler/ActionWebformHandler.php
index 3e0354d8..c88e89ef 100644
--- a/src/Plugin/WebformHandler/ActionWebformHandler.php
+++ b/src/Plugin/WebformHandler/ActionWebformHandler.php
@@ -103,6 +103,7 @@ class ActionWebformHandler extends WebformHandlerBase {
'states' => [WebformSubmissionInterface::STATE_COMPLETED],
'notes' => '',
'sticky' => NULL,
+ 'locked' => NULL,
'data' => '',
'message' => '',
'message_type' => 'status',
@@ -148,6 +149,16 @@ class ActionWebformHandler extends WebformHandlerBase {
],
'#default_value' => ($this->configuration['sticky'] === NULL) ? '' : ($this->configuration['sticky'] ? '1' : '0'),
];
+ $form['actions']['locked'] = [
+ '#type' => 'select',
+ '#title' => $this->t('Change lock'),
+ '#options' => [
+ '' => '',
+ '1' => $this->t('Lock'),
+ '0' => $this->t('Unlock'),
+ ],
+ '#default_value' => ($this->configuration['locked'] === NULL) ? '' : ($this->configuration['locked'] ? '1' : '0'),
+ ];
$form['actions']['notes'] = [
'#type' => 'webform_codemirror',
'#mode' => 'text',
@@ -243,6 +254,9 @@ class ActionWebformHandler extends WebformHandlerBase {
// Cleanup sticky.
$this->configuration['sticky'] = ($this->configuration['sticky'] === '') ? NULL : (bool) $this->configuration['sticky'];
+ // Cleanup locked.
+ $this->configuration['locked'] = ($this->configuration['locked'] === '') ? NULL : (bool) $this->configuration['locked'];
+
// Cast debug.
$this->configuration['debug'] = (bool) $this->configuration['debug'];
}
@@ -273,6 +287,11 @@ class ActionWebformHandler extends WebformHandlerBase {
$webform_submission->setSticky($this->configuration['sticky']);
}
+ // Set locked.
+ if ($this->configuration['locked'] !== NULL) {
+ $webform_submission->setLock($this->configuration['locked']);
+ }
+
// Append notes.
if ($this->configuration['notes']) {
$notes = rtrim($webform_submission->getNotes());
diff --git a/src/Tests/Exporter/WebformExporterOptionsTest.php b/src/Tests/Exporter/WebformExporterOptionsTest.php
index 7677ae18..18f16b16 100644
--- a/src/Tests/Exporter/WebformExporterOptionsTest.php
+++ b/src/Tests/Exporter/WebformExporterOptionsTest.php
@@ -33,7 +33,7 @@ class WebformExporterOptionsTest extends WebformTestBase {
// Set default edit export settings.
$edit = [
// Exclude all columns except sid.
- 'excluded_columns' => 'serial,uuid,token,uri,created,completed,changed,in_draft,current_page,remote_addr,uid,langcode,webform_id,entity_type,entity_id,sticky,notes',
+ 'excluded_columns' => 'serial,uuid,token,uri,created,completed,changed,in_draft,current_page,remote_addr,uid,langcode,webform_id,entity_type,entity_id,sticky,locked,notes',
];
// Check default options.
diff --git a/src/Tests/WebformSubmissionListBuilderTest.php b/src/Tests/WebformSubmissionListBuilderTest.php
index dea9e8a0..696533fb 100644
--- a/src/Tests/WebformSubmissionListBuilderTest.php
+++ b/src/Tests/WebformSubmissionListBuilderTest.php
@@ -59,7 +59,7 @@ class WebformSubmissionListBuilderTest extends WebformTestBase {
$this->drupalGet('admin/structure/webform/manage/' . $webform->id() . '/results/submissions');
// Check state options with totals.
- $this->assertRaw('');
+ $this->assertRaw('');
// Check results with no filtering.
$this->assertLinkByHref($submissions[0]->toUrl()->toString());
diff --git a/src/WebformMessageManagerInterface.php b/src/WebformMessageManagerInterface.php
index 9b5a007c..1cf2a608 100644
--- a/src/WebformMessageManagerInterface.php
+++ b/src/WebformMessageManagerInterface.php
@@ -133,6 +133,11 @@ interface WebformMessageManagerInterface {
*/
const SUBMISSION_EXCEPTION = 'submission_exception_message';
+ /**
+ * Submission exception.
+ */
+ const SUBMISSION_LOCKED = 'submission_locked_message';
+
/**
* Template preview.
*/
@@ -143,6 +148,11 @@ interface WebformMessageManagerInterface {
*/
const PREPOPULATE_SOURCE_ENTITY_REQUIRED = 'prepopulate_source_entity_required';
+ /**
+ * Prepopulate source entity type.
+ */
+ const PREPOPULATE_SOURCE_ENTITY_TYPE = 'prepopulate_source_entity_type';
+
/**
* Set the webform submission used for token replacement.
*
@@ -153,11 +163,6 @@ interface WebformMessageManagerInterface {
*/
public function setWebformSubmission(WebformSubmissionInterface $webform_submission = NULL);
- /**
- * Prepopulate source entity type.
- */
- const PREPOPULATE_SOURCE_ENTITY_TYPE = 'prepopulate_source_entity_type';
-
/**
* Set the webform used for custom messages and token replacement.
*
diff --git a/src/WebformSubmissionExporter.php b/src/WebformSubmissionExporter.php
index 556464da..86d9585c 100644
--- a/src/WebformSubmissionExporter.php
+++ b/src/WebformSubmissionExporter.php
@@ -257,6 +257,7 @@ class WebformSubmissionExporter implements WebformSubmissionExporterInterface {
'range_start' => '',
'range_end' => '',
'state' => 'all',
+ 'locked' => '',
'sticky' => '',
'download' => TRUE,
'files' => FALSE,
diff --git a/src/WebformSubmissionForm.php b/src/WebformSubmissionForm.php
index 92a950cb..b722fefb 100644
--- a/src/WebformSubmissionForm.php
+++ b/src/WebformSubmissionForm.php
@@ -595,6 +595,11 @@ class WebformSubmissionForm extends ContentEntityForm {
return $this->getMessageManager()->append($form, WebformMessageManagerInterface::FORM_EXCEPTION, 'warning');
}
+ // Exit if submission is locked.
+ if ($webform_submission->isLocked()) {
+ return $this->getMessageManager()->append($form, WebformMessageManagerInterface::SUBMISSION_LOCKED, 'warning');
+ }
+
// Check prepopulate source entity required and type.
if ($webform->getSetting('form_prepopulate_source_entity')) {
if ($webform->getSetting('form_prepopulate_source_entity_required') && empty($this->getSourceEntity())) {
diff --git a/src/WebformSubmissionInterface.php b/src/WebformSubmissionInterface.php
index 89e550aa..45a5796f 100644
--- a/src/WebformSubmissionInterface.php
+++ b/src/WebformSubmissionInterface.php
@@ -26,6 +26,11 @@ interface WebformSubmissionInterface extends ContentEntityInterface, EntityOwner
*/
const STATE_COMPLETED = 'completed';
+ /**
+ * Return status for submission that has been locked.
+ */
+ const STATE_LOCKED = 'locked';
+
/**
* Return status for submission that has been updated.
*/
@@ -139,6 +144,24 @@ interface WebformSubmissionInterface extends ContentEntityInterface, EntityOwner
*/
public function setSticky($sticky);
+ /**
+ * Get the submission's locked status.
+ *
+ * @return string
+ * The submission's lock status.
+ */
+ public function isLocked();
+
+ /**
+ * Sets the submission's locked flag.
+ *
+ * @param bool $locked
+ * The submission's locked flag.
+ *
+ * @return $this
+ */
+ public function setLocked($locked);
+
/**
* Gets the remote IP address of the submission.
*
diff --git a/src/WebformSubmissionListBuilder.php b/src/WebformSubmissionListBuilder.php
index 5f9194ee..68a19dec 100644
--- a/src/WebformSubmissionListBuilder.php
+++ b/src/WebformSubmissionListBuilder.php
@@ -29,6 +29,16 @@ class WebformSubmissionListBuilder extends EntityListBuilder {
*/
const STATE_UNSTARRED = 'unstarred';
+ /**
+ * Submission state locked.
+ */
+ const STATE_LOCKED = 'locked';
+
+ /**
+ * Submission state unlocked.
+ */
+ const STATE_UNLOCKED = 'unlocked';
+
/**
* Submission state completed.
*/
@@ -258,6 +268,8 @@ class WebformSubmissionListBuilder extends EntityListBuilder {
'' => $this->t('All [@total]', ['@total' => $this->getTotal(NULL, NULL)]),
self::STATE_STARRED => $this->t('Starred [@total]', ['@total' => $this->getTotal(NULL, self::STATE_STARRED)]),
self::STATE_UNSTARRED => $this->t('Unstarred [@total]', ['@total' => $this->getTotal(NULL, self::STATE_UNSTARRED)]),
+ self::STATE_LOCKED => $this->t('Locked[@total]', ['@total' => $this->getTotal(NULL, self::STATE_LOCKED)]),
+ self::STATE_UNLOCKED => $this->t('Unlocked [@total]', ['@total' => $this->getTotal(NULL, self::STATE_UNLOCKED)]),
];
// Add draft to state options.
if (!$this->webform || $this->webform->getSetting('draft') != WebformInterface::DRAFT_NONE) {
@@ -391,11 +403,12 @@ class WebformSubmissionListBuilder extends EntityListBuilder {
switch ($name) {
case 'notes':
case 'sticky':
+ case 'locked':
return [
'data' => new FormattableMarkup('', ['@name' => $name]),
'class' => ['webform-results__icon'],
- 'field' => 'sticky',
- 'specifier' => 'sticky',
+ 'field' => $name,
+ 'specifier' => $name,
];
default:
@@ -531,6 +544,23 @@ class WebformSubmissionListBuilder extends EntityListBuilder {
'class' => ['webform-results__icon'],
];
+ case 'locked':
+ $route_name = 'entity.webform_submission.locked_toggle';
+ $route_parameters = ['webform' => $entity->getWebform()->id(), 'webform_submission' => $entity->id()];
+ $state = $entity->isLocked() ? 'on' : 'off';
+ return [
+ 'data' => [
+ '#type' => 'link',
+ '#title' => new FormattableMarkup('', ['@state' => $state]),
+ '#url' => Url::fromRoute($route_name, $route_parameters),
+ '#attributes' => [
+ 'id' => 'webform-submission-' . $entity->id() . '-locked',
+ 'class' => ['use-ajax'],
+ ],
+ ],
+ 'class' => ['webform-results__icon'],
+ ];
+
case 'uid':
return ($is_raw) ? $entity->getOwner()->id() : ($entity->getOwner()->getAccountName() ?: t('Anonymous'));
@@ -594,7 +624,7 @@ class WebformSubmissionListBuilder extends EntityListBuilder {
];
}
- if ($entity->access('update')) {
+ if ($entity->access('notes')) {
$operations['notes'] = [
'title' => $this->t('Notes'),
'weight' => 21,
@@ -779,6 +809,14 @@ class WebformSubmissionListBuilder extends EntityListBuilder {
$query->condition('sticky', 0);
break;
+ case self::STATE_LOCKED:
+ $query->condition('locked', 1);
+ break;
+
+ case self::STATE_UNLOCKED:
+ $query->condition('locked', 0);
+ break;
+
case self::STATE_DRAFT:
$query->condition('in_draft', 1);
break;
diff --git a/src/WebformSubmissionNotesForm.php b/src/WebformSubmissionNotesForm.php
index d9481055..7b9da7ec 100644
--- a/src/WebformSubmissionNotesForm.php
+++ b/src/WebformSubmissionNotesForm.php
@@ -69,9 +69,18 @@ class WebformSubmissionNotesForm extends ContentEntityForm {
'#description' => $this->t('Enter notes about this submission. These notes are only visible to submission administrators.'),
'#default_value' => $webform_submission->getNotes(),
];
+ $form['locked'] = [
+ '#type' => 'checkbox',
+ '#title' => $this->t('Lock this submission'),
+ '#description' => $this->t('If checked, users will not be able to update this submission.'),
+ '#default_value' => $webform_submission->isLocked(),
+ '#return_value' => TRUE,
+ '#access' => $this->isDialog() ? FALSE : TRUE,
+ ];
$form['sticky'] = [
'#type' => 'checkbox',
'#title' => $this->t('Star/flag the status of this submission'),
+ '#description' => $this->t('If checked, this submissions will be starred when reviewing results.'),
'#default_value' => $webform_submission->isSticky(),
'#return_value' => TRUE,
'#access' => $this->isDialog() ? FALSE : TRUE,
diff --git a/src/WebformSubmissionStorage.php b/src/WebformSubmissionStorage.php
index 8a9b3b78..5189c37e 100644
--- a/src/WebformSubmissionStorage.php
+++ b/src/WebformSubmissionStorage.php
@@ -543,12 +543,17 @@ class WebformSubmissionStorage extends SqlContentEntityStorage implements Webfor
'default' => FALSE,
];
- // Sticky (Starred/Unstarred).
if (empty($account)) {
+ // Sticky (Starred/Unstarred).
$columns['sticky'] = [
'title' => $this->t('Starred'),
];
+ // Locked.
+ $columns['locked'] = [
+ 'title' => $this->t('Locked'),
+ ];
+
// Notes.
$columns['notes'] = [
'title' => $this->t('Notes'),
diff --git a/templates/webform-handler-action-summary.html.twig b/templates/webform-handler-action-summary.html.twig
index ed852d81..0ba546bb 100644
--- a/templates/webform-handler-action-summary.html.twig
+++ b/templates/webform-handler-action-summary.html.twig
@@ -12,6 +12,7 @@
#}
{% if settings.debug %}{{ 'Debugging is enabled'|t }}
{% endif %}
+{% if settings.lock is not null %}{{ 'Lock:'|t }} {{ settings.lock ? 'Locked'|t : 'Unlocked'|t }}
{% endif %}
{% if settings.sticky is not null %}{{ 'Status:'|t }} {{ settings.sticky ? 'Flag/Star'|t : 'Unflag/Unstar'|t }}
{% endif %}
{% if settings.notes %}{{ 'Notes:'|t }} {{ settings.notes }}
{% endif %}
{% if settings.message %}{{ 'Message:'|t }} {{ settings.message }} ({{ settings.message_type }})
{% endif %}
diff --git a/templates/webform-submission-information.html.twig b/templates/webform-submission-information.html.twig
index ca1bbf3b..2fdd3812 100644
--- a/templates/webform-submission-information.html.twig
+++ b/templates/webform-submission-information.html.twig
@@ -41,11 +41,14 @@
{% if submitted_to %}
{{ notes }}diff --git a/tests/modules/webform_test/config/install/webform.webform.test_form_limit.yml b/tests/modules/webform_test/config/install/webform.webform.test_form_limit.yml index 2938c646..43f641e1 100644 --- a/tests/modules/webform_test/config/install/webform.webform.test_form_limit.yml +++ b/tests/modules/webform_test/config/install/webform.webform.test_form_limit.yml @@ -52,6 +52,7 @@ settings: submission_login: false submission_login_message: '' submission_exception_message: '' + submission_locked_message: '' wizard_progress_bar: true wizard_progress_pages: false wizard_progress_percentage: false diff --git a/tests/modules/webform_test_handler_remote_post/config/install/webform.webform.test_handler_remote_get.yml b/tests/modules/webform_test_handler_remote_post/config/install/webform.webform.test_handler_remote_get.yml index f4bd8f8a..f83fc4d5 100644 --- a/tests/modules/webform_test_handler_remote_post/config/install/webform.webform.test_handler_remote_get.yml +++ b/tests/modules/webform_test_handler_remote_post/config/install/webform.webform.test_handler_remote_get.yml @@ -178,6 +178,7 @@ handlers: entity_type: entity_type entity_id: entity_id sticky: sticky + locked: locked notes: notes confirmation_number: confirmation_number custom_data: | diff --git a/tests/modules/webform_test_handler_remote_post/config/install/webform.webform.test_handler_remote_post.yml b/tests/modules/webform_test_handler_remote_post/config/install/webform.webform.test_handler_remote_post.yml index 5a87e050..ebbdcbff 100644 --- a/tests/modules/webform_test_handler_remote_post/config/install/webform.webform.test_handler_remote_post.yml +++ b/tests/modules/webform_test_handler_remote_post/config/install/webform.webform.test_handler_remote_post.yml @@ -178,6 +178,7 @@ handlers: entity_type: entity_type entity_id: entity_id sticky: sticky + locked: locked notes: notes confirmation_number: confirmation_number custom_data: | diff --git a/webform.routing.yml b/webform.routing.yml index 5cb08d52..876ff10d 100644 --- a/webform.routing.yml +++ b/webform.routing.yml @@ -520,7 +520,7 @@ entity.webform_submission.notes_form: _entity_form: 'webform_submission.notes' _title_callback: '\Drupal\webform\Controller\WebformSubmissionViewController::title' requirements: - _entity_access: 'webform_submission.update_any' + _entity_access: 'webform_submission.notes' entity.webform_submission.resend_form: path: '/admin/structure/webform/manage/{webform}/submission/{webform_submission}/resend' @@ -547,12 +547,20 @@ entity.webform_submission.delete_form: requirements: _entity_access: 'webform_submission.delete' +entity.webform_submission.locked_toggle: + path: '/admin/structure/webform/manage/{webform}/submission/{webform_submission}/locked' + defaults: + _controller: '\Drupal\webform\Controller\WebformSubmissionController::locked' + requirements: + _entity_access: 'webform_submission.notes' + _csrf_token: 'TRUE' + entity.webform_submission.sticky_toggle: path: '/admin/structure/webform/manage/{webform}/submission/{webform_submission}/sticky' defaults: _controller: '\Drupal\webform\Controller\WebformSubmissionController::sticky' requirements: - _entity_access: 'webform_submission.update' + _entity_access: 'webform_submission.notes' _csrf_token: 'TRUE' webform_submission.multiple_delete_confirm: