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 %}
{{ 'Submitted to'|t }}: {{ submitted_to }}
{% endif %} - {% if sticky or notes %} + {% if sticky or locked or notes %}
{% if sticky %}
{{ 'Flagged'|t }}: {{ sticky }}
{% endif %} + {% if locked %} +
{{ 'Locked'|t }}: {{ locked }}
+ {% endif %} {% if notes %}
{{ 'Notes'|t }}:
{{ 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: