Update Composer, update everything
This commit is contained in:
parent
ea3e94409f
commit
dda5c284b6
19527 changed files with 1135420 additions and 351004 deletions
|
@ -1,146 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Access;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\webform\Plugin\Field\FieldType\WebformEntityReferenceItem;
|
||||
use Drupal\webform\WebformHandlerMessageInterface;
|
||||
use Drupal\webform\WebformInterface;
|
||||
use Drupal\webform\WebformSubmissionInterface;
|
||||
|
||||
/**
|
||||
* Defines the custom access control handler for the webform entities.
|
||||
*/
|
||||
class WebformAccess {
|
||||
|
||||
/**
|
||||
* Check whether the webform has results.
|
||||
*
|
||||
* @param \Drupal\webform\WebformInterface $webform
|
||||
* A webform.
|
||||
* @param \Drupal\Core\Entity\EntityInterface|null $source_entity
|
||||
* The source entity.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
public static function checkResultsAccess(WebformInterface $webform, EntityInterface $source_entity = NULL) {
|
||||
// If results are not disabled return neutral.
|
||||
if (!$webform->getSetting('results_disabled')) {
|
||||
$access_result = AccessResult::allowed();
|
||||
}
|
||||
// If webform has any results return neutral.
|
||||
elseif (\Drupal::entityTypeManager()->getStorage('webform_submission')->getTotal($webform, $source_entity)) {
|
||||
$access_result = AccessResult::allowed();
|
||||
}
|
||||
// Finally, forbid access to the results.
|
||||
else {
|
||||
$access_result = AccessResult::forbidden();
|
||||
}
|
||||
return $access_result->addCacheableDependency($webform);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the user has 'administer webform' or 'administer webform submission' permission.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* Run access checks for this account.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
public static function checkAdminAccess(AccountInterface $account) {
|
||||
return AccessResult::allowedIf($account->hasPermission('administer webform') || $account->hasPermission('administer webform submission'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the user can view submissions.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* Run access checks for this account.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
public static function checkSubmissionAccess(AccountInterface $account) {
|
||||
return AccessResult::allowedIf($account->hasPermission('administer webform') || $account->hasPermission('administer webform submission') || $account->hasPermission('view any webform submission'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the user has 'administer' or 'overview' permission.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* Run access checks for this account.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
public static function checkOverviewAccess(AccountInterface $account) {
|
||||
return AccessResult::allowedIf($account->hasPermission('administer webform') || $account->hasPermission('administer webform submission') || $account->hasPermission('access webform overview'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that webform submission has email and the user can update any webform submission.
|
||||
*
|
||||
* @param \Drupal\webform\WebformSubmissionInterface $webform_submission
|
||||
* A webform submission.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* Run access checks for this account.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
public static function checkEmailAccess(WebformSubmissionInterface $webform_submission, AccountInterface $account) {
|
||||
$webform = $webform_submission->getWebform();
|
||||
if ($webform->access('submission_update_any', $account)) {
|
||||
$handlers = $webform->getHandlers();
|
||||
foreach ($handlers as $handler) {
|
||||
if ($handler instanceof WebformHandlerMessageInterface) {
|
||||
return AccessResult::allowed();
|
||||
}
|
||||
}
|
||||
}
|
||||
return AccessResult::forbidden();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the user can access an entity's webform results.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* An entity.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* Run access checks for this account.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
public static function checkEntityResultsAccess(EntityInterface $entity, AccountInterface $account) {
|
||||
$webform_field_name = WebformEntityReferenceItem::getEntityWebformFieldName($entity);
|
||||
return AccessResult::allowedIf($entity->access('update', $account) && $webform_field_name && $entity->$webform_field_name->entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the webform has wizard pages.
|
||||
*
|
||||
* @param \Drupal\webform\WebformInterface $webform
|
||||
* A webform.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*
|
||||
* @see \Drupal\webform\WebformSubmissionForm::buildForm
|
||||
* @see \Drupal\webform\Entity\Webform::getPages
|
||||
*/
|
||||
public static function checkWebformWizardPagesAccess(WebformInterface $webform) {
|
||||
$elements = $webform->getElementsInitialized();
|
||||
foreach ($elements as $key => $element) {
|
||||
if (isset($element['#type']) && $element['#type'] == 'webform_wizard_page') {
|
||||
return AccessResult::allowed();
|
||||
}
|
||||
}
|
||||
return AccessResult::forbidden();
|
||||
}
|
||||
|
||||
}
|
107
web/modules/contrib/webform/src/Access/WebformAccessResult.php
Normal file
107
web/modules/contrib/webform/src/Access/WebformAccessResult.php
Normal file
|
@ -0,0 +1,107 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Access;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\webform\WebformSubmissionInterface;
|
||||
|
||||
/**
|
||||
* Value object indicating an allowed access result, with cacheability metadata.
|
||||
*/
|
||||
class WebformAccessResult {
|
||||
|
||||
/**
|
||||
* Creates an allowed or neutral access result.
|
||||
*
|
||||
* @param bool $condition
|
||||
* The condition to evaluate.
|
||||
* @param \Drupal\Core\Entity\EntityInterface|null $webform_entity
|
||||
* A webform or webform submission.
|
||||
* @param bool $cache_per_user
|
||||
* Cache per user.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResult
|
||||
* If $condition is TRUE, isAllowed() will be TRUE, otherwise isNeutral()
|
||||
* will be TRUE.
|
||||
*/
|
||||
public static function allowedIf($condition, EntityInterface $webform_entity = NULL, $cache_per_user = FALSE) {
|
||||
return $condition ? static::allowed($webform_entity, $cache_per_user) : static::neutral($webform_entity, $cache_per_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an AccessResultInterface object with isAllowed() === TRUE.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface|null $webform_entity
|
||||
* A webform or webform submission.
|
||||
* @param bool $cache_per_user
|
||||
* Cache per user.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultAllowed
|
||||
* isAllowed() will be TRUE.
|
||||
*/
|
||||
public static function allowed(EntityInterface $webform_entity = NULL, $cache_per_user = FALSE) {
|
||||
return static::addDependencies(AccessResult::allowed(), $webform_entity, $cache_per_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an AccessResultInterface object with isNeutral() === TRUE.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface|null $webform_entity
|
||||
* A webform or webform submission.
|
||||
* @param bool $cache_per_user
|
||||
* Cache per user.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultForbidden
|
||||
* isNeutral() will be TRUE.
|
||||
*/
|
||||
public static function neutral(EntityInterface $webform_entity = NULL, $cache_per_user = FALSE) {
|
||||
return static::addDependencies(AccessResult::neutral(), $webform_entity, $cache_per_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an AccessResultInterface object with isForbidden() === TRUE.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface|null $webform_entity
|
||||
* A webform or webform submission.
|
||||
* @param bool $cache_per_user
|
||||
* Cache per user.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultForbidden
|
||||
* isForbidden() will be TRUE.
|
||||
*/
|
||||
public static function forbidden(EntityInterface $webform_entity = NULL, $cache_per_user = FALSE) {
|
||||
return static::addDependencies(AccessResult::forbidden(), $webform_entity, $cache_per_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds dependencies to an access result.
|
||||
*
|
||||
* @param \Drupal\Core\Access\AccessResult $access_result
|
||||
* The access result.
|
||||
* @param \Drupal\Core\Entity\EntityInterface|null $webform_entity
|
||||
* A webform or webform submission.
|
||||
* @param bool $cache_per_user
|
||||
* Cache per user.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResult
|
||||
* The access result with dependencies.
|
||||
*/
|
||||
public static function addDependencies(AccessResult $access_result, EntityInterface $webform_entity = NULL, $cache_per_user = FALSE) {
|
||||
$access_result->cachePerPermissions();
|
||||
|
||||
if ($cache_per_user) {
|
||||
$access_result->cachePerUser();
|
||||
}
|
||||
|
||||
if ($webform_entity) {
|
||||
if ($webform_entity instanceof WebformSubmissionInterface) {
|
||||
$access_result->addCacheableDependency($webform_entity->getWebform());
|
||||
}
|
||||
$access_result->addCacheableDependency($webform_entity);
|
||||
}
|
||||
|
||||
return $access_result;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Access;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Defines the custom access control handler for the user accounts.
|
||||
*/
|
||||
class WebformAccountAccess {
|
||||
|
||||
/**
|
||||
* Check whether the user has 'administer webform' or 'administer webform submission' permission.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* Run access checks for this account.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
public static function checkAdminAccess(AccountInterface $account) {
|
||||
return AccessResult::allowedIfHasPermissions($account, ['administer webform', 'administer webform submission'], 'OR');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the user has 'administer' or 'overview' permission.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* Run access checks for this account.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
public static function checkOverviewAccess(AccountInterface $account) {
|
||||
return AccessResult::allowedIfHasPermissions($account, ['administer webform', 'administer webform submission', 'access webform overview'], 'OR');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the user has 'overview' with 'create' permission.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* Run access checks for this account.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
public static function checkTemplatesAccess(AccountInterface $account) {
|
||||
$condition = ($account->hasPermission('access webform overview') &&
|
||||
($account->hasPermission('administer webform') || $account->hasPermission('create webform')));
|
||||
return AccessResult::allowedIf($condition)->cachePerPermissions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the user can view submissions.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* Run access checks for this account.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
public static function checkSubmissionAccess(AccountInterface $account) {
|
||||
return AccessResult::allowedIfHasPermissions($account, ['administer webform', 'administer webform submission', 'view any webform submission'], 'OR');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the user can view own submissions.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* Run access checks for this account.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
public static function checkUserSubmissionsAccess(AccountInterface $account) {
|
||||
$condition = ($account->hasPermission('administer webform') || $account->hasPermission('administer webform submission') || $account->hasPermission('view any webform submission'))
|
||||
|| ($account->hasPermission('access webform submission user') && \Drupal::currentUser()->id() === $account->id());
|
||||
return AccessResult::allowedIf($condition)->cachePerPermissions();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Access;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\webform\WebformInterface;
|
||||
|
||||
/**
|
||||
* Defines the custom access control handler for the webform entities.
|
||||
*/
|
||||
class WebformEntityAccess {
|
||||
|
||||
/**
|
||||
* Check whether the webform has results.
|
||||
*
|
||||
* @param \Drupal\webform\WebformInterface $webform
|
||||
* A webform.
|
||||
* @param \Drupal\Core\Entity\EntityInterface|null $source_entity
|
||||
* The source entity.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
public static function checkResultsAccess(WebformInterface $webform, EntityInterface $source_entity = NULL) {
|
||||
// If results are not disabled return neutral.
|
||||
if (!$webform->getSetting('results_disabled')) {
|
||||
$access_result = AccessResult::allowed();
|
||||
}
|
||||
// If webform has any results return neutral.
|
||||
elseif (\Drupal::entityTypeManager()->getStorage('webform_submission')->getTotal($webform, $source_entity)) {
|
||||
$access_result = AccessResult::allowed();
|
||||
}
|
||||
// Finally, forbid access to the results.
|
||||
else {
|
||||
$access_result = AccessResult::forbidden();
|
||||
}
|
||||
return $access_result->addCacheableDependency($webform);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the webform has log.
|
||||
*
|
||||
* @param \Drupal\webform\WebformInterface $webform
|
||||
* A webform.
|
||||
* @param \Drupal\Core\Entity\EntityInterface|null $source_entity
|
||||
* The source entity.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
public static function checkLogAccess(WebformInterface $webform = NULL, EntityInterface $source_entity = NULL) {
|
||||
// ISSUE:
|
||||
// Devel routes do not use 'webform' parameter which throws the below error.
|
||||
// Some mandatory parameters are missing ("webform") to generate a URL for
|
||||
// route "entity.webform_submission.canonical"
|
||||
//
|
||||
// WORKAROUND:
|
||||
// Make sure webform parameter is set for all routes.
|
||||
// @see webform_menu_local_tasks_alter()
|
||||
if (!$webform && $webform_submission = \Drupal::routeMatch()->getParameter('webform_submission')) {
|
||||
$webform = $webform_submission->getWebform();
|
||||
}
|
||||
|
||||
if (!$webform->hasSubmissionLog()) {
|
||||
$access_result = AccessResult::forbidden()->addCacheableDependency($webform);
|
||||
}
|
||||
else {
|
||||
$access_result = static::checkResultsAccess($webform, $source_entity);
|
||||
}
|
||||
return $access_result->addCacheTags(['config:webform.settings']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a webform setting is set to specified value.
|
||||
*
|
||||
* @param \Drupal\webform\WebformInterface $webform
|
||||
* A webform.
|
||||
* @param string $setting
|
||||
* A webform setting.
|
||||
* @param string $value
|
||||
* The setting value used to determine access.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
public static function checkWebformSettingValue(WebformInterface $webform = NULL, $setting = NULL, $value = NULL) {
|
||||
return AccessResult::allowedIf($webform->getSetting($setting) === $value)
|
||||
->addCacheableDependency($webform);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Access;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
|
||||
/**
|
||||
* Defines the custom access control handler for the webform handlers.
|
||||
*/
|
||||
class WebformHandlerAccess {
|
||||
|
||||
/**
|
||||
* Check whether the webform handler is enabled.
|
||||
*
|
||||
* @param string $webform_handler
|
||||
* A webform handler id.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
public static function checkHandlerAccess($webform_handler = NULL) {
|
||||
/** @var \Drupal\webform\Plugin\WebformHandlerManagerInterface $handler_manager */
|
||||
$handler_manager = \Drupal::service('plugin.manager.webform.handler');
|
||||
$handler_definitions = $handler_manager->getDefinitions();
|
||||
$handler_definitions = $handler_manager->removeExcludeDefinitions($handler_definitions);
|
||||
if ($webform_handler) {
|
||||
$access_result = AccessResult::allowedIf(!empty($handler_definitions[$webform_handler]));
|
||||
}
|
||||
else {
|
||||
unset($handler_definitions['broken'], $handler_definitions['email']);
|
||||
$access_result = AccessResult::allowedIf(!empty($handler_definitions));
|
||||
}
|
||||
return $access_result->addCacheTags(['config:webform.settings']);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Access;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Defines the custom access control handler for the webform source entities.
|
||||
*/
|
||||
class WebformSourceEntityAccess {
|
||||
|
||||
/**
|
||||
* Check whether the user can access an entity's webform results.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* An entity.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* Run access checks for this account.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
public static function checkEntityResultsAccess(EntityInterface $entity, AccountInterface $account) {
|
||||
/** @var \Drupal\webform\WebformEntityReferenceManagerInterface $entity_reference_manager */
|
||||
$entity_reference_manager = \Drupal::service('webform.entity_reference_manager');
|
||||
|
||||
$access = $entity->access('update', $account, TRUE);
|
||||
$access->andIf(AccessResult::allowedIf($entity_reference_manager->getWebform($entity)));
|
||||
return $access;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Access;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\webform\WebformSubmissionInterface;
|
||||
|
||||
/**
|
||||
* Defines the custom access control handler for the webform submission entities.
|
||||
*/
|
||||
class WebformSubmissionAccess {
|
||||
|
||||
/**
|
||||
* Check whether a webform submissions' webform has wizard pages.
|
||||
*
|
||||
* @param \Drupal\webform\WebformSubmissionInterface $webform_submission
|
||||
* A webform submission.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
public static function checkWizardPagesAccess(WebformSubmissionInterface $webform_submission) {
|
||||
$condition = $webform_submission->getWebform()->hasWizardPages();
|
||||
return AccessResult::allowedIf($condition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that webform submission has (email) messages and the user can update any webform submission.
|
||||
*
|
||||
* @param \Drupal\webform\WebformSubmissionInterface $webform_submission
|
||||
* A webform submission.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* Run access checks for this account.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
public static function checkResendAccess(WebformSubmissionInterface $webform_submission, AccountInterface $account) {
|
||||
if ($webform_submission->getWebform()->hasMessageHandler()) {
|
||||
return AccessResult::allowed();
|
||||
}
|
||||
else {
|
||||
return AccessResult::forbidden();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Ajax;
|
||||
|
||||
use Drupal\Core\Ajax\CommandInterface;
|
||||
|
||||
/**
|
||||
* Provides an Ajax command to trigger audio UAs to read the supplied text.
|
||||
*
|
||||
* This command is implemented in Drupal.AjaxCommands.prototype.webformAnnounce.
|
||||
*/
|
||||
class WebformAnnounceCommand implements CommandInterface {
|
||||
|
||||
/**
|
||||
* A string to be read by the UA.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $text;
|
||||
|
||||
/**
|
||||
* A string to indicate the priority of the message.
|
||||
*
|
||||
* Can be either 'polite' or 'assertive'.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $priority;
|
||||
|
||||
/**
|
||||
* Constructs a \Drupal\webform\Ajax\ScrollTopCommand object.
|
||||
*
|
||||
* @param string $text
|
||||
* A string to be read by the UA.
|
||||
* @param string $priority
|
||||
* A string to indicate the priority of the message. Can be either
|
||||
* 'polite' or 'assertive'.
|
||||
*/
|
||||
public function __construct($text, $priority = 'polite') {
|
||||
$this->text = $text;
|
||||
$this->priority = $priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render() {
|
||||
return [
|
||||
'command' => 'webformAnnounce',
|
||||
'text' => $this->text,
|
||||
'priority' => $this->priority,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Ajax;
|
||||
|
||||
use Drupal\Core\Ajax\CloseDialogCommand;
|
||||
|
||||
/**
|
||||
* Provides an Ajax command for closing webform dialog and system tray.
|
||||
*
|
||||
* This command is implemented in Drupal.AjaxCommands.prototype.webformCloseDialog.
|
||||
*/
|
||||
class WebformCloseDialogCommand extends CloseDialogCommand {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render() {
|
||||
return [
|
||||
'command' => 'webformCloseDialog',
|
||||
'selector' => $this->selector,
|
||||
'persist' => $this->persist,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
27
web/modules/contrib/webform/src/Ajax/WebformHtmlCommand.php
Normal file
27
web/modules/contrib/webform/src/Ajax/WebformHtmlCommand.php
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Ajax;
|
||||
|
||||
use Drupal\Core\Ajax\HtmlCommand;
|
||||
|
||||
/**
|
||||
* Provides an Ajax command for calling the jQuery html() method.
|
||||
*
|
||||
* This command is implemented in Drupal.AjaxCommands.prototype.webformHtml.
|
||||
*/
|
||||
class WebformHtmlCommand extends HtmlCommand {
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Ajax\CommandInterface:render().
|
||||
*/
|
||||
public function render() {
|
||||
return [
|
||||
'command' => 'webformInsert',
|
||||
'method' => 'html',
|
||||
'selector' => $this->selector,
|
||||
'data' => $this->getRenderedContent(),
|
||||
'settings' => $this->settings,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Ajax;
|
||||
|
||||
use Drupal\Core\Ajax\RedirectCommand;
|
||||
|
||||
/**
|
||||
* Provides an Ajax command for refreshing webform page/.
|
||||
*
|
||||
* This command is implemented in Drupal.AjaxCommands.prototype.webformRefresh.
|
||||
*/
|
||||
class WebformRefreshCommand extends RedirectCommand {
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Ajax\CommandInterface:render().
|
||||
*/
|
||||
public function render() {
|
||||
return [
|
||||
'command' => 'webformRefresh',
|
||||
'url' => $this->url,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -5,11 +5,11 @@ namespace Drupal\webform\Ajax;
|
|||
use Drupal\Core\Ajax\CommandInterface;
|
||||
|
||||
/**
|
||||
* Provides an AJAX command for scrolling to the top of an element.
|
||||
* Provides an Ajax command for scrolling to the top of an element.
|
||||
*
|
||||
* This command is implemented in Drupal.AjaxCommands.prototype.webformScrollTop.
|
||||
*/
|
||||
class ScrollTopCommand implements CommandInterface {
|
||||
class WebformScrollTopCommand implements CommandInterface {
|
||||
|
||||
/**
|
||||
* A CSS selector string.
|
||||
|
@ -18,14 +18,24 @@ class ScrollTopCommand implements CommandInterface {
|
|||
*/
|
||||
protected $selector;
|
||||
|
||||
/**
|
||||
* Scroll to target.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $target;
|
||||
|
||||
/**
|
||||
* Constructs a \Drupal\webform\Ajax\ScrollTopCommand object.
|
||||
*
|
||||
* @param string $selector
|
||||
* A CSS selector.
|
||||
* @param string $target
|
||||
* Scroll to target which can be 'form' or 'page'. Defaults to 'form'.
|
||||
*/
|
||||
public function __construct($selector) {
|
||||
public function __construct($selector, $target = 'form') {
|
||||
$this->selector = $selector;
|
||||
$this->target = $target;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -35,6 +45,7 @@ class ScrollTopCommand implements CommandInterface {
|
|||
return [
|
||||
'command' => 'webformScrollTop',
|
||||
'selector' => $this->selector,
|
||||
'target' => $this->target,
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Ajax;
|
||||
|
||||
use Drupal\Core\Ajax\AjaxResponse;
|
||||
use Drupal\webform\WebformSubmissionInterface;
|
||||
|
||||
/**
|
||||
* Custom JSON response object for an ajax webform submission response.
|
||||
*
|
||||
* We use a special response object to be able to fire a proper alter hook.
|
||||
*
|
||||
* @see https://www.linkedin.com/pulse/how-alter-ajax-commands-view-response-drupal-8-dalibor-stojakovic/
|
||||
*/
|
||||
class WebformSubmissionAjaxResponse extends AjaxResponse {
|
||||
|
||||
/**
|
||||
* The webform submission of this ajax request.
|
||||
*
|
||||
* @var \Drupal\webform\WebformSubmissionInterface
|
||||
*/
|
||||
protected $submission;
|
||||
|
||||
/**
|
||||
* Sets the webform submission of this response.
|
||||
*
|
||||
* @param \Drupal\webform\WebformSubmissionInterface $webform_submission
|
||||
* The webform submission of this ajax request.
|
||||
*/
|
||||
public function setWebformSubmission(WebformSubmissionInterface $webform_submission) {
|
||||
$this->submission = $webform_submission;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the webform submission of this response.
|
||||
*
|
||||
* @return \Drupal\webform\WebformSubmissionInterface
|
||||
* The webform submission of this ajax request.
|
||||
*/
|
||||
public function getWebformSubmission() {
|
||||
return $this->submission;
|
||||
}
|
||||
|
||||
}
|
|
@ -13,10 +13,10 @@ use Drupal\Component\Annotation\Plugin;
|
|||
* \Drupal\webform\Plugin\WebformElement\Email
|
||||
*
|
||||
* @see hook_webform_element_info_alter()
|
||||
* @see \Drupal\webform\WebformElementInterface
|
||||
* @see \Drupal\webform\WebformElementBase
|
||||
* @see \Drupal\webform\WebformElementManager
|
||||
* @see \Drupal\webform\WebformElementManagerInterface
|
||||
* @see \Drupal\webform\Plugin\WebformElementInterface
|
||||
* @see \Drupal\webform\Plugin\WebformElementBase
|
||||
* @see \Drupal\webform\Plugin\WebformElementManager
|
||||
* @see \Drupal\webform\Plugin\WebformElementManagerInterface
|
||||
* @see plugin_api
|
||||
*
|
||||
* @Annotation
|
||||
|
@ -37,6 +37,15 @@ class WebformElement extends Plugin {
|
|||
*/
|
||||
public $api;
|
||||
|
||||
/**
|
||||
* The element's module dependencies.
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
* @see webform_webform_element_info_alter()
|
||||
*/
|
||||
public $dependencies = [];
|
||||
|
||||
/**
|
||||
* The human-readable name of the webform element.
|
||||
*
|
||||
|
@ -46,6 +55,13 @@ class WebformElement extends Plugin {
|
|||
*/
|
||||
public $label;
|
||||
|
||||
/**
|
||||
* The default key used for new webform element.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $default_key = '';
|
||||
|
||||
/**
|
||||
* The category in the admin UI where the webform will be listed.
|
||||
*
|
||||
|
@ -94,4 +110,20 @@ class WebformElement extends Plugin {
|
|||
*/
|
||||
public $states_wrapper = FALSE;
|
||||
|
||||
/**
|
||||
* Flag that indicates the element has been deprecated.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $deprecated = FALSE;
|
||||
|
||||
/**
|
||||
* Deprecated message.
|
||||
*
|
||||
* @var \Drupal\Core\Annotation\Translation
|
||||
*
|
||||
* @ingroup plugin_translatable
|
||||
*/
|
||||
public $deprecated_message = '';
|
||||
|
||||
}
|
||||
|
|
|
@ -13,9 +13,9 @@ use Drupal\Component\Annotation\Plugin;
|
|||
* \Drupal\webform\Plugin\WebformExporter\DelimitedText/WebformExporter
|
||||
*
|
||||
* @see hook_webform_exporter_info_alter()
|
||||
* @see \Drupal\webform\WebformExporterInterface
|
||||
* @see \Drupal\webform\WebformExporterBase
|
||||
* @see \Drupal\webform\WebformExporterManager
|
||||
* @see \Drupal\webform\Plugin\WebformExporterInterface
|
||||
* @see \Drupal\webform\Plugin\WebformExporterBase
|
||||
* @see \Drupal\webform\Plugin\WebformExporterManager
|
||||
* @see plugin_api
|
||||
*
|
||||
* @Annotation
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
namespace Drupal\webform\Annotation;
|
||||
|
||||
use Drupal\Component\Annotation\Plugin;
|
||||
use Drupal\webform\WebformHandlerInterface;
|
||||
use Drupal\webform\Plugin\WebformHandlerInterface;
|
||||
|
||||
/**
|
||||
* Defines a webform handler annotation object.
|
||||
|
@ -14,9 +14,9 @@ use Drupal\webform\WebformHandlerInterface;
|
|||
* \Drupal\webform\Plugin\WebformHandler\EmailWebformHandler
|
||||
*
|
||||
* @see hook_webform_handler_info_alter()
|
||||
* @see \Drupal\webform\WebformHandlerInterface
|
||||
* @see \Drupal\webform\WebformHandlerBase
|
||||
* @see \Drupal\webform\WebformHandlerManager
|
||||
* @see \Drupal\webform\Plugin\WebformHandlerInterface
|
||||
* @see \Drupal\webform\Plugin\WebformHandlerBase
|
||||
* @see \Drupal\webform\Plugin\WebformHandlerManager
|
||||
* @see plugin_api
|
||||
*
|
||||
* @Annotation
|
||||
|
@ -63,8 +63,8 @@ class WebformHandler extends Plugin {
|
|||
* The maximum number of instances allowed for this webform handler.
|
||||
*
|
||||
* Possible values are positive integers or
|
||||
* \Drupal\webform\WebformHandlerInterface::CARDINALITY_UNLIMITED or
|
||||
* \Drupal\webform\WebformHandlerInterface::CARDINALITY_SINGLE.
|
||||
* \Drupal\webform\Plugin\WebformHandlerInterface::CARDINALITY_UNLIMITED or
|
||||
* \Drupal\webform\Plugin\WebformHandlerInterface::CARDINALITY_SINGLE.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
|
@ -79,4 +79,29 @@ class WebformHandler extends Plugin {
|
|||
*/
|
||||
public $results = WebformHandlerInterface::RESULTS_IGNORED;
|
||||
|
||||
/**
|
||||
* Indicated whether handler supports condition logic.
|
||||
*
|
||||
* Most handlers will support conditional logic, this flat allows custom
|
||||
* handlers and custom modules to easily disabled conditional logic for
|
||||
* a handler.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $conditions = TRUE;
|
||||
|
||||
/**
|
||||
* Indicated whether handler supports tokens.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $tokens = FALSE;
|
||||
|
||||
/**
|
||||
* Indicated whether submission must be stored in the database for this handler processes results.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $submission = WebformHandlerInterface::SUBMISSION_OPTIONAL;
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Annotation;
|
||||
|
||||
use Drupal\Component\Annotation\Plugin;
|
||||
|
||||
/**
|
||||
* Defines a webform source entity annotation object.
|
||||
*
|
||||
* Plugin Namespace: Plugin\WebformSourceEntity.
|
||||
*
|
||||
* For a working example, see
|
||||
* \Drupal\webform\Plugin\WebformSourceEntity\QueryStringWebformSourceEntity
|
||||
*
|
||||
* @see hook_webform_source_entity_info()
|
||||
* @see \Drupal\webform\Plugin\WebformSourceEntityInterface
|
||||
* @see \Drupal\webform\Plugin\WebformSourceEntityManager
|
||||
* @see \Drupal\webform\Plugin\WebformSourceEntityManagerInterface
|
||||
* @see plugin_api
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class WebformSourceEntity extends Plugin {
|
||||
|
||||
/**
|
||||
* The plugin ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* The human-readable name of the plugin.
|
||||
*
|
||||
* @var \Drupal\Core\Annotation\Translation
|
||||
*
|
||||
* @ingroup plugin_translatable
|
||||
*/
|
||||
public $label;
|
||||
|
||||
/**
|
||||
* A brief description of the plugin.
|
||||
*
|
||||
* @var \Drupal\Core\Annotation\Translation
|
||||
*
|
||||
* @ingroup plugin_translatable
|
||||
*/
|
||||
public $description = '';
|
||||
|
||||
/**
|
||||
* Weight (priority) of the plugin.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $weight = 0;
|
||||
|
||||
/**
|
||||
* The element's module dependencies.
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
* @see webform_webform_element_info_alter()
|
||||
*/
|
||||
public $dependencies = [];
|
||||
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\BreadCrumb;
|
||||
namespace Drupal\webform\Breadcrumb;
|
||||
|
||||
use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
|
||||
use Drupal\Core\Breadcrumb\Breadcrumb;
|
||||
|
@ -72,48 +72,64 @@ class WebformBreadcrumbBuilder implements BreadcrumbBuilderInterface {
|
|||
|
||||
// Skip all config_translation routes except the overview
|
||||
// and allow Drupal to use the path as the breadcrumb.
|
||||
if (strpos($route_name, 'config_translation') !== FALSE && $route_name != 'entity.webform.config_translation_overview') {
|
||||
if (strpos($route_name, 'config_translation') !== FALSE && !in_array($route_name, ['entity.webform.config_translation_overview', 'config_translation.item.overview.webform.config', 'config_translation.item.add.webform.config', 'config_translation.item.edit.webform.config', 'config_translation.item.delete.webform.config'])) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
try {
|
||||
$path = Url::fromRouteMatch($route_match)->toString();
|
||||
$base_path = base_path();
|
||||
}
|
||||
catch (\Exception $exception) {
|
||||
$path = '';
|
||||
$base_path = '/';
|
||||
}
|
||||
if ((count($args) > 2) && $args[0] == 'entity' && ($args[2] == 'webform' || $args[2] == 'webform_submission')) {
|
||||
|
||||
/** @var \Drupal\webform\WebformInterface $webform */
|
||||
$webform = ($route_match->getParameter('webform') instanceof WebformInterface) ? $route_match->getParameter('webform') : NULL;
|
||||
|
||||
/** @var \Drupal\webform\WebformSubmissionInterface $webform_submission */
|
||||
$webform_submission = ($route_match->getParameter('webform_submission') instanceof WebformSubmissionInterface) ? $route_match->getParameter('webform_submission') : NULL;
|
||||
|
||||
if ((count($args) > 2) && $args[0] == 'entity' && ($args[2] == 'webform' || $args[2] == 'webform_submission')) {
|
||||
$this->type = 'webform_source_entity';
|
||||
}
|
||||
elseif ($route_name === 'webform.reports_plugins.elements.test') {
|
||||
$this->type = 'webform_plugins_elements';
|
||||
}
|
||||
elseif (strpos($route_name, 'webform.contribute') === 0) {
|
||||
$this->type = 'webform_contribute';
|
||||
}
|
||||
elseif (strpos($route_name, 'webform.help.') === 0) {
|
||||
$this->type = 'webform_help';
|
||||
}
|
||||
elseif (strpos($route_name, 'entity.webform_ui.element') === 0) {
|
||||
$this->type = 'webform_element';
|
||||
}
|
||||
elseif (strpos($route_name, 'entity.webform.handler.') === 0) {
|
||||
$this->type = 'webform_handler';
|
||||
}
|
||||
elseif ($route_match->getParameter('webform_submission') instanceof WebformSubmissionInterface && strpos($route_name, 'webform.user.submission') !== FALSE) {
|
||||
elseif ($webform_submission && strpos($route_name, '.webform.user.submission') !== FALSE) {
|
||||
$this->type = 'webform_user_submission';
|
||||
}
|
||||
elseif (strpos($route_match->getRouteName(), 'webform.user.submissions') !== FALSE) {
|
||||
elseif (strpos($route_name, '.webform.user.submissions') !== FALSE) {
|
||||
$this->type = 'webform_user_submissions';
|
||||
}
|
||||
elseif ($route_match->getParameter('webform_submission') instanceof WebformSubmissionInterface && $route_match->getParameter('webform_submission')->access('admin')) {
|
||||
elseif (strpos($route_name, '.webform.user.drafts') !== FALSE) {
|
||||
$this->type = 'webform_user_drafts';
|
||||
}
|
||||
elseif ($webform_submission && $webform_submission->access('admin')) {
|
||||
$this->type = 'webform_submission';
|
||||
}
|
||||
elseif (($route_match->getParameter('webform') instanceof WebformInterface && $route_match->getParameter('webform')->access('admin'))) {
|
||||
/** @var \Drupal\webform\WebformInterface $webform */
|
||||
$webform = $route_match->getParameter('webform');
|
||||
elseif ($webform && $webform->access('admin')) {
|
||||
$this->type = ($webform->isTemplate() && $this->moduleHandler->moduleExists('webform_templates')) ? 'webform_template' : 'webform';
|
||||
}
|
||||
elseif (strpos($path, $base_path . 'admin/structure/webform/test/') === 0) {
|
||||
elseif (strpos($path, 'admin/structure/webform/test/') !== FALSE) {
|
||||
$this->type = 'webform_test';
|
||||
}
|
||||
elseif (strpos($path, 'admin/structure/webform/config/') !== FALSE) {
|
||||
$this->type = 'webform_config';
|
||||
}
|
||||
else {
|
||||
$this->type = NULL;
|
||||
}
|
||||
|
||||
return ($this->type) ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
|
@ -121,6 +137,8 @@ class WebformBreadcrumbBuilder implements BreadcrumbBuilderInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function build(RouteMatchInterface $route_match) {
|
||||
$route_name = $route_match->getRouteName();
|
||||
|
||||
if ($this->type == 'webform_source_entity') {
|
||||
$source_entity = $this->requestHandler->getCurrentSourceEntity(['webform', 'webform_submission']);
|
||||
$entity_type = $source_entity->getEntityTypeId();
|
||||
|
@ -141,6 +159,20 @@ class WebformBreadcrumbBuilder implements BreadcrumbBuilderInterface {
|
|||
}
|
||||
}
|
||||
}
|
||||
elseif ($this->type == 'webform_help') {
|
||||
$breadcrumb = new Breadcrumb();
|
||||
$breadcrumb->addLink(Link::createFromRoute($this->t('Home'), '<front>'));
|
||||
$breadcrumb->addLink(Link::createFromRoute($this->t('Administration'), 'system.admin'));
|
||||
$breadcrumb->addLink(Link::createFromRoute($this->t('Help'), 'help.main'));
|
||||
$breadcrumb->addLink(Link::createFromRoute($this->t('Webform'), 'help.page', ['name' => 'webform']));
|
||||
}
|
||||
elseif ($this->type == 'webform_plugins_elements') {
|
||||
$breadcrumb = new Breadcrumb();
|
||||
$breadcrumb->addLink(Link::createFromRoute($this->t('Home'), '<front>'));
|
||||
$breadcrumb->addLink(Link::createFromRoute($this->t('Administration'), 'system.admin'));
|
||||
$breadcrumb->addLink(Link::createFromRoute($this->t('Reports'), 'system.admin_reports'));
|
||||
$breadcrumb->addLink(Link::createFromRoute($this->t('Elements'), 'webform.reports_plugins.elements'));
|
||||
}
|
||||
else {
|
||||
$breadcrumb = new Breadcrumb();
|
||||
$breadcrumb->addLink(Link::createFromRoute($this->t('Home'), '<front>'));
|
||||
|
@ -148,6 +180,21 @@ class WebformBreadcrumbBuilder implements BreadcrumbBuilderInterface {
|
|||
$breadcrumb->addLink(Link::createFromRoute($this->t('Structure'), 'system.admin_structure'));
|
||||
$breadcrumb->addLink(Link::createFromRoute($this->t('Webforms'), 'entity.webform.collection'));
|
||||
switch ($this->type) {
|
||||
case 'webform_config':
|
||||
$breadcrumb->addLink(Link::createFromRoute($this->t('Configuration'), 'webform.config'));
|
||||
if (strpos($route_name, 'config_translation.item.') === 0 && $route_name != 'config_translation.item.overview.webform.config') {
|
||||
$breadcrumb->addLink(Link::createFromRoute($this->t('Translate'), 'config_translation.item.overview.webform.config'));
|
||||
}
|
||||
elseif (strpos($route_name, 'entity.webform_options') === 0 && $route_name !== 'entity.webform_options.collection') {
|
||||
$breadcrumb->addLink(Link::createFromRoute($this->t('Options'), 'entity.webform_options.collection'));
|
||||
}
|
||||
elseif (strpos($route_name, 'entity.webform_image_select_images') === 0 && $route_name !== 'entity.webform_image_select_imagess.collection') {
|
||||
// @todo Refactor or move to webform_image_select.module.
|
||||
// @see webform_image_select.module.
|
||||
$breadcrumb->addLink(Link::createFromRoute($this->t('Images'), 'entity.webform_image_select_images.collection'));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'webform_test':
|
||||
$breadcrumb->addLink(Link::createFromRoute($this->t('Testing'), 'webform_test.index'));
|
||||
break;
|
||||
|
@ -167,7 +214,7 @@ class WebformBreadcrumbBuilder implements BreadcrumbBuilderInterface {
|
|||
/** @var \Drupal\webform\WebformInterface $webform */
|
||||
$webform = $route_match->getParameter('webform');
|
||||
$breadcrumb->addLink(Link::createFromRoute($webform->label(), 'entity.webform.canonical', ['webform' => $webform->id()]));
|
||||
$breadcrumb->addLink(Link::createFromRoute($this->t('Emails / Handlers'), 'entity.webform.handlers_form', ['webform' => $webform->id()]));
|
||||
$breadcrumb->addLink(Link::createFromRoute($this->t('Emails / Handlers'), 'entity.webform.handlers', ['webform' => $webform->id()]));
|
||||
break;
|
||||
|
||||
case 'webform_submission':
|
||||
|
@ -179,6 +226,7 @@ class WebformBreadcrumbBuilder implements BreadcrumbBuilderInterface {
|
|||
break;
|
||||
|
||||
case 'webform_user_submissions':
|
||||
case 'webform_user_drafts':
|
||||
/** @var \Drupal\webform\WebformInterface $webform */
|
||||
$webform = $route_match->getParameter('webform');
|
||||
$breadcrumb = new Breadcrumb();
|
||||
|
|
1358
web/modules/contrib/webform/src/Commands/WebformCliService.php
Normal file
1358
web/modules/contrib/webform/src/Commands/WebformCliService.php
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Commands;
|
||||
|
||||
use Drush\Commands\DrushCommands;
|
||||
|
||||
/**
|
||||
* Defines an interface for Drush version agnostic commands.
|
||||
*/
|
||||
interface WebformCliServiceInterface {
|
||||
|
||||
/**
|
||||
* Set the Drush 9.x command.
|
||||
*
|
||||
* @param \Drush\Commands\DrushCommands $command
|
||||
* A Drush 9.x command.
|
||||
*/
|
||||
public function setCommand(DrushCommands $command);
|
||||
|
||||
/**
|
||||
* Implements hook_drush_command().
|
||||
*/
|
||||
public function webform_drush_command();
|
||||
|
||||
/**
|
||||
* Implements drush_hook_COMMAND_validate().
|
||||
*/
|
||||
public function drush_webform_export_validate($webform_id = NULL);
|
||||
|
||||
/**
|
||||
* Implements drush_hook_COMMAND().
|
||||
*/
|
||||
public function drush_webform_export($webform_id = NULL);
|
||||
|
||||
/**
|
||||
* Implements drush_hook_COMMAND_validate().
|
||||
*/
|
||||
public function drush_webform_purge_validate($webform_id = NULL);
|
||||
|
||||
/**
|
||||
* Implements drush_hook_COMMAND().
|
||||
*/
|
||||
public function drush_webform_purge($webform_id = NULL);
|
||||
|
||||
/**
|
||||
* Implements drush_hook_COMMAND_validate().
|
||||
*/
|
||||
public function drush_webform_tidy_validate($target = NULL);
|
||||
|
||||
/**
|
||||
* Implements drush_hook_COMMAND().
|
||||
*/
|
||||
public function drush_webform_tidy($target = NULL);
|
||||
|
||||
/**
|
||||
* Implements drush_hook_COMMAND_validate().
|
||||
*/
|
||||
public function drush_webform_generate_validate($webform_id = NULL);
|
||||
|
||||
/**
|
||||
* Implements drush_hook_COMMAND().
|
||||
*/
|
||||
function drush_webform_generate($webform_id = NULL, $num = NULL);
|
||||
|
||||
/**
|
||||
* Implements drush_hook_COMMAND().
|
||||
*/
|
||||
public function drush_webform_libraries_status();
|
||||
|
||||
/**
|
||||
* Implements drush_hook_COMMAND().
|
||||
*/
|
||||
public function drush_webform_libraries_make();
|
||||
|
||||
/**
|
||||
* Implements drush_hook_COMMAND().
|
||||
*
|
||||
* How to handle module library dependencies #68
|
||||
*
|
||||
* @see https://github.com/drupal-composer/drupal-project/issues/68
|
||||
*/
|
||||
public function drush_webform_libraries_composer();
|
||||
|
||||
/**
|
||||
* Implements drush_hook_COMMAND().
|
||||
*/
|
||||
public function drush_webform_libraries_download();
|
||||
|
||||
/**
|
||||
* Implements drush_hook_COMMAND().
|
||||
*/
|
||||
public function drush_webform_libraries_remove($status = NULL);
|
||||
|
||||
/**
|
||||
* Implements drush_hook_COMMAND().
|
||||
*/
|
||||
public function drush_webform_repair();
|
||||
|
||||
/**
|
||||
* Implements drush_hook_COMMAND_validate().
|
||||
*/
|
||||
public function drush_webform_docs_validate();
|
||||
|
||||
/**
|
||||
* Implements drush_hook_COMMAND().
|
||||
*/
|
||||
public function drush_webform_docs();
|
||||
|
||||
/**
|
||||
* Implements drush_hook_COMMAND_validate().
|
||||
*/
|
||||
public function drush_webform_composer_update_validate();
|
||||
|
||||
/**
|
||||
* Implements drush_hook_COMMAND().
|
||||
*/
|
||||
public function drush_webform_composer_update();
|
||||
|
||||
/**
|
||||
* Implements drush_hook_COMMAND().
|
||||
*/
|
||||
public function drush_webform_generate_commands();
|
||||
|
||||
}
|
317
web/modules/contrib/webform/src/Commands/WebformCommands.php
Normal file
317
web/modules/contrib/webform/src/Commands/WebformCommands.php
Normal file
|
@ -0,0 +1,317 @@
|
|||
<?php
|
||||
// @codingStandardsIgnoreFile
|
||||
|
||||
/**
|
||||
* This is file was generated using Drush. DO NOT EDIT.
|
||||
*
|
||||
* @see drush webform-generate-commands
|
||||
* @see \Drupal\webform\Commands\DrushCliServiceBase::generate_commands_drush9
|
||||
*/
|
||||
namespace Drupal\webform\Commands;
|
||||
|
||||
use Consolidation\AnnotatedCommand\CommandData;
|
||||
|
||||
/**
|
||||
* Webform commands for Drush 9.x.
|
||||
*/
|
||||
class WebformCommands extends WebformCommandsBase {
|
||||
|
||||
/****************************************************************************/
|
||||
// drush webform:export. DO NOT EDIT.
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* @hook validate webform:export
|
||||
*/
|
||||
public function drush_webform_export_validate(CommandData $commandData) {
|
||||
$arguments = $commandData->arguments();
|
||||
array_shift($arguments);
|
||||
call_user_func_array([$this->cliService, 'drush_webform_export_validate'], $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports webform submissions to a file.
|
||||
*
|
||||
* @command webform:export
|
||||
* @param $webform The webform ID you want to export (required unless --entity-type and --entity-id are specified)
|
||||
* @option delimiter Delimiter between columns (defaults to site-wide setting). This option may need to be wrapped in quotes. i.e. --delimiter="\t".
|
||||
* @option multiple-delimiter Delimiter between an element with multiple values (defaults to site-wide setting).
|
||||
* @option file-name File name used to export submission and uploaded filed. You may use tokens.
|
||||
* @option header-format Set to "label" (default) or "key"
|
||||
* @option options-item-format Set to "label" (default) or "key". Set to "key" to print select list values by their keys instead of labels.
|
||||
* @option options-single-format Set to "separate" (default) or "compact" to determine how single select list values are exported.
|
||||
* @option options-multiple-format Set to "separate" (default) or "compact" to determine how multiple select list values are exported.
|
||||
* @option entity-reference-items Comma-separated list of entity reference items (id, title, and/or url) to be exported.
|
||||
* @option excluded-columns Comma-separated list of component IDs or webform keys to exclude.
|
||||
* @option entity-type The entity type to which this submission was submitted from.
|
||||
* @option entity-id The ID of the entity of which this webform submission was submitted from.
|
||||
* @option range-type Range of submissions to export: "all", "latest", "serial", "sid", or "date".
|
||||
* @option range-latest Integer specifying the latest X submissions will be downloaded. Used if "range-type" is "latest" or no other range options are provided.
|
||||
* @option range-start The submission ID or start date at which to start exporting.
|
||||
* @option range-end The submission ID or end date at which to end exporting.
|
||||
* @option order The submission order "asc" (default) or "desc".
|
||||
* @option state Submission state to be included: "completed", "draft" or "all" (default).
|
||||
* @option sticky Flagged/starred submission status.
|
||||
* @option files Download files: "1" or "0" (default). If set to 1, the exported CSV file and any submission file uploads will be download in a gzipped tar file.
|
||||
* @option destination The full path and filename in which the CSV or archive should be stored. If omitted the CSV file or archive will be outputted to the commandline.
|
||||
* @aliases wfx
|
||||
*/
|
||||
public function drush_webform_export($webform = NULL, array $options = ['delimiter' => NULL, 'multiple-delimiter' => NULL, 'file-name' => NULL, 'header-format' => NULL, 'options-item-format' => NULL, 'options-single-format' => NULL, 'options-multiple-format' => NULL, 'entity-reference-items' => NULL, 'excluded-columns' => NULL, 'entity-type' => NULL, 'entity-id' => NULL, 'range-type' => NULL, 'range-latest' => NULL, 'range-start' => NULL, 'range-end' => NULL, 'order' => NULL, 'state' => NULL, 'sticky' => NULL, 'files' => NULL, 'destination' => NULL]) {
|
||||
$this->cliService->drush_webform_export($webform);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
// drush webform:purge. DO NOT EDIT.
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* @hook validate webform:purge
|
||||
*/
|
||||
public function drush_webform_purge_validate(CommandData $commandData) {
|
||||
$arguments = $commandData->arguments();
|
||||
array_shift($arguments);
|
||||
call_user_func_array([$this->cliService, 'drush_webform_purge_validate'], $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge webform submissions from the databases
|
||||
*
|
||||
* @command webform:purge
|
||||
* @param $webform_id A webform machine name. If not provided, user may choose from a list of names.
|
||||
* @option all Flush all submissions
|
||||
* @option entity-type The entity type for webform submissions to be purged
|
||||
* @option entity-id The ID of the entity for webform submissions to be purged
|
||||
* @usage drush webform-purge
|
||||
* Pick a webform and then purge its submissions.
|
||||
* @usage drush webform-purge contact
|
||||
* Delete 'Contact' webform submissions.
|
||||
* @usage drush webform-purge --all
|
||||
* Purge all webform submissions.
|
||||
* @aliases wfp
|
||||
*/
|
||||
public function drush_webform_purge($webform_id = NULL, array $options = ['all' => FALSE, 'entity-type' => NULL, 'entity-id' => NULL]) {
|
||||
$this->cliService->drush_webform_purge($webform_id);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
// drush webform:tidy. DO NOT EDIT.
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* @hook validate webform:tidy
|
||||
*/
|
||||
public function drush_webform_tidy_validate(CommandData $commandData) {
|
||||
$arguments = $commandData->arguments();
|
||||
array_shift($arguments);
|
||||
call_user_func_array([$this->cliService, 'drush_webform_tidy_validate'], $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tidy export webform configuration files
|
||||
*
|
||||
* @command webform:tidy
|
||||
* @param $target The module (config/install), config directory (sync), or path (/some/path) that needs its YAML configuration files tidied. (Defaults to webform)
|
||||
* @option dependencies Add module dependencies to installed webform and options configuration entities.
|
||||
* @option prefix Prefix for file names to be tidied. (Defaults to webform)
|
||||
* @usage drush webform-tidy webform
|
||||
* Tidies YAML configuration files in 'webform/config' for the Webform module
|
||||
* @aliases wft
|
||||
*/
|
||||
public function drush_webform_tidy($target = NULL, array $options = ['dependencies' => FALSE, 'prefix' => NULL]) {
|
||||
$this->cliService->drush_webform_tidy($target);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
// drush webform:libraries:status. DO NOT EDIT.
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* Displays the status of third party libraries required by the Webform module.
|
||||
*
|
||||
* @command webform:libraries:status
|
||||
* @usage webform-libraries-status
|
||||
* Displays the status of third party libraries required by the Webform module.
|
||||
* @aliases wfls
|
||||
*/
|
||||
public function drush_webform_libraries_status() {
|
||||
$this->cliService->drush_webform_libraries_status();
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
// drush webform:libraries:make. DO NOT EDIT.
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* Generates libraries YAML to be included in a drush.make.yml files.
|
||||
*
|
||||
* @command webform:libraries:make
|
||||
* @usage webform-libraries-make
|
||||
* Generates libraries YAML to be included in a drush.make.yml file.
|
||||
* @aliases wflm
|
||||
*/
|
||||
public function drush_webform_libraries_make() {
|
||||
$this->cliService->drush_webform_libraries_make();
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
// drush webform:libraries:composer. DO NOT EDIT.
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* Generates the Webform module's composer.json with libraries as repositories.
|
||||
*
|
||||
* @command webform:libraries:composer
|
||||
* @option disable-tls If set to true all HTTPS URLs will be tried with HTTP instead and no network level encryption is performed.
|
||||
* @usage webform-libraries-composer
|
||||
* Generates the Webform module's composer.json with libraries as repositories.
|
||||
* @aliases wflc
|
||||
*/
|
||||
public function drush_webform_libraries_composer(array $options = ['disable-tls' => FALSE]) {
|
||||
$this->cliService->drush_webform_libraries_composer();
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
// drush webform:libraries:download. DO NOT EDIT.
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* Download third party libraries required by the Webform module.
|
||||
*
|
||||
* @command webform:libraries:download
|
||||
* @usage webform-libraries-download
|
||||
* Download third party libraries required by the Webform module.
|
||||
* @aliases wfld
|
||||
*/
|
||||
public function drush_webform_libraries_download() {
|
||||
$this->cliService->drush_webform_libraries_download();
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
// drush webform:libraries:remove. DO NOT EDIT.
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* Removes all downloaded third party libraries required by the Webform module.
|
||||
*
|
||||
* @command webform:libraries:remove
|
||||
* @usage webform-libraries-remove
|
||||
* Removes all downloaded third party libraries required by the Webform module.
|
||||
* @aliases wflr
|
||||
*/
|
||||
public function drush_webform_libraries_remove() {
|
||||
$this->cliService->drush_webform_libraries_remove();
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
// drush webform:generate. DO NOT EDIT.
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* @hook validate webform:generate
|
||||
*/
|
||||
public function drush_webform_generate_validate(CommandData $commandData) {
|
||||
$arguments = $commandData->arguments();
|
||||
array_shift($arguments);
|
||||
call_user_func_array([$this->cliService, 'drush_webform_generate_validate'], $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create submissions in specified webform.
|
||||
*
|
||||
* @command webform:generate
|
||||
* @param $webform_id Webform id into which new submissions will be inserted.
|
||||
* @param $num Number of submissions to insert. Defaults to 50.
|
||||
* @option kill Delete all submissions in specified webform before generating.
|
||||
* @option feedback An integer representing interval for insertion rate logging. Defaults to 1000
|
||||
* @option entity-type The entity type to which this submission was submitted from.
|
||||
* @option entity-id The ID of the entity of which this webform submission was submitted from.
|
||||
* @aliases wfg
|
||||
*/
|
||||
public function drush_webform_generate($webform_id = NULL, $num = NULL, array $options = ['kill' => FALSE, 'feedback' => NULL, 'entity-type' => NULL, 'entity-id' => NULL]) {
|
||||
$this->cliService->drush_webform_generate($webform_id, $num);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
// drush webform:repair. DO NOT EDIT.
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* Makes sure all Webform admin settings and webforms are up-to-date.
|
||||
*
|
||||
* @command webform:repair
|
||||
* @usage webform-repair
|
||||
* Repairs admin settings and webforms are up-to-date.
|
||||
* @aliases wfr
|
||||
*/
|
||||
public function drush_webform_repair() {
|
||||
$this->cliService->drush_webform_repair();
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
// drush webform:docs. DO NOT EDIT.
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* @hook validate webform:docs
|
||||
*/
|
||||
public function drush_webform_docs_validate(CommandData $commandData) {
|
||||
$arguments = $commandData->arguments();
|
||||
array_shift($arguments);
|
||||
call_user_func_array([$this->cliService, 'drush_webform_docs_validate'], $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates HTML documentation.
|
||||
*
|
||||
* @command webform:docs
|
||||
* @usage webform-repair
|
||||
* Generates HTML documentation used by the Webform module's documentation pages.
|
||||
* @aliases wfd
|
||||
*/
|
||||
public function drush_webform_docs() {
|
||||
$this->cliService->drush_webform_docs();
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
// drush webform:composer:update. DO NOT EDIT.
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* @hook validate webform:composer:update
|
||||
*/
|
||||
public function drush_webform_composer_update_validate(CommandData $commandData) {
|
||||
$arguments = $commandData->arguments();
|
||||
array_shift($arguments);
|
||||
call_user_func_array([$this->cliService, 'drush_webform_composer_update_validate'], $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the Drupal installation's composer.json to include the Webform module's selected libraries as repositories.
|
||||
*
|
||||
* @command webform:composer:update
|
||||
* @option disable-tls If set to true all HTTPS URLs will be tried with HTTP instead and no network level encryption is performed.
|
||||
* @usage webform-composer-update
|
||||
* Updates the Drupal installation's composer.json to include the Webform module's selected libraries as repositories.
|
||||
* @aliases wfcu
|
||||
*/
|
||||
public function drush_webform_composer_update(array $options = ['disable-tls' => FALSE]) {
|
||||
$this->cliService->drush_webform_composer_update();
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
// drush webform:generate:commands. DO NOT EDIT.
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* Generate Drush commands from webform.drush.inc for Drush 8.x to WebformCommands for Drush 9.x.
|
||||
*
|
||||
* @command webform:generate:commands
|
||||
* @usage drush webform-generate-commands
|
||||
* Generate Drush commands from webform.drush.inc for Drush 8.x to WebformCommands for Drush 9.x.
|
||||
* @aliases wfgc
|
||||
*/
|
||||
public function drush_webform_generate_commands() {
|
||||
$this->cliService->drush_webform_generate_commands();
|
||||
}
|
||||
|
||||
}
|
128
web/modules/contrib/webform/src/Commands/WebformCommandsBase.php
Normal file
128
web/modules/contrib/webform/src/Commands/WebformCommandsBase.php
Normal file
|
@ -0,0 +1,128 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Commands;
|
||||
|
||||
use Drush\Commands\DrushCommands;
|
||||
use Drush\Drush;
|
||||
use Drush\Exceptions\UserAbortException;
|
||||
use Psr\Log\LogLevel;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
|
||||
/**
|
||||
* Base class for Webform commands for Drush 9.x.
|
||||
*/
|
||||
abstract class WebformCommandsBase extends DrushCommands {
|
||||
|
||||
/**
|
||||
* The webform CLI service.
|
||||
*
|
||||
* @var \Drupal\webform\Commands\WebformCliServiceInterface
|
||||
*/
|
||||
protected $cliService;
|
||||
|
||||
/**
|
||||
* Constructs a WebformCommandsBase object.
|
||||
*
|
||||
* @param \Drupal\webform\Commands\WebformCliServiceInterface $cli_service
|
||||
* The webform CLI service.
|
||||
*/
|
||||
public function __construct(WebformCliServiceInterface $cli_service) {
|
||||
$this->cliService = $cli_service;
|
||||
|
||||
// Injecting the WebformCommand into the CLI service so that calls to
|
||||
// drush functions can be delegatef back the below methods.
|
||||
// @see \Drupal\webform\Commands\WebformCliService::__call
|
||||
$this->cliService->setCommand($this);
|
||||
}
|
||||
|
||||
public function drush_confirm($question) {
|
||||
return $this->io()->confirm($question);
|
||||
}
|
||||
|
||||
public function drush_choice($choices, $msg, $default = NULL) {
|
||||
return $this->io()->choice($msg, $choices, $default);
|
||||
}
|
||||
|
||||
public function drush_log($message, $type = LogLevel::INFO) {
|
||||
$this->logger()->log($type, $message);
|
||||
}
|
||||
|
||||
public function drush_print($message) {
|
||||
$this->output()->writeln($message);
|
||||
}
|
||||
|
||||
public function drush_get_option($name) {
|
||||
return $this->input()->getOption($name);
|
||||
}
|
||||
|
||||
public function drush_user_abort() {
|
||||
throw new UserAbortException();
|
||||
}
|
||||
|
||||
public function drush_set_error($error) {
|
||||
throw new \Exception($error);
|
||||
}
|
||||
|
||||
public function drush_redispatch_get_options() {
|
||||
return Drush::redispatchOptions();
|
||||
}
|
||||
|
||||
public function drush_download_file($url, $destination) {
|
||||
// Copied from: \Drush\Commands\SyncViaHttpCommands::downloadFile
|
||||
static $use_wget;
|
||||
if ($use_wget === NULL) {
|
||||
$use_wget = drush_shell_exec('which wget');
|
||||
}
|
||||
|
||||
$destination_tmp = drush_tempnam('download_file');
|
||||
if ($use_wget) {
|
||||
drush_shell_exec("wget -q --timeout=30 -O %s %s", $destination_tmp, $url);
|
||||
}
|
||||
else {
|
||||
drush_shell_exec("curl -s -L --connect-timeout 30 -o %s %s", $destination_tmp, $url);
|
||||
}
|
||||
if (!drush_file_not_empty($destination_tmp) && $file = @file_get_contents($url)) {
|
||||
@file_put_contents($destination_tmp, $file);
|
||||
}
|
||||
if (!drush_file_not_empty($destination_tmp)) {
|
||||
// Download failed.
|
||||
throw new \Exception(dt("The URL !url could not be downloaded.", ['!url' => $url]));
|
||||
}
|
||||
if ($destination) {
|
||||
$fs = new Filesystem();
|
||||
$fs->rename($destination_tmp, $destination, TRUE);
|
||||
return $destination;
|
||||
}
|
||||
return $destination_tmp;
|
||||
}
|
||||
|
||||
public function drush_move_dir($src, $dest) {
|
||||
$fs = new Filesystem();
|
||||
$fs->rename($src, $dest, TRUE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
public function drush_mkdir($path) {
|
||||
$fs = new Filesystem();
|
||||
$fs->mkdir($path);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
public function drush_tarball_extract($path, $destination = FALSE) {
|
||||
$this->drush_mkdir($destination);
|
||||
if (preg_match('/\.tgz$/', $path)) {
|
||||
$return = drush_shell_cd_and_exec(dirname($path), "tar -xvzf %s -C %s", $path, $destination);
|
||||
if (!$return) {
|
||||
throw new \Exception(dt('Unable to extract !filename.' . PHP_EOL . implode(PHP_EOL, drush_shell_exec_output()), ['!filename' => $path]));
|
||||
}
|
||||
}
|
||||
else {
|
||||
$return = drush_shell_cd_and_exec(dirname($path), "unzip %s -d %s", $path, $destination);
|
||||
if (!$return) {
|
||||
throw new \Exception(dt('Unable to extract !filename.' . PHP_EOL . implode(PHP_EOL, drush_shell_exec_output()), ['!filename' => $path]));
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
}
|
|
@ -24,7 +24,7 @@ class WebformRouteContext implements ContextProviderInterface {
|
|||
protected $routeMatch;
|
||||
|
||||
/**
|
||||
* Constructs a new WebformRouteContext.
|
||||
* Constructs a WebformRouteContext.
|
||||
*
|
||||
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
|
||||
* The route match object.
|
||||
|
|
|
@ -24,7 +24,7 @@ class WebformSubmissionRouteContext implements ContextProviderInterface {
|
|||
protected $routeMatch;
|
||||
|
||||
/**
|
||||
* Constructs a new WebformSubmissionRouteContext.
|
||||
* Constructs a WebformSubmissionRouteContext.
|
||||
*
|
||||
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
|
||||
* The route match object.
|
||||
|
|
|
@ -4,8 +4,10 @@ namespace Drupal\webform\Controller;
|
|||
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\webform\Element\WebformMessage;
|
||||
use Drupal\webform\WebformAddonsManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
||||
/**
|
||||
* Provides route responses for webform add-on.
|
||||
|
@ -13,7 +15,14 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
|||
class WebformAddonsController extends ControllerBase implements ContainerInjectionInterface {
|
||||
|
||||
/**
|
||||
* The add-ons manager.
|
||||
* The current request.
|
||||
*
|
||||
* @var \Symfony\Component\HttpFoundation\Request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* The webform add-ons manager.
|
||||
*
|
||||
* @var \Drupal\webform\WebformAddonsManagerInterface
|
||||
*/
|
||||
|
@ -22,10 +31,13 @@ class WebformAddonsController extends ControllerBase implements ContainerInjecti
|
|||
/**
|
||||
* Constructs a WebformAddonsController object.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
|
||||
* The request stack.
|
||||
* @param \Drupal\webform\WebformAddonsManagerInterface $addons
|
||||
* The add-ons manager.
|
||||
* The webform add-ons manager.
|
||||
*/
|
||||
public function __construct(WebformAddonsManagerInterface $addons) {
|
||||
public function __construct(RequestStack $request_stack, WebformAddonsManagerInterface $addons) {
|
||||
$this->request = $request_stack->getCurrentRequest();
|
||||
$this->addons = $addons;
|
||||
}
|
||||
|
||||
|
@ -34,6 +46,7 @@ class WebformAddonsController extends ControllerBase implements ContainerInjecti
|
|||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('request_stack'),
|
||||
$container->get('webform.addons_manager')
|
||||
);
|
||||
}
|
||||
|
@ -48,28 +61,104 @@ class WebformAddonsController extends ControllerBase implements ContainerInjecti
|
|||
$build = [
|
||||
'#type' => 'container',
|
||||
'#attributes' => [
|
||||
'class' => ['webform-addons', 'js-webform-details-toggle', 'webform-details-toggle'],
|
||||
'class' => ['webform-addons'],
|
||||
],
|
||||
];
|
||||
$build['#attached']['library'][] = 'webform/webform.admin';
|
||||
$build['#attached']['library'][] = 'webform/webform.element.details.toggle';
|
||||
|
||||
// Filter.
|
||||
$build['filter'] = [
|
||||
'#type' => 'search',
|
||||
'#title' => $this->t('Filter'),
|
||||
'#title_display' => 'invisible',
|
||||
'#size' => 30,
|
||||
'#placeholder' => $this->t('Filter by keyword'),
|
||||
'#attributes' => [
|
||||
'class' => ['webform-form-filter-text'],
|
||||
'data-summary' => '.webform-addons-summary',
|
||||
'data-item-single' => $this->t('add-on'),
|
||||
'data-item-plural' => $this->t('add-ons'),
|
||||
'data-no-results' => '.webform-addons-no-results',
|
||||
'data-element' => '.admin-list',
|
||||
'data-source' => 'li',
|
||||
'data-parent' => 'li',
|
||||
'title' => $this->t('Enter a keyword to filter by.'),
|
||||
'autofocus' => 'autofocus',
|
||||
],
|
||||
];
|
||||
|
||||
// Display info.
|
||||
$build['info'] = [
|
||||
'#markup' => $this->t('@total add-ons', ['@total' => count($this->addons->getProjects())]),
|
||||
'#prefix' => '<p class="webform-addons-summary">',
|
||||
'#suffix' => '</p>',
|
||||
];
|
||||
|
||||
// Projects.
|
||||
$build['projects'] = [
|
||||
'#type' => 'container',
|
||||
'#attributes' => [
|
||||
'class' => ['webform-addons-projects', 'js-webform-details-toggle', 'webform-details-toggle'],
|
||||
],
|
||||
];
|
||||
|
||||
// Store and disable compact mode.
|
||||
// @see system_admin_compact_mode
|
||||
$system_admin_compact_mode = system_admin_compact_mode();
|
||||
$this->request->cookies->set('Drupal_visitor_admin_compact_mode', FALSE);
|
||||
|
||||
$categories = $this->addons->getCategories();
|
||||
foreach ($categories as $category_name => $category) {
|
||||
$build[$category_name] = [
|
||||
$build['projects'][$category_name] = [
|
||||
'#type' => 'details',
|
||||
'#title' => $category['title'],
|
||||
'#attributes' => ['data-webform-element-id' => 'webform-addons-' . $category_name],
|
||||
'#open' => TRUE,
|
||||
];
|
||||
$projects = $this->addons->getProjects($category_name);
|
||||
foreach ($projects as &$project) {
|
||||
$project['description'] .= ' ' . '<br/><small>' . $project['url']->toString() . '</small>';
|
||||
foreach ($projects as $project_name => &$project) {
|
||||
$project['description'] .= '<br /><small>' . $project['url']->toString() . '</small>';
|
||||
|
||||
// Append recommended to project's description.
|
||||
if (!empty($project['recommended'])) {
|
||||
$project['description'] .= '<br /><b class="color-success"> ★' . $this->t('Recommended') . '</b>';
|
||||
}
|
||||
|
||||
if (!empty($project['install']) && !$this->moduleHandler()->moduleExists($project_name)) {
|
||||
// If current user can install module then display a dismissible warning.
|
||||
if ($this->currentUser()->hasPermission('administer modules')) {
|
||||
$build['projects'][$project_name . '_message'] = [
|
||||
'#type' => 'webform_message',
|
||||
'#message_id' => $project_name . '_message',
|
||||
'#message_type' => 'warning',
|
||||
'#message_close' => TRUE,
|
||||
'#message_storage' => WebformMessage::STORAGE_USER,
|
||||
'#message_message' => $this->t('Please install to the <a href=":href">@title</a> project to improve the Webform module\'s user experience.', [':href' => $project['url']->toString(), '@title' => $project['title']]),
|
||||
'#weight' => -100,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
$build[$category_name]['content'] = [
|
||||
|
||||
$build['projects'][$category_name]['content'] = [
|
||||
'#theme' => 'admin_block_content',
|
||||
'#content' => $projects,
|
||||
];
|
||||
}
|
||||
|
||||
// Reset compact mode to stored setting.
|
||||
$this->request->cookies->get('Drupal_visitor_admin_compact_mode', $system_admin_compact_mode);
|
||||
|
||||
// No results.
|
||||
$build['no_results'] = [
|
||||
'#type' => 'webform_message',
|
||||
'#message_message' => $this->t('No add-ons found. Try a different search.'),
|
||||
'#message_type' => 'info',
|
||||
'#attributes' => ['class' => ['webform-addons-no-results']],
|
||||
];
|
||||
|
||||
$build['#attached']['library'][] = 'webform/webform.addons';
|
||||
$build['#attached']['library'][] = 'webform/webform.admin';
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,286 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Controller;
|
||||
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\webform\WebformContributeManagerInterface;
|
||||
use GuzzleHttp\ClientInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides route responses for webform contribute.
|
||||
*/
|
||||
class WebformContributeController extends ControllerBase implements ContainerInjectionInterface {
|
||||
|
||||
/**
|
||||
* The Guzzle HTTP client.
|
||||
*
|
||||
* @var \GuzzleHttp\ClientInterface
|
||||
*/
|
||||
protected $httpClient;
|
||||
|
||||
/**
|
||||
* The Guzzle HTTP client.
|
||||
*
|
||||
* @var \Drupal\webform\WebformContributeManagerInterface
|
||||
*/
|
||||
protected $contributeManager;
|
||||
|
||||
/**
|
||||
* Constructs a WebformContributeController object.
|
||||
*
|
||||
* @param \GuzzleHttp\ClientInterface $http_client
|
||||
* The Guzzle HTTP client.
|
||||
* @param \Drupal\webform\WebformContributeManagerInterface $contribute_manager
|
||||
* The webform contribute manager.
|
||||
*/
|
||||
public function __construct(ClientInterface $http_client, WebformContributeManagerInterface $contribute_manager) {
|
||||
$this->httpClient = $http_client;
|
||||
$this->contributeManager = $contribute_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('http_client'),
|
||||
$container->get('webform.contribute_manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns webform contribute page.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The current request.
|
||||
*
|
||||
* @return array
|
||||
* A renderable array containing a webform about page.
|
||||
*/
|
||||
public function index(Request $request) {
|
||||
// Message.
|
||||
$build['message'] = [];
|
||||
$build['message']['divide'] = $this->buildDivider();
|
||||
$build['message']['quote'] = [
|
||||
'#markup' => $this->t('The question is not should you contribute, but how can you contribute'),
|
||||
'#prefix' => '<blockquote class="webform-contribute-quote">',
|
||||
'#suffix' => '</blockquote>',
|
||||
];
|
||||
|
||||
// Community Information.
|
||||
$build['community_info'] = [
|
||||
'#theme' => 'webform_contribute',
|
||||
'#account' => $this->contributeManager->getAccount(),
|
||||
'#membership' => $this->contributeManager->getMembership(),
|
||||
'#contribution' => $this->contributeManager->getContribution(),
|
||||
];
|
||||
|
||||
// Drupal.
|
||||
$build['content']['#prefix'] = '<div class="webform-contribute-content">';
|
||||
$build['content']['#suffix'] = '</div>';
|
||||
$build['content']['drupal'] = [];
|
||||
$build['content']['drupal']['title'] = [
|
||||
'#markup' => $this->t('About Drupal'),
|
||||
'#prefix' => '<h2>',
|
||||
'#suffix' => '</h2>',
|
||||
];
|
||||
// Image.
|
||||
// @see https://pixabay.com/p-2009183
|
||||
$build['content']['drupal']['image'] = [
|
||||
'#theme' => 'image',
|
||||
'#uri' => drupal_get_path('module', 'webform') . '/images/contribute/contribute.png',
|
||||
'#alt' => $this->t('Webform: Contribute'),
|
||||
'#attributes' => [
|
||||
'class' => ['webform-contribute-image'],
|
||||
],
|
||||
];
|
||||
|
||||
$build['content']['drupal']['content'] = [
|
||||
'#markup' => $this->t("The Drupal project is open source software. Anyone can download, use, work on, and share it with others. It's built on <a href=\"https://www.drupal.org/about/mission-and-principles\">principles</a> like collaboration, globalism, and innovation. It's distributed under the terms of the <a href=\"http://www.gnu.org/copyleft/gpl.html\">GNU General Public License</a> (GPL). There are <a href=\"https://www.drupal.org/about/licensing\">no licensing fees</a>, ever. Drupal will always be free."),
|
||||
'#prefix' => '<p>',
|
||||
'#suffix' => '</p>',
|
||||
];
|
||||
$build['content']['drupal']['link'] = $this->buildLink(
|
||||
$this->t('Become a member of the Drupal community'),
|
||||
'https://register.drupal.org/user/register?destination=/project/webform'
|
||||
);
|
||||
$build['content']['drupal']['divider'] = $this->buildDivider();
|
||||
|
||||
// Community.
|
||||
$build['content']['community'] = [];
|
||||
$build['content']['community']['title'] = [
|
||||
'#markup' => $this->t('The Drupal Community'),
|
||||
'#prefix' => '<h2>',
|
||||
'#suffix' => '</h2>',
|
||||
];
|
||||
$build['content']['community']['image'] = [
|
||||
'#theme' => 'image',
|
||||
'#uri' => 'https://pbs.twimg.com/media/C-RXmp7XsAEgMN2.jpg',
|
||||
'#alt' => $this->t('DrupalCon Baltimore'),
|
||||
'#attributes' => [
|
||||
'class' => ['webform-contribute-image'],
|
||||
],
|
||||
];
|
||||
$build['content']['community']['quote'] = [
|
||||
'#prefix' => '<blockquote>',
|
||||
'#suffix' => '</blockquote>',
|
||||
];
|
||||
$build['content']['community']['quote'][] = [
|
||||
'#markup' => $this->t("It’s really the Drupal community and not so much the software that makes the Drupal project what it is. So fostering the Drupal community is actually more important than just managing the code base."),
|
||||
'#prefix' => '<address>',
|
||||
'#suffix' => '</address>',
|
||||
];
|
||||
$build['content']['community']['quote'][] = [
|
||||
'#markup' => $this->t('- Dries Buytaert'),
|
||||
];
|
||||
|
||||
$build['content']['community']['content'] = [
|
||||
'#markup' => $this->t("The Drupal community is one of the largest open source communities in the world. We're more than 1,000,000 passionate developers, designers, trainers, strategists, coordinators, editors, and sponsors working together. We build Drupal, provide support, create documentation, share networking opportunities, and more. Our shared commitment to the open source spirit pushes the Drupal project forward. New members are always welcome."),
|
||||
'#prefix' => '<p>',
|
||||
'#suffix' => '</p>',
|
||||
];
|
||||
$build['content']['community']['link'] = $this->buildLink(
|
||||
$this->t('Get involved in the Drupal community'),
|
||||
'https://www.drupal.org/getting-involved'
|
||||
);
|
||||
$build['content']['community']['divide'] = $this->buildDivider();
|
||||
|
||||
// Association.
|
||||
$build['content']['association'] = [];
|
||||
$build['content']['association']['title'] = [
|
||||
'#markup' => $this->t('Meet the Drupal Association'),
|
||||
'#prefix' => '<h2>',
|
||||
'#suffix' => '</h2>',
|
||||
];
|
||||
$build['content']['association']['video'] = $this->buildVideo('LZWqFSMul84');
|
||||
$build['content']['association']['content'] = [
|
||||
'#markup' => $this->t("The Drupal Association is dedicated to fostering and supporting the Drupal software project, the community, and its growth. We help the Drupal community with funding, infrastructure, education, promotion, distribution, and online collaboration at Drupal.org."),
|
||||
'#prefix' => '<p>',
|
||||
'#suffix' => '</p>',
|
||||
];
|
||||
$build['content']['association']['link'] = $this->buildLink(
|
||||
$this->t('Learn more about the Drupal Association'),
|
||||
'https://www.drupal.org/association/campaign/value-2017?utm_source=webform&utm_medium=referral&utm_campaign=membership-webform-2017-11-06'
|
||||
);
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns account type autocomplete matches.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The current request.
|
||||
* @param string $account_type
|
||||
* The account type to autocomplete.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\JsonResponse
|
||||
* The JSON response.
|
||||
*/
|
||||
public function autocomplete(Request $request, $account_type = 'user') {
|
||||
$q = $request->query->get('q');
|
||||
|
||||
switch ($account_type) {
|
||||
case 'user':
|
||||
$response = $this->httpClient->get('https://www.drupal.org/index.php?q=admin/views/ajax/autocomplete/user/' . urlencode($q));
|
||||
$data = Json::decode($response->getBody());
|
||||
$matches = [];
|
||||
foreach ($data as $value) {
|
||||
$matches[] = ['value' => $value, 'label' => $value];
|
||||
}
|
||||
return new JsonResponse($matches);
|
||||
|
||||
case 'organization':
|
||||
$response = $this->httpClient->get('https://www.drupal.org/index.php?q=entityreference/autocomplete/tags/field_for_customer/comment/comment_node_project_issue/NULL/' . urlencode($q));
|
||||
$data = Json::decode($response->getBody());
|
||||
$matches = [];
|
||||
foreach ($data as $value) {
|
||||
$value = strip_tags($value);
|
||||
$matches[] = ['value' => $value, 'label' => $value];
|
||||
}
|
||||
return new JsonResponse($matches);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
// Build methods.
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* Build a divider.
|
||||
*
|
||||
* @return array
|
||||
* A render array containing an HR.
|
||||
*/
|
||||
protected function buildDivider() {
|
||||
return ['#markup' => '<p><hr /></p>'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a link.
|
||||
*
|
||||
* @param string $title
|
||||
* Link title.
|
||||
* @param string $url
|
||||
* Link URL.
|
||||
* @param array $class
|
||||
* Link class names.
|
||||
*
|
||||
* @return array
|
||||
* A render array containing a link.
|
||||
*/
|
||||
protected function buildLink($title, $url, array $class = ['button']) {
|
||||
if (is_string($url)) {
|
||||
$url = Url::fromUri($url);
|
||||
}
|
||||
return [
|
||||
'#type' => 'link',
|
||||
'#title' => $title . ' ›',
|
||||
'#url' => $url,
|
||||
'#attributes' => ['class' => $class],
|
||||
'#prefix' => '<p>',
|
||||
'#suffix' => '</p>',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Build about video player or linked button.
|
||||
*
|
||||
* @param string $youtube_id
|
||||
* A YouTube id.
|
||||
*
|
||||
* @return array
|
||||
* A video player, linked button, or an empty array if videos are disabled.
|
||||
*/
|
||||
protected function buildVideo($youtube_id) {
|
||||
$video_display = $this->config('webform.settings')->get('ui.video_display');
|
||||
switch ($video_display) {
|
||||
case 'dialog':
|
||||
return [
|
||||
'#theme' => 'webform_help_video_youtube',
|
||||
'#youtube_id' => $youtube_id,
|
||||
'#autoplay' => FALSE,
|
||||
];
|
||||
|
||||
case 'link':
|
||||
return [
|
||||
'#type' => 'link',
|
||||
'#title' => $this->t('Watch video'),
|
||||
'#url' => Url::fromUri('https://youtu.be/' . $youtube_id),
|
||||
'#attributes' => ['class' => ['button', 'button-action', 'button--small', 'button-webform-play']],
|
||||
'#prefix' => ' ',
|
||||
];
|
||||
|
||||
case 'hidden':
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace Drupal\webform\Controller;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Ajax\AjaxResponse;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Database\Database;
|
||||
|
@ -26,7 +25,7 @@ class WebformElementController extends ControllerBase {
|
|||
* The unique id of the message.
|
||||
*
|
||||
* @return \Drupal\Core\Ajax\AjaxResponse
|
||||
* An empty AJAX response.
|
||||
* An empty Ajax response.
|
||||
*
|
||||
* @throws \Exception
|
||||
* Throws exception is storage is not set to 'user' or 'state'.
|
||||
|
@ -34,7 +33,7 @@ class WebformElementController extends ControllerBase {
|
|||
* @see \Drupal\webform\Element\WebformMessage::setClosed
|
||||
*/
|
||||
public function close($storage, $id) {
|
||||
if (!in_array($storage, ['user', 'state'])) {
|
||||
if (!in_array($storage, [WebformMessage::STORAGE_USER, WebformMessage::STORAGE_STATE, WebformMessage::STORAGE_CUSTOM])) {
|
||||
throw new \Exception('Undefined storage mechanism for Webform close message.');
|
||||
}
|
||||
WebformMessage::setClosed($storage, $id);
|
||||
|
@ -77,7 +76,7 @@ class WebformElementController extends ControllerBase {
|
|||
];
|
||||
|
||||
// Check minimum number of characters.
|
||||
if (Unicode::strlen($q) < (int) $element['#autocomplete_match']) {
|
||||
if (mb_strlen($q) < (int) $element['#autocomplete_match']) {
|
||||
return new JsonResponse([]);
|
||||
}
|
||||
|
||||
|
@ -195,7 +194,7 @@ class WebformElementController extends ControllerBase {
|
|||
* Match operator either CONTAINS or STARTS_WITH.
|
||||
*/
|
||||
protected function getMatchesFromOptionsRecursive($q, array $options, array &$matches, $operator = 'CONTAINS') {
|
||||
foreach ($options as $value => $label) {
|
||||
foreach ($options as $label) {
|
||||
if (is_array($label)) {
|
||||
$this->getMatchesFromOptionsRecursive($q, $label, $matches, $operator);
|
||||
continue;
|
||||
|
|
|
@ -6,8 +6,10 @@ use Drupal\Component\Render\FormattableMarkup;
|
|||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\Core\Render\RendererInterface;
|
||||
use Drupal\webform\Element\Webform as WebformElement;
|
||||
use Drupal\webform\WebformInterface;
|
||||
use Drupal\webform\WebformRequestInterface;
|
||||
use Drupal\webform\WebformSubmissionInterface;
|
||||
use Drupal\webform\WebformTokenManagerInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
@ -17,7 +19,7 @@ use Symfony\Component\HttpFoundation\Response;
|
|||
/**
|
||||
* Provides route responses for webform.
|
||||
*/
|
||||
class WebformController extends ControllerBase implements ContainerInjectionInterface {
|
||||
class WebformEntityController extends ControllerBase implements ContainerInjectionInterface {
|
||||
|
||||
/**
|
||||
* The renderer service.
|
||||
|
@ -34,21 +36,21 @@ class WebformController extends ControllerBase implements ContainerInjectionInte
|
|||
protected $requestHandler;
|
||||
|
||||
/**
|
||||
* The token manager.
|
||||
* The webform token manager.
|
||||
*
|
||||
* @var \Drupal\webform\WebformTranslationManagerInterface
|
||||
* @var \Drupal\webform\WebformTokenManagerInterface
|
||||
*/
|
||||
protected $tokenManager;
|
||||
|
||||
/**
|
||||
* Constructs a WebformController object.
|
||||
* Constructs a WebformEntityController object.
|
||||
*
|
||||
* @param \Drupal\Core\Render\RendererInterface $renderer
|
||||
* The renderer service.
|
||||
* @param \Drupal\webform\WebformRequestInterface $request_handler
|
||||
* The webform request handler.
|
||||
* @param \Drupal\webform\WebformTokenManagerInterface $token_manager
|
||||
* The token manager.
|
||||
* The webform token manager.
|
||||
*/
|
||||
public function __construct(RendererInterface $renderer, WebformRequestInterface $request_handler, WebformTokenManagerInterface $token_manager) {
|
||||
$this->renderer = $renderer;
|
||||
|
@ -121,11 +123,13 @@ class WebformController extends ControllerBase implements ContainerInjectionInte
|
|||
* The current request.
|
||||
* @param \Drupal\webform\WebformInterface|null $webform
|
||||
* A webform.
|
||||
* @param \Drupal\webform\WebformSubmissionInterface|null $webform_submission
|
||||
* A webform submission.
|
||||
*
|
||||
* @return array
|
||||
* A render array representing a webform confirmation page
|
||||
*/
|
||||
public function confirmation(Request $request, WebformInterface $webform = NULL) {
|
||||
public function confirmation(Request $request, WebformInterface $webform = NULL, WebformSubmissionInterface $webform_submission = NULL) {
|
||||
/** @var \Drupal\Core\Entity\EntityInterface $source_entity */
|
||||
if (!$webform) {
|
||||
list($webform, $source_entity) = $this->requestHandler->getWebformEntities();
|
||||
|
@ -134,9 +138,6 @@ class WebformController extends ControllerBase implements ContainerInjectionInte
|
|||
$source_entity = $this->requestHandler->getCurrentSourceEntity('webform');
|
||||
}
|
||||
|
||||
/** @var \Drupal\webform\WebformSubmissionInterface $webform_submission */
|
||||
$webform_submission = NULL;
|
||||
|
||||
if ($token = $request->get('token')) {
|
||||
/** @var \Drupal\webform\WebformSubmissionStorageInterface $webform_submission_storage */
|
||||
$webform_submission_storage = $this->entityTypeManager()->getStorage('webform_submission');
|
||||
|
@ -145,6 +146,11 @@ class WebformController extends ControllerBase implements ContainerInjectionInte
|
|||
}
|
||||
}
|
||||
|
||||
// Alter webform settings before setting the entity.
|
||||
if ($webform_submission) {
|
||||
$webform_submission->getWebform()->invokeHandlers('overrideSettings', $webform_submission);
|
||||
}
|
||||
|
||||
// Get title.
|
||||
$title = $webform->getSetting('confirmation_title') ?: (($source_entity) ? $source_entity->label() : $webform->label());
|
||||
|
||||
|
@ -159,7 +165,15 @@ class WebformController extends ControllerBase implements ContainerInjectionInte
|
|||
'#webform_submission' => $webform_submission,
|
||||
];
|
||||
|
||||
// Add entities cacheable dependency.
|
||||
$this->renderer->addCacheableDependency($build, $webform);
|
||||
if ($webform_submission) {
|
||||
$this->renderer->addCacheableDependency($build, $webform_submission);
|
||||
}
|
||||
if ($source_entity) {
|
||||
$this->renderer->addCacheableDependency($build, $source_entity);
|
||||
}
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
|
@ -170,19 +184,25 @@ class WebformController extends ControllerBase implements ContainerInjectionInte
|
|||
* The current request.
|
||||
* @param bool $templates
|
||||
* If TRUE, limit autocomplete matches to webform templates.
|
||||
* @param bool $archived
|
||||
* If TRUE, limit autocomplete matches to archived webforms and templates.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\JsonResponse
|
||||
* The JSON response.
|
||||
*/
|
||||
public function autocomplete(Request $request, $templates = FALSE) {
|
||||
public function autocomplete(Request $request, $templates = FALSE, $archived = FALSE) {
|
||||
$q = $request->query->get('q');
|
||||
|
||||
$webform_storage = $this->entityTypeManager()->getStorage('webform');
|
||||
|
||||
$query = $webform_storage->getQuery()
|
||||
->condition('title', $q, 'CONTAINS')
|
||||
->range(0, 10)
|
||||
->sort('title');
|
||||
// Query title and id.
|
||||
$or = $query->orConditionGroup()
|
||||
->condition('id', $q, 'CONTAINS')
|
||||
->condition('title', $q, 'CONTAINS');
|
||||
$query->condition($or);
|
||||
|
||||
// Limit query to templates.
|
||||
if ($templates) {
|
||||
|
@ -193,6 +213,9 @@ class WebformController extends ControllerBase implements ContainerInjectionInte
|
|||
$query->condition('template', FALSE);
|
||||
}
|
||||
|
||||
// Limit query to archived.
|
||||
$query->condition('archive', $archived);
|
||||
|
||||
$entity_ids = $query->execute();
|
||||
|
||||
if (empty($entity_ids)) {
|
||||
|
@ -211,6 +234,32 @@ class WebformController extends ControllerBase implements ContainerInjectionInte
|
|||
return new JsonResponse($matches);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a webform's access denied page.
|
||||
*
|
||||
* @param \Drupal\webform\WebformInterface $webform
|
||||
* The webform.
|
||||
*
|
||||
* @return array
|
||||
* A renderable array containing an access denied page.
|
||||
*/
|
||||
public function accessDenied(WebformInterface $webform) {
|
||||
return WebformElement::buildAccessDenied($webform);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a webform's access denied title.
|
||||
*
|
||||
* @param \Drupal\webform\WebformInterface $webform
|
||||
* The webform.
|
||||
*
|
||||
* @return string|\Drupal\Core\StringTranslation\TranslatableMarkup
|
||||
* The webform submissions's access denied title.
|
||||
*/
|
||||
public function accessDeniedTitle(WebformInterface $webform) {
|
||||
return $webform->getSetting('form_access_denied_title') ?: $this->t('Access denied');
|
||||
}
|
||||
|
||||
/**
|
||||
* Route title callback.
|
||||
*
|
||||
|
@ -228,7 +277,42 @@ class WebformController extends ControllerBase implements ContainerInjectionInte
|
|||
else {
|
||||
$source_entity = $this->requestHandler->getCurrentSourceEntity('webform');
|
||||
}
|
||||
return ($source_entity) ? $source_entity->label() : $webform->label();
|
||||
|
||||
// If source entity does not exist or does not have a label always use
|
||||
// the webform's label.
|
||||
if (!$source_entity || !method_exists($source_entity, 'label')) {
|
||||
return $webform->label();
|
||||
}
|
||||
|
||||
// Handler duplicate titles.
|
||||
if ($source_entity->label() === $webform->label()) {
|
||||
return $webform->label();
|
||||
}
|
||||
|
||||
// Get the webform's title.
|
||||
switch ($webform->getSetting('form_title')) {
|
||||
case WebformInterface::TITLE_SOURCE_ENTITY:
|
||||
return $source_entity->label();
|
||||
|
||||
case WebformInterface::TITLE_WEBFORM:
|
||||
return $webform->label();
|
||||
|
||||
case WebformInterface::TITLE_WEBFORM_SOURCE_ENTITY:
|
||||
$t_args = [
|
||||
'@source_entity' => $source_entity->label(),
|
||||
'@webform' => $webform->label(),
|
||||
];
|
||||
return $this->t('@webform: @source_entity', $t_args);
|
||||
|
||||
case WebformInterface::TITLE_SOURCE_ENTITY_WEBFORM:
|
||||
default:
|
||||
$t_args = [
|
||||
'@source_entity' => $source_entity->label(),
|
||||
'@webform' => $webform->label(),
|
||||
];
|
||||
return $this->t('@source_entity: @webform', $t_args);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Controller;
|
||||
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\webform\WebformHelpManagerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides route responses for webform help.
|
||||
*/
|
||||
class WebformHelpController extends ControllerBase implements ContainerInjectionInterface {
|
||||
|
||||
/**
|
||||
* The help manager.
|
||||
*
|
||||
* @var \Drupal\Component\Plugin\PluginManagerInterface
|
||||
*/
|
||||
protected $helpManager;
|
||||
|
||||
/**
|
||||
* Constructs a WebformHelpController object.
|
||||
*
|
||||
* @param \Drupal\webform\WebformHelpManagerInterface $help_manager
|
||||
* The help manager.
|
||||
*/
|
||||
public function __construct(WebformHelpManagerInterface $help_manager) {
|
||||
$this->helpManager = $help_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('webform.help_manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns dedicated help video page.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The current request.
|
||||
* @param string $id
|
||||
* The video id.
|
||||
*
|
||||
* @return array
|
||||
* A renderable array containing a help video player page.
|
||||
*/
|
||||
public function index(Request $request, $id) {
|
||||
$id = str_replace('-', '_', $id);
|
||||
$video = $this->helpManager->getVideo($id);
|
||||
if (!$video) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
$build = [];
|
||||
if (is_array($video['content'])) {
|
||||
$build['content'] = $video['content'];
|
||||
}
|
||||
else {
|
||||
$build['content'] = [
|
||||
'#markup' => $video['content'],
|
||||
];
|
||||
}
|
||||
if ($video['youtube_id']) {
|
||||
$build['video'] = [
|
||||
'#theme' => 'webform_help_video_youtube',
|
||||
'#youtube_id' => $video['youtube_id'],
|
||||
];
|
||||
}
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Route title callback.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The current request.
|
||||
* @param string $id
|
||||
* The id of the dedicated help section.
|
||||
*
|
||||
* @return string
|
||||
* The dedicated help section's title.
|
||||
*/
|
||||
public function title(Request $request, $id) {
|
||||
$id = str_replace('-', '_', $id);
|
||||
$video = $this->helpManager->getVideo($id);
|
||||
return (isset($video)) ? $video['title'] : $this->t('Watch video');
|
||||
}
|
||||
|
||||
}
|
|
@ -5,7 +5,6 @@ namespace Drupal\webform\Controller;
|
|||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\webform\Entity\WebformOptions;
|
||||
use Drupal\webform\WebformInterface;
|
||||
use Drupal\webform\WebformOptionsInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
|
@ -79,17 +78,4 @@ class WebformOptionsController extends ControllerBase {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Route title callback.
|
||||
*
|
||||
* @param \Drupal\webform\WebformOptionsInterface $webform_options
|
||||
* The webform options.
|
||||
*
|
||||
* @return string
|
||||
* The webform options label as a render array.
|
||||
*/
|
||||
public function title(WebformOptionsInterface $webform_options) {
|
||||
return $webform_options->label();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ use Drupal\Core\Controller\ControllerBase;
|
|||
use Drupal\Core\Render\ElementInfoManagerInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\webform\Utility\WebformReflectionHelper;
|
||||
use Drupal\webform\WebformElementManagerInterface;
|
||||
use Drupal\webform\Plugin\WebformElementManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
|
@ -34,7 +34,7 @@ class WebformPluginElementController extends ControllerBase implements Container
|
|||
/**
|
||||
* A webform element plugin manager.
|
||||
*
|
||||
* @var \Drupal\webform\WebformElementManagerInterface
|
||||
* @var \Drupal\webform\Plugin\WebformElementManagerInterface
|
||||
*/
|
||||
protected $elementManager;
|
||||
|
||||
|
@ -45,7 +45,7 @@ class WebformPluginElementController extends ControllerBase implements Container
|
|||
* The module handler.
|
||||
* @param \Drupal\Core\Render\ElementInfoManagerInterface $element_info
|
||||
* A element info plugin manager.
|
||||
* @param \Drupal\webform\WebformElementManagerInterface $element_manager
|
||||
* @param \Drupal\webform\Plugin\WebformElementManagerInterface $element_manager
|
||||
* A webform element plugin manager.
|
||||
*/
|
||||
public function __construct(ModuleHandlerInterface $module_handler, ElementInfoManagerInterface $element_info, WebformElementManagerInterface $element_manager) {
|
||||
|
@ -72,6 +72,8 @@ class WebformPluginElementController extends ControllerBase implements Container
|
|||
$webform_form_element_rows = [];
|
||||
$element_rows = [];
|
||||
|
||||
$excluded_elements = $this->config('webform.settings')->get('element.excluded_elements');
|
||||
|
||||
$default_properties = [
|
||||
// Element settings.
|
||||
'title',
|
||||
|
@ -119,13 +121,45 @@ class WebformPluginElementController extends ControllerBase implements Container
|
|||
foreach ($element_plugin_definitions as $element_plugin_id => $element_plugin_definition) {
|
||||
if ($this->elementManager->hasDefinition($element_plugin_id)) {
|
||||
|
||||
/** @var \Drupal\webform\WebformElementInterface $webform_element */
|
||||
/** @var \Drupal\webform\Plugin\WebformElementInterface $webform_element */
|
||||
$webform_element = $this->elementManager->createInstance($element_plugin_id);
|
||||
$webform_element_plugin_definition = $this->elementManager->getDefinition($element_plugin_id);
|
||||
$webform_element_info = $webform_element->getInfo();
|
||||
|
||||
// Title.
|
||||
if ($test_element_enabled) {
|
||||
$title = [
|
||||
'data' => [
|
||||
'#type' => 'link',
|
||||
'#title' => $element_plugin_id,
|
||||
'#url' => new Url('webform.reports_plugins.elements.test', ['type' => $element_plugin_id]),
|
||||
'#attributes' => ['class' => ['webform-form-filter-text-source']],
|
||||
],
|
||||
];
|
||||
}
|
||||
else {
|
||||
$title = new FormattableMarkup('<div class="webform-form-filter-text-source">@id</div>', ['@id' => $element_plugin_id]);
|
||||
}
|
||||
|
||||
// Description.
|
||||
$description = [
|
||||
'data' => [
|
||||
'title_description' => ['#markup' => new FormattableMarkup('<strong>@label</strong><br />@description', ['@label' => $webform_element->getPluginLabel(), '@description' => $webform_element->getPluginDescription()])],
|
||||
],
|
||||
];
|
||||
// Add deprecated warning.
|
||||
if (!empty($webform_element_plugin_definition['deprecated'])) {
|
||||
$description['data']['deprecated'] = [
|
||||
'#type' => 'webform_message',
|
||||
'#message_message' => $webform_element_plugin_definition['deprecated_message'],
|
||||
'#message_type' => 'warning',
|
||||
];
|
||||
}
|
||||
|
||||
// Parent classes.
|
||||
$parent_classes = WebformReflectionHelper::getParentClasses($webform_element, 'WebformElementBase');
|
||||
|
||||
// Formats.
|
||||
$default_format = $webform_element->getItemDefaultFormat();
|
||||
$format_names = array_keys($webform_element->getItemFormats());
|
||||
$formats = array_combine($format_names, $format_names);
|
||||
|
@ -133,22 +167,32 @@ class WebformPluginElementController extends ControllerBase implements Container
|
|||
$formats[$default_format] = '<b>' . $formats[$default_format] . '</b>';
|
||||
}
|
||||
|
||||
// Related types.
|
||||
$related_types = $webform_element->getRelatedTypes($element);
|
||||
|
||||
// Dependencies.
|
||||
$dependencies = $webform_element_plugin_definition['dependencies'];
|
||||
|
||||
// Webform element info.
|
||||
$webform_info_definitions = [
|
||||
'excluded' => isset($excluded_elements[$element_plugin_id]),
|
||||
'input' => $webform_element->isInput($element),
|
||||
'container' => $webform_element->isContainer($element),
|
||||
'root' => $webform_element->isRoot(),
|
||||
'hidden' => $webform_element->isHidden(),
|
||||
'composite' => $webform_element->isComposite(),
|
||||
'multiple' => $webform_element->supportsMultipleValues(),
|
||||
'multiline' => $webform_element->isMultiline($element),
|
||||
'default_key' => $webform_element_plugin_definition['default_key'],
|
||||
'states_wrapper' => $webform_element_plugin_definition['states_wrapper'],
|
||||
'deprecated' => $webform_element_plugin_definition['deprecated'],
|
||||
];
|
||||
$webform_info = [];
|
||||
foreach ($webform_info_definitions as $key => $value) {
|
||||
$webform_info[] = '<b>' . $key . '</b>: ' . ($value ? $this->t('Yes') : $this->t('No'));
|
||||
}
|
||||
|
||||
// Element info.
|
||||
$element_info_definitions = [
|
||||
'input' => (empty($webform_element_info['#input'])) ? $this->t('No') : $this->t('Yes'),
|
||||
'theme' => (isset($webform_element_info['#theme'])) ? $webform_element_info['#theme'] : 'N/A',
|
||||
|
@ -159,6 +203,7 @@ class WebformPluginElementController extends ControllerBase implements Container
|
|||
$element_info[] = '<b>' . $key . '</b>: ' . $value;
|
||||
}
|
||||
|
||||
// Properties.
|
||||
$properties = [];
|
||||
$element_default_properties = array_keys($webform_element->getDefaultProperties());
|
||||
foreach ($element_default_properties as $key => $value) {
|
||||
|
@ -172,14 +217,15 @@ class WebformPluginElementController extends ControllerBase implements Container
|
|||
}
|
||||
$properties += $element_default_properties;
|
||||
if (count($properties) >= 20) {
|
||||
$properties = array_slice($properties, 0, 20) + ['...' => '...'];
|
||||
$properties = array_slice($properties, 0, 20) + ['…' => '…'];
|
||||
}
|
||||
|
||||
// Operations.
|
||||
$operations = [];
|
||||
if ($test_element_enabled) {
|
||||
$operations['test'] = [
|
||||
'title' => $this->t('Test'),
|
||||
'url' => new Url('webform.element_plugins.test', ['type' => $element_plugin_id]),
|
||||
'url' => new Url('webform.reports_plugins.elements.test', ['type' => $element_plugin_id]),
|
||||
];
|
||||
}
|
||||
if ($api_url = $webform_element->getPluginApiUrl()) {
|
||||
|
@ -188,21 +234,33 @@ class WebformPluginElementController extends ControllerBase implements Container
|
|||
'url' => $api_url,
|
||||
];
|
||||
}
|
||||
|
||||
$webform_form_element_rows[$element_plugin_id] = [
|
||||
'data' => [
|
||||
new FormattableMarkup('<div class="webform-form-filter-text-source">@id</div>', ['@id' => $element_plugin_id]),
|
||||
new FormattableMarkup('<strong>@label</strong><br/>@description', ['@label' => $webform_element->getPluginLabel(), '@description' => $webform_element->getPluginDescription()]),
|
||||
['data' => ['#markup' => implode('<br/> → ', $parent_classes)], 'nowrap' => 'nowrap'],
|
||||
['data' => ['#markup' => implode('<br/>', $webform_info)], 'nowrap' => 'nowrap'],
|
||||
['data' => ['#markup' => implode('<br/>', $element_info)], 'nowrap' => 'nowrap'],
|
||||
['data' => ['#markup' => implode('<br/>', $properties)]],
|
||||
$formats ? ['data' => ['#markup' => '• ' . implode('<br/>• ', $formats)], 'nowrap' => 'nowrap'] : '',
|
||||
$related_types ? ['data' => ['#markup' => '• ' . implode('<br/>• ', $related_types)], 'nowrap' => 'nowrap'] : '<' . $this->t('none') . '>',
|
||||
$title,
|
||||
$description,
|
||||
['data' => ['#markup' => implode('<br /> → ', $parent_classes)], 'nowrap' => 'nowrap'],
|
||||
['data' => ['#markup' => implode('<br />', $webform_info)], 'nowrap' => 'nowrap'],
|
||||
['data' => ['#markup' => implode('<br />', $element_info)], 'nowrap' => 'nowrap'],
|
||||
['data' => ['#markup' => implode('<br />', $properties)]],
|
||||
$formats ? ['data' => ['#markup' => '• ' . implode('<br />• ', $formats)], 'nowrap' => 'nowrap'] : '',
|
||||
$related_types ? ['data' => ['#markup' => '• ' . implode('<br />• ', $related_types)], 'nowrap' => 'nowrap'] : '<' . $this->t('none') . '>',
|
||||
$dependencies ? ['data' => ['#markup' => '• ' . implode('<br />• ', $dependencies)], 'nowrap' => 'nowrap'] : '',
|
||||
$element_plugin_definition['provider'],
|
||||
$webform_element_plugin_definition['provider'],
|
||||
$operations ? ['data' => ['#type' => 'operations', '#links' => $operations]] : '',
|
||||
$operations ? [
|
||||
'data' => [
|
||||
'#type' => 'operations',
|
||||
'#links' => $operations,
|
||||
'#prefix' => '<div class="webform-dropbutton">',
|
||||
'#suffix' => '</div>',
|
||||
],
|
||||
] : '',
|
||||
],
|
||||
];
|
||||
if (isset($excluded_elements[$element_plugin_id])) {
|
||||
$webform_form_element_rows[$element_plugin_id]['class'] = ['color-warning'];
|
||||
}
|
||||
}
|
||||
else {
|
||||
$element_rows[$element_plugin_id] = [
|
||||
|
@ -222,12 +280,30 @@ class WebformPluginElementController extends ControllerBase implements Container
|
|||
'#placeholder' => $this->t('Filter by element name'),
|
||||
'#attributes' => [
|
||||
'class' => ['webform-form-filter-text'],
|
||||
'data-element' => '.webform-element-plugin',
|
||||
'data-element' => '.webform-element-plugin-table',
|
||||
'data-summary' => '.webform-element-plugin-summary',
|
||||
'data-item-single' => $this->t('element'),
|
||||
'data-item-plural' => $this->t('elements'),
|
||||
'title' => $this->t('Enter a part of the element type to filter by.'),
|
||||
'autofocus' => 'autofocus',
|
||||
],
|
||||
];
|
||||
|
||||
// Settings.
|
||||
$build['settings'] = [
|
||||
'#type' => 'link',
|
||||
'#title' => $this->t('Edit configuration'),
|
||||
'#url' => Url::fromRoute('webform.config.elements'),
|
||||
'#attributes' => ['class' => ['button', 'button--small'], 'style' => 'float: right'],
|
||||
];
|
||||
|
||||
// Display info.
|
||||
$build['info'] = [
|
||||
'#markup' => $this->t('@total elements', ['@total' => count($webform_form_element_rows)]),
|
||||
'#prefix' => '<p class="webform-element-plugin-summary">',
|
||||
'#suffix' => '</p>',
|
||||
];
|
||||
|
||||
ksort($webform_form_element_rows);
|
||||
$build['webform_elements'] = [
|
||||
'#type' => 'table',
|
||||
|
@ -240,13 +316,15 @@ class WebformPluginElementController extends ControllerBase implements Container
|
|||
$this->t('Properties'),
|
||||
$this->t('Formats'),
|
||||
$this->t('Related'),
|
||||
$this->t('Dependencies'),
|
||||
$this->t('Provided by'),
|
||||
$this->t('Integrated by'),
|
||||
$this->t('Operations'),
|
||||
],
|
||||
'#rows' => $webform_form_element_rows,
|
||||
'#sticky' => TRUE,
|
||||
'#attributes' => [
|
||||
'class' => ['webform-element-plugin'],
|
||||
'class' => ['webform-element-plugin-table'],
|
||||
],
|
||||
];
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace Drupal\webform\Controller;
|
|||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Component\Plugin\PluginManagerInterface;
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
|
@ -42,31 +43,60 @@ class WebformPluginExporterController extends ControllerBase implements Containe
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function index() {
|
||||
$excluded_exporters = $this->config('webform.settings')->get('export.excluded_exporters');
|
||||
|
||||
$definitions = $this->pluginManager->getDefinitions();
|
||||
$definitions = $this->pluginManager->getSortedDefinitions($definitions);
|
||||
|
||||
$rows = [];
|
||||
foreach ($definitions as $plugin_id => $definition) {
|
||||
$rows[$plugin_id] = [
|
||||
$plugin_id,
|
||||
$definition['label'],
|
||||
$definition['description'],
|
||||
$definition['provider'],
|
||||
'data' => [
|
||||
$plugin_id,
|
||||
$definition['label'],
|
||||
$definition['description'],
|
||||
(isset($excluded_exporters[$plugin_id])) ? $this->t('Yes') : $this->t('No'),
|
||||
$definition['provider'],
|
||||
],
|
||||
];
|
||||
if (isset($excluded_exporters[$plugin_id])) {
|
||||
$rows[$plugin_id]['class'] = ['color-warning'];
|
||||
}
|
||||
}
|
||||
|
||||
ksort($rows);
|
||||
return [
|
||||
|
||||
$build = [];
|
||||
|
||||
// Settings.
|
||||
$build['settings'] = [
|
||||
'#type' => 'link',
|
||||
'#title' => $this->t('Edit configuration'),
|
||||
'#url' => Url::fromRoute('webform.config.exporters'),
|
||||
'#attributes' => ['class' => ['button', 'button--small'], 'style' => 'float: right'],
|
||||
];
|
||||
|
||||
// Display info.
|
||||
$build['info'] = [
|
||||
'#markup' => $this->t('@total exporters', ['@total' => count($rows)]),
|
||||
'#prefix' => '<p>',
|
||||
'#suffix' => '</p>',
|
||||
];
|
||||
|
||||
// Exporters.
|
||||
$build['webform_exporters'] = [
|
||||
'#type' => 'table',
|
||||
'#header' => [
|
||||
$this->t('ID'),
|
||||
$this->t('Label'),
|
||||
$this->t('Description'),
|
||||
$this->t('Excluded'),
|
||||
$this->t('Provided by'),
|
||||
],
|
||||
'#rows' => $rows,
|
||||
'#sticky' => TRUE,
|
||||
];
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use Drupal\Core\Controller\ControllerBase;
|
|||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\webform\Utility\WebformDialogHelper;
|
||||
use Drupal\webform\WebformHandlerInterface;
|
||||
use Drupal\webform\Plugin\WebformHandlerInterface;
|
||||
use Drupal\webform\WebformInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
@ -25,7 +25,7 @@ class WebformPluginHandlerController extends ControllerBase implements Container
|
|||
protected $pluginManager;
|
||||
|
||||
/**
|
||||
* Constructs a WebformPluginHanderController object.
|
||||
* Constructs a WebformPluginHandlerController object.
|
||||
*
|
||||
* @param \Drupal\Component\Plugin\PluginManagerInterface $plugin_manager
|
||||
* A webform handler plugin manager.
|
||||
|
@ -47,37 +47,70 @@ class WebformPluginHandlerController extends ControllerBase implements Container
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function index() {
|
||||
$excluded_handlers = $this->config('webform.settings')->get('handler.excluded_handlers');
|
||||
|
||||
$definitions = $this->pluginManager->getDefinitions();
|
||||
$definitions = $this->pluginManager->getSortedDefinitions($definitions);
|
||||
|
||||
$rows = [];
|
||||
foreach ($definitions as $plugin_id => $definition) {
|
||||
$rows[$plugin_id] = [
|
||||
$plugin_id,
|
||||
$definition['label'],
|
||||
$definition['description'],
|
||||
$definition['category'],
|
||||
($definition['cardinality'] == -1) ? $this->t('Unlimited') : $definition['cardinality'],
|
||||
$definition['results'] ? $this->t('Processed') : $this->t('Ignored'),
|
||||
$definition['provider'],
|
||||
'data' => [
|
||||
$plugin_id,
|
||||
$definition['label'],
|
||||
$definition['description'],
|
||||
$definition['category'],
|
||||
(isset($excluded_handlers[$plugin_id])) ? $this->t('Yes') : $this->t('No'),
|
||||
($definition['cardinality'] == -1) ? $this->t('Unlimited') : $definition['cardinality'],
|
||||
$definition['conditions'] ? $this->t('Yes') : $this->t('No'),
|
||||
$definition['submission'] ? $this->t('Required') : $this->t('Optional'),
|
||||
$definition['results'] ? $this->t('Processed') : $this->t('Ignored'),
|
||||
$definition['provider'],
|
||||
],
|
||||
];
|
||||
if (isset($excluded_handlers[$plugin_id])) {
|
||||
$rows[$plugin_id]['class'] = ['color-warning'];
|
||||
}
|
||||
}
|
||||
|
||||
ksort($rows);
|
||||
return [
|
||||
|
||||
$build = [];
|
||||
|
||||
// Settings.
|
||||
$build['settings'] = [
|
||||
'#type' => 'link',
|
||||
'#title' => $this->t('Edit configuration'),
|
||||
'#url' => Url::fromRoute('webform.config.handlers'),
|
||||
'#attributes' => ['class' => ['button', 'button--small'], 'style' => 'float: right'],
|
||||
];
|
||||
|
||||
// Display info.
|
||||
$build['info'] = [
|
||||
'#markup' => $this->t('@total handlers', ['@total' => count($rows)]),
|
||||
'#prefix' => '<p>',
|
||||
'#suffix' => '</p>',
|
||||
];
|
||||
|
||||
// Handlers.
|
||||
$build['webform_handlers'] = [
|
||||
'#type' => 'table',
|
||||
'#header' => [
|
||||
$this->t('ID'),
|
||||
$this->t('Label'),
|
||||
$this->t('Description'),
|
||||
$this->t('Category'),
|
||||
$this->t('Excluded'),
|
||||
$this->t('Cardinality'),
|
||||
$this->t('Conditional'),
|
||||
$this->t('Database'),
|
||||
$this->t('Results'),
|
||||
$this->t('Provided by'),
|
||||
],
|
||||
'#rows' => $rows,
|
||||
'#sticky' => TRUE,
|
||||
];
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -101,6 +134,7 @@ class WebformPluginHandlerController extends ControllerBase implements Container
|
|||
|
||||
$definitions = $this->pluginManager->getDefinitions();
|
||||
$definitions = $this->pluginManager->getSortedDefinitions($definitions);
|
||||
$definitions = $this->pluginManager->removeExcludeDefinitions($definitions);
|
||||
|
||||
$rows = [];
|
||||
foreach ($definitions as $plugin_id => $definition) {
|
||||
|
@ -111,35 +145,66 @@ class WebformPluginHandlerController extends ControllerBase implements Container
|
|||
|
||||
// Check cardinality.
|
||||
$cardinality = $definition['cardinality'];
|
||||
$is_cardinality_unlimited = ($cardinality == WebformHandlerInterface::CARDINALITY_UNLIMITED);
|
||||
$is_cardinality_unlimited = ($cardinality === WebformHandlerInterface::CARDINALITY_UNLIMITED);
|
||||
$is_cardinality_reached = ($webform->getHandlers($plugin_id)->count() >= $cardinality);
|
||||
if (!$is_cardinality_unlimited && $is_cardinality_reached) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$is_submission_required = ($definition['submission'] === WebformHandlerInterface::SUBMISSION_REQUIRED);
|
||||
$is_results_disabled = $webform->getSetting('results_disabled');
|
||||
|
||||
$row = [];
|
||||
$row['title']['data'] = [
|
||||
'#type' => 'inline_template',
|
||||
'#template' => '<div class="webform-form-filter-text-source">{{ label }}</div>',
|
||||
'#context' => [
|
||||
'label' => $definition['label'],
|
||||
],
|
||||
];
|
||||
|
||||
if ($is_submission_required && $is_results_disabled) {
|
||||
$row['title']['data'] = [
|
||||
'#markup' => $definition['label'],
|
||||
'#prefix' => '<div class="webform-form-filter-text-source">',
|
||||
'#suffix' => '</div>',
|
||||
];
|
||||
}
|
||||
else {
|
||||
$row['title']['data'] = [
|
||||
'#type' => 'link',
|
||||
'#title' => $definition['label'],
|
||||
'#url' => Url::fromRoute('entity.webform.handler.add_form', ['webform' => $webform->id(), 'webform_handler' => $plugin_id]),
|
||||
'#attributes' => WebformDialogHelper::getOffCanvasDialogAttributes(),
|
||||
'#prefix' => '<div class="webform-form-filter-text-source">',
|
||||
'#suffix' => '</div>',
|
||||
];
|
||||
}
|
||||
|
||||
$row['description'] = [
|
||||
'data' => [
|
||||
'#markup' => $definition['description'],
|
||||
],
|
||||
];
|
||||
|
||||
$row['category'] = $definition['category'];
|
||||
$links['add'] = [
|
||||
'title' => $this->t('Add handler'),
|
||||
'url' => Url::fromRoute('entity.webform.handler.add_form', ['webform' => $webform->id(), 'webform_handler' => $plugin_id]),
|
||||
'attributes' => WebformDialogHelper::getModalDialogAttributes(800),
|
||||
];
|
||||
$row['operations']['data'] = [
|
||||
'#type' => 'operations',
|
||||
'#links' => $links,
|
||||
];
|
||||
|
||||
// Check submission required.
|
||||
if ($is_submission_required && $is_results_disabled) {
|
||||
$row['operations']['data'] = [
|
||||
'#type' => 'html_tag',
|
||||
'#tag' => 'span',
|
||||
'#value' => $this->t('Requires saving of submissions.'),
|
||||
'#attributes' => ['class' => ['color-warning']],
|
||||
];
|
||||
}
|
||||
else {
|
||||
$links['add'] = [
|
||||
'title' => $this->t('Add handler'),
|
||||
'url' => Url::fromRoute('entity.webform.handler.add_form', ['webform' => $webform->id(), 'webform_handler' => $plugin_id]),
|
||||
'attributes' => WebformDialogHelper::getOffCanvasDialogAttributes(),
|
||||
];
|
||||
$row['operations']['data'] = [
|
||||
'#type' => 'operations',
|
||||
'#links' => $links,
|
||||
'#prefix' => '<div class="webform-dropbutton">',
|
||||
'#suffix' => '</div>',
|
||||
];
|
||||
}
|
||||
|
||||
$rows[] = $row;
|
||||
}
|
||||
|
||||
|
@ -154,6 +219,8 @@ class WebformPluginHandlerController extends ControllerBase implements Container
|
|||
'#attributes' => [
|
||||
'class' => ['webform-form-filter-text'],
|
||||
'data-element' => '.webform-handler-add-table',
|
||||
'data-item-single' => $this->t('handler'),
|
||||
'data-item-plural' => $this->t('handlers'),
|
||||
'title' => $this->t('Enter a part of the handler name to filter by.'),
|
||||
'autofocus' => 'autofocus',
|
||||
],
|
||||
|
@ -163,6 +230,7 @@ class WebformPluginHandlerController extends ControllerBase implements Container
|
|||
'#type' => 'table',
|
||||
'#header' => $headers,
|
||||
'#rows' => $rows,
|
||||
'#sticky' => TRUE,
|
||||
'#empty' => $this->t('No handler available.'),
|
||||
'#attributes' => [
|
||||
'class' => ['webform-handler-add-table'],
|
||||
|
|
|
@ -12,10 +12,10 @@ use Drupal\webform\WebformInterface;
|
|||
use Drupal\webform\WebformRequestInterface;
|
||||
use Drupal\webform\WebformSubmissionExporterInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Controller routines for webform submission export.
|
||||
|
@ -95,7 +95,7 @@ class WebformResultsExportController extends ControllerBase implements Container
|
|||
$route_name = $this->requestHandler->getRouteName($webform, $source_entity, 'webform.results_export_file');
|
||||
$route_parameters = $this->requestHandler->getRouteParameters($webform, $source_entity) + ['filename' => $query['filename']];
|
||||
$file_url = Url::fromRoute($route_name, $route_parameters, ['absolute' => TRUE])->toString();
|
||||
drupal_set_message($this->t('Export creation complete. Your download should begin now. If it does not start, <a href=":href">download the file here</a>. This file may only be downloaded once.', [':href' => $file_url]));
|
||||
$this->messenger()->addStatus($this->t('Export creation complete. Your download should begin now. If it does not start, <a href=":href">download the file here</a>. This file may only be downloaded once.', [':href' => $file_url]));
|
||||
$build['#attached']['html_head'][] = [
|
||||
[
|
||||
'#tag' => 'meta',
|
||||
|
@ -110,19 +110,21 @@ class WebformResultsExportController extends ControllerBase implements Container
|
|||
|
||||
return $build;
|
||||
}
|
||||
elseif ($query && empty($query['ajax_form'])) {
|
||||
if (!empty($query['excluded_columns']) && is_string($query['excluded_columns'])) {
|
||||
$excluded_columns = explode(',', $query['excluded_columns']);
|
||||
$query['excluded_columns'] = array_combine($excluded_columns, $excluded_columns);
|
||||
elseif ($query && empty($query['ajax_form']) && isset($query['download'])) {
|
||||
$default_options = $this->submissionExporter->getDefaultExportOptions();
|
||||
foreach ($query as $key => $value) {
|
||||
if (isset($default_options[$key]) && is_array($default_options[$key]) && is_string($value)) {
|
||||
$query[$key] = explode(',', $value);
|
||||
}
|
||||
}
|
||||
|
||||
$export_options = $query + $this->submissionExporter->getDefaultExportOptions();
|
||||
if (!empty($query['excluded_columns'])) {
|
||||
$query['excluded_columns'] = array_combine($query['excluded_columns'], $query['excluded_columns']);
|
||||
}
|
||||
$export_options = $query + $default_options;
|
||||
$this->submissionExporter->setExporter($export_options);
|
||||
if ($this->submissionExporter->isBatch()) {
|
||||
self::batchSet($webform, $source_entity, $export_options);
|
||||
$route_name = $this->requestHandler->getRouteName($webform, $source_entity, 'webform.results_export');
|
||||
$route_parameters = $this->requestHandler->getRouteParameters($webform, $source_entity);
|
||||
return batch_process(Url::fromRoute($route_name, $route_parameters));
|
||||
static::batchSet($webform, $source_entity, $export_options);
|
||||
return batch_process($this->requestHandler->getUrl($webform, $source_entity, 'webform.results_export'));
|
||||
}
|
||||
else {
|
||||
$this->submissionExporter->generate();
|
||||
|
@ -154,10 +156,8 @@ class WebformResultsExportController extends ControllerBase implements Container
|
|||
|
||||
$file_path = $this->submissionExporter->getFileTempDirectory() . '/' . $filename;
|
||||
if (!file_exists($file_path)) {
|
||||
$route_name = $this->requestHandler->getRouteName($webform, $source_entity, 'webform.results_export');
|
||||
$route_parameters = $this->requestHandler->getRouteParameters($webform, $source_entity);
|
||||
$t_args = [
|
||||
':href' => Url::fromRoute($route_name, $route_parameters)->toString(),
|
||||
':href' => $this->requestHandler->getUrl($webform, $source_entity, 'webform.results_export')->toString(),
|
||||
];
|
||||
$build = [
|
||||
'#markup' => $this->t('No export file ready for download. The file may have already been downloaded by your browser. Visit the <a href=":href">download export webform</a> to create a new export.', $t_args),
|
||||
|
@ -181,30 +181,9 @@ class WebformResultsExportController extends ControllerBase implements Container
|
|||
* A response object containing the CSV file.
|
||||
*/
|
||||
public function downloadFile($file_path, $download = TRUE) {
|
||||
// Return the export file.
|
||||
$contents = file_get_contents($file_path);
|
||||
unlink($file_path);
|
||||
|
||||
$content_type = $this->mimeTypeGuesser->guess($file_path);
|
||||
|
||||
if ($download) {
|
||||
$headers = [
|
||||
'Content-Length' => strlen($contents),
|
||||
'Content-Type' => $content_type,
|
||||
'Content-Disposition' => 'attachment; filename="' . basename($file_path) . '"',
|
||||
];
|
||||
}
|
||||
else {
|
||||
if ($content_type != 'text/html') {
|
||||
$content_type = 'text/plain';
|
||||
}
|
||||
$headers = [
|
||||
'Content-Length' => strlen($contents),
|
||||
'Content-Type' => $content_type . '; charset=utf-8',
|
||||
];
|
||||
}
|
||||
|
||||
return new Response($contents, 200, $headers);
|
||||
$response = new BinaryFileResponse($file_path, 200, [], FALSE, $download ? 'attachment' : 'inline');
|
||||
$response->deleteFileAfterSend(TRUE);
|
||||
return $response;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
@ -276,7 +255,7 @@ class WebformResultsExportController extends ControllerBase implements Container
|
|||
|
||||
if (empty($context['sandbox'])) {
|
||||
$context['sandbox']['progress'] = 0;
|
||||
$context['sandbox']['current_sid'] = 0;
|
||||
$context['sandbox']['offset'] = 0;
|
||||
$context['sandbox']['max'] = $submission_exporter->getQuery()->count()->execute();
|
||||
// Store entity ids and not the actual webform or source entity in the
|
||||
// $context to prevent "The container was serialized" errors.
|
||||
|
@ -290,17 +269,16 @@ class WebformResultsExportController extends ControllerBase implements Container
|
|||
|
||||
// Write CSV records.
|
||||
$query = $submission_exporter->getQuery();
|
||||
$query->condition('sid', $context['sandbox']['current_sid'], '>');
|
||||
$query->range(0, $submission_exporter->getBatchLimit());
|
||||
$query->range($context['sandbox']['offset'], $submission_exporter->getBatchLimit());
|
||||
$entity_ids = $query->execute();
|
||||
$webform_submissions = WebformSubmission::loadMultiple($entity_ids);
|
||||
$submission_exporter->writeRecords($webform_submissions);
|
||||
|
||||
// Track progress.
|
||||
$context['sandbox']['progress'] += count($webform_submissions);
|
||||
$context['sandbox']['current_sid'] = ($webform_submissions) ? end($webform_submissions)->id() : 0;
|
||||
$context['sandbox']['offset'] += $submission_exporter->getBatchLimit();
|
||||
|
||||
$context['message'] = t('Exported @count of @total submissions...', ['@count' => $context['sandbox']['progress'], '@total' => $context['sandbox']['max']]);
|
||||
$context['message'] = t('Exported @count of @total submissions…', ['@count' => $context['sandbox']['progress'], '@total' => $context['sandbox']['max']]);
|
||||
|
||||
// Track finished.
|
||||
if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
|
||||
|
@ -344,7 +322,7 @@ class WebformResultsExportController extends ControllerBase implements Container
|
|||
@unlink($file_path);
|
||||
$archive_path = $submission_exporter->getArchiveFilePath();
|
||||
@unlink($archive_path);
|
||||
drupal_set_message(t('Finished with an error.'));
|
||||
\Drupal::messenger()->addStatus(t('Finished with an error.'));
|
||||
}
|
||||
else {
|
||||
$submission_exporter->writeFooter();
|
||||
|
@ -358,9 +336,7 @@ class WebformResultsExportController extends ControllerBase implements Container
|
|||
|
||||
/** @var \Drupal\webform\WebformRequestInterface $request_handler */
|
||||
$request_handler = \Drupal::service('webform.request');
|
||||
$route_name = $request_handler->getRouteName($webform, $source_entity, 'webform.results_export');
|
||||
$route_parameters = $request_handler->getRouteParameters($webform, $source_entity);
|
||||
$redirect_url = Url::fromRoute($route_name, $route_parameters, ['query' => ['filename' => $filename], 'absolute' => TRUE]);
|
||||
$redirect_url = $request_handler->getUrl($webform, $source_entity, 'webform.results_export', ['query' => ['filename' => $filename], 'absolute' => TRUE]);
|
||||
return new RedirectResponse($redirect_url->toString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,15 +6,26 @@ use Drupal\Component\Render\FormattableMarkup;
|
|||
use Drupal\Core\Ajax\AjaxResponse;
|
||||
use Drupal\Core\Ajax\HtmlCommand;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\Core\Render\RendererInterface;
|
||||
use Drupal\webform\Ajax\WebformAnnounceCommand;
|
||||
use Drupal\webform\Element\WebformHtmlEditor;
|
||||
use Drupal\webform\WebformInterface;
|
||||
use Drupal\webform\WebformSubmissionInterface;
|
||||
use Drupal\webform\WebformRequestInterface;
|
||||
use Drupal\webform\WebformTokenManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides route responses for webform submissions.
|
||||
*/
|
||||
class WebformSubmissionController extends ControllerBase implements ContainerInjectionInterface {
|
||||
class WebformSubmissionController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* The renderer service.
|
||||
*
|
||||
* @var \Drupal\Core\Render\RendererInterface
|
||||
*/
|
||||
protected $renderer;
|
||||
|
||||
/**
|
||||
* Webform request handler.
|
||||
|
@ -23,14 +34,27 @@ class WebformSubmissionController extends ControllerBase implements ContainerInj
|
|||
*/
|
||||
protected $requestHandler;
|
||||
|
||||
/**
|
||||
* The webform token manager.
|
||||
*
|
||||
* @var \Drupal\webform\WebformTokenManagerInterface
|
||||
*/
|
||||
protected $tokenManager;
|
||||
|
||||
/**
|
||||
* Constructs a WebformSubmissionController object.
|
||||
*
|
||||
* @param \Drupal\Core\Render\RendererInterface $renderer
|
||||
* The renderer service.
|
||||
* @param \Drupal\webform\WebformRequestInterface $request_handler
|
||||
* The webform request handler.
|
||||
* @param \Drupal\webform\WebformTokenManagerInterface $token_manager
|
||||
* The webform token manager.
|
||||
*/
|
||||
public function __construct(WebformRequestInterface $request_handler) {
|
||||
public function __construct(RendererInterface $renderer, WebformRequestInterface $request_handler, WebformTokenManagerInterface $token_manager) {
|
||||
$this->renderer = $renderer;
|
||||
$this->requestHandler = $request_handler;
|
||||
$this->tokenManager = $token_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -38,63 +62,12 @@ class WebformSubmissionController extends ControllerBase implements ContainerInj
|
|||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('webform.request')
|
||||
$container->get('renderer'),
|
||||
$container->get('webform.request'),
|
||||
$container->get('webform.token_manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a webform submission in a specified format type.
|
||||
*
|
||||
* @param \Drupal\webform\WebformSubmissionInterface $webform_submission
|
||||
* A webform submission.
|
||||
* @param string $type
|
||||
* The format type.
|
||||
*
|
||||
* @return array
|
||||
* A render array representing a webform submission in a specified format
|
||||
* type.
|
||||
*/
|
||||
public function index(WebformSubmissionInterface $webform_submission, $type) {
|
||||
if ($type == 'default') {
|
||||
$type = 'html';
|
||||
}
|
||||
|
||||
$build = [];
|
||||
$source_entity = $this->requestHandler->getCurrentSourceEntity('webform_submission');
|
||||
// Navigation.
|
||||
$build['navigation'] = [
|
||||
'#theme' => 'webform_submission_navigation',
|
||||
'#webform_submission' => $webform_submission,
|
||||
];
|
||||
|
||||
// Information.
|
||||
$build['information'] = [
|
||||
'#theme' => 'webform_submission_information',
|
||||
'#webform_submission' => $webform_submission,
|
||||
'#source_entity' => $source_entity,
|
||||
];
|
||||
|
||||
// Submission.
|
||||
$build['submission'] = [
|
||||
'#theme' => 'webform_submission_' . $type,
|
||||
'#webform_submission' => $webform_submission,
|
||||
'#source_entity' => $source_entity,
|
||||
];
|
||||
|
||||
// Wrap plain text and YAML in CodeMirror view widget.
|
||||
if (in_array($type, ['text', 'yaml'])) {
|
||||
$build['submission'] = [
|
||||
'#theme' => 'webform_codemirror',
|
||||
'#code' => $build['submission'],
|
||||
'#type' => $type,
|
||||
];
|
||||
}
|
||||
|
||||
$build['#attached']['library'][] = 'webform/webform.admin';
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle webform submission sticky.
|
||||
*
|
||||
|
@ -102,43 +75,139 @@ class WebformSubmissionController extends ControllerBase implements ContainerInj
|
|||
* A webform submission.
|
||||
*
|
||||
* @return \Drupal\Core\Ajax\AjaxResponse
|
||||
* An AJAX response that toggle the sticky icon.
|
||||
* An Ajax response that toggle the sticky icon.
|
||||
*/
|
||||
public function sticky(WebformSubmissionInterface $webform_submission) {
|
||||
// Toggle sticky.
|
||||
$webform_submission->setSticky(!$webform_submission->isSticky())->save();
|
||||
|
||||
// Get state.
|
||||
$state = $webform_submission->isSticky() ? 'on' : 'off';
|
||||
// Get selector.
|
||||
$selector = '#webform-submission-' . $webform_submission->id() . '-sticky';
|
||||
|
||||
$response = new AjaxResponse();
|
||||
$response->addCommand(new HtmlCommand(
|
||||
'#webform-submission-' . $webform_submission->id() . '-sticky',
|
||||
new FormattableMarkup('<span class="webform-icon webform-icon-sticky webform-icon-sticky--@state"></span>', ['@state' => $state])
|
||||
));
|
||||
|
||||
// Update sticky.
|
||||
$response->addCommand(new HtmlCommand($selector, static::buildSticky($webform_submission)));
|
||||
|
||||
// Announce sticky status.
|
||||
$t_args = ['@label' => $webform_submission->label()];
|
||||
$text = $webform_submission->isSticky() ? $this->t('@label flagged/starred.', $t_args) : $this->t('@label unflagged/unstarred.', $t_args);
|
||||
$response->addCommand(new WebformAnnounceCommand($text));
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Route title callback.
|
||||
* Toggle webform submission locked.
|
||||
*
|
||||
* @param \Drupal\webform\WebformSubmissionInterface $webform_submission
|
||||
* The webform submission.
|
||||
* @param bool $duplicate
|
||||
* Flag indicating if submission is being duplicated.
|
||||
* 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 selector.
|
||||
$selector = '#webform-submission-' . $webform_submission->id() . '-locked';
|
||||
|
||||
$response = new AjaxResponse();
|
||||
|
||||
// Update lock.
|
||||
$response->addCommand(new HtmlCommand($selector, static::buildLocked($webform_submission)));
|
||||
|
||||
// Announce lock status.
|
||||
$t_args = ['@label' => $webform_submission->label()];
|
||||
$text = $webform_submission->isLocked() ? $this->t('@label locked.', $t_args) : $this->t('@label unlocked.', $t_args);
|
||||
$response->addCommand(new WebformAnnounceCommand($text));
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build sticky icon.
|
||||
*
|
||||
* @param \Drupal\webform\WebformSubmissionInterface $webform_submission
|
||||
* A webform submission.
|
||||
*
|
||||
* @return \Drupal\Component\Render\FormattableMarkup
|
||||
* Sticky icon.
|
||||
*/
|
||||
public static function buildSticky(WebformSubmissionInterface $webform_submission) {
|
||||
$t_args = ['@label' => $webform_submission->label()];
|
||||
$args = [
|
||||
'@state' => $webform_submission->isSticky() ? 'on' : 'off',
|
||||
'@label' => $webform_submission->isSticky() ? t('Unstar/Unflag @label', $t_args) : t('Star/flag @label', $t_args),
|
||||
];
|
||||
return new FormattableMarkup('<span class="webform-icon webform-icon-sticky webform-icon-sticky--@state"></span><span class="visually-hidden">@label</span>', $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build locked icon.
|
||||
*
|
||||
* @param \Drupal\webform\WebformSubmissionInterface $webform_submission
|
||||
* A webform submission.
|
||||
*
|
||||
* @return \Drupal\Component\Render\FormattableMarkup
|
||||
* Locked icon.
|
||||
*/
|
||||
public static function buildLocked(WebformSubmissionInterface $webform_submission) {
|
||||
$t_args = ['@label' => $webform_submission->label()];
|
||||
$args = [
|
||||
'@state' => $webform_submission->isLocked() ? 'on' : 'off',
|
||||
'@label' => $webform_submission->isLocked() ? t('Unlock @label', $t_args) : t('Lock @label', $t_args),
|
||||
];
|
||||
return new FormattableMarkup('<span class="webform-icon webform-icon-lock webform-icon-locked--@state"></span><span class="visually-hidden">@label</span>', $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a webform submissions's access denied page.
|
||||
*
|
||||
* @param \Drupal\webform\WebformInterface $webform
|
||||
* The webform.
|
||||
* @param \Drupal\webform\WebformSubmissionInterface $webform_submission
|
||||
* A webform submission.
|
||||
*
|
||||
* @return array
|
||||
* The webform submission as a render array.
|
||||
* A renderable array containing an access denied page.
|
||||
*/
|
||||
public function title(WebformSubmissionInterface $webform_submission, $duplicate = FALSE) {
|
||||
$source_entity = $this->requestHandler->getCurrentSourceEntity('webform_submission');
|
||||
$t_args = [
|
||||
'@form' => ($source_entity) ? $source_entity->label() : $webform_submission->getWebform()->label(),
|
||||
'@id' => $webform_submission->serial(),
|
||||
public function accessDenied(WebformInterface $webform, WebformSubmissionInterface $webform_submission) {
|
||||
// Message.
|
||||
$config = $this->config('webform.settings');
|
||||
$message = $webform->getSetting('submission_access_denied_message')
|
||||
?: $config->get('settings.default_submission_access_denied_message');
|
||||
$message = $this->tokenManager->replace($message, $webform_submission);
|
||||
|
||||
// Attributes.
|
||||
$attributes = $webform->getSetting('submission_access_denied_attributes');
|
||||
$attributes['class'][] = 'webform-submission-access-denied';
|
||||
|
||||
// Build message.
|
||||
$build = [
|
||||
'#type' => 'container',
|
||||
'#attributes' => $attributes,
|
||||
'message' => WebformHtmlEditor::checkMarkup($message),
|
||||
];
|
||||
|
||||
$title = $this->t('@form: Submission #@id', $t_args);
|
||||
return ($duplicate) ? $this->t('Duplicate @title', ['@title' => $title]) : $title;
|
||||
// Add config and webform to cache contexts.
|
||||
$this->renderer->addCacheableDependency($build, $config);
|
||||
$this->renderer->addCacheableDependency($build, $webform);
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a webform 's access denied title.
|
||||
*
|
||||
* @param \Drupal\webform\WebformInterface $webform
|
||||
* The webform.
|
||||
*
|
||||
* @return string|\Drupal\Core\StringTranslation\TranslatableMarkup
|
||||
* The webform's access denied title.
|
||||
*/
|
||||
public function accessDeniedTitle(WebformInterface $webform) {
|
||||
return $webform->getSetting('submission_access_denied_title') ?: $this->t('Access denied');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,236 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Controller;
|
||||
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Datetime\DateFormatterInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\webform\WebformInterface;
|
||||
use Drupal\webform\WebformRequestInterface;
|
||||
use Drupal\webform\WebformSubmissionInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Returns responses for webform submission log routes.
|
||||
*
|
||||
* Copied from: \Drupal\dblog\Controller\DbLogController.
|
||||
*/
|
||||
class WebformSubmissionLogController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* The database service.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* The date formatter service.
|
||||
*
|
||||
* @var \Drupal\Core\Datetime\DateFormatterInterface
|
||||
*/
|
||||
protected $dateFormatter;
|
||||
|
||||
/**
|
||||
* The user storage.
|
||||
*
|
||||
* @var \Drupal\user\UserStorageInterface
|
||||
*/
|
||||
protected $userStorage;
|
||||
|
||||
/**
|
||||
* The webform storage.
|
||||
*
|
||||
* @var \Drupal\webform\WebformStorageInterface
|
||||
*/
|
||||
protected $webformStorage;
|
||||
|
||||
/**
|
||||
* The webform submission storage.
|
||||
*
|
||||
* @var \Drupal\webform\WebformSubmissionStorageInterface
|
||||
*/
|
||||
protected $webformSubmissionStorage;
|
||||
|
||||
/**
|
||||
* Webform request handler.
|
||||
*
|
||||
* @var \Drupal\webform\WebformRequestInterface
|
||||
*/
|
||||
protected $requestHandler;
|
||||
|
||||
/**
|
||||
* Constructs a WebformSubmissionLogController object.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Connection $database
|
||||
* A database connection.
|
||||
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
|
||||
* The date formatter service.
|
||||
* @param \Drupal\webform\WebformRequestInterface $request_handler
|
||||
* The webform request handler.
|
||||
*/
|
||||
public function __construct(Connection $database, DateFormatterInterface $date_formatter, WebformRequestInterface $request_handler) {
|
||||
$this->database = $database;
|
||||
$this->dateFormatter = $date_formatter;
|
||||
$this->webformStorage = $this->entityTypeManager()->getStorage('webform');
|
||||
$this->webformSubmissionStorage = $this->entityTypeManager()->getStorage('webform_submission');
|
||||
$this->userStorage = $this->entityTypeManager()->getStorage('user');
|
||||
$this->requestHandler = $request_handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('database'),
|
||||
$container->get('date.formatter'),
|
||||
$container->get('webform.request')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a listing of webform submission log messages.
|
||||
*
|
||||
* @param \Drupal\webform\WebformInterface|null $webform
|
||||
* A webform.
|
||||
* @param \Drupal\webform\WebformSubmissionInterface|null $webform_submission
|
||||
* A webform submission.
|
||||
* @param \Drupal\Core\Entity\EntityInterface|null $source_entity
|
||||
* A source entity.
|
||||
*
|
||||
* @return array
|
||||
* A render array as expected by drupal_render().
|
||||
*/
|
||||
public function overview(WebformInterface $webform = NULL, WebformSubmissionInterface $webform_submission = NULL, EntityInterface $source_entity = NULL) {
|
||||
if (empty($webform) && !empty($webform_submission)) {
|
||||
$webform = $webform_submission->getWebform();
|
||||
}
|
||||
if (empty($source_entity) && !empty($webform_submission)) {
|
||||
$source_entity = $webform_submission->getSourceEntity();
|
||||
}
|
||||
|
||||
// Header.
|
||||
$header = [];
|
||||
$header['lid'] = ['data' => $this->t('#'), 'field' => 'l.lid', 'sort' => 'desc'];
|
||||
if (empty($webform)) {
|
||||
$header['webform_id'] = ['data' => $this->t('Webform'), 'field' => 'l.webform_id', 'class' => [RESPONSIVE_PRIORITY_MEDIUM]];
|
||||
}
|
||||
if (empty($source_entity) && empty($webform_submission)) {
|
||||
$header['entity'] = ['data' => $this->t('Submitted to'), 'class' => [RESPONSIVE_PRIORITY_LOW]];
|
||||
}
|
||||
if (empty($webform_submission)) {
|
||||
$header['sid'] = ['data' => $this->t('Submission'), 'field' => 'l.sid'];
|
||||
}
|
||||
$header['handler_id'] = ['data' => $this->t('Handler'), 'field' => 'l.handler_id'];
|
||||
$header['operation'] = ['data' => $this->t('Operation'), 'field' => 'l.operation', 'class' => [RESPONSIVE_PRIORITY_MEDIUM]];
|
||||
$header['message'] = ['data' => $this->t('Message'), 'field' => 'l.message', 'class' => [RESPONSIVE_PRIORITY_LOW]];
|
||||
$header['uid'] = ['data' => $this->t('User'), 'field' => 'ufd.name', 'class' => [RESPONSIVE_PRIORITY_LOW]];
|
||||
$header['timestamp'] = ['data' => $this->t('Date'), 'field' => 'l.timestamp', 'sort' => 'desc', 'class' => [RESPONSIVE_PRIORITY_LOW]];
|
||||
|
||||
// Query.
|
||||
$query = $this->database->select('webform_submission_log', 'l')
|
||||
->extend('\Drupal\Core\Database\Query\PagerSelectExtender')
|
||||
->extend('\Drupal\Core\Database\Query\TableSortExtender');
|
||||
$query->leftJoin('users_field_data', 'ufd', 'l.uid = ufd.uid');
|
||||
$query->leftJoin('webform_submission', 'ws', 'l.sid = ws.sid');
|
||||
$query->fields('l', [
|
||||
'lid',
|
||||
'uid',
|
||||
'webform_id',
|
||||
'sid',
|
||||
'handler_id',
|
||||
'operation',
|
||||
'message',
|
||||
'timestamp',
|
||||
]);
|
||||
$query->fields('ws', [
|
||||
'entity_type',
|
||||
'entity_id',
|
||||
]);
|
||||
if ($webform) {
|
||||
$query->condition('l.webform_id', $webform->id());
|
||||
}
|
||||
if ($webform_submission) {
|
||||
$query->condition('l.sid', $webform_submission->id());
|
||||
}
|
||||
if ($source_entity) {
|
||||
$query->condition('ws.entity_type', $source_entity->getEntityTypeId());
|
||||
$query->condition('ws.entity_id', $source_entity->id());
|
||||
}
|
||||
$result = $query
|
||||
->limit(50)
|
||||
->orderByHeader($header)
|
||||
->execute();
|
||||
|
||||
// Rows.
|
||||
$rows = [];
|
||||
foreach ($result as $log) {
|
||||
$row = [];
|
||||
$row['lid'] = $log->lid;
|
||||
if (empty($webform)) {
|
||||
$log_webform = $this->webformStorage->load($log->webform_id);
|
||||
$row['webform_id'] = $log_webform->toLink($log_webform->label(), 'results-log');
|
||||
}
|
||||
if (empty($source_entity) && empty($webform_submission)) {
|
||||
$entity = NULL;
|
||||
if ($log->entity_type && $log->entity_id) {
|
||||
$entity_type = $log->entity_type;
|
||||
$entity_id = $log->entity_id;
|
||||
if ($entity = $this->entityTypeManager()->getStorage($entity_type)->load($entity_id)) {
|
||||
$row['entity'] = ($entity->hasLinkTemplate('canonical')) ? $entity->toLink() : "$entity_type:$entity_id";
|
||||
}
|
||||
else {
|
||||
$row['entity'] = "$entity_type:$entity_id";
|
||||
}
|
||||
}
|
||||
else {
|
||||
$row['entity'] = '';
|
||||
}
|
||||
}
|
||||
if (empty($webform_submission)) {
|
||||
if ($log->sid) {
|
||||
$log_webform_submission = $this->webformSubmissionStorage->load($log->sid);
|
||||
$row['sid'] = [
|
||||
'data' => [
|
||||
'#type' => 'link',
|
||||
'#title' => $log->sid,
|
||||
'#url' => $this->requestHandler->getUrl($log_webform_submission, $source_entity, 'webform_submission.log'),
|
||||
],
|
||||
];
|
||||
}
|
||||
else {
|
||||
$row['sid'] = '';
|
||||
}
|
||||
}
|
||||
$row['handler_id'] = $log->handler_id;
|
||||
$row['operation'] = $log->operation;
|
||||
$row['message'] = [
|
||||
'data' => [
|
||||
'#markup' => $log->message,
|
||||
],
|
||||
];
|
||||
$row['uid'] = [
|
||||
'data' => [
|
||||
'#theme' => 'username',
|
||||
'#account' => $this->userStorage->load($log->uid),
|
||||
],
|
||||
];
|
||||
$row['timestamp'] = $this->dateFormatter->format($log->timestamp, 'short');
|
||||
|
||||
$rows[] = $row;
|
||||
}
|
||||
|
||||
$build['table'] = [
|
||||
'#type' => 'table',
|
||||
'#header' => $header,
|
||||
'#rows' => $rows,
|
||||
'#sticky' => TRUE,
|
||||
'#empty' => $this->t('No log messages available.'),
|
||||
];
|
||||
$build['pager'] = ['#type' => 'pager'];
|
||||
return $build;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Controller;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\Controller\EntityViewController;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Render\RendererInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\webform\WebformRequestInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Defines a controller to render a single webform submission.
|
||||
*/
|
||||
class WebformSubmissionViewController extends EntityViewController {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* The current user.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
protected $currentUser;
|
||||
|
||||
/**
|
||||
* Webform request handler.
|
||||
*
|
||||
* @var \Drupal\webform\WebformRequestInterface
|
||||
*/
|
||||
protected $requestHandler;
|
||||
|
||||
/**
|
||||
* Creates an WebformSubmissionViewController object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
* @param \Drupal\Core\Render\RendererInterface $renderer
|
||||
* The renderer service.
|
||||
* @param \Drupal\Core\Session\AccountInterface $current_user
|
||||
* The current user.
|
||||
* @param \Drupal\webform\WebformRequestInterface $webform_request
|
||||
* The webform request handler.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager, RendererInterface $renderer, AccountInterface $current_user, WebformRequestInterface $webform_request) {
|
||||
parent::__construct($entity_manager, $renderer);
|
||||
$this->currentUser = $current_user;
|
||||
$this->requestHandler = $webform_request;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity.manager'),
|
||||
$container->get('renderer'),
|
||||
$container->get('current_user'),
|
||||
$container->get('webform.request')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function view(EntityInterface $webform_submission, $view_mode = 'default', $langcode = NULL) {
|
||||
$webform = $this->requestHandler->getCurrentWebform();
|
||||
$source_entity = $this->requestHandler->getCurrentSourceEntity('webform_submission');
|
||||
|
||||
// Navigation.
|
||||
$build['navigation'] = [
|
||||
'#type' => 'webform_submission_navigation',
|
||||
'#webform_submission' => $webform_submission,
|
||||
];
|
||||
|
||||
// Information.
|
||||
$build['information'] = [
|
||||
'#type' => 'webform_submission_information',
|
||||
'#webform_submission' => $webform_submission,
|
||||
'#source_entity' => $source_entity,
|
||||
];
|
||||
|
||||
// Submission.
|
||||
$build['submission'] = parent::view($webform_submission, $view_mode, $langcode);
|
||||
|
||||
// Library.
|
||||
$build['#attached']['library'][] = 'webform/webform.admin';
|
||||
|
||||
// Add entities cacheable dependency.
|
||||
$this->renderer->addCacheableDependency($build, $this->currentUser);
|
||||
$this->renderer->addCacheableDependency($build, $webform);
|
||||
$this->renderer->addCacheableDependency($build, $webform_submission);
|
||||
if ($source_entity) {
|
||||
$this->renderer->addCacheableDependency($build, $source_entity);
|
||||
}
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* The _title_callback for the page that renders a single webform submission.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $webform_submission
|
||||
* The current webform submission.
|
||||
* @param bool $duplicate
|
||||
* Flag indicating if submission is being duplicated.
|
||||
*
|
||||
* @return string
|
||||
* The page title.
|
||||
*/
|
||||
public function title(EntityInterface $webform_submission, $duplicate = FALSE) {
|
||||
$title = $this->entityManager->getTranslationFromContext($webform_submission)->label();
|
||||
return ($duplicate) ? $this->t('Duplicate @title', ['@title' => $title]) : $title;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Controller;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\webform\WebformInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Provides route responses for webform submissions.
|
||||
*/
|
||||
class WebformSubmissionsController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* Returns response for the source entity autocompletion.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The current request object containing the search string.
|
||||
* @param \Drupal\webform\WebformInterface $webform
|
||||
* A webform.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\JsonResponse
|
||||
* A JSON response containing the autocomplete suggestions.
|
||||
*/
|
||||
public function sourceEntityAutocomplete(Request $request, WebformInterface $webform) {
|
||||
$match = $request->query->get('q');
|
||||
|
||||
$webform_submission_storage = $this->entityTypeManager()->getStorage('webform_submission');
|
||||
$source_entities = $webform_submission_storage->getSourceEntities($webform);
|
||||
$matches = [];
|
||||
|
||||
// @see \Drupal\Core\Entity\Plugin\EntityReferenceSelection\DefaultSelection::buildEntityQuery
|
||||
foreach ($source_entities as $source_entity_type => $source_entity_ids) {
|
||||
$definition = $this->entityTypeManager()->getDefinition($source_entity_type);
|
||||
$storage = $this->entityTypeManager()->getStorage($source_entity_type);
|
||||
|
||||
if (empty($definition->getKey('id')) || empty($definition->getKey('label'))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$query = $storage->getQuery();
|
||||
$query->range(0, 10);
|
||||
$query->condition($definition->getKey('id'), $source_entity_ids, 'IN');
|
||||
$query->condition($query->orConditionGroup()
|
||||
->condition($definition->getKey('label'), $match, 'CONTAINS')
|
||||
->condition($definition->getKey('id'), $match, 'CONTAINS')
|
||||
);
|
||||
$query->addTag($source_entity_type . '_access');
|
||||
$entity_ids = $query->execute();
|
||||
|
||||
$entities = $storage->loadMultiple($entity_ids);
|
||||
foreach ($entities as $source_entity_id => $source_entity) {
|
||||
$label = Html::escape($this->entityManager()->getTranslationFromContext($source_entity)->label());
|
||||
$value = "$label ($source_entity_type:$source_entity_id)";
|
||||
$matches[] = [
|
||||
'value' => $value,
|
||||
'label' => $label,
|
||||
];
|
||||
|
||||
if (count($matches) === 10) {
|
||||
new JsonResponse($matches);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new JsonResponse($matches);
|
||||
}
|
||||
|
||||
}
|
|
@ -23,7 +23,7 @@ class WebformTestController extends ControllerBase implements ContainerInjection
|
|||
protected $requestHandler;
|
||||
|
||||
/**
|
||||
* Webform submission generation service.
|
||||
* The webform submission generation service.
|
||||
*
|
||||
* @var \Drupal\webform\WebformSubmissionGenerateInterface
|
||||
*/
|
||||
|
@ -73,14 +73,9 @@ class WebformTestController extends ControllerBase implements ContainerInjection
|
|||
$values['entity_id'] = $source_entity->id();
|
||||
}
|
||||
|
||||
if ($request->query->get('webform_id') == $webform->id()) {
|
||||
return $webform->getSubmissionForm($values);
|
||||
}
|
||||
|
||||
// Generate date.
|
||||
$values['data'] = $this->generate->getData($webform);
|
||||
|
||||
return $webform->getSubmissionForm($values);
|
||||
return $webform->getSubmissionForm($values, 'test');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Render\Element\RenderElement;
|
||||
use Drupal\webform\Entity\Webform as WebformEntity;
|
||||
use Drupal\webform\WebformInterface;
|
||||
|
@ -24,7 +25,7 @@ class Webform extends RenderElement {
|
|||
],
|
||||
'#webform' => NULL,
|
||||
'#default_data' => [],
|
||||
'#cache' => ['max-age' => 0],
|
||||
'#action' => NULL,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -33,12 +34,84 @@ class Webform extends RenderElement {
|
|||
*/
|
||||
public static function preRenderWebformElement($element) {
|
||||
$webform = ($element['#webform'] instanceof WebformInterface) ? $element['#webform'] : WebformEntity::load($element['#webform']);
|
||||
if (!$webform || !$webform->access('submission_create')) {
|
||||
if (!$webform) {
|
||||
return $element;
|
||||
}
|
||||
|
||||
$values = ['data' => $element['#default_data']];
|
||||
return $element + $webform->getSubmissionForm($values);
|
||||
if ($webform->access('submission_create')) {
|
||||
$values = [];
|
||||
|
||||
// Set data.
|
||||
$values['data'] = $element['#default_data'];
|
||||
|
||||
// Set source entity type and id.
|
||||
if (!empty($element['#entity']) && $element['#entity'] instanceof EntityInterface) {
|
||||
$values['entity_type'] = $element['#entity']->getEntityTypeId();
|
||||
$values['entity_id'] = $element['#entity']->id();
|
||||
}
|
||||
elseif (!empty($element['#entity_type']) && !empty($element['#entity_id'])) {
|
||||
$values['entity_type'] = $element['#entity_type'];
|
||||
$values['entity_id'] = $element['#entity_id'];
|
||||
}
|
||||
|
||||
// Build the webform.
|
||||
$element['webform_build'] = $webform->getSubmissionForm($values);
|
||||
|
||||
// Set custom form action.
|
||||
if (!empty($element['#action'])) {
|
||||
$element['webform_build']['#action'] = $element['#action'];
|
||||
}
|
||||
}
|
||||
elseif ($webform->getSetting('form_access_denied') !== WebformInterface::ACCESS_DENIED_DEFAULT) {
|
||||
// Set access denied message.
|
||||
$element['webform_access_denied'] = static::buildAccessDenied($webform);
|
||||
}
|
||||
else {
|
||||
// Add config and webform to cache contexts.
|
||||
$config = \Drupal::configFactory()->get('webform.settings');
|
||||
$renderer = \Drupal::service('renderer');
|
||||
$renderer->addCacheableDependency($element, $config);
|
||||
$renderer->addCacheableDependency($element, $webform);
|
||||
}
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build access denied message for a webform.
|
||||
*
|
||||
* @param \Drupal\webform\WebformInterface $webform
|
||||
* A webform.
|
||||
*
|
||||
* @return array
|
||||
* A renderable array containing thea ccess denied message for a webform.
|
||||
*/
|
||||
public static function buildAccessDenied(WebformInterface $webform) {
|
||||
/** @var \Drupal\webform\WebformTokenManagerInterface $webform_token_manager */
|
||||
$webform_token_manager = \Drupal::service('webform.token_manager');
|
||||
|
||||
// Message.
|
||||
$config = \Drupal::configFactory()->get('webform.settings');
|
||||
$message = $webform->getSetting('form_access_denied_message')
|
||||
?: $config->get('settings.default_form_access_denied_message');
|
||||
$message = $webform_token_manager->replace($message, $webform);
|
||||
|
||||
// Attributes.
|
||||
$attributes = $webform->getSetting('form_access_denied_attributes');
|
||||
$attributes['class'][] = 'webform-access-denied';
|
||||
|
||||
$build = [
|
||||
'#type' => 'container',
|
||||
'#attributes' => $attributes,
|
||||
'message' => WebformHtmlEditor::checkMarkup($message),
|
||||
];
|
||||
|
||||
// Add config and webform to cache contexts.
|
||||
$renderer = \Drupal::service('renderer');
|
||||
$renderer->addCacheableDependency($build, $config);
|
||||
$renderer->addCacheableDependency($build, $webform);
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
129
web/modules/contrib/webform/src/Element/WebformActions.php
Normal file
129
web/modules/contrib/webform/src/Element/WebformActions.php
Normal file
|
@ -0,0 +1,129 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\Element\Container;
|
||||
use Drupal\webform\Utility\WebformElementHelper;
|
||||
|
||||
/**
|
||||
* Provides a wrapper element to group one or more Webform buttons in a form.
|
||||
*
|
||||
* @RenderElement("webform_actions")
|
||||
*
|
||||
* @see \Drupal\Core\Render\Element\Actions
|
||||
*/
|
||||
class WebformActions extends Container {
|
||||
|
||||
public static $buttons = [
|
||||
'submit',
|
||||
'reset',
|
||||
'draft',
|
||||
'wizard_prev',
|
||||
'wizard_next',
|
||||
'preview_prev',
|
||||
'preview_next',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
$class = get_class($this);
|
||||
return [
|
||||
'#process' => [
|
||||
[$class, 'processWebformActions'],
|
||||
[$class, 'processContainer'],
|
||||
],
|
||||
'#theme_wrappers' => ['container'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a form actions container element.
|
||||
*
|
||||
* @param array $element
|
||||
* An associative array containing the properties and children of the
|
||||
* form actions container.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
* @param array $complete_form
|
||||
* The complete form structure.
|
||||
*
|
||||
* @return array
|
||||
* The processed element.
|
||||
*/
|
||||
public static function processWebformActions(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
/** @var \Drupal\webform\WebformSubmissionForm $form_object */
|
||||
$form_object = $form_state->getFormObject();
|
||||
/** @var \Drupal\webform\webform_submission $webform_submission */
|
||||
$webform_submission = $form_object->getEntity();
|
||||
|
||||
$prefix = ($element['#webform_key']) ? 'edit-' . $element['#webform_key'] . '-' : '';
|
||||
|
||||
// Add class names only if form['actions']['#type'] is set to 'actions'.
|
||||
if (WebformElementHelper::isType($complete_form['actions'], 'actions')) {
|
||||
$element['#attributes']['class'][] = 'form-actions';
|
||||
$element['#attributes']['class'][] = 'webform-actions';
|
||||
}
|
||||
|
||||
// Copy the form's actions to this element.
|
||||
$element += $complete_form['actions'];
|
||||
|
||||
// Track if buttons are visible.
|
||||
$has_visible_button = FALSE;
|
||||
foreach (static::$buttons as $button_name) {
|
||||
// Make sure the button exists.
|
||||
if (!isset($element[$button_name])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set unique id for each button.
|
||||
if ($prefix) {
|
||||
$element[$button_name]['#id'] = Html::getUniqueId("$prefix$button_name");
|
||||
}
|
||||
|
||||
// Hide buttons using #access.
|
||||
if (!empty($element['#' . $button_name . '_hide'])) {
|
||||
$element[$button_name]['#access'] = FALSE;
|
||||
}
|
||||
|
||||
// Apply custom label except to update button (aka Save).
|
||||
if (!empty($element['#' . $button_name . '__label']) && empty($element[$button_name]['#webform_actions_button_custom'])) {
|
||||
$is_update_button = ($button_name === 'submit' && !($webform_submission->isNew() || $webform_submission->isDraft()));
|
||||
if (!$is_update_button) {
|
||||
$element[$button_name]['#value'] = $element['#' . $button_name . '__label'];
|
||||
}
|
||||
}
|
||||
|
||||
// Apply attributes (class, style, properties).
|
||||
if (!empty($element['#' . $button_name . '__attributes'])) {
|
||||
foreach ($element['#' . $button_name . '__attributes'] as $attribute_name => $attribute_value) {
|
||||
if ($attribute_name == 'class') {
|
||||
// Merge class names.
|
||||
$element[$button_name]['#attributes']['class'] = array_merge($element[$button_name]['#attributes']['class'], $attribute_value);
|
||||
}
|
||||
else {
|
||||
$element[$button_name]['#attributes'][$attribute_name] = $attribute_value;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (!isset($element[$button_name]['#access']) || $element[$button_name]['#access'] === TRUE) {
|
||||
$has_visible_button = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// Hide actions element if no buttons are visible (i.e. #access = FALSE).
|
||||
if (!$has_visible_button) {
|
||||
$element['#access'] = FALSE;
|
||||
}
|
||||
|
||||
// Hide form actions.
|
||||
$complete_form['actions']['#access'] = FALSE;
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
}
|
|
@ -12,7 +12,14 @@ class WebformAddress extends WebformCompositeBase {
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getCompositeElements() {
|
||||
public function getInfo() {
|
||||
return parent::getInfo() + ['#theme' => 'webform_composite_address'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getCompositeElements(array $element) {
|
||||
$elements = [];
|
||||
$elements['address'] = [
|
||||
'#type' => 'textfield',
|
||||
|
@ -26,6 +33,9 @@ class WebformAddress extends WebformCompositeBase {
|
|||
'#type' => 'textfield',
|
||||
'#title' => t('City/Town'),
|
||||
];
|
||||
// Any webform options prefixed with 'states_province' will automatically
|
||||
// be included within the Composite Element UI.
|
||||
// @see \Drupal\webform\Plugin\WebformElement\WebformCompositeBase::getCompositeElementOptions
|
||||
$elements['state_province'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => t('State/Province'),
|
||||
|
@ -33,8 +43,11 @@ class WebformAddress extends WebformCompositeBase {
|
|||
];
|
||||
$elements['postal_code'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Zip/Postal Code'),
|
||||
'#title' => t('ZIP/Postal Code'),
|
||||
];
|
||||
// Any webform options prefixed with 'country' will automatically
|
||||
// be included within the Composite Element UI.
|
||||
// @see \Drupal\webform\Plugin\WebformElement\WebformCompositeBase::getCompositeElementOptions
|
||||
$elements['country'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => t('Country'),
|
||||
|
|
|
@ -9,4 +9,33 @@ use Drupal\Core\Render\Element\Textfield;
|
|||
*
|
||||
* @FormElement("webform_autocomplete")
|
||||
*/
|
||||
class WebformAutocomplete extends Textfield {}
|
||||
class WebformAutocomplete extends Textfield {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
$class = get_class($this);
|
||||
|
||||
$info = parent::getInfo();
|
||||
$info['#pre_render'][] = [$class, 'preRenderWebformAutocomplete'];
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a #type 'webform_autocomplete' render element for input.html.twig.
|
||||
*
|
||||
* @param array $element
|
||||
* An associative array containing the properties of the element.
|
||||
* Properties used: #title, #value, #description, #size, #maxlength,
|
||||
* #placeholder, #required, #attributes.
|
||||
*
|
||||
* @return array
|
||||
* The $element with prepared variables ready for input.html.twig.
|
||||
*/
|
||||
public static function preRenderWebformAutocomplete($element) {
|
||||
static::setAttributes($element, ['webform-autocomplete']);
|
||||
return $element;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,10 +17,13 @@ class WebformButtons extends Radios {
|
|||
*/
|
||||
public static function processRadios(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
$element = parent::processRadios($element, $form_state, $complete_form);
|
||||
$element['#attached']['library'][] = 'webform/webform.element.buttons';
|
||||
|
||||
$element['#attributes']['class'][] = 'js-webform-buttons';
|
||||
$element['#attributes']['class'][] = 'webform-buttons';
|
||||
$element['#options_display'] = 'side_by_side';
|
||||
|
||||
$element['#attached']['library'][] = 'webform/webform.element.buttons';
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,12 @@ class WebformButtonsOther extends WebformOtherBase {
|
|||
* @see \Drupal\Core\Render\Element\Select
|
||||
*/
|
||||
public static function processWebformOther(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
// Attach element buttons before other handler.
|
||||
$element['#attached']['library'][] = 'webform/webform.element.buttons';
|
||||
|
||||
$element['#wrapper_attributes']['class'][] = "js-webform-buttons";
|
||||
$element['#wrapper_attributes']['class'][] = "webform-buttons";
|
||||
|
||||
$element = parent::processWebformOther($element, $form_state, $complete_form);
|
||||
return $element;
|
||||
}
|
||||
|
|
140
web/modules/contrib/webform/src/Element/WebformCheckboxValue.php
Normal file
140
web/modules/contrib/webform/src/Element/WebformCheckboxValue.php
Normal file
|
@ -0,0 +1,140 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\Element\FormElement;
|
||||
use Drupal\webform\Utility\WebformElementHelper;
|
||||
|
||||
/**
|
||||
* Provides a webform element for checking a box before entering a value.
|
||||
*
|
||||
* @FormElement("webform_checkbox_value")
|
||||
*/
|
||||
class WebformCheckboxValue extends FormElement {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
$class = get_class($this);
|
||||
return [
|
||||
'#input' => TRUE,
|
||||
'#process' => [
|
||||
[$class, 'processWebformCheckboxValue'],
|
||||
[$class, 'processAjaxForm'],
|
||||
],
|
||||
'#theme_wrappers' => ['form_element'],
|
||||
'#states' => [],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
|
||||
$element += ['#default_value' => NULL];
|
||||
if ($input === FALSE) {
|
||||
return [
|
||||
'checkbox' => ($element['#default_value']) ? TRUE : FALSE,
|
||||
'value' => $element['#default_value'],
|
||||
];
|
||||
}
|
||||
else {
|
||||
return $input;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a 'webform_checkbox_value' element.
|
||||
*/
|
||||
public static function processWebformCheckboxValue(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
$element['#tree'] = TRUE;
|
||||
|
||||
$properties = [
|
||||
'#title' => '#title',
|
||||
'#description' => '#description',
|
||||
'#help' => '#help',
|
||||
];
|
||||
|
||||
// Build checkbox element.
|
||||
$element['checkbox'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#default_value' => (!empty($element['#default_value'])) ? TRUE : FALSE,
|
||||
];
|
||||
$element['checkbox'] += array_intersect_key($element, $properties);
|
||||
|
||||
// Build value element.
|
||||
$selector = 'edit-' . str_replace('_', '-', implode('-', $element['#parents'])) . '-checkbox';
|
||||
$element['value'] = [
|
||||
'#default_value' => $element['#default_value'],
|
||||
'#states' => [
|
||||
'visible' => [
|
||||
':input[data-drupal-selector="' . $selector . '"]' => ['checked' => TRUE],
|
||||
],
|
||||
'required' => [
|
||||
':input[data-drupal-selector="' . $selector . '"]' => ['checked' => TRUE],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
// Pass '#value__*' properties to the value element.
|
||||
foreach ($element as $key => $value) {
|
||||
if (strpos($key, '#value__') === 0) {
|
||||
$value_key = str_replace('#value__', '#', $key);
|
||||
$element['value'][$value_key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
// Pass entire element to the value element.
|
||||
if (isset($element['#element'])) {
|
||||
$element['value'] += $element['#element'];
|
||||
}
|
||||
|
||||
// Make sure the value element has a #type.
|
||||
$element['value'] += ['#type' => 'textfield'];
|
||||
|
||||
// Always add a title to the value element for validation.
|
||||
if (!isset($element['value']['#title']) && isset($element['#title'])) {
|
||||
$element['value']['#title'] = $element['#title'];
|
||||
$element['value']['#title_display'] = 'invisible';
|
||||
}
|
||||
|
||||
// Attach libraries.
|
||||
$element['#attached']['library'][] = 'webform/webform.element.checkbox_value';
|
||||
|
||||
// Add validate callback.
|
||||
$element += ['#element_validate' => []];
|
||||
array_unshift($element['#element_validate'], [get_called_class(), 'validateWebformCheckboxValue']);
|
||||
|
||||
// Remove properties from the element.
|
||||
$element = array_diff_key($element, $properties);
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a checkbox value element.
|
||||
*/
|
||||
public static function validateWebformCheckboxValue(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
$value = NestedArray::getValue($form_state->getValues(), $element['#parents']);
|
||||
|
||||
// Always require a value when the checkbox is checked.
|
||||
if (!empty($value['checkbox']) && empty($value['value'])) {
|
||||
WebformElementHelper::setRequiredError($element['value'], $form_state);
|
||||
}
|
||||
|
||||
// If checkbox is not checked then empty the value.
|
||||
if (empty($value['checkbox'])) {
|
||||
$value['value'] = '';
|
||||
}
|
||||
|
||||
$form_state->setValueForElement($element['checkbox'], NULL);
|
||||
$form_state->setValueForElement($element['value'], NULL);
|
||||
|
||||
$element['#value'] = $value['value'];
|
||||
$form_state->setValueForElement($element, $value['value']);
|
||||
}
|
||||
|
||||
}
|
|
@ -5,10 +5,17 @@ namespace Drupal\webform\Element;
|
|||
use Drupal\Core\Serialization\Yaml;
|
||||
use Drupal\Core\Render\Element\Textarea;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\webform\Entity\WebformSubmission;
|
||||
use Drupal\webform\Twig\TwigExtension;
|
||||
use Drupal\webform\Utility\WebformYaml;
|
||||
|
||||
/**
|
||||
* Provides a webform element for HTML, YAML, or Plain text using CodeMirror.
|
||||
* Provides a webform element for using CodeMirror.
|
||||
*
|
||||
* Known Issues/Feature Requests:
|
||||
*
|
||||
* - Mixed Twig Mode #3292
|
||||
* https://github.com/codemirror/CodeMirror/issues/3292
|
||||
*
|
||||
* @FormElement("webform_codemirror")
|
||||
*/
|
||||
|
@ -22,9 +29,12 @@ class WebformCodeMirror extends Textarea {
|
|||
protected static $modes = [
|
||||
'css' => 'text/css',
|
||||
'html' => 'text/html',
|
||||
'htmlmixed' => 'htmlmixed',
|
||||
'javascript' => 'text/javascript',
|
||||
'text' => 'text/plain',
|
||||
'yaml' => 'text/x-yaml',
|
||||
'php' => 'text/x-php',
|
||||
'twig' => 'twig',
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -60,7 +70,7 @@ class WebformCodeMirror extends Textarea {
|
|||
if ($input === FALSE && $element['#mode'] == 'yaml' && isset($element['#default_value'])) {
|
||||
// Convert associative array in default value to YAML.
|
||||
if (is_array($element['#default_value'])) {
|
||||
$element['#default_value'] = WebformYaml::tidy(Yaml::encode($element['#default_value']));
|
||||
$element['#default_value'] = WebformYaml::encode($element['#default_value']);
|
||||
}
|
||||
// Convert empty YAML into an empty string.
|
||||
if ($element['#default_value'] == '{ }') {
|
||||
|
@ -76,18 +86,27 @@ class WebformCodeMirror extends Textarea {
|
|||
*/
|
||||
public static function processWebformCodeMirror(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
// Check that mode is defined and valid, if not default to (plain) text.
|
||||
if (empty($element['#mode']) || !isset(self::$modes[$element['#mode']])) {
|
||||
if (empty($element['#mode']) || !isset(static::$modes[$element['#mode']])) {
|
||||
$element['#mode'] = 'text';
|
||||
}
|
||||
|
||||
// Set validation.
|
||||
if (isset($element['#element_validate'])) {
|
||||
$element['#element_validate'] = array_merge([[get_called_class(), 'validateWebformCodeMirror']], $element['#element_validate']);
|
||||
}
|
||||
else {
|
||||
$element['#element_validate'] = [[get_called_class(), 'validateWebformCodeMirror']];
|
||||
// Check edit Twig template permission and complete disable editing.
|
||||
if ($element['#mode'] == 'twig') {
|
||||
if (!TwigExtension::hasEditTwigAccess()) {
|
||||
$element['#disable'] = TRUE;
|
||||
$element['#attributes']['disabled'] = 'disabled';
|
||||
$element['#field_prefix'] = [
|
||||
'#type' => 'webform_message',
|
||||
'#message_type' => 'warning',
|
||||
'#message_message' => t("Only webform administrators and user's assigned the 'Edit webform Twig templates' permission are allowed to edit this Twig template."),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Add validate callback.
|
||||
$element += ['#element_validate' => []];
|
||||
array_unshift($element['#element_validate'], [get_called_class(), 'validateWebformCodeMirror']);
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
|
@ -113,7 +132,14 @@ class WebformCodeMirror extends Textarea {
|
|||
* Webform element validation handler for #type 'webform_codemirror'.
|
||||
*/
|
||||
public static function validateWebformCodeMirror(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
if ($errors = static::getErrors($element, $form_state, $complete_form)) {
|
||||
// If element is disabled then use the #default_value.
|
||||
if (!empty($element['#disable'])) {
|
||||
$element['#value'] = $element['#default_value'];
|
||||
$form_state->setValueForElement($element, $element['#default_value']);
|
||||
}
|
||||
|
||||
$errors = static::getErrors($element, $form_state, $complete_form);
|
||||
if ($errors) {
|
||||
$build = [
|
||||
'title' => [
|
||||
'#markup' => t('%title is not valid.', ['%title' => (isset($element['#title']) ? $element['#title'] : t('YAML'))]),
|
||||
|
@ -125,16 +151,18 @@ class WebformCodeMirror extends Textarea {
|
|||
];
|
||||
$form_state->setError($element, \Drupal::service('renderer')->render($build));
|
||||
}
|
||||
|
||||
if ($element['#mode'] == 'yaml' && (isset($element['#default_value']) && is_array($element['#default_value']))) {
|
||||
// Handle rare case where single array value is not parsed correctly.
|
||||
if (preg_match('/^- (.*?)\s*$/', $element['#value'], $match)) {
|
||||
$value = [$match[1]];
|
||||
else {
|
||||
// If editing YAML and #default_value is an array, decode #value.
|
||||
if ($element['#mode'] == 'yaml' && (isset($element['#default_value']) && is_array($element['#default_value']))) {
|
||||
// Handle rare case where single array value is not parsed correctly.
|
||||
if (preg_match('/^- (.*?)\s*$/', $element['#value'], $match)) {
|
||||
$value = [$match[1]];
|
||||
}
|
||||
else {
|
||||
$value = $element['#value'] ? Yaml::decode($element['#value']) : [];
|
||||
}
|
||||
$form_state->setValueForElement($element, $value);
|
||||
}
|
||||
else {
|
||||
$value = $element['#value'] ? Yaml::decode($element['#value']) : [];
|
||||
}
|
||||
$form_state->setValueForElement($element, $value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -148,37 +176,13 @@ class WebformCodeMirror extends Textarea {
|
|||
|
||||
switch ($element['#mode']) {
|
||||
case 'html':
|
||||
// @see: http://stackoverflow.com/questions/3167074/which-function-in-php-validate-if-the-string-is-valid-html
|
||||
// @see: http://stackoverflow.com/questions/5030392/x-html-validator-in-php
|
||||
libxml_use_internal_errors(TRUE);
|
||||
if (simplexml_load_string('<fragment>' . $element['#value'] . '</fragment>')) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
$errors = libxml_get_errors();
|
||||
libxml_clear_errors();
|
||||
if (!$errors) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
$messages = [];
|
||||
foreach ($errors as $error) {
|
||||
$messages[] = $error->message;
|
||||
}
|
||||
return $messages;
|
||||
return static::validateHtml($element, $form_state, $complete_form);
|
||||
|
||||
case 'yaml':
|
||||
try {
|
||||
$value = trim($element['#value']);
|
||||
$data = Yaml::decode($value);
|
||||
if (!is_array($data) && $value) {
|
||||
throw new \Exception(t('YAML must contain an associative array of elements.'));
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
catch (\Exception $exception) {
|
||||
return [$exception->getMessage()];
|
||||
}
|
||||
return static::validateYaml($element, $form_state, $complete_form);
|
||||
|
||||
case 'twig':
|
||||
return static::validateTwig($element, $form_state, $complete_form);
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
|
@ -198,4 +202,117 @@ class WebformCodeMirror extends Textarea {
|
|||
return (isset(static::$modes[$mode])) ? static::$modes[$mode] : static::$modes['text'];
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
// Language/markup validation callback.
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* Validate HTML.
|
||||
*
|
||||
* @param array $element
|
||||
* The form element whose value is being validated.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
* @param array $complete_form
|
||||
* The complete form structure.
|
||||
*
|
||||
* @return array|null
|
||||
* An array of error messages.
|
||||
*/
|
||||
protected static function validateHtml($element, FormStateInterface $form_state, $complete_form) {
|
||||
// @see: http://stackoverflow.com/questions/3167074/which-function-in-php-validate-if-the-string-is-valid-html
|
||||
// @see: http://stackoverflow.com/questions/5030392/x-html-validator-in-php
|
||||
libxml_use_internal_errors(TRUE);
|
||||
if (simplexml_load_string('<fragment>' . $element['#value'] . '</fragment>')) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
$errors = libxml_get_errors();
|
||||
libxml_clear_errors();
|
||||
if (!$errors) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
$messages = [];
|
||||
foreach ($errors as $error) {
|
||||
$messages[] = $error->message;
|
||||
}
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate Twig.
|
||||
*
|
||||
* @param array $element
|
||||
* The form element whose value is being validated.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
* @param array $complete_form
|
||||
* The complete form structure.
|
||||
*
|
||||
* @return array|null
|
||||
* An array of error messages.
|
||||
*/
|
||||
protected static function validateTwig($element, FormStateInterface $form_state, $complete_form) {
|
||||
$template = $element['#value'];
|
||||
$form_object = $form_state->getFormObject();
|
||||
try {
|
||||
// If form object has ::getWebform method validate Twig template
|
||||
// using a temporary webform submission context.
|
||||
if (method_exists($form_object, 'getWebform')) {
|
||||
/** @var \Drupal\webform\WebformInterface $webform */
|
||||
$webform = $form_object->getWebform();
|
||||
|
||||
// Get a temporary webform submission.
|
||||
/** @var \Drupal\webform\WebformSubmissionGenerateInterface $webform_submission_generate */
|
||||
$webform_submission_generate = \Drupal::service('webform_submission.generate');
|
||||
$values = [
|
||||
'webform_id' => $webform->id(),
|
||||
'data' => $webform_submission_generate->getData($webform),
|
||||
];
|
||||
$webform_submission = WebformSubmission::create($values);
|
||||
$build = TwigExtension::buildTwigTemplate($webform_submission, $template, []);
|
||||
}
|
||||
else {
|
||||
$build = [
|
||||
'#type' => 'inline_template',
|
||||
'#template' => $element['#value'],
|
||||
'#context' => [],
|
||||
];
|
||||
}
|
||||
\Drupal::service('renderer')->renderPlain($build);
|
||||
return NULL;
|
||||
}
|
||||
catch (\Exception $exception) {
|
||||
return [$exception->getMessage()];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate YAML.
|
||||
*
|
||||
* @param array $element
|
||||
* The form element whose value is being validated.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
* @param array $complete_form
|
||||
* The complete form structure.
|
||||
*
|
||||
* @return array|null
|
||||
* An array of error messages.
|
||||
*/
|
||||
protected static function validateYaml($element, FormStateInterface $form_state, $complete_form) {
|
||||
try {
|
||||
$value = $element['#value'];
|
||||
$data = Yaml::decode($value);
|
||||
if (!is_array($data) && $value) {
|
||||
throw new \Exception(t('YAML must contain an associative array of elements.'));
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
catch (\Exception $exception) {
|
||||
return [$exception->getMessage()];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,17 +2,18 @@
|
|||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\Core\Render\Element\FormElement;
|
||||
use Drupal\Core\Render\Element\CompositeFormElementTrait;
|
||||
use Drupal\webform\Entity\WebformOptions as WebformOptionsEntity;
|
||||
use Drupal\webform\Utility\WebformElementHelper;
|
||||
|
||||
/**
|
||||
* Provides an base composite webform element.
|
||||
*/
|
||||
abstract class WebformCompositeBase extends FormElement {
|
||||
abstract class WebformCompositeBase extends FormElement implements WebformCompositeInterface {
|
||||
|
||||
use CompositeFormElementTrait;
|
||||
use WebformCompositeFormElementTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
@ -21,15 +22,14 @@ abstract class WebformCompositeBase extends FormElement {
|
|||
$class = get_class($this);
|
||||
return [
|
||||
'#input' => TRUE,
|
||||
'#access' => TRUE,
|
||||
'#process' => [
|
||||
[$class, 'processWebformComposite'],
|
||||
[$class, 'processAjaxForm'],
|
||||
],
|
||||
'#pre_render' => [
|
||||
[$class, 'preRenderCompositeFormElement'],
|
||||
[$class, 'preRenderWebformCompositeFormElement'],
|
||||
],
|
||||
'#theme' => str_replace('webform_', 'webform_composite_', $this->getPluginId()),
|
||||
'#theme_wrappers' => ['container'],
|
||||
'#title_display' => 'invisible',
|
||||
'#required' => FALSE,
|
||||
'#flexbox' => TRUE,
|
||||
|
@ -40,10 +40,16 @@ abstract class WebformCompositeBase extends FormElement {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
|
||||
$composite_elements = static::getCompositeElements();
|
||||
/** @var \Drupal\webform\Plugin\WebformElementManagerInterface $element_manager */
|
||||
$element_manager = \Drupal::service('plugin.manager.webform.element');
|
||||
$composite_elements = static::getCompositeElements($element);
|
||||
$composite_elements = WebformElementHelper::getFlattened($composite_elements);
|
||||
|
||||
// Get default value for inputs.
|
||||
$default_value = [];
|
||||
foreach ($composite_elements as $composite_key => $composite_element) {
|
||||
if (isset($composite_element['#type']) && $composite_element['#type'] != 'label') {
|
||||
$element_plugin = $element_manager->getElementInstance($composite_element);
|
||||
if ($element_plugin->isInput($composite_element)) {
|
||||
$default_value[$composite_key] = '';
|
||||
}
|
||||
}
|
||||
|
@ -54,30 +60,23 @@ abstract class WebformCompositeBase extends FormElement {
|
|||
}
|
||||
return $element['#default_value'] + $default_value;
|
||||
}
|
||||
return (is_array($input)) ? $input + $default_value : $default_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a renderable array of webform elements.
|
||||
*
|
||||
* @return array
|
||||
* A renderable array of webform elements, containing the base properties
|
||||
* for the composite's webform elements.
|
||||
*/
|
||||
public static function getCompositeElements() {
|
||||
return [];
|
||||
return (is_array($input)) ? $input + $default_value : $default_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function preRenderCompositeFormElement($element) {
|
||||
$element = CompositeFormElementTrait::preRenderCompositeFormElement($element);
|
||||
$element['#theme_wrappers'][] = 'form_element';
|
||||
$element['#wrapper_attributes']['id'] = $element['#id'] . '--wrapper';
|
||||
$element['#wrapper_attributes']['class'][] = 'form-composite';
|
||||
|
||||
$element['#attributes']['id'] = $element['#id'];
|
||||
|
||||
// Add class name to wrapper attributes.
|
||||
$class_name = str_replace('_', '-', $element['#type']);
|
||||
$element['#attributes']['class'][] = 'js-' . $class_name;
|
||||
$element['#attributes']['class'][] = $class_name;
|
||||
static::setAttributes($element, ['js-' . $class_name, $class_name]);
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
@ -89,73 +88,16 @@ abstract class WebformCompositeBase extends FormElement {
|
|||
if (isset($element['#initialize'])) {
|
||||
return $element;
|
||||
}
|
||||
|
||||
$element['#initialize'] = TRUE;
|
||||
|
||||
$element['#tree'] = TRUE;
|
||||
$composite_elements = static::getCompositeElements();
|
||||
foreach ($composite_elements as $composite_key => &$composite_element) {
|
||||
// Transfer '#{composite_key}_{property}' from main element to composite
|
||||
// element.
|
||||
foreach ($element as $property_key => $property_value) {
|
||||
if (strpos($property_key, '#' . $composite_key . '__') === 0) {
|
||||
$composite_property_key = str_replace('#' . $composite_key . '__', '#', $property_key);
|
||||
$composite_element[$composite_property_key] = $property_value;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($element['#value'][$composite_key])) {
|
||||
$composite_element['#value'] = $element['#value'][$composite_key];
|
||||
}
|
||||
|
||||
// Always set #access which is used to hide/show the elements container.
|
||||
$composite_element += [
|
||||
'#access' => TRUE,
|
||||
];
|
||||
|
||||
// Never required hidden composite elements.
|
||||
if ($composite_element['#access'] == FALSE) {
|
||||
unset($composite_element['#required']);
|
||||
}
|
||||
|
||||
// Load options.
|
||||
if (isset($composite_element['#options'])) {
|
||||
$composite_element['#options'] = WebformOptionsEntity::getElementOptions($composite_element);
|
||||
}
|
||||
|
||||
// Handle #type specific customizations.
|
||||
if (isset($composite_element['#type'])) {
|
||||
switch ($composite_element['#type']) {
|
||||
case 'tel':
|
||||
// Add international phone library.
|
||||
// Add internation library and classes.
|
||||
if (!empty($composite_element['#international'])) {
|
||||
$composite_element['#attached']['library'][] = 'webform/webform.element.telephone';
|
||||
$composite_element['#attributes']['class'][] = 'js-webform-telephone-international';
|
||||
$composite_element['#attributes']['class'][] = 'webform-webform-telephone-international';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'select':
|
||||
case 'webform_select_other':
|
||||
// Always include an empty option, even if the composite element
|
||||
// is not required.
|
||||
// @see https://api.drupal.org/api/drupal/core!lib!Drupal!Core!Render!Element!Select.php/class/Select/8.2.x
|
||||
// Use placeholder as empty option.
|
||||
if (!isset($composite_element['#empty_option'])) {
|
||||
if (isset($composite_element['#placeholder'])) {
|
||||
$composite_element['#empty_option'] = $composite_element['#placeholder'];
|
||||
}
|
||||
elseif (empty($composite_element['#required'])) {
|
||||
$composite_element['#empty_option'] = t('- None -');
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$composite_elements = static::initializeCompositeElements($element);
|
||||
static::processWebformCompositeElementsRecursive($element, $composite_elements, $form_state, $complete_form);
|
||||
$element += $composite_elements;
|
||||
$element['#element_validate'] = [[get_called_class(), 'validateWebformComposite']];
|
||||
|
||||
// Add validate callback.
|
||||
$element += ['#element_validate' => []];
|
||||
array_unshift($element['#element_validate'], [get_called_class(), 'validateWebformComposite']);
|
||||
|
||||
if (!empty($element['#flexbox'])) {
|
||||
$element['#attached']['library'][] = 'webform/webform.element.flexbox';
|
||||
|
@ -164,21 +106,171 @@ abstract class WebformCompositeBase extends FormElement {
|
|||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively processes a composite's elements.
|
||||
*/
|
||||
public static function processWebformCompositeElementsRecursive(&$element, array &$composite_elements, FormStateInterface $form_state, &$complete_form) {
|
||||
/** @var \Drupal\webform\Plugin\WebformElementManagerInterface $element_manager */
|
||||
$element_manager = \Drupal::service('plugin.manager.webform.element');
|
||||
|
||||
// Get composite element required/options states from visible/hidden states.
|
||||
$composite_required_states = WebformElementHelper::getRequiredFromVisibleStates($element);
|
||||
|
||||
foreach ($composite_elements as $composite_key => &$composite_element) {
|
||||
if (!Element::child($composite_key) || !is_array($composite_element)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set parents.
|
||||
$composite_element['#parents'] = array_merge($element['#parents'], [$composite_key]);
|
||||
|
||||
// If the element's #access is FALSE, apply it to all sub elements.
|
||||
if ($element['#access'] === FALSE) {
|
||||
$composite_element['#access'] = FALSE;
|
||||
}
|
||||
|
||||
// Get element plugin and set inputs #default_value.
|
||||
$element_plugin = $element_manager->getElementInstance($composite_element);
|
||||
if ($element_plugin->isInput($composite_element)) {
|
||||
// Set #default_value for sub elements.
|
||||
if (isset($element['#value'][$composite_key])) {
|
||||
$composite_element['#default_value'] = $element['#value'][$composite_key];
|
||||
}
|
||||
}
|
||||
|
||||
// Build the webform element.
|
||||
$element_manager->buildElement($composite_element, $complete_form, $form_state);
|
||||
|
||||
// Custom validate required sub-element because they can be hidden
|
||||
// via #access or #states.
|
||||
// @see \Drupal\webform\Element\WebformCompositeBase::validateWebformComposite
|
||||
if ($composite_required_states && !empty($composite_element['#required'])) {
|
||||
unset($composite_element['#required']);
|
||||
$composite_element['#_required'] = TRUE;
|
||||
if (!isset($composite_element['#states'])) {
|
||||
$composite_element['#states'] = [];
|
||||
}
|
||||
$composite_element['#states'] += $composite_required_states;
|
||||
}
|
||||
|
||||
static::processWebformCompositeElementsRecursive($element, $composite_element, $form_state, $complete_form);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a composite element.
|
||||
*/
|
||||
public static function validateWebformComposite(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
$value = $element['#value'];
|
||||
// IMPORTANT: Must get values from the $form_states since sub-elements
|
||||
// may call $form_state->setValueForElement() via their validation hook.
|
||||
// @see \Drupal\webform\Element\WebformEmailConfirm::validateWebformEmailConfirm
|
||||
// @see \Drupal\webform\Element\WebformOtherBase::validateWebformOther
|
||||
$value = NestedArray::getValue($form_state->getValues(), $element['#parents']);
|
||||
|
||||
// Validate required composite elements.
|
||||
$composite_elements = static::getCompositeElements();
|
||||
foreach ($composite_elements as $composite_key => $composite_element) {
|
||||
if (!empty($element[$composite_key]['#required']) && $value[$composite_key] == '') {
|
||||
if (isset($element[$composite_key]['#title'])) {
|
||||
$form_state->setError($element[$composite_key], t('@name field is required.', ['@name' => $element[$composite_key]['#title']]));
|
||||
// Only validate composite elements that are visible.
|
||||
$has_access = (!isset($element['#access']) || $element['#access'] === TRUE);
|
||||
if ($has_access) {
|
||||
// Validate required composite elements.
|
||||
$composite_elements = static::getCompositeElements($element);
|
||||
$composite_elements = WebformElementHelper::getFlattened($composite_elements);
|
||||
foreach ($composite_elements as $composite_key => $composite_element) {
|
||||
$is_required = !empty($element[$composite_key]['#required']);
|
||||
$is_empty = (isset($value[$composite_key]) && $value[$composite_key] === '');
|
||||
if ($is_required && $is_empty) {
|
||||
WebformElementHelper::setRequiredError($element[$composite_key], $form_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear empty composites value.
|
||||
if (empty(array_filter($value))) {
|
||||
$element['#value'] = NULL;
|
||||
$form_state->setValueForElement($element, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
// Composite Elements.
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getCompositeElements(array $element) {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function initializeCompositeElements(array &$element) {
|
||||
$composite_elements = static::getCompositeElements($element);
|
||||
static::initializeCompositeElementsRecursive($element, $composite_elements);
|
||||
return $composite_elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a composite's elements recursively.
|
||||
*
|
||||
* @param array $element
|
||||
* A render array for the current element.
|
||||
* @param array $composite_elements
|
||||
* A render array containing a composite's elements.
|
||||
*
|
||||
* @throws \Exception
|
||||
* Throws exception when unsupported element type is used with a composite
|
||||
* element.
|
||||
*/
|
||||
protected static function initializeCompositeElementsRecursive(array &$element, array &$composite_elements) {
|
||||
/** @var \Drupal\webform\Plugin\WebformElementManagerInterface $element_manager */
|
||||
$element_manager = \Drupal::service('plugin.manager.webform.element');
|
||||
|
||||
foreach ($composite_elements as $composite_key => &$composite_element) {
|
||||
if (Element::property($composite_key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Transfer '#{composite_key}_{property}' from main element to composite
|
||||
// element.
|
||||
foreach ($element as $property_key => $property_value) {
|
||||
if (strpos($property_key, '#' . $composite_key . '__') === 0) {
|
||||
$composite_property_key = str_replace('#' . $composite_key . '__', '#', $property_key);
|
||||
$composite_element[$composite_property_key] = $property_value;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize composite sub-element.
|
||||
$element_plugin = $element_manager->getElementInstance($composite_element);
|
||||
|
||||
// Make sure to remove any #options references from unsupported elements.
|
||||
// This prevents "An illegal choice has been detected." error.
|
||||
// @see FormValidator::performRequiredValidation()
|
||||
if (isset($composite_element['#options']) && !$element_plugin->hasProperty('options')) {
|
||||
unset($composite_element['#options']);
|
||||
}
|
||||
|
||||
// Convert #placeholder to #empty_option for select elements.
|
||||
if (isset($composite_element['#placeholder']) && $element_plugin->hasProperty('empty_option')) {
|
||||
$composite_element['#empty_option'] = $composite_element['#placeholder'];
|
||||
}
|
||||
|
||||
// Apply #select2 and #chosen to select elements.
|
||||
if (isset($composite_element['#type']) && strpos($composite_element['#type'], 'select') !== FALSE) {
|
||||
$select_properties = ['#select2' => '#select2', '#chosen' => '#chosen'];
|
||||
$composite_element += array_intersect_key($element, $select_properties);
|
||||
}
|
||||
|
||||
if ($element_plugin->hasMultipleValues($composite_element)) {
|
||||
throw new \Exception('Multiple elements are not supported within composite elements.');
|
||||
}
|
||||
if ($element_plugin->isComposite()) {
|
||||
throw new \Exception('Nested composite elements are not supported within composite elements.');
|
||||
}
|
||||
|
||||
$element_plugin->initialize($composite_element);
|
||||
|
||||
static::initializeCompositeElementsRecursive($element, $composite_element);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Core\Render\Element;
|
||||
|
||||
/**
|
||||
* Provides a trait for webform composite form elements.
|
||||
*
|
||||
* Any form element that is comprised of several distinct parts can use this
|
||||
* trait to add support for a composite title or description.
|
||||
*
|
||||
* The Webform overrides any element that is using the CompositeFormElementTrait
|
||||
* and applies the below pre renderer which adds support for
|
||||
* #wrapper_attributes and additional some classes.
|
||||
*
|
||||
* @see \Drupal\Core\Render\Element\CompositeFormElementTrait
|
||||
* @see \Drupal\webform\Plugin\WebformElementBase::prepareCompositeFormElement
|
||||
*/
|
||||
trait WebformCompositeFormElementTrait {
|
||||
|
||||
/**
|
||||
* Adds form element theming to an element if its title or description is set.
|
||||
*
|
||||
* This is used as a pre render function for checkboxes and radios.
|
||||
*/
|
||||
public static function preRenderWebformCompositeFormElement($element) {
|
||||
$has_content = (isset($element['#title']) || isset($element['#description']));
|
||||
if (!$has_content) {
|
||||
return $element;
|
||||
}
|
||||
|
||||
// Set attributes.
|
||||
if (!isset($element['#attributes'])) {
|
||||
$element['#attributes'] = [];
|
||||
}
|
||||
|
||||
// Apply wrapper attributes to attributes.
|
||||
if (isset($element['#wrapper_attributes'])) {
|
||||
$element['#attributes'] = NestedArray::mergeDeep($element['#attributes'], $element['#wrapper_attributes']);
|
||||
}
|
||||
|
||||
// Set id and classes.
|
||||
if (!isset($element['#attributes']['id'])) {
|
||||
$element['#attributes']['id'] = $element['#id'] . '--wrapper';
|
||||
}
|
||||
$element['#attributes']['class'][] = Html::getClass($element['#type']) . '--wrapper';
|
||||
$element['#attributes']['class'][] = 'fieldgroup';
|
||||
$element['#attributes']['class'][] = 'form-composite';
|
||||
|
||||
// Add composite library.
|
||||
$element['#attached']['library'][] = 'webform/webform.composite';
|
||||
|
||||
// Set theme wrapper to wrapper type.
|
||||
$wrapper_type = (isset($element['#wrapper_type'])) ? $element['#wrapper_type'] : 'fieldset';
|
||||
$element['#theme_wrappers'][] = $wrapper_type;
|
||||
|
||||
// Apply fieldset specific enhancements.
|
||||
if ($wrapper_type === 'fieldset') {
|
||||
// Set the element's title attribute to show #title as a tooltip, if needed.
|
||||
if (isset($element['#title']) && $element['#title_display'] == 'attribute') {
|
||||
$element['#attributes']['title'] = $element['#title'];
|
||||
if (!empty($element['#required'])) {
|
||||
// Append an indication that this fieldset is required.
|
||||
$element['#attributes']['title'] .= ' (' . t('Required') . ')';
|
||||
}
|
||||
}
|
||||
|
||||
// Add hidden and visible title class to fix composite fieldset
|
||||
// top/bottom margins.
|
||||
if (isset($element['#title'])) {
|
||||
if (!empty($element['#title_display']) && in_array($element['#title_display'], ['invisible', 'attribute'])) {
|
||||
$element['#attributes']['class'][] = 'webform-composite-hidden-title';
|
||||
}
|
||||
else {
|
||||
$element['#attributes']['class'][] = 'webform-composite-visible-title';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Issue #3007132: [accessibility] Radios and checkboxes the WAI-ARIA
|
||||
// 'aria-describedby' attribute has a reference to an ID that does not
|
||||
// exist or an ID that is not unique
|
||||
// https://www.drupal.org/project/webform/issues/3007132
|
||||
// @see \Drupal\Core\Form\FormBuilder::doBuildForm
|
||||
if (!empty($element['#description'])) {
|
||||
$fix_aria_describedby = (preg_match('/^(?:webform_)?(?:radios|checkboxes|buttons)(?:_other)?$/', $element['#type']));
|
||||
foreach (Element::children($element) as $key) {
|
||||
// Skip if child element has a dedicated description.
|
||||
if (!empty($element[$key]['#description'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip if 'aria-describedby' is not set.
|
||||
if (empty($element[$key]['#attributes']['aria-describedby'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only fix 'aria-describedby' attribute if it pointing to a broken id.
|
||||
if ($element[$key]['#attributes']['aria-describedby'] === $element['#id'] . '--description') {
|
||||
if ($fix_aria_describedby) {
|
||||
$element[$key]['#attributes']['aria-describedby'] = $element['#attributes']['id'] . '--description';
|
||||
}
|
||||
else {
|
||||
unset($element[$key]['#attributes']['aria-describedby']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
/**
|
||||
* Defines an interface for webform composite element.
|
||||
*/
|
||||
interface WebformCompositeInterface {
|
||||
|
||||
/**
|
||||
* Get a renderable array of webform elements.
|
||||
*
|
||||
* @param array $element
|
||||
* A render array for the current element.
|
||||
*
|
||||
* @return array
|
||||
* A renderable array of webform elements, containing the base properties
|
||||
* for the composite's webform elements.
|
||||
*/
|
||||
public static function getCompositeElements(array $element);
|
||||
|
||||
/**
|
||||
* Initialize a composite's elements.
|
||||
*
|
||||
* @param array $element
|
||||
* A render array for the current element.
|
||||
*
|
||||
* @return array
|
||||
* A renderable array of webform elements, containing the base properties
|
||||
* for the composite's webform elements.
|
||||
*/
|
||||
public static function initializeCompositeElements(array &$element);
|
||||
|
||||
}
|
369
web/modules/contrib/webform/src/Element/WebformComputedBase.php
Normal file
369
web/modules/contrib/webform/src/Element/WebformComputedBase.php
Normal file
|
@ -0,0 +1,369 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\Element\FormElement;
|
||||
use Drupal\webform\Entity\WebformSubmission;
|
||||
use Drupal\webform\Utility\WebformHtmlHelper;
|
||||
use Drupal\webform\Utility\WebformXss;
|
||||
use Drupal\webform\WebformSubmissionForm;
|
||||
use Drupal\webform\WebformSubmissionInterface;
|
||||
|
||||
/**
|
||||
* Provides a base class for 'webform_computed' elements.
|
||||
*/
|
||||
abstract class WebformComputedBase extends FormElement {
|
||||
|
||||
/**
|
||||
* Denotes HTML.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const MODE_HTML = 'html';
|
||||
|
||||
/**
|
||||
* Denotes plain text.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const MODE_TEXT = 'text';
|
||||
|
||||
/**
|
||||
* Denotes markup whose content type should be detected.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const MODE_AUTO = 'auto';
|
||||
|
||||
/**
|
||||
* Cache of submissions being processed.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $submissions = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
$class = get_class($this);
|
||||
return [
|
||||
'#process' => [
|
||||
[$class, 'processWebformComputed'],
|
||||
],
|
||||
'#input' => TRUE,
|
||||
'#value' => '',
|
||||
'#mode' => NULL,
|
||||
'#hide_empty' => FALSE,
|
||||
// Note: Computed elements do not use the default #ajax wrapper, which is
|
||||
// why we can use #ajax as a boolean.
|
||||
// @see \Drupal\Core\Render\Element\RenderElement::preRenderAjaxForm
|
||||
'#ajax' => FALSE,
|
||||
'#webform_submission' => NULL,
|
||||
'#theme_wrappers' => ['form_element'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a Webform computed token element.
|
||||
*
|
||||
* @param array $element
|
||||
* The element.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
* @param array $complete_form
|
||||
* The complete form structure.
|
||||
*
|
||||
* @return array
|
||||
* The processed element.
|
||||
*/
|
||||
public static function processWebformComputed(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
$webform_submission = static::getWebformSubmission($element, $form_state, $complete_form);
|
||||
if ($webform_submission) {
|
||||
// Set tree.
|
||||
$element['#tree'] = TRUE;
|
||||
|
||||
// Set #type to item to trigger #states behavior.
|
||||
// @see drupal_process_states;
|
||||
$element['#type'] = 'item';
|
||||
|
||||
$value = static::processValue($element, $webform_submission);
|
||||
static::setWebformComputedElementValue($element, $value);
|
||||
}
|
||||
|
||||
if (!empty($element['#states'])) {
|
||||
webform_process_states($element, '#wrapper_attributes');
|
||||
}
|
||||
|
||||
// Add validate callback.
|
||||
$element += ['#element_validate' => []];
|
||||
array_unshift($element['#element_validate'], [get_called_class(), 'validateWebformComputed']);
|
||||
|
||||
/**************************************************************************/
|
||||
// Ajax support
|
||||
/**************************************************************************/
|
||||
|
||||
// Enabled Ajax support only for computed elements associated with a
|
||||
// webform submission form.
|
||||
if ($element['#ajax'] && $form_state->getFormObject() instanceof WebformSubmissionForm) {
|
||||
// Get button name and wrapper id.
|
||||
$button_name = 'webform-computed-' . implode('-', $element['#parents']) . '-button';
|
||||
$wrapper_id = 'webform-computed-' . implode('-', $element['#parents']) . '-wrapper';
|
||||
|
||||
// Get computed value element keys which are used to trigger Ajax updates.
|
||||
preg_match_all('/(?:\[webform_submission:values:|data\.)([_a-z]+)/', $element['#value'], $matches);
|
||||
$element_keys = $matches[1] ?: [];
|
||||
$element_keys = array_unique($element_keys);
|
||||
|
||||
// Wrapping the computed element is two div tags.
|
||||
// div.js-webform-computed is used to initialize the Ajax updates.
|
||||
// div#wrapper_id is used to display response from the Ajax updates.
|
||||
$element['#wrapper_id'] = $wrapper_id;
|
||||
$element['#prefix'] = '<div class="js-webform-computed" data-webform-element-keys="' . implode(',', $element_keys) . '">' .
|
||||
'<div class="js-webform-computed-wrapper" id="' . $wrapper_id . '">';
|
||||
$element['#suffix'] = '</div></div>';
|
||||
|
||||
// Add hidden update button.
|
||||
$element['update'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Update'),
|
||||
'#validate' => [[get_called_class(), 'validateWebformComputedCallback']],
|
||||
'#submit' => [[get_called_class(), 'submitWebformComputedCallback']],
|
||||
'#ajax' => [
|
||||
'callback' => [get_called_class(), 'ajaxWebformComputedCallback'],
|
||||
'wrapper' => $wrapper_id,
|
||||
'progress' => ['type' => 'none'],
|
||||
],
|
||||
// Hide button and disable validation.
|
||||
'#attributes' => [
|
||||
'class' => [
|
||||
'js-hide',
|
||||
'js-webform-novalidate',
|
||||
'js-webform-computed-submit',
|
||||
],
|
||||
],
|
||||
// Issue #1342066 Document that buttons with the same #value need a unique
|
||||
// #name for the Form API to distinguish them, or change the Form API to
|
||||
// assign unique #names automatically.
|
||||
'#name' => $button_name,
|
||||
];
|
||||
|
||||
// Attached computed element library.
|
||||
$element['#attached']['library'][] = 'webform/webform.element.computed';
|
||||
}
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process computed value.
|
||||
*
|
||||
* @param array $element
|
||||
* The element.
|
||||
* @param \Drupal\webform\WebformSubmissionInterface $webform_submission
|
||||
* A webform submission.
|
||||
*
|
||||
* @return array|string
|
||||
* The string with tokens replaced.
|
||||
*/
|
||||
public static function processValue(array $element, WebformSubmissionInterface $webform_submission) {
|
||||
return $element['#value'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates an computed element.
|
||||
*/
|
||||
public static function validateWebformComputed(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
// Make sure the form's state value uses the computed value and not the
|
||||
// raw #value. This ensures conditional handlers are triggered using
|
||||
// the accurate computed value.
|
||||
$webform_submission = static::getWebformSubmission($element, $form_state, $complete_form);
|
||||
if ($webform_submission) {
|
||||
$value = static::processValue($element, $webform_submission);
|
||||
$form_state->setValueForElement($element['value'], NULL);
|
||||
$form_state->setValueForElement($element['hidden'], NULL);
|
||||
$form_state->setValueForElement($element, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
// Form/Ajax callbacks.
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* Set computed element's value.
|
||||
*
|
||||
* @param array $element
|
||||
* A computed element.
|
||||
* @param string $value
|
||||
* A computer value.
|
||||
*/
|
||||
protected static function setWebformComputedElementValue(array &$element, $value) {
|
||||
// Hide empty computed element using display:none so that #states API
|
||||
// can still use the empty computed value.
|
||||
if ($value === '' && $element['#hide_empty']) {
|
||||
$element['#wrapper_attributes']['style'] = 'display:none';
|
||||
}
|
||||
else {
|
||||
unset($element['#wrapper_attributes']);
|
||||
}
|
||||
|
||||
// Display markup.
|
||||
$element['value']['#markup'] = $value;
|
||||
$element['value']['#allowed_tags'] = WebformXss::getAdminTagList();
|
||||
|
||||
// Include hidden element so that computed value will be available to
|
||||
// conditions (#states).
|
||||
$element['hidden']['#type'] = 'hidden';
|
||||
$element['hidden']['#value'] = ['#markup' => $value];
|
||||
$element['hidden']['#parents'] = $element['#parents'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the current request is using Ajax.
|
||||
*/
|
||||
protected static function isAjax() {
|
||||
return (\Drupal::request()->get(MainContentViewSubscriber::WRAPPER_FORMAT) === 'drupal_ajax');
|
||||
}
|
||||
|
||||
/**
|
||||
* Webform computed element validate callback.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
*/
|
||||
public static function validateWebformComputedCallback(array $form, FormStateInterface $form_state) {
|
||||
$form_state->clearErrors();
|
||||
}
|
||||
|
||||
/**
|
||||
* Webform computed element submit callback.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
*/
|
||||
public static function submitWebformComputedCallback(array $form, FormStateInterface $form_state) {
|
||||
// Only rebuild if the request is not using Ajax.
|
||||
if (!static::isAjax()) {
|
||||
$form_state->setRebuild();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Webform computed element Ajax callback.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
*
|
||||
* @return array
|
||||
* The computed element element.
|
||||
*/
|
||||
public static function ajaxWebformComputedCallback(array $form, FormStateInterface $form_state) {
|
||||
$button = $form_state->getTriggeringElement();
|
||||
$element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -1));
|
||||
|
||||
// Set element value and #markup after the form has been validated.
|
||||
$webform_submission = static::getWebformSubmission($element, $form_state, $form);
|
||||
$value = static::processValue($element, $webform_submission);
|
||||
static::setWebformComputedElementValue($element, $value);
|
||||
|
||||
// Only return the wrapper id, this prevents the computed element from
|
||||
// being reinitialized via JS after each update.
|
||||
// @see js/webform.element.computed.js
|
||||
$element['#prefix'] = '<div class="js-webform-computed-wrapper" id="' . $element['#wrapper_id'] . '">';
|
||||
$element['#suffix'] = '</div>';
|
||||
|
||||
// Remove flexbox wrapper because it already been render outside this
|
||||
// computed element's ajax wrapper.
|
||||
// @see \Drupal\webform\Plugin\WebformElementBase::prepareWrapper
|
||||
// @see \Drupal\webform\Plugin\WebformElementBase::preRenderFixFlexboxWrapper
|
||||
$preRenderFixFlexWrapper = ['Drupal\webform\Plugin\WebformElement\WebformComputedTwig', 'preRenderFixFlexboxWrapper'];
|
||||
foreach ($element['#pre_render'] as $index => $pre_render) {
|
||||
if (is_array($pre_render) && $pre_render === $preRenderFixFlexWrapper) {
|
||||
unset($element['#pre_render'][$index]);
|
||||
}
|
||||
}
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
// Form/Ajax helpers and callbacks.
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* Get an element's value mode/type.
|
||||
*
|
||||
* @param array $element
|
||||
* The element.
|
||||
*
|
||||
* @return string
|
||||
* The markup type (html or text).
|
||||
*/
|
||||
public static function getMode(array $element) {
|
||||
if (empty($element['#mode']) || $element['#mode'] === static::MODE_AUTO) {
|
||||
return (WebformHtmlHelper::containsHtml($element['#value'])) ? static::MODE_HTML : static::MODE_TEXT;
|
||||
}
|
||||
else {
|
||||
return $element['#mode'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Webform submission for element.
|
||||
*
|
||||
* @param array $element
|
||||
* The element.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
* @param array $complete_form
|
||||
* The complete form structure.
|
||||
*
|
||||
* @return \Drupal\webform\WebformSubmissionInterface|null
|
||||
* A webform submission.
|
||||
*/
|
||||
protected static function getWebformSubmission(array $element, FormStateInterface $form_state, array &$complete_form) {
|
||||
$form_object = $form_state->getFormObject();
|
||||
if ($form_object instanceof WebformSubmissionForm) {
|
||||
/** @var \Drupal\webform\WebformSubmissionInterface $webform_submission */
|
||||
$webform_submission = $form_object->getEntity();
|
||||
|
||||
// We must continually copy validated form values to the
|
||||
// webform submission since a computed element's value can be based on
|
||||
// another computed element's value.
|
||||
//
|
||||
// Therefore, we are creating a single clone of the webform submission
|
||||
// and only copying the submitted form values to the cached submission.
|
||||
if ($form_state->isValidationComplete()) {
|
||||
if (!isset(static::$submissions[$webform_submission->uuid()])) {
|
||||
static::$submissions[$webform_submission->uuid()] = clone $form_object->getEntity();
|
||||
}
|
||||
$webform_submission = static::$submissions[$webform_submission->uuid()];
|
||||
$form_object->copyFormValuesToEntity($webform_submission, $complete_form, $form_state);
|
||||
}
|
||||
|
||||
return $webform_submission;
|
||||
}
|
||||
elseif (isset($element['#webform_submission'])) {
|
||||
if (is_string($element['#webform_submission'])) {
|
||||
return WebformSubmission::load($element['#webform_submission']);
|
||||
}
|
||||
else {
|
||||
return $element['#webform_submission'];
|
||||
}
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\webform\WebformSubmissionInterface;
|
||||
|
||||
/**
|
||||
* Provides an item to display computed webform submission values using tokens.
|
||||
*
|
||||
* @RenderElement("webform_computed_token")
|
||||
*/
|
||||
class WebformComputedToken extends WebformComputedBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function processValue(array $element, WebformSubmissionInterface $webform_submission) {
|
||||
$mode = static::getMode($element);
|
||||
|
||||
/** @var \Drupal\webform\WebformTokenManagerInterface $token_manager */
|
||||
$token_manager = \Drupal::service('webform.token_manager');
|
||||
|
||||
// Replace tokens in value.
|
||||
return $token_manager->replace($element['#value'], $webform_submission, [], ['html' => ($mode == static::MODE_HTML)]);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\webform\Twig\TwigExtension;
|
||||
use Drupal\webform\WebformSubmissionInterface;
|
||||
|
||||
/**
|
||||
* Provides an item to display computed webform submission values using Twig.
|
||||
*
|
||||
* @RenderElement("webform_computed_twig")
|
||||
*/
|
||||
class WebformComputedTwig extends WebformComputedBase {
|
||||
|
||||
/**
|
||||
* Whitespace spaceless.
|
||||
*
|
||||
* Remove whitespace around the computed value and between HTML tags.
|
||||
*/
|
||||
const WHITESPACE_SPACELESS = 'spaceless';
|
||||
|
||||
/**
|
||||
* Whitespace trim.
|
||||
*
|
||||
* Remove whitespace around the computed value.
|
||||
*/
|
||||
const WHITESPACE_TRIM = 'trim';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
return parent::getInfo() + [
|
||||
'#whitespace' => '',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function processValue(array $element, WebformSubmissionInterface $webform_submission) {
|
||||
$whitespace = (!empty($element['#whitespace'])) ? $element['#whitespace'] : '';
|
||||
|
||||
$template = ($whitespace === static::WHITESPACE_SPACELESS) ? '{% spaceless %}' . $element['#value'] . '{% endspaceless %}' : $element['#value'];
|
||||
|
||||
$options = ['html' => (static::getMode($element) === static::MODE_HTML)];
|
||||
|
||||
$value = TwigExtension::renderTwigTemplate($webform_submission, $template, $options);
|
||||
|
||||
return ($whitespace === static::WHITESPACE_TRIM) ? trim($value) : $value;
|
||||
}
|
||||
|
||||
}
|
|
@ -7,12 +7,19 @@ namespace Drupal\webform\Element;
|
|||
*
|
||||
* @FormElement("webform_contact")
|
||||
*/
|
||||
class WebformContact extends WebformAddress {
|
||||
class WebformContact extends WebformCompositeBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getCompositeElements() {
|
||||
public function getInfo() {
|
||||
return parent::getInfo() + ['#theme' => 'webform_composite_contact'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getCompositeElements(array $element) {
|
||||
$elements = [];
|
||||
$elements['name'] = [
|
||||
'#type' => 'textfield',
|
||||
|
@ -30,7 +37,32 @@ class WebformContact extends WebformAddress {
|
|||
'#type' => 'tel',
|
||||
'#title' => t('Phone'),
|
||||
];
|
||||
$elements += parent::getCompositeElements();
|
||||
$elements['address'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Address'),
|
||||
];
|
||||
$elements['address_2'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Address 2'),
|
||||
];
|
||||
$elements['city'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('City/Town'),
|
||||
];
|
||||
$elements['state_province'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => t('State/Province'),
|
||||
'#options' => 'state_province_names',
|
||||
];
|
||||
$elements['postal_code'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('ZIP/Postal Code'),
|
||||
];
|
||||
$elements['country'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => t('Country'),
|
||||
'#options' => 'country_names',
|
||||
];
|
||||
return $elements;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
/**
|
||||
* Provides a webform element for a credit card element.
|
||||
*
|
||||
* @FormElement("webform_creditcard")
|
||||
*/
|
||||
class WebformCreditCard extends WebformCompositeBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getCompositeElements() {
|
||||
$month_options = range(1, 12);
|
||||
$year_options = range(date('Y'), date('Y') + 10);
|
||||
|
||||
$elements = [];
|
||||
$elements['warning'] = [
|
||||
'#type' => 'webform_message',
|
||||
'#message_type' => 'warning',
|
||||
'#message_message' => t('The credit card element is experimental and insecure because it stores submitted information as plain text.'),
|
||||
];
|
||||
$elements['name'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => t("Name on Card"),
|
||||
];
|
||||
$elements['type'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => t('Type of Card'),
|
||||
'#options' => 'creditcard_codes',
|
||||
];
|
||||
$elements['number'] = [
|
||||
'#type' => 'webform_creditcard_number',
|
||||
'#title' => t('Card Number'),
|
||||
];
|
||||
$elements['civ'] = [
|
||||
'#type' => 'number',
|
||||
'#title' => t('CIV Number'),
|
||||
'#min' => 1,
|
||||
'#size' => 4,
|
||||
'#maxlength' => 4,
|
||||
'#test' => [111, 222, 333],
|
||||
];
|
||||
$elements['expiration'] = [
|
||||
'#type' => 'label',
|
||||
'#title' => t('Expiration Date'),
|
||||
];
|
||||
$elements['expiration_month'] = [
|
||||
'#title' => t('Expiration Month'),
|
||||
'#title_display' => 'invisible',
|
||||
'#type' => 'select',
|
||||
'#options' => array_combine($month_options, $month_options),
|
||||
'#prefix' => '<div class="container-inline clearfix">',
|
||||
];
|
||||
$elements['expiration_year'] = [
|
||||
'#title' => t('Expiration Year'),
|
||||
'#title_display' => 'invisible',
|
||||
'#type' => 'select',
|
||||
'#options' => array_combine($year_options, $year_options),
|
||||
'#suffix' => '</div>',
|
||||
];
|
||||
|
||||
return $elements;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,122 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\Core\Render\Element\FormElement;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Provides a webform element for entering a credit card number.
|
||||
*
|
||||
* @FormElement("webform_creditcard_number")
|
||||
*/
|
||||
class WebformCreditCardNumber extends FormElement {
|
||||
|
||||
/**
|
||||
* Defines the max length for an credit card number.
|
||||
*/
|
||||
const CREDITCARD_MAX_LENGTH = 16;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
$class = get_class($this);
|
||||
return [
|
||||
'#input' => TRUE,
|
||||
'#size' => self::CREDITCARD_MAX_LENGTH,
|
||||
'#maxlength' => self::CREDITCARD_MAX_LENGTH,
|
||||
'#autocomplete_route_name' => FALSE,
|
||||
'#process' => [
|
||||
[$class, 'processAutocomplete'],
|
||||
[$class, 'processAjaxForm'],
|
||||
[$class, 'processPattern'],
|
||||
],
|
||||
'#element_validate' => [
|
||||
[$class, 'validateWebformCreditCardNumber'],
|
||||
],
|
||||
'#pre_render' => [
|
||||
[$class, 'preRenderWebformCreditCardNumber'],
|
||||
],
|
||||
'#theme' => 'input__creditcard_number',
|
||||
'#theme_wrappers' => ['form_element'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Webform element validation handler for #type 'creditcard_number'.
|
||||
*/
|
||||
public static function validateWebformCreditCardNumber(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
$value = trim($element['#value']);
|
||||
$form_state->setValueForElement($element, $value);
|
||||
|
||||
if ($value !== '' && !self::validCreditCardNumber($value)) {
|
||||
$form_state->setError($element, t('The credit card number is not valid.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation rule for credit card number.
|
||||
*
|
||||
* Luhn algorithm number checker - (c) 2005-2008 shaman - www.planzero.org
|
||||
* This code has been released into the public domain, however please
|
||||
* give credit to the original author where possible.
|
||||
*
|
||||
* @param string $number
|
||||
* A credit card number.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE is credit card number is valid.
|
||||
*
|
||||
* @see: http://stackoverflow.com/questions/174730/what-is-the-best-way-to-validate-a-credit-card-in-php
|
||||
*/
|
||||
public static function validCreditCardNumber($number) {
|
||||
// If number is not 15 or 16 digits return FALSE.
|
||||
if (!preg_match('/^\d{15,16}$/', $number)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Set the string length and parity.
|
||||
$number_length = strlen($number);
|
||||
$parity = $number_length % 2;
|
||||
|
||||
// Loop through each digit and do the maths.
|
||||
$total = 0;
|
||||
for ($i = 0; $i < $number_length; $i++) {
|
||||
$digit = $number[$i];
|
||||
// Multiply alternate digits by two.
|
||||
if ($i % 2 == $parity) {
|
||||
$digit *= 2;
|
||||
// If the sum is two digits, add them together (in effect).
|
||||
if ($digit > 9) {
|
||||
$digit -= 9;
|
||||
}
|
||||
}
|
||||
// Total up the digits.
|
||||
$total += $digit;
|
||||
}
|
||||
|
||||
// If the total mod 10 equals 0, the number is valid.
|
||||
return ($total % 10 == 0) ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a #type 'creditcard_number' render element for theme_element().
|
||||
*
|
||||
* @param array $element
|
||||
* An associative array containing the properties of the element.
|
||||
* Properties used: #title, #value, #description, #size, #maxlength,
|
||||
* #placeholder, #required, #attributes.
|
||||
*
|
||||
* @return array
|
||||
* The $element with prepared variables ready for theme_element().
|
||||
*/
|
||||
public static function preRenderWebformCreditCardNumber(array $element) {
|
||||
$element['#attributes']['type'] = 'text';
|
||||
Element::setAttributes($element, ['id', 'name', 'value', 'size', 'maxlength', 'placeholder']);
|
||||
static::setAttributes($element, ['form-textfield', 'form-creditcard-number']);
|
||||
return $element;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
/**
|
||||
* Provides a webform custom composite element.
|
||||
*
|
||||
* @FormElement("webform_custom_composite")
|
||||
*/
|
||||
class WebformCustomComposite extends WebformMultiple {}
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\Element\FormElement;
|
||||
use Drupal\Core\Serialization\Yaml;
|
||||
use Drupal\webform\Utility\WebformElementHelper;
|
||||
use Drupal\webform\Utility\WebformYaml;
|
||||
|
||||
/**
|
||||
|
@ -25,6 +25,9 @@ class WebformElementAttributes extends FormElement {
|
|||
'#process' => [
|
||||
[$class, 'processWebformElementAttributes'],
|
||||
],
|
||||
'#pre_render' => [
|
||||
[$class, 'preRenderWebformElementAttributes'],
|
||||
],
|
||||
'#theme_wrappers' => ['container'],
|
||||
'#classes' => '',
|
||||
];
|
||||
|
@ -57,7 +60,7 @@ class WebformElementAttributes extends FormElement {
|
|||
|
||||
$t_args = [
|
||||
'@title' => $element['#title'],
|
||||
'@type' => Unicode::strtolower($type),
|
||||
'@type' => mb_strtolower($type),
|
||||
];
|
||||
|
||||
// Class.
|
||||
|
@ -67,32 +70,20 @@ class WebformElementAttributes extends FormElement {
|
|||
$element['class'] = [
|
||||
'#type' => 'webform_select_other',
|
||||
'#title' => t('@title CSS classes', $t_args),
|
||||
'#description' => t("Apply classes to the @type. Select 'custom...' to enter custom classes.", $t_args),
|
||||
'#description' => t("Apply classes to the @type. Select 'custom…' to enter custom classes.", $t_args),
|
||||
'#multiple' => TRUE,
|
||||
'#options' => [WebformSelectOther::OTHER_OPTION => t('custom...')] + array_combine($classes, $classes),
|
||||
'#options' => [WebformSelectOther::OTHER_OPTION => t('custom…')] + array_combine($classes, $classes),
|
||||
'#other__placeholder' => t('Enter custom classes…'),
|
||||
'#other__option_delimiter' => ' ',
|
||||
'#attributes' => [
|
||||
'class' => [
|
||||
'js-webform-select2',
|
||||
'webform-select2',
|
||||
'js-' . $element['#id'] . '-attributes-style',
|
||||
],
|
||||
],
|
||||
'#attached' => ['library' => ['webform/webform.element.select2']],
|
||||
'#select2' => TRUE,
|
||||
'#default_value' => $element['#default_value']['class'],
|
||||
];
|
||||
|
||||
// ISSUE:
|
||||
// Nested element with #element_validate callback that alter an
|
||||
// element's value can break the returned value.
|
||||
//
|
||||
// WORKAROUND:
|
||||
// Manually process the 'webform_select_other' element.
|
||||
WebformSelectOther::valueCallback($element['class'], FALSE, $form_state);
|
||||
WebformSelectOther::processWebformOther($element['class'], $form_state, $complete_form);
|
||||
|
||||
$element['class']['#type'] = 'item';
|
||||
unset($element['class']['#element_validate']);
|
||||
WebformElementHelper::process($element['class']);
|
||||
}
|
||||
else {
|
||||
$element['class'] = [
|
||||
|
@ -106,7 +97,7 @@ class WebformElementAttributes extends FormElement {
|
|||
// Custom options.
|
||||
$element['custom'] = [
|
||||
'#type' => 'texfield',
|
||||
'#placeholder' => t('Enter custom classes...'),
|
||||
'#placeholder' => t('Enter custom classes…'),
|
||||
'#states' => [
|
||||
'visible' => [
|
||||
'select.js-' . $element['#id'] . '-attributes-style' => ['value' => '_custom_'],
|
||||
|
@ -133,7 +124,7 @@ class WebformElementAttributes extends FormElement {
|
|||
'#title' => t('@title custom attributes (YAML)', $t_args),
|
||||
'#description' => t('Enter additional attributes to be added the @type.', $t_args),
|
||||
'#attributes__access' => (!\Drupal::moduleHandler()->moduleExists('webform_ui') || \Drupal::currentUser()->hasPermission('edit webform source')),
|
||||
'#default_value' => WebformYaml::tidy(Yaml::encode($attributes)),
|
||||
'#default_value' => WebformYaml::encode($attributes),
|
||||
];
|
||||
|
||||
// Apply custom properties. Typically used for descriptions.
|
||||
|
@ -144,13 +135,9 @@ class WebformElementAttributes extends FormElement {
|
|||
}
|
||||
}
|
||||
|
||||
// Set validation.
|
||||
if (isset($element['#element_validate'])) {
|
||||
$element['#element_validate'] = array_merge([[get_called_class(), 'validateWebformElementAttributes']], $element['#element_validate']);
|
||||
}
|
||||
else {
|
||||
$element['#element_validate'] = [[get_called_class(), 'validateWebformElementAttributes']];
|
||||
}
|
||||
// Add validate callback.
|
||||
$element += ['#element_validate' => []];
|
||||
array_unshift($element['#element_validate'], [get_called_class(), 'validateWebformElementAttributes']);
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
@ -191,7 +178,23 @@ class WebformElementAttributes extends FormElement {
|
|||
$form_state->setValueForElement($element['class'], NULL);
|
||||
$form_state->setValueForElement($element['style'], NULL);
|
||||
$form_state->setValueForElement($element['attributes'], NULL);
|
||||
|
||||
$element['#value'] = $attributes;
|
||||
$form_state->setValueForElement($element, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a #type 'webform_element_attributes' render element.
|
||||
*
|
||||
* @param array $element
|
||||
* An associative array containing the properties of the element.
|
||||
*
|
||||
* @return array
|
||||
* The $element.
|
||||
*/
|
||||
public static function preRenderWebformElementAttributes($element) {
|
||||
static::setAttributes($element, ['webform-element-attributes']);
|
||||
return $element;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,364 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\Element\FormElement;
|
||||
use Drupal\Core\Serialization\Yaml;
|
||||
use Drupal\webform\Plugin\WebformElement\WebformCompositeBase as WebformCompositeBaseElement;
|
||||
use Drupal\webform\Utility\WebformArrayHelper;
|
||||
use Drupal\webform\Utility\WebformYaml;
|
||||
|
||||
/**
|
||||
* Provides a element for the composite elements.
|
||||
*
|
||||
* @FormElement("webform_element_composite")
|
||||
*/
|
||||
class WebformElementComposite extends FormElement {
|
||||
|
||||
/**
|
||||
* List of supported element properties.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $supportedProperties = [
|
||||
'key' => 'key',
|
||||
'type' => 'type',
|
||||
'title' => 'title',
|
||||
'help' => 'help',
|
||||
'placeholder' => 'placeholder',
|
||||
'description' => 'description',
|
||||
'options' => 'options',
|
||||
'required' => 'required',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
$class = get_class($this);
|
||||
return [
|
||||
'#input' => TRUE,
|
||||
'#process' => [
|
||||
[$class, 'processWebformElementComposite'],
|
||||
[$class, 'processAjaxForm'],
|
||||
],
|
||||
'#theme_wrappers' => ['form_element'],
|
||||
// Add '#markup' property to add an 'id' attribute to the form element.
|
||||
// @see template_preprocess_form_element()
|
||||
'#markup' => '',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
|
||||
if ($input === FALSE) {
|
||||
if (!isset($element['#default_value']) || !is_array($element['#default_value'])) {
|
||||
return [];
|
||||
}
|
||||
else {
|
||||
$default_value = [];
|
||||
foreach ($element['#default_value'] as $composite_key => $composite_element) {
|
||||
$composite_element = ['key' => $composite_key] + WebformArrayHelper::removePrefix($composite_element);
|
||||
// Get supported properties.
|
||||
$composite_properties = array_intersect_key($composite_element, static::$supportedProperties);
|
||||
// Move 'unsupported' properties to 'custom'.
|
||||
$custom_properties = array_diff_key($composite_element, static::$supportedProperties);
|
||||
$composite_properties['custom'] = $custom_properties ? WebformYaml::encode($custom_properties) : '';
|
||||
$default_value[] = $composite_properties;
|
||||
}
|
||||
$element['#default_value'] = $default_value;
|
||||
return $default_value;
|
||||
}
|
||||
}
|
||||
elseif (is_array($input)) {
|
||||
return $input;
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a webform element composite (builder) element.
|
||||
*/
|
||||
public static function processWebformElementComposite(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
/** @var \Drupal\webform\Plugin\WebformElementManagerInterface $element_manager */
|
||||
$element_manager = \Drupal::service('plugin.manager.webform.element');
|
||||
|
||||
$placeholder_elements = [];
|
||||
$options_elements = [];
|
||||
$type_options = [];
|
||||
|
||||
$elements = $element_manager->getInstances();
|
||||
|
||||
$definitions = $element_manager->getDefinitions();
|
||||
$definitions = $element_manager->getSortedDefinitions($definitions, 'category');
|
||||
$definitions = $element_manager->removeExcludeDefinitions($definitions);
|
||||
$grouped_definitions = $element_manager->getGroupedDefinitions($definitions);
|
||||
|
||||
foreach ($grouped_definitions as $group => $definitions) {
|
||||
foreach ($definitions as $element_type => $definition) {
|
||||
if (!WebformCompositeBaseElement::isSupportedElementType($element_type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$element_plugin = $elements[$element_type];
|
||||
|
||||
$type_options[$group][$element_type] = $definition['label'];
|
||||
if ($element_plugin->hasProperty('options')) {
|
||||
$options_elements[$element_type] = $element_type;
|
||||
}
|
||||
if ($element_plugin->hasProperty('placeholder')) {
|
||||
$placeholder_elements[$element_type] = $element_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$edit_source = \Drupal::currentUser()->hasPermission('edit webform source');
|
||||
$element['#tree'] = TRUE;
|
||||
$element['elements'] = [
|
||||
'#type' => 'webform_multiple',
|
||||
'#title' => t('Elements'),
|
||||
'#title_display' => t('Invisible'),
|
||||
'#label' => t('element'),
|
||||
'#labels' => t('elements'),
|
||||
'#empty_items' => 0,
|
||||
'#min_items' => 1,
|
||||
'#header' => TRUE,
|
||||
'#add' => FALSE,
|
||||
'#default_value' => (isset($element['#default_value'])) ? $element['#default_value'] : NULL,
|
||||
'#error_no_message' => TRUE,
|
||||
'#element' => [
|
||||
'settings' => [
|
||||
'#type' => 'container',
|
||||
'#title' => t('Settings'),
|
||||
'#help' => '<b>' . t('Key') . ':</b> ' . t('A unique machine-readable name. Can only contain lowercase letters, numbers, and underscores.') .
|
||||
'<hr/>' . '<b>' . t('Type') . ':</b> ' . t('The type of element to be displayed.') .
|
||||
'<hr/>' . '<b>' . t('Options') . ':</b> ' . t('Please select predefined options or enter custom options.') . ' ' . t('Key-value pairs MUST be specified as "safe_key: \'Some readable options\'". Use of only alphanumeric characters and underscores is recommended in keys. One option per line.') .
|
||||
($edit_source ? '<hr/>' . '<b>' . t('Custom Properties') . ':</b> ' . t('Properties do not have to be prepended with a hash (#) character, the hash character will be automatically added to the custom properties.') : '') .
|
||||
'<hr/>' . '<b>' . t('Required') . ':</b> ' . t('Check this option if the user must enter a value.'),
|
||||
'key' => [
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Key'),
|
||||
'#title_display' => 'invisible',
|
||||
'#placeholder' => t('Enter key…'),
|
||||
'#pattern' => '^[a-z0-9_]+$',
|
||||
'#attributes' => [
|
||||
'title' => t('Enter a unique machine-readable name. Can only contain lowercase letters, numbers, and underscores.'),
|
||||
],
|
||||
'#required' => TRUE,
|
||||
'#error_no_message' => TRUE,
|
||||
],
|
||||
'type' => [
|
||||
'#type' => 'select',
|
||||
'#title' => t('Type'),
|
||||
'#title_display' => 'invisible',
|
||||
'#description' => t('The type of element to be displayed.'),
|
||||
'#description_display' => 'invisible',
|
||||
'#options' => $type_options,
|
||||
'#empty_option' => t('- Select type -'),
|
||||
'#required' => TRUE,
|
||||
'#attributes' => ['class' => ['js-webform-composite-type']],
|
||||
'#error_no_message' => TRUE,
|
||||
],
|
||||
'options' => [
|
||||
'#type' => 'webform_element_options',
|
||||
'#yaml' => TRUE,
|
||||
'#title' => t('Options'),
|
||||
'#title_display' => 'invisible',
|
||||
'#description' => t('Please select predefined options or enter custom options.') . ' ' . t('Key-value pairs MUST be specified as "safe_key: \'Some readable options\'". Use of only alphanumeric characters and underscores is recommended in keys. One option per line.'),
|
||||
'#description_display' => 'invisible',
|
||||
'#wrapper_attributes' => [
|
||||
'data-composite-types' => implode(',', $options_elements),
|
||||
'data-composite-required' => 'data-composite-required',
|
||||
],
|
||||
'#error_no_message' => TRUE,
|
||||
],
|
||||
// ISSUE:
|
||||
// Set #access: FALSE is losing the custom properties.
|
||||
//
|
||||
// WORKAROUND:
|
||||
// Use 'hidden' element.
|
||||
// @see \Drupal\webform\Element\WebformMultiple::buildElementRow
|
||||
'custom' => $edit_source ? [
|
||||
'#type' => 'webform_codemirror',
|
||||
'#mode' => 'yaml',
|
||||
'#title' => t('Custom properties'),
|
||||
'#title_display' => 'invisible',
|
||||
'#description' => t('Properties do not have to be prepended with a hash (#) character, the hash character will be automatically added to the custom properties.'),
|
||||
'#description_display' => 'invisible',
|
||||
'#placeholder' => t('Enter custom properties…'),
|
||||
'#error_no_message' => TRUE,
|
||||
] : [
|
||||
'#type' => 'hidden',
|
||||
],
|
||||
// Note: Setting #return_value: TRUE is not returning any value.
|
||||
'required' => [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Required'),
|
||||
'#description' => t('Check this option if the user must enter a value.'),
|
||||
'#description_display' => 'invisible',
|
||||
'#error_no_message' => TRUE,
|
||||
],
|
||||
],
|
||||
'labels' => [
|
||||
'#type' => 'container',
|
||||
'#title' => t('Labels'),
|
||||
'#help' => '<b>' . t('Title') . ':</b> ' . t('This is used as a descriptive label when displaying this webform element.') .
|
||||
'<hr/><b>' . t('Placeholder') . ':</b> ' . t('The placeholder will be shown in the element until the user starts entering a value.') .
|
||||
'<hr/><b>' . t('Description') . ':</b> ' . t('A short description of the element used as help for the user when he/she uses the webform.') .
|
||||
'<hr/><b>' . t('Help text') . ':</b> ' . t('A tooltip displayed after the title.'),
|
||||
'title' => [
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Title'),
|
||||
'#title_display' => 'invisible',
|
||||
'#description' => t('This is used as a descriptive label when displaying this webform element.'),
|
||||
'#description_display' => 'invisible',
|
||||
'#placeholder' => t('Enter title…'),
|
||||
'#required' => TRUE,
|
||||
'#error_no_message' => TRUE,
|
||||
],
|
||||
'placeholder' => [
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Placeholder'),
|
||||
'#title_display' => 'invisible',
|
||||
'#description' => t('The placeholder will be shown in the element until the user starts entering a value.'),
|
||||
'#description_display' => 'invisible',
|
||||
'#placeholder' => t('Enter placeholder…'),
|
||||
'#attributes' => ['data-composite-types' => implode(',', $placeholder_elements)],
|
||||
'#error_no_message' => TRUE,
|
||||
],
|
||||
'description' => [
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('Description'),
|
||||
'#description' => t('A short description of the element used as help for the user when he/she uses the webform.'),
|
||||
'#description_display' => 'invisible',
|
||||
'#title_display' => 'invisible',
|
||||
'#placeholder' => t('Enter description…'),
|
||||
'#rows' => 2,
|
||||
'#error_no_message' => TRUE,
|
||||
],
|
||||
'help' => [
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('Help text'),
|
||||
'#title_display' => 'invisible',
|
||||
'#description' => t('A tooltip displayed after the title.'),
|
||||
'#description_display' => 'invisible',
|
||||
'#placeholder' => t('Enter help text…'),
|
||||
'#rows' => 2,
|
||||
'#error_no_message' => TRUE,
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$element['#attached']['library'][] = 'webform/webform.element.composite';
|
||||
|
||||
// Add validate callback.
|
||||
$element += ['#element_validate' => []];
|
||||
array_unshift($element['#element_validate'], [get_called_class(), 'validateWebformElementComposite']);
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a webform element composite (builder) element.
|
||||
*/
|
||||
public static function validateWebformElementComposite(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
/** @var \Drupal\webform\Plugin\WebformElementManagerInterface $element_manager */
|
||||
$element_manager = \Drupal::service('plugin.manager.webform.element');
|
||||
$elements_value = NestedArray::getValue($form_state->getValues(), $element['elements']['#parents']);
|
||||
|
||||
// Check for duplicate keys.
|
||||
$keys = [];
|
||||
foreach ($elements_value as $element_value) {
|
||||
$key = $element_value['key'];
|
||||
if (isset($keys[$key])) {
|
||||
$form_state->setError($element, t('Duplicate key found. The %key key must only be assigned on one element.', ['%key' => $key]));
|
||||
return;
|
||||
}
|
||||
$keys[$key] = $key;
|
||||
}
|
||||
|
||||
// Convert the $elements value which is a simple associative array into a
|
||||
// render array.
|
||||
$elements = [];
|
||||
foreach ($elements_value as $element_value) {
|
||||
$key = $element_value['key'];
|
||||
unset($element_value['key']);
|
||||
|
||||
// Remove empty strings from array.
|
||||
$element_value = array_filter($element_value, function ($value) {
|
||||
return ($value !== '');
|
||||
});
|
||||
|
||||
// Unset empty required or case to boolean.
|
||||
if (empty($element_value['required'])) {
|
||||
unset($element_value['required']);
|
||||
}
|
||||
else {
|
||||
$element_value['required'] = TRUE;
|
||||
}
|
||||
|
||||
// Limit value keys to supported element properties.
|
||||
// This removes options from elements that don't support #options.
|
||||
if (isset($element_value['type'])) {
|
||||
foreach ($element_value as $property_name => $property_value) {
|
||||
if (!in_array($property_name, ['type', 'custom'])) {
|
||||
/** @var \Drupal\webform\Plugin\WebformElementInterface $element_plugin */
|
||||
$element_plugin = $element_manager->createInstance($element_value['type']);
|
||||
if (!$element_plugin->hasProperty($property_name)) {
|
||||
unset($element_value[$property_name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($element_value['custom'])) {
|
||||
if ($element_value['custom']) {
|
||||
$custom = Yaml::decode($element_value['custom']);
|
||||
if ($custom && is_array($custom)) {
|
||||
$element_value += $custom;
|
||||
}
|
||||
}
|
||||
unset($element_value['custom']);
|
||||
}
|
||||
|
||||
$elements[$key] = WebformArrayHelper::addPrefix($element_value);
|
||||
}
|
||||
|
||||
foreach ($elements as $composite_element_key => $composite_element) {
|
||||
if (!isset($composite_element['#type'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var \Drupal\webform\Plugin\WebformElementInterface $element_plugin */
|
||||
$element_plugin = $element_manager->getElementInstance($composite_element);
|
||||
|
||||
$t_args = [
|
||||
'%title' => (isset($composite_element['#title'])) ? $composite_element['#title'] : $composite_element_key,
|
||||
'@type' => $composite_element['#type'],
|
||||
];
|
||||
|
||||
// Make sure #options is set for composite element's that require #options.
|
||||
if ($element_plugin->hasProperty('options') && empty($composite_element['#options'])) {
|
||||
$form_state->setError($element, t('Options for %title is required.', $t_args));
|
||||
}
|
||||
|
||||
// Make sure element is not storing multiple values.
|
||||
if ($element_plugin->hasMultipleValues($composite_element)) {
|
||||
$form_state->setError($element, t('Multiple value is not supported for %title (@type).', $t_args));
|
||||
}
|
||||
}
|
||||
|
||||
$form_state->setValueForElement($element['elements'], NULL);
|
||||
|
||||
$element['#value'] = $elements;
|
||||
$form_state->setValueForElement($element, $elements);
|
||||
}
|
||||
|
||||
}
|
|
@ -58,12 +58,12 @@ class WebformElementMultiple extends FormElement {
|
|||
$cardinality = $element['#value'];
|
||||
|
||||
$element['#tree'] = TRUE;
|
||||
|
||||
|
||||
$element['container'] = [
|
||||
'#type' => 'container',
|
||||
'#attributes' => ['class' => [
|
||||
'container-inline',
|
||||
]],
|
||||
'#attributes' => [
|
||||
'class' => ['container-inline'],
|
||||
],
|
||||
];
|
||||
|
||||
$element['container']['cardinality'] = [
|
||||
|
@ -90,19 +90,19 @@ class WebformElementMultiple extends FormElement {
|
|||
],
|
||||
];
|
||||
|
||||
// Set disabled
|
||||
// Set disabled.
|
||||
if (!empty($element['#disabled'])) {
|
||||
$element['container']['cardinality']['#disabled'] = TRUE;
|
||||
$element['container']['cardinality_number']['#disabled'] = TRUE;
|
||||
}
|
||||
|
||||
// Set validation.
|
||||
if (isset($element['#element_validate'])) {
|
||||
$element['#element_validate'] = array_merge([[get_called_class(), 'validateWebformElementMultiple']], $element['#element_validate']);
|
||||
}
|
||||
else {
|
||||
$element['#element_validate'] = [[get_called_class(), 'validateWebformElementMultiple']];
|
||||
}
|
||||
// Add validate callback.
|
||||
$element += ['#element_validate' => []];
|
||||
array_unshift($element['#element_validate'], [get_called_class(), 'validateWebformElementMultiple']);
|
||||
|
||||
// Set #type to item to apply #states.
|
||||
// @see drupal_process_states
|
||||
$element['#type'] = 'item';
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
@ -131,6 +131,8 @@ class WebformElementMultiple extends FormElement {
|
|||
|
||||
$form_state->setValueForElement($element['container']['cardinality'], NULL);
|
||||
$form_state->setValueForElement($element['container']['cardinality_number'], NULL);
|
||||
|
||||
$element['#value'] = $multiple;
|
||||
$form_state->setValueForElement($element, $multiple);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,11 +8,14 @@ use Drupal\Core\Form\FormStateInterface;
|
|||
use Drupal\Core\Render\Element\FormElement;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\webform\Entity\WebformOptions as WebformOptionsEntity;
|
||||
use Drupal\webform\Utility\WebformElementHelper;
|
||||
use Drupal\webform\Utility\WebformOptionsHelper;
|
||||
|
||||
/**
|
||||
* Provides a webform element for managing webform element options.
|
||||
* Provides a form element for managing webform element options.
|
||||
*
|
||||
* This element is used by select, radios, checkboxes, and likert elements.
|
||||
* This element is used by select, radios, checkboxes, likert, and
|
||||
* mapping elements.
|
||||
*
|
||||
* @FormElement("webform_element_options")
|
||||
*/
|
||||
|
@ -27,6 +30,7 @@ class WebformElementOptions extends FormElement {
|
|||
$class = get_class($this);
|
||||
return [
|
||||
'#input' => TRUE,
|
||||
'#yaml' => FALSE,
|
||||
'#likert' => FALSE,
|
||||
'#process' => [
|
||||
[$class, 'processWebformElementOptions'],
|
||||
|
@ -34,6 +38,7 @@ class WebformElementOptions extends FormElement {
|
|||
],
|
||||
'#theme_wrappers' => ['form_element'],
|
||||
'#custom__type' => 'webform_options',
|
||||
'#options_description' => FALSE,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -71,36 +76,31 @@ class WebformElementOptions extends FormElement {
|
|||
public static function processWebformElementOptions(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
$element['#tree'] = TRUE;
|
||||
|
||||
// Predefined options.
|
||||
// @see (/admin/structure/webform/settings/options/manage)
|
||||
$options = [];
|
||||
$webform_options = WebformOptionsEntity::loadMultiple();
|
||||
foreach ($webform_options as $id => $webform_option) {
|
||||
// Filter likert options for answers to the likert element.
|
||||
if ($element['#likert'] && strpos($id, 'likert_') !== 0) {
|
||||
continue;
|
||||
}
|
||||
$options[$id] = $webform_option->label();
|
||||
}
|
||||
asort($options);
|
||||
/** @var \Drupal\webform\WebformOptionsStorageInterface $webform_options_storage */
|
||||
$webform_options_storage = \Drupal::entityTypeManager()->getStorage('webform_options');
|
||||
$options = ($element['#likert']) ? $webform_options_storage->getLikerts() : $webform_options_storage->getOptions();
|
||||
|
||||
$t_args = [
|
||||
'@type' => ($element['#likert']) ? t('answers') : t('options'),
|
||||
':href' => Url::fromRoute('entity.webform_options.collection')->toString(),
|
||||
];
|
||||
|
||||
$has_options = (count($options)) ? TRUE : FALSE;
|
||||
|
||||
// Select options.
|
||||
$element['options'] = [
|
||||
'#type' => 'select',
|
||||
'#description' => t('Please select <a href=":href">predefined @type</a> or enter custom @type.', $t_args),
|
||||
'#options' => [
|
||||
self::CUSTOM_OPTION => t('Custom...'),
|
||||
self::CUSTOM_OPTION => t('Custom @type…', $t_args),
|
||||
] + $options,
|
||||
|
||||
'#attributes' => [
|
||||
'class' => ['js-' . $element['#id'] . '-options'],
|
||||
],
|
||||
'#error_no_message' => TRUE,
|
||||
'#default_value' => (isset($element['#default_value']) && !is_array($element['#default_value'])) ? $element['#default_value'] : '',
|
||||
'#access' => $has_options,
|
||||
'#default_value' => (isset($element['#default_value']) && !is_array($element['#default_value']) && WebformOptionsHelper::hasOption($element['#default_value'], $options)) ? $element['#default_value'] : '',
|
||||
];
|
||||
|
||||
// Custom options.
|
||||
|
@ -109,11 +109,6 @@ class WebformElementOptions extends FormElement {
|
|||
'#type' => 'webform_multiple',
|
||||
'#title' => $element['#title'],
|
||||
'#title_display' => 'invisible',
|
||||
'#states' => [
|
||||
'visible' => [
|
||||
'select.js-' . $element['#id'] . '-options' => ['value' => ''],
|
||||
],
|
||||
],
|
||||
'#error_no_message' => TRUE,
|
||||
'#default_value' => (isset($element['#default_value']) && !is_string($element['#default_value'])) ? $element['#default_value'] : [],
|
||||
];
|
||||
|
@ -121,21 +116,32 @@ class WebformElementOptions extends FormElement {
|
|||
else {
|
||||
$element['custom'] = [
|
||||
'#type' => 'webform_options',
|
||||
'#yaml' => $element['#yaml'],
|
||||
'#title' => $element['#title'],
|
||||
'#title_display' => 'invisible',
|
||||
'#label' => ($element['#likert']) ? t('answer') : t('option'),
|
||||
'#labels' => ($element['#likert']) ? t('answers') : t('options'),
|
||||
'#states' => [
|
||||
'visible' => [
|
||||
'select.js-' . $element['#id'] . '-options' => ['value' => ''],
|
||||
],
|
||||
],
|
||||
'#error_no_message' => TRUE,
|
||||
'#options_description' => $element['#options_description'],
|
||||
'#default_value' => (isset($element['#default_value']) && !is_string($element['#default_value'])) ? $element['#default_value'] : [],
|
||||
];
|
||||
}
|
||||
// If there are options set #states.
|
||||
if ($has_options) {
|
||||
$element['custom']['#states'] = [
|
||||
'visible' => [
|
||||
'select.js-' . $element['#id'] . '-options' => ['value' => self::CUSTOM_OPTION],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$element['#element_validate'] = [[get_called_class(), 'validateWebformElementOptions']];
|
||||
// Add validate callback.
|
||||
$element += ['#element_validate' => []];
|
||||
array_unshift($element['#element_validate'], [get_called_class(), 'validateWebformElementOptions']);
|
||||
|
||||
if (!empty($element['#states'])) {
|
||||
webform_process_states($element, '#wrapper_attributes');
|
||||
}
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
@ -160,19 +166,13 @@ class WebformElementOptions extends FormElement {
|
|||
|
||||
$has_access = (!isset($element['#access']) || $element['#access'] === TRUE);
|
||||
if ($element['#required'] && empty($value) && $has_access) {
|
||||
if (isset($element['#required_error'])) {
|
||||
$form_state->setError($element, $element['#required_error']);
|
||||
}
|
||||
elseif (isset($element['#title'])) {
|
||||
$form_state->setError($element, t('@name field is required.', ['@name' => $element['#title']]));
|
||||
}
|
||||
else {
|
||||
$form_state->setError($element);
|
||||
}
|
||||
WebformElementHelper::setRequiredError($element, $form_state);
|
||||
}
|
||||
|
||||
$form_state->setValueForElement($element['options'], NULL);
|
||||
$form_state->setValueForElement($element['custom'], NULL);
|
||||
|
||||
$element['#value'] = $value;
|
||||
$form_state->setValueForElement($element, $value);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,11 +2,13 @@
|
|||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Core\Form\OptGroup;
|
||||
use Drupal\Core\Serialization\Yaml;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Render\Element\FormElement;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\webform\Utility\WebformAccessibilityHelper;
|
||||
use Drupal\webform\Utility\WebformArrayHelper;
|
||||
use Drupal\webform\Utility\WebformYaml;
|
||||
|
||||
|
@ -30,6 +32,7 @@ class WebformElementStates extends FormElement {
|
|||
[$class, 'processWebformStates'],
|
||||
],
|
||||
'#theme_wrappers' => ['form_element'],
|
||||
'#multiple' => TRUE,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -45,14 +48,14 @@ class WebformElementStates extends FormElement {
|
|||
else {
|
||||
$default_value = $element['#default_value'] ?: [];
|
||||
}
|
||||
return self::convertFormApiStatesToStatesArray($default_value);
|
||||
return static::convertFormApiStatesToStatesArray($default_value);
|
||||
}
|
||||
else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
elseif (is_array($input) && isset($input['states'])) {
|
||||
return (is_string($input['states'])) ? Yaml::decode($input['states']) : self::convertFormValuesToStatesArray($input['states']);
|
||||
return (is_string($input['states'])) ? Yaml::decode($input['states']) : static::convertFormValuesToStatesArray($input['states']);
|
||||
}
|
||||
else {
|
||||
return [];
|
||||
|
@ -64,38 +67,20 @@ class WebformElementStates extends FormElement {
|
|||
*/
|
||||
public static function processWebformStates(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
// Define default #state_options and #trigger_options.
|
||||
// There are also defined by \Drupal\webform\WebformElementBase::form.
|
||||
// There are also defined by \Drupal\webform\Plugin\WebformElementBase::form.
|
||||
$element += [
|
||||
'#state_options' => [
|
||||
'enabled' => t('Enabled'),
|
||||
'disabled' => t('Disabled'),
|
||||
'required' => t('Required'),
|
||||
'optional' => t('Optional'),
|
||||
'visible' => t('Visible'),
|
||||
'invisible' => t('Invisible'),
|
||||
'checked' => t('Checked'),
|
||||
'unchecked' => t('Unchecked'),
|
||||
'expanded' => t('Expanded'),
|
||||
'collapsed' => t('Collapsed'),
|
||||
],
|
||||
'#trigger_options' => [
|
||||
'empty' => t('Empty'),
|
||||
'filled' => t('Filled'),
|
||||
'checked' => t('Checked'),
|
||||
'unchecked' => t('Unchecked'),
|
||||
'expanded' => t('Expanded'),
|
||||
'collapsed' => t('Collapsed'),
|
||||
'value' => t('Value is'),
|
||||
],
|
||||
'#state_options' => static::getStateOptions(),
|
||||
'#trigger_options' => static::getTriggerOptions(),
|
||||
];
|
||||
|
||||
$element['#tree'] = TRUE;
|
||||
|
||||
// Add validate callback that extracts the associative array of states.
|
||||
$element['#element_validate'] = [[get_called_class(), 'validateWebformElementStates']];
|
||||
$element += ['#element_validate' => []];
|
||||
array_unshift($element['#element_validate'], [get_called_class(), 'validateWebformElementStates']);
|
||||
|
||||
// For customized #states display a CodeMirror YAML editor.
|
||||
if ($warning_message = self::isDefaultValueCustomizedFormApiStates($element)) {
|
||||
if ($warning_message = static::isDefaultValueCustomizedFormApiStates($element)) {
|
||||
$warning_message .= ' ' . t('Form API #states must be manually entered.');
|
||||
$element['messages'] = [
|
||||
'#type' => 'webform_message',
|
||||
|
@ -104,8 +89,10 @@ class WebformElementStates extends FormElement {
|
|||
];
|
||||
$element['states'] = [
|
||||
'#type' => 'webform_codemirror',
|
||||
'#title' => t('Conditional Logic (YAML)'),
|
||||
'#title_display' => 'invisible',
|
||||
'#mode' => 'yaml',
|
||||
'#default_value' => WebformYaml::tidy(Yaml::encode($element['#default_value'])),
|
||||
'#default_value' => WebformYaml::encode($element['#default_value']),
|
||||
'#description' => t('Learn more about Drupal\'s <a href=":href">Form API #states</a>.', [':href' => 'https://www.lullabot.com/articles/form-api-states']),
|
||||
];
|
||||
return $element;
|
||||
|
@ -114,7 +101,7 @@ class WebformElementStates extends FormElement {
|
|||
$table_id = implode('_', $element['#parents']) . '_table';
|
||||
|
||||
// Store the number of rows.
|
||||
$storage_key = self::getStorageKey($element, 'number_of_rows');
|
||||
$storage_key = static::getStorageKey($element, 'number_of_rows');
|
||||
if ($form_state->get($storage_key) === NULL) {
|
||||
if (empty($element['#default_value']) || !is_array($element['#default_value'])) {
|
||||
$number_of_rows = 2;
|
||||
|
@ -126,20 +113,20 @@ class WebformElementStates extends FormElement {
|
|||
}
|
||||
$number_of_rows = $form_state->get($storage_key);
|
||||
|
||||
// DEBUG: Disable AJAX callback by commenting out the below callback and
|
||||
// DEBUG: Disable Ajax callback by commenting out the below callback and
|
||||
// wrapper.
|
||||
$ajax_settings = [
|
||||
'callback' => [get_called_class(), 'ajaxCallback'],
|
||||
'wrapper' => $table_id,
|
||||
'progress' => ['type' => 'none'],
|
||||
];
|
||||
|
||||
// Build header.
|
||||
$header = [
|
||||
['data' => t('State'), 'width' => '20%'],
|
||||
['data' => t('Element/Selector'), 'width' => '45%'],
|
||||
['data' => t('Trigger'), 'width' => '20%'],
|
||||
['data' => t('Value'), 'width' => '10%'],
|
||||
['data' => ''],
|
||||
['data' => t('State'), 'width' => '25%'],
|
||||
['data' => t('Element'), 'width' => '50%'],
|
||||
['data' => t('Trigger/Value'), 'width' => '25%'],
|
||||
['data' => WebformAccessibilityHelper::buildVisuallyHidden(t('Operations'))],
|
||||
];
|
||||
|
||||
// Get states and number of rows.
|
||||
|
@ -147,27 +134,32 @@ class WebformElementStates extends FormElement {
|
|||
$states = $element['#value'];
|
||||
}
|
||||
else {
|
||||
$states = (isset($element['#default_value'])) ? self::convertFormApiStatesToStatesArray($element['#default_value']) : [];
|
||||
$states = (isset($element['#default_value'])) ? static::convertFormApiStatesToStatesArray($element['#default_value']) : [];
|
||||
}
|
||||
|
||||
// Track state row indexes for disable/enabled warning message.
|
||||
$state_row_indexes = [];
|
||||
|
||||
// Build state and conditions rows.
|
||||
$row_index = 0;
|
||||
$rows = [];
|
||||
foreach ($states as $state_settings) {
|
||||
$rows[$row_index] = self::buildStateRow($element, $state_settings, $table_id, $row_index, $ajax_settings);
|
||||
$rows[$row_index] = static::buildStateRow($element, $state_settings, $table_id, $row_index, $ajax_settings);
|
||||
$state_row_indexes[] = $row_index;
|
||||
$row_index++;
|
||||
foreach ($state_settings['conditions'] as $condition) {
|
||||
$rows[$row_index] = self::buildConditionRow($element, $condition, $table_id, $row_index, $ajax_settings);
|
||||
$rows[$row_index] = static::buildConditionRow($element, $condition, $table_id, $row_index, $ajax_settings);
|
||||
$row_index++;
|
||||
}
|
||||
}
|
||||
|
||||
// Generator empty state with conditions rows.
|
||||
if ($row_index < $number_of_rows) {
|
||||
$rows[$row_index] = self::buildStateRow($element, [], $table_id, $row_index, $ajax_settings);;
|
||||
$rows[$row_index] = static::buildStateRow($element, [], $table_id, $row_index, $ajax_settings);;
|
||||
$state_row_indexes[] = $row_index;
|
||||
$row_index++;
|
||||
while ($row_index < $number_of_rows) {
|
||||
$rows[$row_index] = self::buildConditionRow($element, [], $table_id, $row_index, $ajax_settings);
|
||||
$rows[$row_index] = static::buildConditionRow($element, [], $table_id, $row_index, $ajax_settings);
|
||||
$row_index++;
|
||||
}
|
||||
}
|
||||
|
@ -181,17 +173,37 @@ class WebformElementStates extends FormElement {
|
|||
] + $rows;
|
||||
|
||||
// Build add state action.
|
||||
$element['add'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Add another state'),
|
||||
'#limit_validation_errors' => [],
|
||||
'#submit' => [[get_called_class(), 'addStateSubmit']],
|
||||
'#ajax' => $ajax_settings,
|
||||
'#name' => $table_id . '_add',
|
||||
];
|
||||
if ($element['#multiple']) {
|
||||
$element['add'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Add another state'),
|
||||
'#limit_validation_errors' => [],
|
||||
'#submit' => [[get_called_class(), 'addStateSubmit']],
|
||||
'#ajax' => $ajax_settings,
|
||||
'#name' => $table_id . '_add',
|
||||
];
|
||||
}
|
||||
|
||||
// Display a warning message when a state is set to disabled or enabled.
|
||||
$total_state_row_indexes = count($state_row_indexes);
|
||||
$triggers = [];
|
||||
foreach ($state_row_indexes as $index => $row_index) {
|
||||
$id = Html::getId('edit-' . implode('-', $element['#parents']) . '-states-' . $row_index . '-state');
|
||||
$triggers[] = [':input[data-drupal-selector="' . $id . '"]' => ['value' => ['pattern' => '^(disabled|enabled)$']]];
|
||||
if (($index + 1) < $total_state_row_indexes) {
|
||||
$triggers[] = 'or';
|
||||
}
|
||||
}
|
||||
if (!empty($element['#disabled_message'])) {
|
||||
$element['disabled_message'] = [
|
||||
'#type' => 'webform_message',
|
||||
'#message_message' => t('<a href="https://www.w3schools.com/tags/att_input_disabled.asp" target="_blank">Disabled</a> elements do not submit data back to the server and the element\'s server-side default or current value will be preserved and saved to the database.'),
|
||||
'#message_type' => 'warning',
|
||||
'#states' => ['visible' => $triggers],
|
||||
];
|
||||
}
|
||||
|
||||
$element['#attached']['library'][] = 'webform/webform.element.states';
|
||||
$element['#attached']['library'][] = 'webform/webform.element.select2';
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
@ -200,7 +212,7 @@ class WebformElementStates extends FormElement {
|
|||
* Build state row.
|
||||
*
|
||||
* @param array $element
|
||||
* The webform element.
|
||||
* The element.
|
||||
* @param array $state
|
||||
* The state.
|
||||
* @param string $table_id
|
||||
|
@ -208,7 +220,7 @@ class WebformElementStates extends FormElement {
|
|||
* @param int $row_index
|
||||
* The row index.
|
||||
* @param array $ajax_settings
|
||||
* An array containing AJAX callback settings.
|
||||
* An array containing Ajax callback settings.
|
||||
*
|
||||
* @return array
|
||||
* A render array containing a state table row.
|
||||
|
@ -222,24 +234,31 @@ class WebformElementStates extends FormElement {
|
|||
];
|
||||
$row['state'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => t('State'),
|
||||
'#title_display' => 'invisible',
|
||||
'#options' => $element['#state_options'],
|
||||
'#default_value' => $state['state'],
|
||||
'#empty_option' => '',
|
||||
'#empty_value' => '',
|
||||
'#attributes' => ['class' => ['js-webform-select2', 'webform-select2']],
|
||||
'#empty_option' => t('- Select -'),
|
||||
'#wrapper_attributes' => ['class' => ['webform-states-table--state']],
|
||||
];
|
||||
$row['operator'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => t('Operator'),
|
||||
'#title_display' => 'invisible',
|
||||
'#options' => [
|
||||
'and' => t('All'),
|
||||
'or' => t('Any'),
|
||||
'xor' => t('One'),
|
||||
],
|
||||
'#default_value' => $state['operator'],
|
||||
'#field_prefix' => t('if'),
|
||||
'#field_suffix' => t('of the following is met:'),
|
||||
'#wrapper_attributes' => ['colspan' => 3, 'align' => 'left'],
|
||||
'#wrapper_attributes' => ['class' => ['webform-states-table--operator'], 'colspan' => 2, 'align' => 'left'],
|
||||
];
|
||||
$row['operations'] = self::buildOperations($table_id, $row_index, $ajax_settings);
|
||||
$row['operations'] = static::buildOperations($table_id, $row_index, $ajax_settings);
|
||||
if (!$element['#multiple']) {
|
||||
unset($row['operations']['remove']);
|
||||
}
|
||||
return $row;
|
||||
}
|
||||
|
||||
|
@ -247,7 +266,7 @@ class WebformElementStates extends FormElement {
|
|||
* Build condition row.
|
||||
*
|
||||
* @param array $element
|
||||
* The webform element.
|
||||
* The element.
|
||||
* @param array $condition
|
||||
* The condition.
|
||||
* @param string $table_id
|
||||
|
@ -255,7 +274,7 @@ class WebformElementStates extends FormElement {
|
|||
* @param int $row_index
|
||||
* The row index.
|
||||
* @param array $ajax_settings
|
||||
* An array containing AJAX callback settings.
|
||||
* An array containing Ajax callback settings.
|
||||
*
|
||||
* @return array
|
||||
* A render array containing a condition table row.
|
||||
|
@ -273,34 +292,68 @@ class WebformElementStates extends FormElement {
|
|||
];
|
||||
$row['state'] = [];
|
||||
$row['selector'] = [
|
||||
'#type' => 'webform_select_other',
|
||||
'#options' => $element['#selector_options'],
|
||||
'#default_value' => $condition['selector'],
|
||||
'#empty_option' => '',
|
||||
'#empty_value' => '',
|
||||
'#attributes' => ['class' => ['js-webform-select2', 'webform-select2']],
|
||||
];
|
||||
$row['trigger'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => t('Selector'),
|
||||
'#title_display' => 'invisible',
|
||||
'#options' => $element['#selector_options'],
|
||||
'#wrapper_attributes' => ['class' => ['webform-states-table--selector']],
|
||||
'#default_value' => $condition['selector'],
|
||||
'#empty_option' => t('- Select -'),
|
||||
];
|
||||
if (!isset($element['#selector_options'][$condition['selector']])) {
|
||||
$row['selector']['#options'][$condition['selector']] = $condition['selector'];
|
||||
}
|
||||
$row['condition'] = [
|
||||
'#wrapper_attributes' => ['class' => ['webform-states-table--condition']],
|
||||
];
|
||||
$row['condition']['trigger'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => t('Trigger'),
|
||||
'#title_display' => 'invisible',
|
||||
'#options' => $element['#trigger_options'],
|
||||
'#default_value' => $condition['trigger'],
|
||||
'#empty_option' => '',
|
||||
'#empty_value' => '',
|
||||
'#attributes' => ['class' => ['js-webform-select2', 'webform-select2']],
|
||||
'#empty_option' => t('- Select -'),
|
||||
'#parents' => [$element_name, 'states', $row_index , 'trigger'],
|
||||
'#wrapper_attributes' => ['class' => ['webform-states-table--trigger']],
|
||||
];
|
||||
$row['value'] = [
|
||||
$row['condition']['value'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Value'),
|
||||
'#title_display' => 'invisible',
|
||||
'#size' => 25,
|
||||
'#default_value' => $condition['value'],
|
||||
'#placeholder' => t('Enter value…'),
|
||||
'#states' => [
|
||||
'visible' => [
|
||||
$trigger_selector => ['value' => 'value'],
|
||||
[$trigger_selector => ['value' => 'value']],
|
||||
'or',
|
||||
[$trigger_selector => ['value' => '!value']],
|
||||
'or',
|
||||
[$trigger_selector => ['value' => 'pattern']],
|
||||
'or',
|
||||
[$trigger_selector => ['value' => '!pattern']],
|
||||
'or',
|
||||
[$trigger_selector => ['value' => 'greater']],
|
||||
'or',
|
||||
[$trigger_selector => ['value' => 'less']],
|
||||
],
|
||||
],
|
||||
'#wrapper_attributes' => ['class' => ['webform-states-table--value']],
|
||||
'#parents' => [$element_name, 'states', $row_index , 'value'],
|
||||
];
|
||||
$row['condition']['pattern'] = [
|
||||
'#type' => 'container',
|
||||
'description' => ['#markup' => t('Enter a <a href=":href">regular expression</a>', [':href' => 'http://www.w3schools.com/js/js_regexp.asp'])],
|
||||
'#states' => [
|
||||
'visible' => [
|
||||
[$trigger_selector => ['value' => 'pattern']],
|
||||
'or',
|
||||
[$trigger_selector => ['value' => '!pattern']],
|
||||
],
|
||||
],
|
||||
];
|
||||
$row['operations'] = self::buildOperations($table_id, $row_index, $ajax_settings);
|
||||
|
||||
$row['operations'] = static::buildOperations($table_id, $row_index, $ajax_settings);
|
||||
return $row;
|
||||
}
|
||||
|
||||
|
@ -312,15 +365,18 @@ class WebformElementStates extends FormElement {
|
|||
* @param int $row_index
|
||||
* The option's row index.
|
||||
* @param array $ajax_settings
|
||||
* An array containing AJAX callback settings.
|
||||
* An array containing Ajax callback settings.
|
||||
*
|
||||
* @return array
|
||||
* A render array containing state operations..
|
||||
* A render array containing state operations.
|
||||
*/
|
||||
protected static function buildOperations($table_id, $row_index, array $ajax_settings) {
|
||||
$operations = [];
|
||||
$operations = [
|
||||
'#wrapper_attributes' => ['class' => ['webform-states-table--operations']],
|
||||
];
|
||||
$operations['add'] = [
|
||||
'#type' => 'image_button',
|
||||
'#title' => t('Add'),
|
||||
'#src' => drupal_get_path('module', 'webform') . '/images/icons/plus.svg',
|
||||
'#limit_validation_errors' => [],
|
||||
'#submit' => [[get_called_class(), 'addConditionSubmit']],
|
||||
|
@ -330,6 +386,7 @@ class WebformElementStates extends FormElement {
|
|||
];
|
||||
$operations['remove'] = [
|
||||
'#type' => 'image_button',
|
||||
'#title' => t('Remove'),
|
||||
'#src' => drupal_get_path('module', 'webform') . '/images/icons/ex.svg',
|
||||
'#limit_validation_errors' => [],
|
||||
'#submit' => [[get_called_class(), 'removeRowSubmit']],
|
||||
|
@ -365,7 +422,7 @@ class WebformElementStates extends FormElement {
|
|||
'operator' => 'and',
|
||||
];
|
||||
$values[] = [
|
||||
'selector' => ['select' => '', 'other' => ''],
|
||||
'selector' => '',
|
||||
'trigger' => '',
|
||||
'value' => '',
|
||||
];
|
||||
|
@ -375,7 +432,7 @@ class WebformElementStates extends FormElement {
|
|||
NestedArray::setValue($form_state->getUserInput(), $element['states']['#parents'], $values);
|
||||
|
||||
// Update the number of rows.
|
||||
$form_state->set(self::getStorageKey($element, 'number_of_rows'), count($values));
|
||||
$form_state->set(static::getStorageKey($element, 'number_of_rows'), count($values));
|
||||
|
||||
// Rebuild the webform.
|
||||
$form_state->setRebuild();
|
||||
|
@ -413,7 +470,7 @@ class WebformElementStates extends FormElement {
|
|||
NestedArray::setValue($form_state->getUserInput(), $element['states']['#parents'], $values);
|
||||
|
||||
// Update the number of rows.
|
||||
$form_state->set(self::getStorageKey($element, 'number_of_rows'), count($values));
|
||||
$form_state->set(static::getStorageKey($element, 'number_of_rows'), count($values));
|
||||
|
||||
// Rebuild the webform.
|
||||
$form_state->setRebuild();
|
||||
|
@ -454,14 +511,14 @@ class WebformElementStates extends FormElement {
|
|||
NestedArray::setValue($form_state->getUserInput(), $element['states']['#parents'], $values);
|
||||
|
||||
// Update the number of rows.
|
||||
$form_state->set(self::getStorageKey($element, 'number_of_rows'), count($values));
|
||||
$form_state->set(static::getStorageKey($element, 'number_of_rows'), count($values));
|
||||
|
||||
// Rebuild the webform.
|
||||
$form_state->setRebuild();
|
||||
}
|
||||
|
||||
/**
|
||||
* Webform submission AJAX callback the returns the states table.
|
||||
* Webform submission Ajax callback the returns the states table.
|
||||
*/
|
||||
public static function ajaxCallback(array &$form, FormStateInterface $form_state) {
|
||||
$button = $form_state->getTriggeringElement();
|
||||
|
@ -478,9 +535,11 @@ class WebformElementStates extends FormElement {
|
|||
$states = Yaml::decode($element['states']['#value']);
|
||||
}
|
||||
else {
|
||||
$states = self::convertFormValuesToFormApiStates($element['states']['#value']);
|
||||
$states = static::convertFormValuesToFormApiStates($element['states']['#value']);
|
||||
}
|
||||
$form_state->setValueForElement($element, NULL);
|
||||
|
||||
$element['#value'] = $states;
|
||||
$form_state->setValueForElement($element, $states);
|
||||
}
|
||||
|
||||
|
@ -493,7 +552,7 @@ class WebformElementStates extends FormElement {
|
|||
*
|
||||
* @param array $element
|
||||
* An element.
|
||||
* @param $name
|
||||
* @param string $name
|
||||
* The name.
|
||||
*
|
||||
* @return string
|
||||
|
@ -528,22 +587,14 @@ class WebformElementStates extends FormElement {
|
|||
|
||||
foreach ($conditions as $condition_key => $condition_value) {
|
||||
if (is_string($condition_key)) {
|
||||
$states[$index]['conditions'][] = [
|
||||
'selector' => $condition_key,
|
||||
'trigger' => key($condition_value),
|
||||
'value' => reset($condition_value),
|
||||
];
|
||||
$states[$index]['conditions'][] = static::getStatesArrayCondition($condition_key, $condition_value);
|
||||
}
|
||||
elseif (is_string($condition_value)) {
|
||||
$states[$index]['operator'] = $condition_value;
|
||||
}
|
||||
else {
|
||||
foreach ($condition_value as $subcondition_key => $subcondition_value) {
|
||||
$states[$index]['conditions'][] = [
|
||||
'selector' => $subcondition_key,
|
||||
'trigger' => key($subcondition_value),
|
||||
'value' => reset($subcondition_value),
|
||||
];
|
||||
$states[$index]['conditions'][] = static::getStatesArrayCondition($subcondition_key, $subcondition_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -552,6 +603,26 @@ class WebformElementStates extends FormElement {
|
|||
return $states;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get states array condition.
|
||||
*
|
||||
* @param string $selector
|
||||
* The selector.
|
||||
* @param array $condition
|
||||
* The condition.
|
||||
*
|
||||
* @return array
|
||||
* Associative array container selector, trigger, and value.
|
||||
*/
|
||||
protected static function getStatesArrayCondition($selector, array $condition) {
|
||||
$trigger = key($condition);
|
||||
$value = reset($condition);
|
||||
if (is_array($value)) {
|
||||
return static::getStatesArrayCondition($selector, $value);
|
||||
}
|
||||
return ['selector' => $selector, 'trigger' => $trigger, 'value' => $value];
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert states array to Form API #states.
|
||||
*
|
||||
|
@ -564,42 +635,42 @@ class WebformElementStates extends FormElement {
|
|||
protected static function convertStatesArrayToFormApiStates(array $states_array = []) {
|
||||
$states = [];
|
||||
foreach ($states_array as $state_array) {
|
||||
if ($state = $state_array['state']) {
|
||||
$operator = $state_array['operator'];
|
||||
$conditions = $state_array['conditions'];
|
||||
if (count($conditions) === 1) {
|
||||
$condition = reset($conditions);
|
||||
$selector = $condition['selector'];
|
||||
$trigger = $condition['trigger'];
|
||||
$state = $state_array['state'];
|
||||
if (!$state) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Define values extracted from
|
||||
// WebformElementStates::getFormApiStatesCondition().
|
||||
$selector = NULL;
|
||||
$trigger = NULL;
|
||||
$value = NULL;
|
||||
|
||||
$operator = $state_array['operator'];
|
||||
$conditions = $state_array['conditions'];
|
||||
if (count($conditions) === 1) {
|
||||
$condition = reset($conditions);
|
||||
extract(static::getFormApiStatesCondition($condition));
|
||||
$states[$state][$selector][$trigger] = $value;
|
||||
}
|
||||
else {
|
||||
foreach ($state_array['conditions'] as $index => $condition) {
|
||||
extract(static::getFormApiStatesCondition($condition));
|
||||
if ($selector && $trigger) {
|
||||
$value = $condition['value'] ?: TRUE;
|
||||
}
|
||||
else {
|
||||
$value = '';
|
||||
}
|
||||
$states[$state][$selector][$trigger] = $value;
|
||||
}
|
||||
else {
|
||||
foreach ($state_array['conditions'] as $index => $condition) {
|
||||
$selector = $condition['selector'];
|
||||
$trigger = $condition['trigger'];
|
||||
$value = $condition['value'] ?: TRUE;
|
||||
if ($selector && $trigger) {
|
||||
if ($operator == 'or') {
|
||||
if ($index !== 0) {
|
||||
$states[$state][] = $operator;
|
||||
}
|
||||
$states[$state][] = [
|
||||
$selector => [
|
||||
$trigger => $value,
|
||||
],
|
||||
];
|
||||
if ($operator == 'or' || $operator == 'xor') {
|
||||
if ($index !== 0) {
|
||||
$states[$state][] = $operator;
|
||||
}
|
||||
else {
|
||||
$states[$state][$selector] = [
|
||||
$states[$state][] = [
|
||||
$selector => [
|
||||
$trigger => $value,
|
||||
];
|
||||
}
|
||||
],
|
||||
];
|
||||
}
|
||||
else {
|
||||
$states[$state][$selector] = [
|
||||
$trigger => $value,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -608,6 +679,40 @@ class WebformElementStates extends FormElement {
|
|||
return $states;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get FAPI states array condition.
|
||||
*
|
||||
* @param array $condition
|
||||
* The condition.
|
||||
*
|
||||
* @return array
|
||||
* Associative array container selector, trigger, and value.
|
||||
*/
|
||||
protected static function getFormApiStatesCondition(array $condition) {
|
||||
$selector = $condition['selector'];
|
||||
$trigger = $condition['trigger'];
|
||||
if ($selector && $trigger) {
|
||||
if (in_array($trigger, ['value', '!value'])) {
|
||||
$value = $condition['value'];
|
||||
}
|
||||
elseif (in_array($trigger, ['pattern', '!pattern', 'less', 'greater'])) {
|
||||
$value = [$trigger => $condition['value']];
|
||||
$trigger = 'value';
|
||||
}
|
||||
else {
|
||||
$value = TRUE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$value = '';
|
||||
}
|
||||
return [
|
||||
'selector' => $selector,
|
||||
'trigger' => $trigger,
|
||||
'value' => $value,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert webform values to states array.
|
||||
*
|
||||
|
@ -626,16 +731,11 @@ class WebformElementStates extends FormElement {
|
|||
$index++;
|
||||
$states[$index] = [
|
||||
'state' => $value['state'],
|
||||
'operator' => $value['operator'],
|
||||
'operator' => (isset($value['operator'])) ? $value['operator'] : 'and',
|
||||
'conditions' => [],
|
||||
];
|
||||
}
|
||||
else {
|
||||
$selector = $value['selector']['select'];
|
||||
if ($selector == WebformSelectOther::OTHER_OPTION) {
|
||||
$selector = $value['selector']['other'];
|
||||
}
|
||||
$value['selector'] = $selector;
|
||||
$states[$index]['conditions'][] = $value;
|
||||
}
|
||||
}
|
||||
|
@ -652,15 +752,15 @@ class WebformElementStates extends FormElement {
|
|||
* An associative array of states.
|
||||
*/
|
||||
public static function convertFormValuesToFormApiStates(array $values = []) {
|
||||
$values = self::convertFormValuesToStatesArray($values);
|
||||
return self::convertStatesArrayToFormApiStates($values);
|
||||
$values = static::convertFormValuesToStatesArray($values);
|
||||
return static::convertStatesArrayToFormApiStates($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if an element's #states array is customized.
|
||||
*
|
||||
* @param array $element
|
||||
* The webform element.
|
||||
* The element.
|
||||
*
|
||||
* @return bool|string
|
||||
* FALSE if #states array is not customized or a warning message.
|
||||
|
@ -676,9 +776,10 @@ class WebformElementStates extends FormElement {
|
|||
return t('Conditional logic (Form API #states) is not an array.');
|
||||
}
|
||||
|
||||
$state_options = OptGroup::flattenOptions($element['#state_options']);
|
||||
$states = $element['#default_value'];
|
||||
foreach ($states as $state => $conditions) {
|
||||
if (!isset($element['#state_options'][$state])) {
|
||||
if (!isset($state_options[$state])) {
|
||||
return t('Conditional logic (Form API #states) is using a custom %state state.', ['%state' => $state]);
|
||||
}
|
||||
|
||||
|
@ -698,15 +799,13 @@ class WebformElementStates extends FormElement {
|
|||
return t('Conditional logic (Form API #states) is using multiple nested conditions.');
|
||||
}
|
||||
elseif (is_string($condition)) {
|
||||
// Make sure only an 'and/or' operator is being used. XOR is not
|
||||
// support in UI because it is confusing to none technicl users.
|
||||
if (!in_array($condition, ['and', 'or'])) {
|
||||
return t('Conditional logic (Form API #states) is using the %operator operator.', ['%operator' => Unicode::strtoupper($condition)]);
|
||||
if (!in_array($condition, ['and', 'or', 'xor'])) {
|
||||
return t('Conditional logic (Form API #states) is using the %operator operator.', ['%operator' => mb_strtoupper($condition)]);
|
||||
}
|
||||
|
||||
// Make sure the same operator is being used between the conditions.
|
||||
if ($operator && $operator != $condition) {
|
||||
return t('Conditional logic (Form API #states) has multiple operators.', ['%operator' => Unicode::strtoupper($condition)]);
|
||||
return t('Conditional logic (Form API #states) has multiple operators.', ['%operator' => mb_strtoupper($condition)]);
|
||||
}
|
||||
|
||||
// Set the operator.
|
||||
|
@ -717,4 +816,64 @@ class WebformElementStates extends FormElement {
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an associative array of translated state options.
|
||||
*
|
||||
* @return array
|
||||
* An associative array of translated state options.
|
||||
*/
|
||||
public static function getStateOptions() {
|
||||
$visibility_optgroup = (string) t('Visibility');
|
||||
$state_optgroup = (string) t('State');
|
||||
$validation_optgroup = (string) t('Validation');
|
||||
$value_optgroup = (string) t('Value');
|
||||
return [
|
||||
$visibility_optgroup => [
|
||||
'visible' => t('Visible'),
|
||||
'invisible' => t('Hidden'),
|
||||
'visible-slide' => t('Visible (Slide)'),
|
||||
'invisible-slide' => t('Hidden (Slide)'),
|
||||
],
|
||||
$state_optgroup => [
|
||||
'enabled' => t('Enabled'),
|
||||
'disabled' => t('Disabled'),
|
||||
'readwrite' => t('Read/write'),
|
||||
'readonly' => t('Read-only'),
|
||||
'expanded' => t('Expanded'),
|
||||
'collapsed' => t('Collapsed'),
|
||||
],
|
||||
$validation_optgroup => [
|
||||
'required' => t('Required'),
|
||||
'optional' => t('Optional'),
|
||||
],
|
||||
$value_optgroup => [
|
||||
'checked' => t('Checked'),
|
||||
'unchecked' => t('Unchecked'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an associative array of translated trigger options.
|
||||
*
|
||||
* @return array
|
||||
* An associative array of translated trigger options.
|
||||
*/
|
||||
public static function getTriggerOptions() {
|
||||
return [
|
||||
'empty' => t('Empty'),
|
||||
'filled' => t('Filled'),
|
||||
'checked' => t('Checked'),
|
||||
'unchecked' => t('Unchecked'),
|
||||
'expanded' => t('Expanded'),
|
||||
'collapsed' => t('Collapsed'),
|
||||
'value' => t('Value is'),
|
||||
'!value' => t('Value is not'),
|
||||
'pattern' => t('Pattern'),
|
||||
'!pattern' => t('Not Pattern'),
|
||||
'less' => t('Less than'),
|
||||
'greater' => t('Greater than'),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,10 +2,9 @@
|
|||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Render\Element\FormElement;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\Element\CompositeFormElementTrait;
|
||||
use Drupal\webform\Utility\WebformElementHelper;
|
||||
|
||||
/**
|
||||
* Provides a webform element requiring users to double-element and confirm an email address.
|
||||
|
@ -17,7 +16,7 @@ use Drupal\Core\Render\Element\CompositeFormElementTrait;
|
|||
*/
|
||||
class WebformEmailConfirm extends FormElement {
|
||||
|
||||
use CompositeFormElementTrait;
|
||||
use WebformCompositeFormElementTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
@ -31,9 +30,8 @@ class WebformEmailConfirm extends FormElement {
|
|||
[$class, 'processWebformEmailConfirm'],
|
||||
],
|
||||
'#pre_render' => [
|
||||
[$class, 'preRenderCompositeFormElement'],
|
||||
[$class, 'preRenderWebformCompositeFormElement'],
|
||||
],
|
||||
'#theme_wrappers' => ['container'],
|
||||
'#required' => FALSE,
|
||||
];
|
||||
}
|
||||
|
@ -83,6 +81,7 @@ class WebformEmailConfirm extends FormElement {
|
|||
$element['mail_1'] = $element_shared_properties + array_intersect_key($element, array_combine($mail_1_properties, $mail_1_properties));
|
||||
$element['mail_1']['#attributes']['class'][] = 'webform-email';
|
||||
$element['mail_1']['#value'] = empty($element['#value']) ? NULL : $element['#value']['mail_1'];
|
||||
$element['mail_1']['#error_no_message'] = TRUE;
|
||||
|
||||
// Build mail_2 confirm email element.
|
||||
$element['mail_2'] = $element_shared_properties;
|
||||
|
@ -94,21 +93,23 @@ class WebformEmailConfirm extends FormElement {
|
|||
}
|
||||
$element['mail_2']['#attributes']['class'][] = 'webform-email-confirm';
|
||||
$element['mail_2']['#value'] = empty($element['#value']) ? NULL : $element['#value']['mail_2'];
|
||||
$element['mail_2']['#error_no_message'] = TRUE;
|
||||
|
||||
// Don't require the main element.
|
||||
$element['#required'] = FALSE;
|
||||
|
||||
// Hide title and description from being display.
|
||||
$element['#title_display'] = 'invisible';
|
||||
$element['#description_display'] = 'invisible';
|
||||
|
||||
// Remove properties that are being applied to the sub elements.
|
||||
$element['#required'] = FALSE;
|
||||
unset($element['#title']);
|
||||
unset($element['#description']);
|
||||
unset($element['#maxlength']);
|
||||
unset($element['#atributes']);
|
||||
unset($element['#attributes']);
|
||||
unset($element['#description']);
|
||||
|
||||
// Set validation.
|
||||
if (isset($element['#element_validate'])) {
|
||||
$element['#element_validate'] = array_merge([[get_called_class(), 'validateWebformEmailConfirm']], $element['#element_validate']);
|
||||
}
|
||||
else {
|
||||
$element['#element_validate'] = [[get_called_class(), 'validateWebformEmailConfirm']];
|
||||
}
|
||||
// Add validate callback.
|
||||
$element += ['#element_validate' => []];
|
||||
array_unshift($element['#element_validate'], [get_called_class(), 'validateWebformEmailConfirm']);
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
@ -117,44 +118,44 @@ class WebformEmailConfirm extends FormElement {
|
|||
* Validates an email confirm element.
|
||||
*/
|
||||
public static function validateWebformEmailConfirm(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
|
||||
$mail_1 = trim($element['mail_1']['#value']);
|
||||
$mail_2 = trim($element['mail_2']['#value']);
|
||||
$has_access = (!isset($element['#access']) || $element['#access'] === TRUE);
|
||||
if ($has_access) {
|
||||
if ((!empty($mail_1) || !empty($mail_2)) && strcmp($mail_1, $mail_2)) {
|
||||
$form_state->setError($element['mail_2'], t('The specified email addresses do not match.'));
|
||||
$form_state->setError($element, t('The specified email addresses do not match.'));
|
||||
}
|
||||
else {
|
||||
// NOTE: Only mail_1 needs to be validated since mail_2 is the same value.
|
||||
// Verify the required value.
|
||||
if ($element['mail_1']['#required'] && empty($mail_1)) {
|
||||
if (isset($element['#required_error'])) {
|
||||
$form_state->setError($element, $element['#required_error']);
|
||||
}
|
||||
elseif (isset($element['mail_1']['#title'])) {
|
||||
$form_state->setError($element, t('@name field is required.', ['@name' => $element['mail_1']['#title']]));
|
||||
}
|
||||
else {
|
||||
$form_state->setError($element);
|
||||
}
|
||||
$required_error_title = (isset($element['mail_1']['#title'])) ? $element['mail_1']['#title'] : NULL;
|
||||
WebformElementHelper::setRequiredError($element, $form_state, $required_error_title);
|
||||
}
|
||||
// Verify that the value is not longer than #maxlength.
|
||||
if (isset($element['mail_1']['#maxlength']) && Unicode::strlen($mail_1) > $element['mail_1']['#maxlength']) {
|
||||
if (isset($element['mail_1']['#maxlength']) && mb_strlen($mail_1) > $element['mail_1']['#maxlength']) {
|
||||
$t_args = [
|
||||
'@name' => $element['mail_1']['#title'],
|
||||
'%max' => $element['mail_1']['#maxlength'],
|
||||
'%length' => Unicode::strlen($mail_1),
|
||||
'%length' => mb_strlen($mail_1),
|
||||
];
|
||||
$form_state->setError($element, t('@name cannot be longer than %max characters but is currently %length characters long.', $t_args));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set #title for other validation callbacks.
|
||||
// @see \Drupal\webform\Plugin\WebformElementBase::validateUnique
|
||||
if (isset($element['mail_1']['#title'])) {
|
||||
$element['#title'] = $element['mail_1']['#title'];
|
||||
}
|
||||
|
||||
// Email field must be converted from a two-element array into a single
|
||||
// string regardless of validation results.
|
||||
$form_state->setValueForElement($element['mail_1'], NULL);
|
||||
$form_state->setValueForElement($element['mail_2'], NULL);
|
||||
|
||||
$element['#value'] = $mail_1;
|
||||
$form_state->setValueForElement($element, $mail_1);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,32 +22,44 @@ class WebformEmailMultiple extends FormElement {
|
|||
'#input' => TRUE,
|
||||
'#description' => $this->t('Multiple email addresses may be separated by commas.'),
|
||||
'#size' => 60,
|
||||
'#cardinality' => NULL,
|
||||
'#allow_tokens' => FALSE,
|
||||
'#process' => [
|
||||
[$class, 'processWebformEmailConfirm'],
|
||||
[$class, 'processAutocomplete'],
|
||||
[$class, 'processAjaxForm'],
|
||||
[$class, 'processPattern'],
|
||||
],
|
||||
'#element_validate' => [
|
||||
[$class, 'validateWebformEmailMultiple'],
|
||||
],
|
||||
'#pre_render' => [
|
||||
[$class, 'preRenderWebformEmailMultiple'],
|
||||
],
|
||||
'#theme' => 'input__email_multiple',
|
||||
'#theme' => 'input__webform_email_multiple',
|
||||
'#theme_wrappers' => ['form_element'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Webform element validation handler for #type 'email_multiple'.
|
||||
* Process email multiple element.
|
||||
*/
|
||||
public static function processWebformEmailConfirm(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
// Add validate callback.
|
||||
$element += ['#element_validate' => []];
|
||||
array_unshift($element['#element_validate'], [get_called_class(), 'validateWebformEmailMultiple']);
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Webform element validation handler for #type 'webform_email_multiple'.
|
||||
*/
|
||||
public static function validateWebformEmailMultiple(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
$value = trim($element['#value']);
|
||||
|
||||
$element['#value'] = $value;
|
||||
$form_state->setValueForElement($element, $value);
|
||||
|
||||
if ($value) {
|
||||
$values = preg_split('/\s*,\s*/', $value);
|
||||
// Validate email.
|
||||
foreach ($values as $value) {
|
||||
// Allow tokens to be be include in multiple email list.
|
||||
if (!empty($element['#allow_tokens'] && preg_match('/^\[.*\]$/', $value))) {
|
||||
|
@ -59,11 +71,34 @@ class WebformEmailMultiple extends FormElement {
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate cardinality.
|
||||
if ($element['#cardinality'] && count($values) > $element['#cardinality']) {
|
||||
if (isset($element['#cardinality_error'])) {
|
||||
$form_state->setError($element, $element['#cardinality_error']);
|
||||
}
|
||||
elseif (isset($element['#title'])) {
|
||||
$t_args = [
|
||||
'%name' => empty($element['#title']) ? $element['#parents'][0] : $element['#title'],
|
||||
'@count' => $element['#cardinality'],
|
||||
];
|
||||
$error_message = \Drupal::translation()->formatPlural(
|
||||
$element['#cardinality'],
|
||||
'%name: this element cannot hold more than @count value.',
|
||||
'%name: this element cannot hold more than @count values.',
|
||||
$t_args
|
||||
);
|
||||
$form_state->setError($element, $error_message);
|
||||
}
|
||||
else {
|
||||
$form_state->setError($element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a #type 'email_multiple' render element for theme_element().
|
||||
* Prepares a #type 'webform_email_multiple' render element for theme_element().
|
||||
*
|
||||
* @param array $element
|
||||
* An associative array containing the properties of the element.
|
||||
|
@ -76,7 +111,7 @@ class WebformEmailMultiple extends FormElement {
|
|||
public static function preRenderWebformEmailMultiple(array $element) {
|
||||
$element['#attributes']['type'] = 'text';
|
||||
Element::setAttributes($element, ['id', 'name', 'value', 'size', 'maxlength', 'placeholder']);
|
||||
static::setAttributes($element, ['form-textfield', 'form-email-multiple']);
|
||||
static::setAttributes($element, ['form-text', 'webform-email-multiple']);
|
||||
return $element;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ class WebformEntityCheckboxes extends Checkboxes {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public static function processCheckboxes(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
self::setOptions($element);
|
||||
static::setOptions($element);
|
||||
return parent::processCheckboxes($element, $form_state, $complete_form);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ class WebformEntityRadios extends Radios {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public static function processRadios(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
self::setOptions($element);
|
||||
static::setOptions($element);
|
||||
return parent::processRadios($element, $form_state, $complete_form);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ class WebformEntitySelect extends Select {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public static function processSelect(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
self::setOptions($element);
|
||||
static::setOptions($element);
|
||||
$element = parent::processSelect($element, $form_state, $complete_form);
|
||||
|
||||
// Must convert this element['#type'] to a 'select' to prevent
|
||||
|
@ -26,6 +26,9 @@ class WebformEntitySelect extends Select {
|
|||
// @see \Drupal\Core\Form\FormValidator::performRequiredValidation
|
||||
$element['#type'] = 'select';
|
||||
|
||||
// Add class.
|
||||
$element['#attributes']['class'][] = 'webform-entity-select';
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
|
|
|
@ -55,6 +55,12 @@ trait WebformEntityTrait {
|
|||
$options += $bundle_options;
|
||||
}
|
||||
|
||||
// If the selection handler is not using views, then translate
|
||||
// the entity reference's options.
|
||||
if ($element['#selection_handler'] != 'views') {
|
||||
$options = self::translateOptions($options, $element);
|
||||
}
|
||||
|
||||
// Only select menu can support optgroups.
|
||||
if ($element['#type'] !== 'webform_entity_select') {
|
||||
$options = OptGroup::flattenOptions($options);
|
||||
|
@ -66,4 +72,35 @@ trait WebformEntityTrait {
|
|||
$element['#options'] = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate the select options.
|
||||
*
|
||||
* @param array $options
|
||||
* Untranslated options.
|
||||
* @param array $element
|
||||
* An element.
|
||||
*
|
||||
* @return array
|
||||
* Translated options.
|
||||
*/
|
||||
protected static function translateOptions(array $options, array $element) {
|
||||
/** @var \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository */
|
||||
$entity_repository = \Drupal::service('entity.repository');
|
||||
|
||||
foreach ($options as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$options[$key] = self::translateOptions($value, $element);
|
||||
}
|
||||
else {
|
||||
// Set the entity in the correct language for display.
|
||||
$option = \Drupal::entityTypeManager()
|
||||
->getStorage($element['#target_type'])
|
||||
->load($key);
|
||||
$option = $entity_repository->getTranslationFromContext($option);
|
||||
$options[$key] = $option->label();
|
||||
}
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace Drupal\webform\Element;
|
|||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\Element\FormElement;
|
||||
use Drupal\webform\Plugin\WebformElement\TableSelect;
|
||||
|
||||
/**
|
||||
* Provides a base webform element for webform excluded elements and columns.
|
||||
|
@ -24,7 +25,7 @@ abstract class WebformExcludedBase extends FormElement {
|
|||
'#process' => [
|
||||
[$class, 'processWebformExcluded'],
|
||||
],
|
||||
'#webform' => NULL,
|
||||
'#webform_id' => NULL,
|
||||
'#theme_wrappers' => ['form_element'],
|
||||
];
|
||||
}
|
||||
|
@ -36,8 +37,12 @@ abstract class WebformExcludedBase extends FormElement {
|
|||
$options = static::getWebformExcludedOptions($element);
|
||||
|
||||
$default_value = array_diff(array_keys($options), array_keys($element['#default_value'] ?: []));
|
||||
|
||||
$element['#tree'] = TRUE;
|
||||
$element['#element_validate'] = [[get_called_class(), 'validateWebformExcluded']];
|
||||
|
||||
// Add validate callback.
|
||||
$element += ['#element_validate' => []];
|
||||
array_unshift($element['#element_validate'], [get_called_class(), 'validateWebformExcluded']);
|
||||
|
||||
$element['tableselect'] = [
|
||||
'#type' => 'tableselect',
|
||||
|
@ -47,6 +52,11 @@ abstract class WebformExcludedBase extends FormElement {
|
|||
'#empty' => t('No elements are available.'),
|
||||
'#default_value' => array_combine($default_value, $default_value),
|
||||
];
|
||||
TableSelect::setProcessTableSelectCallback($element['tableselect']);
|
||||
|
||||
if (isset($element['#parents'])) {
|
||||
$element['tableselect']['#parents'] = array_merge($element['#parents'], ['tableselect']);
|
||||
}
|
||||
|
||||
// Build tableselect element with selected properties.
|
||||
$properties = [
|
||||
|
@ -73,7 +83,20 @@ abstract class WebformExcludedBase extends FormElement {
|
|||
|
||||
// Unset tableselect and set the element's value to excluded.
|
||||
$form_state->setValueForElement($element['tableselect'], NULL);
|
||||
$form_state->setValueForElement($element, array_combine($excluded, $excluded));
|
||||
|
||||
$value = array_combine($excluded, $excluded);
|
||||
$element['#value'] = $value;
|
||||
$form_state->setValueForElement($element, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get header for the excluded tableselect element.
|
||||
*
|
||||
* @return array
|
||||
* An array container the header for the excluded tableselect element.
|
||||
*/
|
||||
public static function getWebformExcludedHeader() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -88,29 +111,7 @@ abstract class WebformExcludedBase extends FormElement {
|
|||
* tableselect element.
|
||||
*/
|
||||
public static function getWebformExcludedOptions(array $element) {
|
||||
/** @var \Drupal\webform\WebformInterface $webform */
|
||||
$webform = $element['#webform'];
|
||||
|
||||
$options = [];
|
||||
$elements = $webform->getElementsInitializedFlattenedAndHasValue('view');
|
||||
foreach ($elements as $key => $element) {
|
||||
$options[$key] = [
|
||||
['title' => $element['#admin_title'] ?:$element['#title'] ?: $key],
|
||||
['name' => $key],
|
||||
['type' => isset($element['#type']) ? $element['#type'] : ''],
|
||||
];
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get header for the excluded tableselect element.
|
||||
*
|
||||
* @return array
|
||||
* An array container the header for the excluded tableselect element.
|
||||
*/
|
||||
public static function getWebformExcludedHeader() {
|
||||
return [t('Title'), t('Name'), t('Type')];
|
||||
return [];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\webform\Entity\Webform as WebformEntity;
|
||||
|
||||
/**
|
||||
* Provides a webform element for webform excluded columns (submission field and elements).
|
||||
*
|
||||
|
@ -13,27 +15,47 @@ class WebformExcludedColumns extends WebformExcludedBase {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getWebformExcludedHeader() {
|
||||
return [t('Title'), t('Name'), t('Date type/Element type')];
|
||||
return [
|
||||
'title' => t('Title'),
|
||||
'name' => t('Name'),
|
||||
'type' => t('Date type/Element type'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getWebformExcludedOptions(array $element) {
|
||||
/** @var \Drupal\webform\WebformInterface $webform */
|
||||
$webform = WebformEntity::load($element['#webform_id']);
|
||||
|
||||
$options = [];
|
||||
|
||||
/** @var \Drupal\webform\WebformSubmissionStorageInterface $submission_storage */
|
||||
$submission_storage = \Drupal::entityTypeManager()->getStorage('webform_submission');
|
||||
$field_definitions = $submission_storage->getFieldDefinitions();
|
||||
$field_definitions = $submission_storage->checkFieldDefinitionAccess($element['#webform'], $field_definitions);
|
||||
$field_definitions = $submission_storage->checkFieldDefinitionAccess($webform, $field_definitions);
|
||||
foreach ($field_definitions as $key => $field_definition) {
|
||||
$options[$key] = [
|
||||
['title' => $field_definition['title']],
|
||||
['name' => $key],
|
||||
['type' => $field_definition['type']],
|
||||
'title' => $field_definition['title'],
|
||||
'name' => $key,
|
||||
'type' => $field_definition['type'],
|
||||
];
|
||||
}
|
||||
$elements = $webform->getElementsInitializedFlattenedAndHasValue('view');
|
||||
|
||||
// Replace tokens which can be used in an element's #title.
|
||||
/** @var \Drupal\webform\WebformTokenManagerInterface $token_manager */
|
||||
$token_manager = \Drupal::service('webform.token_manager');
|
||||
$elements = $token_manager->replace($elements, $webform);
|
||||
|
||||
foreach ($elements as $key => $element) {
|
||||
$options[$key] = [
|
||||
'title' => $element['#admin_title'] ?:$element['#title'] ?: $key,
|
||||
'name' => $key,
|
||||
'type' => isset($element['#type']) ? $element['#type'] : '',
|
||||
];
|
||||
}
|
||||
$options += parent::getWebformExcludedOptions($element);
|
||||
return $options;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,9 +2,85 @@
|
|||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\user\Entity\Role;
|
||||
use Drupal\webform\Entity\Webform as WebformEntity;
|
||||
use Drupal\webform\Utility\WebformArrayHelper;
|
||||
|
||||
/**
|
||||
* Provides a webform element for webform excluded elements.
|
||||
*
|
||||
* @FormElement("webform_excluded_elements")
|
||||
*/
|
||||
class WebformExcludedElements extends WebformExcludedBase {}
|
||||
class WebformExcludedElements extends WebformExcludedBase {
|
||||
|
||||
/**
|
||||
* Get header for the excluded tableselect element.
|
||||
*
|
||||
* @return array
|
||||
* An array container the header for the excluded tableselect element.
|
||||
*/
|
||||
public static function getWebformExcludedHeader() {
|
||||
$header = [];
|
||||
$header['title'] = [
|
||||
'data' => t('Title'),
|
||||
];
|
||||
$header['key'] = [
|
||||
'data' => t('Key'),
|
||||
'class' => [RESPONSIVE_PRIORITY_LOW],
|
||||
];
|
||||
$header['type'] = [
|
||||
'data' => t('Type'),
|
||||
'class' => [RESPONSIVE_PRIORITY_LOW],
|
||||
];
|
||||
$header['private'] = [
|
||||
'data' => t('Private'),
|
||||
];
|
||||
$header['access'] = [
|
||||
'data' => t('Access'),
|
||||
];
|
||||
return $header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get options for excluded tableselect element.
|
||||
*
|
||||
* @param array $element
|
||||
* An associative array containing the properties and children of the
|
||||
* generic element element.
|
||||
*
|
||||
* @return array
|
||||
* An array of options containing title, name, and type of items for a
|
||||
* tableselect element.
|
||||
*/
|
||||
public static function getWebformExcludedOptions(array $element) {
|
||||
|
||||
/** @var \Drupal\webform\WebformInterface $webform */
|
||||
$webform = WebformEntity::load($element['#webform_id']);
|
||||
|
||||
$options = [];
|
||||
$elements = $webform->getElementsInitializedFlattenedAndHasValue();
|
||||
foreach ($elements as $key => $element) {
|
||||
if (!empty($element['#access_view_roles'])) {
|
||||
$roles = array_map(function ($item) {
|
||||
return $item->label();
|
||||
}, Role::loadMultiple($element['#access_view_roles']));
|
||||
}
|
||||
else {
|
||||
$roles = [];
|
||||
}
|
||||
|
||||
$options[$key] = [
|
||||
'title' => $element['#admin_title'] ?:$element['#title'] ?: $key,
|
||||
'key' => $key,
|
||||
'type' => isset($element['#type']) ? $element['#type'] : '',
|
||||
'private' => empty($element['#private']) ? t('No') : t('Yes'),
|
||||
'access' => $roles ? WebformArrayHelper::toString($roles) : t('All roles'),
|
||||
];
|
||||
if (!empty($element['#private']) || $roles) {
|
||||
$options[$key]['#attributes']['class'][] = 'color-warning';
|
||||
}
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ class WebformFlexbox extends Container {
|
|||
if (isset($element['#align_items'])) {
|
||||
$element['#attributes']['class'][] = 'webform-flexbox--' . $element['#align_items'];
|
||||
}
|
||||
$element['#attributes']['class'][] = 'js-form-wrapper';
|
||||
$element['#attached']['library'][] = 'webform/webform.element.flexbox';
|
||||
return $element;
|
||||
}
|
||||
|
|
26
web/modules/contrib/webform/src/Element/WebformHelp.php
Normal file
26
web/modules/contrib/webform/src/Element/WebformHelp.php
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\Core\Render\Element\RenderElement;
|
||||
|
||||
/**
|
||||
* Provides a render element for help (tooltip).
|
||||
*
|
||||
* @FormElement("webform_help")
|
||||
*/
|
||||
class WebformHelp extends RenderElement {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
return [
|
||||
'#help' => '',
|
||||
'#help_title' => '',
|
||||
'#theme' => 'webform_element_help',
|
||||
'#attributes' => [],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\Core\Render\Element\RenderElement;
|
||||
|
||||
/**
|
||||
* Provides a render element for horizontal rule.
|
||||
*
|
||||
* @FormElement("webform_horizontal_rule")
|
||||
*/
|
||||
class WebformHorizontalRule extends RenderElement {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
return [
|
||||
'#theme' => 'webform_horizontal_rule',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -4,46 +4,148 @@ namespace Drupal\webform\Element;
|
|||
|
||||
use Drupal\Component\Utility\Xss;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\Element\Textarea;
|
||||
use Drupal\Core\Render\Element\FormElement;
|
||||
use Drupal\webform\Utility\WebformElementHelper;
|
||||
use Drupal\webform\Utility\WebformXss;
|
||||
|
||||
/**
|
||||
* Provides a webform element for entering HTML using CKEditor or CodeMirror.
|
||||
* Provides a webform element for entering HTML using CodeMirror, TextFormat, or custom CKEditor.
|
||||
*
|
||||
* @FormElement("webform_html_editor")
|
||||
*/
|
||||
class WebformHtmlEditor extends Textarea {
|
||||
class WebformHtmlEditor extends FormElement {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
$class = get_class($this);
|
||||
$info = parent::getInfo();
|
||||
$info['#pre_render'][] = [$class, 'preRenderWebformHtmlEditor'];
|
||||
$info['#element_validate'][] = [$class, 'validateWebformHtmlEditor'];
|
||||
return $info;
|
||||
return [
|
||||
'#input' => TRUE,
|
||||
'#process' => [
|
||||
[$class, 'processWebformHtmlEditor'],
|
||||
[$class, 'processAjaxForm'],
|
||||
[$class, 'processGroup'],
|
||||
],
|
||||
'#pre_render' => [
|
||||
[$class, 'preRenderGroup'],
|
||||
],
|
||||
'#theme_wrappers' => ['form_element'],
|
||||
'#format' => '',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a #type 'html_editor' render element for input.html.twig.
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
|
||||
$element += ['#default_value' => ''];
|
||||
if ($input === FALSE) {
|
||||
return [
|
||||
'value' => $element['#default_value'],
|
||||
];
|
||||
}
|
||||
else {
|
||||
// Get value from TextFormat element.
|
||||
if (isset($input['value']['value'])) {
|
||||
$input['value'] = $input['value']['value'];
|
||||
}
|
||||
return $input;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a #type 'webform_html_editor' render element for input.html.twig.
|
||||
*
|
||||
* @param array $element
|
||||
* An associative array containing the properties of the element.
|
||||
* Properties used: #title, #value, #return_value, #description, #required,
|
||||
* #attributes, #checked.
|
||||
*
|
||||
* @return array
|
||||
* The $element with prepared variables ready for input.html.twig.
|
||||
* The HTML Editor which can be a CodeMirror element, TextFormat, or
|
||||
* Textarea which is transformed into a custom HTML Editor.
|
||||
*/
|
||||
public static function preRenderWebformHtmlEditor(array $element) {
|
||||
if (\Drupal::config('webform.settings')->get('ui.html_editor_disabled')) {
|
||||
$element['#mode'] = 'html';
|
||||
$element = WebformCodeMirror::preRenderWebformCodeMirror($element);
|
||||
public static function processWebformHtmlEditor(array $element) {
|
||||
$element['#tree'] = TRUE;
|
||||
|
||||
// Define value element.
|
||||
$element += ['value' => []];
|
||||
|
||||
// Copy properties to value element.
|
||||
$properties = ['#title', '#required', '#attributes', '#default_value'];
|
||||
$element['value'] += array_intersect_key($element, array_combine($properties, $properties));
|
||||
|
||||
// Hide title.
|
||||
$element['value']['#title_display'] = 'invisible';
|
||||
|
||||
// Don't display inline form error messages.
|
||||
$element['#error_no_message'] = TRUE;
|
||||
|
||||
// Add validate callback.
|
||||
$element += ['#element_validate' => []];
|
||||
array_unshift($element['#element_validate'], [get_called_class(), 'validateWebformHtmlEditor']);
|
||||
|
||||
// If HTML disabled and no #format is specified return simple CodeMirror
|
||||
// HTML editor.
|
||||
$disabled = \Drupal::config('webform.settings')->get('html_editor.disabled') ?: ($element['#format'] === FALSE);
|
||||
if ($disabled) {
|
||||
$element['value'] += [
|
||||
'#type' => 'webform_codemirror',
|
||||
'#mode' => 'html',
|
||||
];
|
||||
return $element;
|
||||
}
|
||||
else {
|
||||
$element['#attached']['library'][] = 'webform/webform.element.html_editor';
|
||||
$element['#attached']['drupalSettings']['webform']['html_editor']['allowedContent'] = self::getAllowedContent();
|
||||
|
||||
// If #format or 'webform.settings.html_editor.element_format' is defined return
|
||||
// a 'text_format' element.
|
||||
$format = $element['#format'] ?: \Drupal::config('webform.settings')->get('html_editor.element_format');
|
||||
if ($format) {
|
||||
$element['value'] += [
|
||||
'#type' => 'text_format',
|
||||
'#format' => $format,
|
||||
'#allowed_formats' => [$format],
|
||||
];
|
||||
WebformElementHelper::fixStatesWrapper($element);
|
||||
return $element;
|
||||
}
|
||||
|
||||
// Else use textarea with completely custom HTML Editor.
|
||||
$element['value'] += [
|
||||
'#type' => 'textarea',
|
||||
];
|
||||
$element['value']['#attributes']['class'][] = 'js-html-editor';
|
||||
|
||||
$element['#attached']['library'][] = 'webform/webform.element.html_editor';
|
||||
$element['#attached']['drupalSettings']['webform']['html_editor']['allowedContent'] = static::getAllowedContent();
|
||||
|
||||
/** @var \Drupal\webform\WebformLibrariesManagerInterface $libraries_manager */
|
||||
$libraries_manager = \Drupal::service('webform.libraries_manager');
|
||||
$libraries = $libraries_manager->getLibraries(TRUE);
|
||||
$element['#attached']['drupalSettings']['webform']['html_editor']['plugins'] = [];
|
||||
foreach ($libraries as $library_name => $library) {
|
||||
if (strpos($library_name, 'ckeditor.') === FALSE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$plugin_name = str_replace('ckeditor.', '', $library_name);
|
||||
$plugin_path = $library['plugin_path'];
|
||||
$plugin_url = $library['plugin_url'];
|
||||
if (file_exists($plugin_path)) {
|
||||
$element['#attached']['drupalSettings']['webform']['html_editor']['plugins'][$plugin_name] = base_path() . $plugin_path;
|
||||
}
|
||||
else {
|
||||
$element['#attached']['drupalSettings']['webform']['html_editor']['plugins'][$plugin_name] = $plugin_url;
|
||||
}
|
||||
}
|
||||
|
||||
if (\Drupal::moduleHandler()->moduleExists('imce') && \Drupal\imce\Imce::access()) {
|
||||
$element['#attached']['library'][] = 'imce/drupal.imce.ckeditor';
|
||||
$element['#attached']['drupalSettings']['webform']['html_editor']['ImceImageIcon'] = file_create_url(drupal_get_path('module', 'imce') . '/js/plugins/ckeditor/icons/imceimage.png');
|
||||
}
|
||||
|
||||
if (!empty($element['#states'])) {
|
||||
webform_process_states($element, '#wrapper_attributes');
|
||||
}
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
|
@ -51,8 +153,17 @@ class WebformHtmlEditor extends Textarea {
|
|||
* Webform element validation handler for #type 'webform_html_editor'.
|
||||
*/
|
||||
public static function validateWebformHtmlEditor(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
$value = $element['#value'];
|
||||
$form_state->setValueForElement($element, trim($value));
|
||||
$value = $element['#value']['value'];
|
||||
if (is_array($value)) {
|
||||
// Get value from TextFormat element.
|
||||
$value = $value['value'];
|
||||
}
|
||||
else {
|
||||
$value = trim($value);
|
||||
}
|
||||
|
||||
$element['#value'] = $value;
|
||||
$form_state->setValueForElement($element, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -62,7 +173,7 @@ class WebformHtmlEditor extends Textarea {
|
|||
* Allowed content (tags) for CKEditor.
|
||||
*/
|
||||
public static function getAllowedContent() {
|
||||
$allowed_tags = \Drupal::config('webform.settings')->get('elements.allowed_tags');
|
||||
$allowed_tags = \Drupal::config('webform.settings')->get('element.allowed_tags');
|
||||
switch ($allowed_tags) {
|
||||
case 'admin':
|
||||
$allowed_tags = Xss::getAdminTagList();
|
||||
|
@ -82,4 +193,74 @@ class WebformHtmlEditor extends Textarea {
|
|||
return implode('; ', $allowed_tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get allowed tags.
|
||||
*
|
||||
* @return array
|
||||
* Allowed tags.
|
||||
*/
|
||||
public static function getAllowedTags() {
|
||||
$allowed_tags = \Drupal::config('webform.settings')->get('element.allowed_tags');
|
||||
switch ($allowed_tags) {
|
||||
case 'admin':
|
||||
return WebformXss::getAdminTagList();
|
||||
|
||||
case 'html':
|
||||
return WebformXss::getHtmlTagList();
|
||||
|
||||
default:
|
||||
return preg_split('/ +/', $allowed_tags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs HTML markup through (optional) text format.
|
||||
*
|
||||
* @param string $text
|
||||
* The text to be filtered.
|
||||
*
|
||||
* @return array
|
||||
* Render array containing 'processed_text'.
|
||||
*
|
||||
* @see \Drupal\webform\Plugin\WebformHandler\EmailWebformHandler::getMessage
|
||||
*/
|
||||
public static function checkMarkup($text) {
|
||||
// Remove <p> tags around a single line of text, which creates minor
|
||||
// margin issues.
|
||||
if (\Drupal::config('webform.settings')->get('html_editor.tidy')) {
|
||||
if (substr_count($text, '<p>') === 1 && preg_match('#^\s*<p>.*</p>\s*$#m', $text)) {
|
||||
$text = preg_replace('#^\s*<p>#', '', $text);
|
||||
$text = preg_replace('#</p>\s*$#', '', $text);
|
||||
}
|
||||
}
|
||||
|
||||
if ($format = \Drupal::config('webform.settings')->get('html_editor.element_format')) {
|
||||
return [
|
||||
'#type' => 'processed_text',
|
||||
'#text' => $text,
|
||||
'#format' => $format,
|
||||
];
|
||||
}
|
||||
else {
|
||||
return [
|
||||
'#markup' => $text,
|
||||
'#allowed_tags' => static::getAllowedTags(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip dis-allowed HTML tags from HTML text.
|
||||
*
|
||||
* @param string $text
|
||||
* HTML text.
|
||||
*
|
||||
* @return string
|
||||
* HTML text with dis-allowed HTML tags removed.
|
||||
*/
|
||||
public static function stripTags($text) {
|
||||
$allowed_tags = '<' . implode('><', static::getAllowedTags()) . '>';
|
||||
return strip_tags($text, $allowed_tags);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\Core\Render\Element\FormElement;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\Element\CompositeFormElementTrait;
|
||||
|
||||
/**
|
||||
* Provides a webform image resolution element .
|
||||
*
|
||||
* @FormElement("webform_image_resolution")
|
||||
*/
|
||||
class WebformImageResolution extends FormElement {
|
||||
|
||||
use CompositeFormElementTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
$class = get_class($this);
|
||||
return [
|
||||
'#input' => TRUE,
|
||||
'#size' => 60,
|
||||
'#process' => [
|
||||
[$class, 'processWebformImageResolution'],
|
||||
],
|
||||
'#theme_wrappers' => ['form_element'],
|
||||
'#required' => FALSE,
|
||||
// Add '#markup' property to add an 'id' attribute to the form element.
|
||||
// @see template_preprocess_form_element()
|
||||
'#markup' => '',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
|
||||
if ($input === FALSE) {
|
||||
if (!isset($element['#default_value'])) {
|
||||
$element['#default_value'] = '';
|
||||
}
|
||||
$max_resolution = explode('x', $element['#default_value']) + ['', ''];
|
||||
|
||||
return [
|
||||
'x' => $max_resolution[0],
|
||||
'y' => $max_resolution[1],
|
||||
];
|
||||
}
|
||||
else {
|
||||
return $input;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand a image resolution field into width and height elements.
|
||||
*
|
||||
* @see \Drupal\image\Plugin\Field\FieldType\ImageItem::fieldSettingsForm
|
||||
*/
|
||||
public static function processWebformImageResolution(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
$element['#tree'] = TRUE;
|
||||
|
||||
$element['#type'] = 'item';
|
||||
$element += [
|
||||
'#description' => t('The maximum allowed image size expressed as WIDTH×HEIGHT (e.g. 640×480). Leave blank for no restriction. If a larger image is uploaded, it will be resized to reflect the given width and height. Resizing images on upload will cause the loss of <a href="http://wikipedia.org/wiki/Exchangeable_image_file_format">EXIF data</a> in the image.'),
|
||||
'#height_title' => t('Maximum height'),
|
||||
'#width_title' => t('Maximum width'),
|
||||
'#field_prefix' => '<div class="container-inline">',
|
||||
'#field_suffix' => '</div>',
|
||||
];
|
||||
$element['x'] = [
|
||||
'#type' => 'number',
|
||||
'#title' => $element['#width_title'],
|
||||
'#title_display' => 'invisible',
|
||||
'#value' => empty($element['#value']) ? NULL : $element['#value']['x'],
|
||||
'#min' => 1,
|
||||
'#field_suffix' => ' × ',
|
||||
];
|
||||
$element['y'] = [
|
||||
'#type' => 'number',
|
||||
'#title' => $element['#height_title'],
|
||||
'#title_display' => 'invisible',
|
||||
'#value' => empty($element['#value']) ? NULL : $element['#value']['y'],
|
||||
'#min' => 1,
|
||||
'#field_suffix' => ' ' . t('pixels'),
|
||||
];
|
||||
|
||||
// Add validate callback.
|
||||
$element += ['#element_validate' => []];
|
||||
array_unshift($element['#element_validate'], [get_called_class(), 'validateWebformImageResolution']);
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates an image resolution element.
|
||||
*
|
||||
* @see \Drupal\image\Plugin\Field\FieldType\ImageItem::validateResolution
|
||||
*/
|
||||
public static function validateWebformImageResolution(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
if (!empty($element['x']['#value']) || !empty($element['y']['#value'])) {
|
||||
foreach (['x', 'y'] as $dimension) {
|
||||
if (!$element[$dimension]['#value']) {
|
||||
// We expect the field name placeholder value to be wrapped in t()
|
||||
// here, so it won't be escaped again as it's already marked safe.
|
||||
$form_state->setError($element[$dimension], t('Both a height and width value must be specified in the @name field.', ['@name' => $element['#title']]));
|
||||
return;
|
||||
}
|
||||
}
|
||||
$form_state->setValueForElement($element, $element['x']['#value'] . 'x' . $element['y']['#value']);
|
||||
}
|
||||
else {
|
||||
$form_state->setValueForElement($element, '');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -2,9 +2,13 @@
|
|||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\Element\FormElement;
|
||||
use Drupal\webform\Utility\WebformArrayHelper;
|
||||
use Drupal\webform\Utility\WebformAccessibilityHelper;
|
||||
use Drupal\webform\Utility\WebformElementHelper;
|
||||
use Drupal\webform\Utility\WebformOptionsHelper;
|
||||
|
||||
/**
|
||||
* Provides a webform element for a likert scale.
|
||||
|
@ -26,10 +30,13 @@ class WebformLikert extends FormElement {
|
|||
],
|
||||
'#theme_wrappers' => ['form_element'],
|
||||
'#required' => FALSE,
|
||||
'#sticky' => TRUE,
|
||||
'#questions' => [],
|
||||
// Using #answers insteads of #options to prevent triggering
|
||||
'#questions_description_display' => 'description',
|
||||
// Using #answers instead of #options to prevent triggering
|
||||
// \Drupal\Core\Form\FormValidator::performRequiredValidation().
|
||||
'#answers' => [],
|
||||
'#answers_description_display' => 'description',
|
||||
'#na_answer' => FALSE,
|
||||
'#na_answer_text' => '',
|
||||
'#na_answer_value' => '',
|
||||
|
@ -41,44 +48,156 @@ class WebformLikert extends FormElement {
|
|||
*/
|
||||
public static function processWebformLikert(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
// Get answer with optional N/A.
|
||||
self::processWebformLikertAnswers($element);
|
||||
static::processWebformLikertAnswers($element);
|
||||
|
||||
// Process answers.
|
||||
$answers = [];
|
||||
foreach ($element['#answers'] as $answer_key => $answer) {
|
||||
$answer = (string) $answer;
|
||||
if (strpos($answer, WebformOptionsHelper::DESCRIPTION_DELIMITER) === FALSE) {
|
||||
$answer_description_property_name = NULL;
|
||||
$answer_title = $answer;
|
||||
$answer_description = '';
|
||||
}
|
||||
else {
|
||||
$answer_description_property_name = ($element['#answers_description_display'] == 'help') ? 'help' : 'description';
|
||||
list($answer_title, $answer_description) = explode(WebformOptionsHelper::DESCRIPTION_DELIMITER, $answer);
|
||||
}
|
||||
$answers[$answer_key] = [
|
||||
'description_property_name' => $answer_description_property_name,
|
||||
'title' => $answer_title,
|
||||
'description' => $answer_description,
|
||||
];
|
||||
}
|
||||
|
||||
// Build header.
|
||||
$header = [
|
||||
'likert_question' => ['question' => FALSE],
|
||||
] + $element['#answers'];
|
||||
'likert_question' => [
|
||||
'data' => [
|
||||
'title' => WebformAccessibilityHelper::buildVisuallyHidden(t('Questions')),
|
||||
],
|
||||
],
|
||||
];
|
||||
foreach ($answers as $answer_key => $answer) {
|
||||
$header[$answer_key] = [
|
||||
'data' => [
|
||||
'title' => ['#markup' => $answer['title']],
|
||||
],
|
||||
];
|
||||
switch ($answer['description_property_name']) {
|
||||
case 'help':
|
||||
$header[$answer_key]['data']['help'] = [
|
||||
'#type' => 'webform_help',
|
||||
'#help' => $answer['description'],
|
||||
'#help_title' => $answer['title'],
|
||||
];
|
||||
break;
|
||||
|
||||
case 'description':
|
||||
$header[$answer_key]['data']['description'] = [
|
||||
'#type' => 'container',
|
||||
'#markup' => $answer['description'],
|
||||
'#attributes' => ['class' => ['description']],
|
||||
];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Randomize questions.
|
||||
if (!empty($element['#questions_randomize'])) {
|
||||
$element['#questions'] = WebformArrayHelper::shuffle($element['#questions']);
|
||||
$element['#questions'] = WebformElementHelper::randomize($element['#questions']);
|
||||
}
|
||||
|
||||
// Build rows.
|
||||
$rows = [];
|
||||
foreach ($element['#questions'] as $question_key => $question_title) {
|
||||
foreach ($element['#questions'] as $question_key => $question) {
|
||||
$question = (string) $question;
|
||||
if (strpos($question, WebformOptionsHelper::DESCRIPTION_DELIMITER) === FALSE) {
|
||||
$question_description_property_name = NULL;
|
||||
$question_title = $question;
|
||||
$question_description = '';
|
||||
}
|
||||
else {
|
||||
$question_description_property_name = ($element['#questions_description_display'] == 'help') ? '#help' : '#description';
|
||||
list($question_title, $question_description) = explode(WebformOptionsHelper::DESCRIPTION_DELIMITER, $question);
|
||||
}
|
||||
|
||||
$value = (isset($element['#value'][$question_key])) ? $element['#value'][$question_key] : NULL;
|
||||
|
||||
// Get question id.
|
||||
// @see \Drupal\Core\Form\FormBuilder::doBuildForm
|
||||
$question_id = 'edit-' . implode('-', array_merge($element['#parents'], ['table', $question_key, 'likert_question']));
|
||||
$question_id = Html::getUniqueId($question_id);
|
||||
|
||||
$row = [];
|
||||
// Must format the label as an item so that inline webform errors will be
|
||||
// displayed.
|
||||
$row['likert_question'] = [
|
||||
'#type' => 'item',
|
||||
'#title' => $question_title,
|
||||
'#id' => $question_id,
|
||||
// Must include an empty <span> so that the item's value is
|
||||
// not required.
|
||||
'#value' => '<span></span>',
|
||||
'#webform_element' => TRUE,
|
||||
'#required' => $element['#required'],
|
||||
];
|
||||
foreach ($element['#answers'] as $answer_key => $answer_title) {
|
||||
if ($question_description_property_name) {
|
||||
$row['likert_question'][$question_description_property_name] = $question_description;
|
||||
}
|
||||
|
||||
foreach ($answers as $answer_key => $answer) {
|
||||
$row[$answer_key] = [
|
||||
'#parents' => [$element['#name'], $question_key],
|
||||
'#type' => 'radio',
|
||||
'#title' => $answer_title,
|
||||
'#title_display' => 'after',
|
||||
// Must cast values as strings to prevent NULL and empty strings.
|
||||
// from being evaluated as 0.
|
||||
'#return_value' => (string) $answer_key,
|
||||
'#value' => (string) $value,
|
||||
// Set value to FALSE to prevent '0' or '' from being checked when
|
||||
// value is NULL.
|
||||
// @see \Drupal\Core\Render\Element\Radio::preRenderRadio
|
||||
'#value' => ($value === NULL) ? FALSE : (string) $value,
|
||||
'#attributes' => ['aria-labelledby' => $question_id],
|
||||
];
|
||||
|
||||
// Wrap title in span.webform-likert-label.visually-hidden
|
||||
// so that it can hidden but accessible to screen readers
|
||||
// when Likert is displayed in grid on desktop.
|
||||
// Wrap help and description in
|
||||
// span.webform-likert-(help|description).hidden to block screen
|
||||
// readers except on mobile.
|
||||
// @see webform.element.likert.css
|
||||
$row[$answer_key]['#title_display'] = 'after';
|
||||
|
||||
switch ($answer['description_property_name']) {
|
||||
case 'help':
|
||||
$build = [
|
||||
'title' => ['#markup' => $answer['title']],
|
||||
'help' => [
|
||||
'#type' => 'webform_help',
|
||||
'#help' => $answer['description'],
|
||||
'#help_title' => $answer['title'],
|
||||
'#prefix' => '<span class="webform-likert-help hidden">',
|
||||
'#suffix' => '</span>',
|
||||
],
|
||||
'#prefix' => '<span class="webform-likert-label visually-hidden">',
|
||||
'#suffix' => '</span>',
|
||||
];
|
||||
$row[$answer_key]['#title'] = \Drupal::service('renderer')->render($build);
|
||||
break;
|
||||
|
||||
case 'description':
|
||||
$row[$answer_key] += [
|
||||
'#title' => new FormattableMarkup('<span class="webform-likert-label visually-hidden">@title</span>', ['@title' => $answer['title']]),
|
||||
'#description' => new FormattableMarkup('<span class="webform-likert-description hidden">@description</span>', ['@description' => $answer['description']]),
|
||||
];
|
||||
break;
|
||||
|
||||
default:
|
||||
$row[$answer_key] += [
|
||||
'#title' => new FormattableMarkup('<span class="webform-likert-label visually-hidden">@title</span>', ['@title' => $answer['title']]),
|
||||
];
|
||||
}
|
||||
}
|
||||
$rows[$question_key] = $row;
|
||||
}
|
||||
|
@ -86,10 +205,13 @@ class WebformLikert extends FormElement {
|
|||
$element['table'] = [
|
||||
'#type' => 'table',
|
||||
'#header' => $header,
|
||||
'#sticky' => $element['#sticky'],
|
||||
'#attributes' => [
|
||||
'class' => ['webform-likert-table'],
|
||||
'data-likert-answers-count' => count($element['#answers']),
|
||||
],
|
||||
'#prefix' => '<div class="webform-likert-table-wrapper">',
|
||||
'#suffix' => '</div>',
|
||||
] + $rows;
|
||||
|
||||
// Build table element with selected properties.
|
||||
|
@ -100,7 +222,11 @@ class WebformLikert extends FormElement {
|
|||
$element['table'] += array_intersect_key($element, array_combine($properties, $properties));
|
||||
|
||||
$element['#tree'] = TRUE;
|
||||
$element['#element_validate'] = [[get_called_class(), 'validateWebformLikert']];
|
||||
|
||||
// Add validate callback.
|
||||
$element += ['#element_validate' => []];
|
||||
array_unshift($element['#element_validate'], [get_called_class(), 'validateWebformLikert']);
|
||||
|
||||
$element['#attached']['library'][] = 'webform/webform.element.likert';
|
||||
|
||||
return $element;
|
||||
|
@ -160,6 +286,7 @@ class WebformLikert extends FormElement {
|
|||
}
|
||||
}
|
||||
|
||||
$element['#value'] = $value;
|
||||
$form_state->setValueForElement($element, $value);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,14 @@ class WebformLink extends WebformCompositeBase {
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getCompositeElements() {
|
||||
public function getInfo() {
|
||||
return parent::getInfo() + ['#theme' => 'webform_composite_link'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getCompositeElements(array $element) {
|
||||
$elements = [];
|
||||
$elements['title'] = [
|
||||
'#type' => 'textfield',
|
||||
|
|
|
@ -1,170 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Provides a webform element for a location element.
|
||||
*
|
||||
* @FormElement("webform_location")
|
||||
*/
|
||||
class WebformLocation extends WebformCompositeBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
return parent::getInfo() + [
|
||||
'#api_key' => '',
|
||||
'#hidden' => FALSE,
|
||||
'#geolocation' => FALSE,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getCompositeElements() {
|
||||
// @see https://developers.google.com/maps/documentation/javascript/geocoding#GeocodingAddressTypes
|
||||
$attributes = [];
|
||||
$attributes['lat'] = [
|
||||
'#title' => t('Latitude'),
|
||||
];
|
||||
$attributes['lng'] = [
|
||||
'#title' => t('Longitude'),
|
||||
];
|
||||
$attributes['location'] = [
|
||||
'#title' => t('Location'),
|
||||
];
|
||||
$attributes['formatted_address'] = [
|
||||
'#title' => t('Formatted Address'),
|
||||
];
|
||||
$attributes['street_address'] = [
|
||||
'#title' => t('Street Address'),
|
||||
];
|
||||
$attributes['street_number'] = [
|
||||
'#title' => t('Street Number'),
|
||||
];
|
||||
$attributes['postal_code'] = [
|
||||
'#title' => t('Postal Code'),
|
||||
];
|
||||
$attributes['locality'] = [
|
||||
'#title' => t('Locality'),
|
||||
];
|
||||
$attributes['sublocality'] = [
|
||||
'#title' => t('City'),
|
||||
];
|
||||
$attributes['administrative_area_level_1'] = [
|
||||
'#title' => t('State/Province'),
|
||||
];
|
||||
$attributes['country'] = [
|
||||
'#title' => t('Country'),
|
||||
];
|
||||
$attributes['country_short'] = [
|
||||
'#title' => t('Country Code'),
|
||||
];
|
||||
|
||||
foreach ($attributes as $name => &$attribute_element) {
|
||||
$attribute_element['#type'] = 'textfield';
|
||||
|
||||
$attribute_element['#attributes'] = [
|
||||
'data-webform-location-attribute' => $name,
|
||||
];
|
||||
}
|
||||
|
||||
$elements = [];
|
||||
$elements['value'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Address'),
|
||||
'#attributes' => [
|
||||
'class' => ['webform-location-geocomplete'],
|
||||
],
|
||||
];
|
||||
$elements += $attributes;
|
||||
return $elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function preRenderCompositeFormElement($element) {
|
||||
$element = WebformCompositeBase::preRenderCompositeFormElement($element);
|
||||
|
||||
// Hide location element webform display only if #geolocation is also set.
|
||||
if (!empty($element['#hidden']) && !empty($element['#geolocation'])) {
|
||||
$element['#attributes']['style'] = 'display: none';
|
||||
}
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function processWebformComposite(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
$element = parent::processWebformComposite($element, $form_state, $complete_form);
|
||||
|
||||
// Composite elements should always be displayed and rendered so that
|
||||
// location data can be populated, so #access is really just converting the
|
||||
// readonly elements to hidden elements.
|
||||
$composite_elements = static::getCompositeElements();
|
||||
foreach ($composite_elements as $composite_key => $composite_element) {
|
||||
if ($composite_key != 'value') {
|
||||
if (isset($element[$composite_key]['#access']) && $element[$composite_key]['#access'] === FALSE) {
|
||||
unset($element[$composite_key]['#access']);
|
||||
$element[$composite_key]['#type'] = 'hidden';
|
||||
}
|
||||
elseif (!empty($element['#hidden']) && !empty($element['#geolocation'])) {
|
||||
$element[$composite_key]['#type'] = 'hidden';
|
||||
}
|
||||
else {
|
||||
$element[$composite_key]['#attributes']['class'][] = 'webform-readonly';
|
||||
$element[$composite_key]['#readonly'] = 'readonly';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set required.
|
||||
if (isset($element['#required'])) {
|
||||
$element['value']['#required'] = $element['#required'];
|
||||
}
|
||||
|
||||
// Set Geolocation detection attribute.
|
||||
if (!empty($element['#geolocation'])) {
|
||||
$element['value']['#attributes']['data-webform-location-geolocation'] = 'data-webform-location-geolocation';
|
||||
}
|
||||
|
||||
// Writing script tags (only once) directly into the page's output to ensure
|
||||
// that Google Maps APi script is loaded using the proper API key.
|
||||
static $google_api;
|
||||
if (empty($google_api)) {
|
||||
$api_key = (!empty($element['#api_key'])) ? $element['#api_key'] : \Drupal::config('webform.settings')->get('elements.default_google_maps_api_key');
|
||||
$element['script'] = [
|
||||
'#markup' => '<script src="https://maps.googleapis.com/maps/api/js?key=' . $api_key . '&libraries=places"></script>',
|
||||
'#allowed_tags' => ['script'],
|
||||
];
|
||||
$google_api = TRUE;
|
||||
}
|
||||
|
||||
$element['#attached']['library'][] = 'webform/webform.element.location';
|
||||
|
||||
$element['#element_validate'] = [[get_called_class(), 'validateWebformLocation']];
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates location.
|
||||
*/
|
||||
public static function validateWebformLocation(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
$value = $element['#value'];
|
||||
|
||||
$has_access = (!isset($element['#access']) || $element['#access'] === TRUE);
|
||||
if ($has_access && !empty($element['#required']) && empty($value['location'])) {
|
||||
$t_args = ['@title' => !empty($element['#title']) ? $element['#title'] : t('Location')];
|
||||
$form_state->setError($element, t('The @title is not valid.', $t_args));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
150
web/modules/contrib/webform/src/Element/WebformLocationBase.php
Normal file
150
web/modules/contrib/webform/src/Element/WebformLocationBase.php
Normal file
|
@ -0,0 +1,150 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Provides a webform base element for a location element.
|
||||
*/
|
||||
abstract class WebformLocationBase extends WebformCompositeBase {
|
||||
|
||||
/**
|
||||
* The location element's class name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected static $name;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
return parent::getInfo() + [
|
||||
'#theme' => 'webform_composite_location',
|
||||
'#map' => FALSE,
|
||||
'#geolocation' => FALSE,
|
||||
'#hidden' => FALSE,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get location attributes.
|
||||
*
|
||||
* @return array
|
||||
* An associative array container location attribute name and titles.
|
||||
*/
|
||||
public static function getLocationAttributes() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getCompositeElements(array $element) {
|
||||
$elements = [];
|
||||
|
||||
$elements['value'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Address'),
|
||||
'#attributes' => [
|
||||
'class' => ['webform-location-' . static::$name],
|
||||
],
|
||||
];
|
||||
|
||||
$attributes = static::getLocationAttributes();
|
||||
foreach ($attributes as $name => $title) {
|
||||
$elements[$name] = [
|
||||
'#title' => $title,
|
||||
'#type' => 'textfield',
|
||||
'#error_no_message' => TRUE,
|
||||
'#attributes' => [
|
||||
'data-webform-location-' . static::$name . '-attribute' => $name,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return $elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function preRenderWebformCompositeFormElement($element) {
|
||||
// Hide location element webform display only if #geolocation is also set.
|
||||
if (!empty($element['#hidden']) && !empty($element['#geolocation'])) {
|
||||
$element['#wrapper_attributes']['style'] = 'display: none';
|
||||
}
|
||||
|
||||
$element = WebformCompositeBase::preRenderWebformCompositeFormElement($element);
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function processWebformComposite(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
$element = parent::processWebformComposite($element, $form_state, $complete_form);
|
||||
// Composite elements should always be displayed and rendered so that
|
||||
// location data can be populated, so #access is really just converting the
|
||||
// readonly elements to hidden elements.
|
||||
$composite_elements = static::getCompositeElements($element);
|
||||
foreach ($composite_elements as $composite_key => $composite_element) {
|
||||
if ($composite_key != 'value') {
|
||||
if (isset($element[$composite_key]['#access']) && $element[$composite_key]['#access'] === FALSE) {
|
||||
unset($element[$composite_key]['#access']);
|
||||
unset($element[$composite_key]['#pre_render']);
|
||||
$element[$composite_key]['#type'] = 'hidden';
|
||||
}
|
||||
elseif (!empty($element['#hidden']) && !empty($element['#geolocation'])) {
|
||||
unset($element[$composite_key]['#pre_render']);
|
||||
$element[$composite_key]['#type'] = 'hidden';
|
||||
}
|
||||
else {
|
||||
$element[$composite_key]['#wrapper_attributes']['class'][] = 'webform-readonly';
|
||||
$element[$composite_key]['#readonly'] = 'readonly';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get shared properties.
|
||||
$shared_properties = [
|
||||
'#required',
|
||||
'#placeholder',
|
||||
];
|
||||
$element['value'] += array_intersect_key($element, array_combine($shared_properties, $shared_properties));
|
||||
|
||||
// Set Geolocation detection attribute.
|
||||
if (!empty($element['#geolocation'])) {
|
||||
$element['value']['#attributes']['data-webform-location-' . static::$name . '-geolocation'] = 'data-webform-location-' . static::$name . '-geolocation';
|
||||
}
|
||||
|
||||
// Set Map attribute.
|
||||
if (!empty($element['#map']) && empty($element['#hidden'])) {
|
||||
$element['value']['#attributes']['data-webform-location-' . static::$name . '-map'] = 'data-webform-location-' . static::$name . '-map';
|
||||
}
|
||||
|
||||
$element += ['#element_validate' => []];
|
||||
array_unshift($element['#element_validate'], [get_called_class(), 'validateWebformLocation']);
|
||||
|
||||
// Attach library.
|
||||
$element['#attached']['library'][] = 'webform/webform.element.location.' . static::$name;
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates location.
|
||||
*/
|
||||
public static function validateWebformLocation(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
$value = $element['#value'];
|
||||
|
||||
$has_access = (!isset($element['#access']) || $element['#access'] === TRUE);
|
||||
if ($has_access && !empty($element['#required']) && empty($value['lat'])) {
|
||||
$t_args = ['@title' => !empty($element['#title']) ? $element['#title'] : t('Location')];
|
||||
$form_state->setError($element['value'], t('The @title is not valid.', $t_args));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Provides a webform element for a location geocomplete element.
|
||||
*
|
||||
* @FormElement("webform_location_geocomplete")
|
||||
*/
|
||||
class WebformLocationGeocomplete extends WebformLocationBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $name = 'geocomplete';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
return parent::getInfo() + [
|
||||
'#api_key' => '',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getLocationAttributes() {
|
||||
return [
|
||||
'lat' => t('Latitude'),
|
||||
'lng' => t('Longitude'),
|
||||
'location' => t('Location'),
|
||||
'formatted_address' => t('Formatted Address'),
|
||||
'street_address' => t('Street Address'),
|
||||
'street_number' => t('Street Number'),
|
||||
'subpremise' => t('Unit'),
|
||||
'postal_code' => t('Postal Code'),
|
||||
'locality' => t('Locality'),
|
||||
'sublocality' => t('City'),
|
||||
'administrative_area_level_1' => t('State/Province'),
|
||||
'country' => t('Country'),
|
||||
'country_short' => t('Country Code'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function processWebformComposite(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
$element = parent::processWebformComposite($element, $form_state, $complete_form);
|
||||
|
||||
// Add Google Maps API key which is required by
|
||||
// https://maps.googleapis.com/maps/api/js?key=API_KEY&libraries=places
|
||||
// @see webform_js_alter()
|
||||
$api_key = (!empty($element['#api_key'])) ? $element['#api_key'] : \Drupal::config('webform.settings')->get('element.default_google_maps_api_key');
|
||||
$element['#attached']['drupalSettings']['webform']['location']['geocomplete']['api_key'] = $api_key;
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Provides a webform element for a location places element.
|
||||
*
|
||||
* @FormElement("webform_location_places")
|
||||
*/
|
||||
class WebformLocationPlaces extends WebformLocationBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $name = 'places';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
return parent::getInfo() + [
|
||||
'#app_id' => '',
|
||||
'#api_key' => '',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getLocationAttributes() {
|
||||
return [
|
||||
'lat' => t('Latitude'),
|
||||
'lng' => t('Longitude'),
|
||||
'name' => t('Name'),
|
||||
'city' => t('City'),
|
||||
'country' => t('Country'),
|
||||
'country_code' => t('Country Code'),
|
||||
'administrative' => t('State/Province'),
|
||||
'county' => t('County'),
|
||||
'suburb' => t('Suburb'),
|
||||
'postcode' => t('Postal Code'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function processWebformComposite(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
$element = parent::processWebformComposite($element, $form_state, $complete_form);
|
||||
|
||||
// Add Algolia application id and API key.
|
||||
$app_id = (!empty($element['#app_id'])) ? $element['#app_id'] : \Drupal::config('webform.settings')->get('element.default_algolia_places_app_id');
|
||||
$api_key = (!empty($element['#api_key'])) ? $element['#api_key'] : \Drupal::config('webform.settings')->get('element.default_algolia_places_api_key');
|
||||
$element['#attached']['drupalSettings']['webform']['location']['places'] = [
|
||||
'app_id' => $app_id,
|
||||
'api_key' => $api_key,
|
||||
];
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
}
|
|
@ -5,6 +5,10 @@ namespace Drupal\webform\Element;
|
|||
use Drupal\Core\Render\Element\FormElement;
|
||||
use Drupal\file\Element\ManagedFile;
|
||||
|
||||
// As we do not force dependency on the core file module, we do this If
|
||||
// statement. So if File module is enabled, we use it, otherwise we fallback on
|
||||
// useless dummy implementation just to keep PHP interpreter happy about
|
||||
// inheriting an existing class.
|
||||
if (class_exists('\Drupal\file\Element\ManagedFile')) {
|
||||
|
||||
/**
|
||||
|
@ -13,7 +17,7 @@ if (class_exists('\Drupal\file\Element\ManagedFile')) {
|
|||
abstract class WebformManagedFileBase extends ManagedFile {
|
||||
|
||||
/**
|
||||
* The the types of files that the server accepts.
|
||||
* The types of files that the server accepts.
|
||||
*
|
||||
* @var string
|
||||
*
|
||||
|
@ -34,10 +38,15 @@ if (class_exists('\Drupal\file\Element\ManagedFile')) {
|
|||
* Render API callback: Adds media capture to the managed_file element type.
|
||||
*/
|
||||
public static function preRenderWebformManagedFile($element) {
|
||||
// Set accept and capture attributes.
|
||||
if (isset($element['upload']) && static::$accept) {
|
||||
$element['upload']['#attributes']['accept'] = static::$accept;;
|
||||
$element['upload']['#attributes']['capture'] = TRUE;
|
||||
}
|
||||
|
||||
// Add class name to wrapper attributes.
|
||||
$class_name = str_replace('_', '-', $element['#type']);
|
||||
static::setAttributes($element, ['js-' . $class_name, $class_name]);
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
|
|
155
web/modules/contrib/webform/src/Element/WebformMapping.php
Normal file
155
web/modules/contrib/webform/src/Element/WebformMapping.php
Normal file
|
@ -0,0 +1,155 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\Element\FormElement;
|
||||
use Drupal\webform\Utility\WebformElementHelper;
|
||||
|
||||
/**
|
||||
* Provides a mapping element.
|
||||
*
|
||||
* @FormElement("webform_mapping")
|
||||
*/
|
||||
class WebformMapping extends FormElement {
|
||||
|
||||
/**
|
||||
* Require all.
|
||||
*/
|
||||
const REQUIRED_ALL = 'all';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
$class = get_class($this);
|
||||
return [
|
||||
'#input' => TRUE,
|
||||
'#process' => [
|
||||
[$class, 'processWebformMapping'],
|
||||
[$class, 'processAjaxForm'],
|
||||
],
|
||||
'#theme_wrappers' => ['form_element'],
|
||||
'#required' => FALSE,
|
||||
'#source' => [],
|
||||
'#destination' => [],
|
||||
'#arrow' => '→',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a likert scale webform element.
|
||||
*/
|
||||
public static function processWebformMapping(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
// Set translated default properties.
|
||||
$element += [
|
||||
'#source__title' => t('Source'),
|
||||
'#destination__title' => t('Destination'),
|
||||
'#arrow' => '→',
|
||||
];
|
||||
|
||||
$arrow = htmlentities($element['#arrow']);
|
||||
|
||||
// Setup destination__type depending if #destination is defined.
|
||||
if (empty($element['#destination__type'])) {
|
||||
$element['#destination__type'] = (empty($element['#destination'])) ? 'textfield' : 'select';
|
||||
}
|
||||
|
||||
// Set base destination element.
|
||||
$destination_element_base = [
|
||||
'#title_display' => 'invisible',
|
||||
'#required' => ($element['#required'] === self::REQUIRED_ALL) ? TRUE : FALSE,
|
||||
];
|
||||
|
||||
// Get base #destination__* properties.
|
||||
foreach ($element as $element_key => $element_value) {
|
||||
if (strpos($element_key, '#destination__') === 0 && !in_array($element_key, ['#destination__title'])) {
|
||||
$destination_element_base[str_replace('#destination__', '#', $element_key)] = $element_value;
|
||||
}
|
||||
}
|
||||
|
||||
// Build header.
|
||||
$header = [
|
||||
['data' => ['#markup' => $element['#source__title'] . ' ' . $arrow], 'width' => '50%'],
|
||||
['data' => ['#markup' => $element['#destination__title']], 'width' => '50%'],
|
||||
];
|
||||
|
||||
// Build rows.
|
||||
$rows = [];
|
||||
foreach ($element['#source'] as $source_key => $source_title) {
|
||||
$default_value = (isset($element['#default_value'][$source_key])) ? $element['#default_value'][$source_key] : NULL;
|
||||
|
||||
$destination_element = $destination_element_base + [
|
||||
'#title' => $source_title,
|
||||
'#required' => $element['#required'],
|
||||
'#default_value' => $default_value,
|
||||
];
|
||||
|
||||
// Apply #parents to destination element.
|
||||
if (isset($element['#parents'])) {
|
||||
$destination_element['#parents'] = array_merge($element['#parents'], [$source_key]);
|
||||
}
|
||||
|
||||
switch ($element['#destination__type']) {
|
||||
case 'select':
|
||||
case 'webform_select_other':
|
||||
$destination_element += [
|
||||
'#empty_option' => t('- Select -'),
|
||||
'#options' => $element['#destination'],
|
||||
];
|
||||
break;
|
||||
}
|
||||
|
||||
$rows[$source_key] = [
|
||||
'source' => ['#markup' => $source_title . ' ' . $arrow],
|
||||
$source_key => $destination_element,
|
||||
];
|
||||
}
|
||||
|
||||
$element['table'] = [
|
||||
'#tree' => TRUE,
|
||||
'#type' => 'table',
|
||||
'#header' => $header,
|
||||
'#attributes' => [
|
||||
'class' => ['webform-mapping-table'],
|
||||
],
|
||||
] + $rows;
|
||||
|
||||
// Build table element with selected properties.
|
||||
$properties = [
|
||||
'#states',
|
||||
'#sticky',
|
||||
];
|
||||
$element['table'] += array_intersect_key($element, array_combine($properties, $properties));
|
||||
|
||||
// Add validate callback.
|
||||
$element += ['#element_validate' => []];
|
||||
array_unshift($element['#element_validate'], [get_called_class(), 'validateWebformMapping']);
|
||||
|
||||
if (!empty($element['#states'])) {
|
||||
webform_process_states($element, '#wrapper_attributes');
|
||||
}
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a mapping element.
|
||||
*/
|
||||
public static function validateWebformMapping(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
$value = NestedArray::getValue($form_state->getValues(), $element['#parents']);
|
||||
$value = array_filter($value);
|
||||
|
||||
// Note: Not validating REQUIRED_ALL because each destination element is
|
||||
// already required.
|
||||
$has_access = (!isset($element['#access']) || $element['#access'] === TRUE);
|
||||
if ($element['#required'] && $element['#required'] !== self::REQUIRED_ALL && empty($value) && $has_access) {
|
||||
WebformElementHelper::setRequiredError($element, $form_state);
|
||||
}
|
||||
|
||||
$element['#value'] = $value;
|
||||
$form_state->setValueForElement($element, $value);
|
||||
}
|
||||
|
||||
}
|
|
@ -38,6 +38,13 @@ class WebformMessage extends RenderElement {
|
|||
*/
|
||||
const STORAGE_STATE = 'state';
|
||||
|
||||
/**
|
||||
* Storage custom.
|
||||
*
|
||||
* @see hook_webform_message_custom()
|
||||
*/
|
||||
const STORAGE_CUSTOM = 'custom';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -80,8 +87,7 @@ class WebformMessage extends RenderElement {
|
|||
$element['#attributes']['class'][] = 'js-webform-message';
|
||||
|
||||
// Ignore 'user' and 'state' storage is current user is anonymous.
|
||||
if (\Drupal::currentUser()->isAnonymous() && in_array($message_storage, [self::STORAGE_USER, self::STORAGE_STATE])
|
||||
) {
|
||||
if (\Drupal::currentUser()->isAnonymous() && in_array($message_storage, [self::STORAGE_USER, self::STORAGE_STATE, self::STORAGE_CUSTOM])) {
|
||||
$message_storage = '';
|
||||
}
|
||||
|
||||
|
@ -98,7 +104,7 @@ class WebformMessage extends RenderElement {
|
|||
'aria-label' => t('close'),
|
||||
'class' => ['js-webform-message__link', 'webform-message__link'],
|
||||
];
|
||||
if (in_array($message_storage, ['user', 'state'])) {
|
||||
if (in_array($message_storage, [self::STORAGE_USER, self::STORAGE_STATE, self::STORAGE_CUSTOM])) {
|
||||
$close_url = Url::fromRoute('webform.element.message.close', [
|
||||
'storage' => $message_storage,
|
||||
'id' => $message_id,
|
||||
|
@ -120,7 +126,7 @@ class WebformMessage extends RenderElement {
|
|||
$element['#attributes']['data-message-id'] = $message_id;
|
||||
$element['#attributes']['data-message-storage'] = $message_storage;
|
||||
$element['#attributes']['class'][] = 'js-webform-message--close-storage';
|
||||
if (self::isClosed($message_storage, $message_id)) {
|
||||
if (static::isClosed($message_storage, $message_id)) {
|
||||
$element['#closed'] = TRUE;
|
||||
}
|
||||
}
|
||||
|
@ -153,7 +159,7 @@ class WebformMessage extends RenderElement {
|
|||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* Is message closed via User Data or State API.
|
||||
* Is message closed via User Data, State API, or Custom.
|
||||
*
|
||||
* @param string $storage
|
||||
* The storage mechanism to check if a message is closed.
|
||||
|
@ -179,12 +185,15 @@ class WebformMessage extends RenderElement {
|
|||
$values = $user_data->get('webform', $account->id(), $namespace) ?: [];
|
||||
return (isset($values[$id])) ? TRUE : FALSE;
|
||||
|
||||
case self::STORAGE_CUSTOM:
|
||||
$result = \Drupal::moduleHandler()->invokeAll('webform_message_custom', ['closed', $id]);
|
||||
return array_filter($result) ? TRUE : FALSE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set message closed via User Data or State API.
|
||||
* Set message closed via User Data, State API, or Custom.
|
||||
*
|
||||
* @param string $storage
|
||||
* The storage mechanism save message closed.
|
||||
|
@ -211,11 +220,16 @@ class WebformMessage extends RenderElement {
|
|||
$values = $user_data->get('webform', $account->id(), $namespace) ?: [];
|
||||
$values[$id] = TRUE;
|
||||
$user_data->set('webform', $account->id(), $namespace, $values);
|
||||
break;
|
||||
|
||||
case self::STORAGE_CUSTOM:
|
||||
\Drupal::moduleHandler()->invokeAll('webform_message_custom', ['close', $id]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset message closed via User Data or State API.
|
||||
* Reset message closed via User Data, State API, or Custom.
|
||||
*
|
||||
* @param string $storage
|
||||
* The storage mechanism save message closed.
|
||||
|
@ -242,6 +256,11 @@ class WebformMessage extends RenderElement {
|
|||
$values = $user_data->get('webform', $account->id(), $namespace) ?: [];
|
||||
unset($values[$id]);
|
||||
$user_data->set('webform', $account->id(), $namespace, $values);
|
||||
break;
|
||||
|
||||
case self::STORAGE_CUSTOM:
|
||||
\Drupal::moduleHandler()->invokeAll('webform_message_custom', ['reset', $id]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
26
web/modules/contrib/webform/src/Element/WebformMore.php
Normal file
26
web/modules/contrib/webform/src/Element/WebformMore.php
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\Core\Render\Element\RenderElement;
|
||||
|
||||
/**
|
||||
* Provides a render element for more.
|
||||
*
|
||||
* @FormElement("webform_more")
|
||||
*/
|
||||
class WebformMore extends RenderElement {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
return [
|
||||
'#theme' => 'webform_element_more',
|
||||
'#more' => '',
|
||||
'#more_title' => '',
|
||||
'#attributes' => [],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -12,8 +12,18 @@ class WebformName extends WebformCompositeBase {
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getCompositeElements() {
|
||||
public function getInfo() {
|
||||
return parent::getInfo() + ['#theme' => 'webform_composite_name'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getCompositeElements(array $element) {
|
||||
$elements = [];
|
||||
// Any webform options prefixed with 'title' will automatically
|
||||
// be included within the Composite Element UI.
|
||||
// @see \Drupal\webform\Plugin\WebformElement\WebformCompositeBase::getCompositeElementOptions
|
||||
$elements['title'] = [
|
||||
'#type' => 'webform_select_other',
|
||||
'#title' => t('Title'),
|
||||
|
|
|
@ -3,10 +3,13 @@
|
|||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Serialization\Yaml;
|
||||
use Drupal\Core\Render\Element\FormElement;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\webform\Utility\WebformElementHelper;
|
||||
use Drupal\webform\Utility\WebformOptionsHelper;
|
||||
use Drupal\webform\Utility\WebformYaml;
|
||||
|
||||
/**
|
||||
* Provides a webform element to assist in creation of options.
|
||||
|
@ -25,10 +28,16 @@ class WebformOptions extends FormElement {
|
|||
$class = get_class($this);
|
||||
return [
|
||||
'#input' => TRUE,
|
||||
'#yaml' => FALSE,
|
||||
'#label' => t('option'),
|
||||
'#labels' => t('options'),
|
||||
'#empty_items' => 5,
|
||||
'#add_more' => 1,
|
||||
'#min_items' => 3,
|
||||
'#empty_items' => 1,
|
||||
'#add_more_items' => 1,
|
||||
'#options_value_maxlength' => 255,
|
||||
'#options_text_maxlength' => 255,
|
||||
'#options_description' => FALSE,
|
||||
'#options_description_maxlength' => NULL,
|
||||
'#process' => [
|
||||
[$class, 'processWebformOptions'],
|
||||
],
|
||||
|
@ -46,10 +55,10 @@ class WebformOptions extends FormElement {
|
|||
}
|
||||
|
||||
$options = (is_string($element['#default_value'])) ? Yaml::decode($element['#default_value']) : $element['#default_value'];
|
||||
if (self::hasOptGroup($options)) {
|
||||
if (static::hasOptGroup($options)) {
|
||||
return $options;
|
||||
}
|
||||
return self::convertOptionsToValues($options);
|
||||
return static::convertOptionsToValues($options, $element['#options_description']);
|
||||
}
|
||||
elseif (is_array($input) && isset($input['options'])) {
|
||||
return (is_string($input['options'])) ? Yaml::decode($input['options']) : $input['options'];
|
||||
|
@ -66,44 +75,95 @@ class WebformOptions extends FormElement {
|
|||
$element['#tree'] = TRUE;
|
||||
|
||||
// Add validate callback that extracts the associative array of options.
|
||||
$element['#element_validate'] = [[get_called_class(), 'validateWebformOptions']];
|
||||
$element += ['#element_validate' => []];
|
||||
array_unshift($element['#element_validate'], [get_called_class(), 'validateWebformOptions']);
|
||||
|
||||
// Wrap this $element in a <div> that handle #states.
|
||||
WebformElementHelper::fixStatesWrapper($element);
|
||||
|
||||
// For options with optgroup display a CodeMirror YAML editor.
|
||||
if (isset($element['#default_value']) && is_array($element['#default_value']) && self::hasOptGroup($element['#default_value'])) {
|
||||
if (!empty($element['#yaml']) || (isset($element['#default_value']) && is_array($element['#default_value']) && static::hasOptGroup($element['#default_value']))) {
|
||||
// Build table.
|
||||
$element['options'] = [
|
||||
'#type' => 'webform_codemirror',
|
||||
'#mode' => 'yaml',
|
||||
'#default_value' => Yaml::encode($element['#default_value']),
|
||||
'#description' => t('Key-value pairs MUST be specified as "safe_key: \'Some readable options\'". Use of only alphanumeric characters and underscores is recommended in keys. One option per line.') . '<br/>' .
|
||||
t('Option groups can be created by using just the group name followed by indented group options.'),
|
||||
'#default_value' => WebformYaml::encode($element['#default_value']),
|
||||
'#placeholder' => t('Enter custom options…'),
|
||||
'#description' => t('Key-value pairs MUST be specified as "safe_key: \'Some readable options\'". Use of only alphanumeric characters and underscores is recommended in keys. One option per line.') . '<br /><br />' .
|
||||
t('Option groups can be created by using just the group name followed by indented group options.'),
|
||||
];
|
||||
return $element;
|
||||
}
|
||||
else {
|
||||
$properties = ['#label', '#labels', '#empty_items', '#add_more'];
|
||||
$t_args = ['@label' => isset($element['#label']) ? Unicode::ucfirst($element['#label']) : t('Options')];
|
||||
$properties = ['#label', '#labels', '#min_items', '#empty_items', '#add_more_items'];
|
||||
|
||||
$element['options'] = array_intersect_key($element, array_combine($properties, $properties)) + [
|
||||
'#type' => 'webform_multiple',
|
||||
'#header' => TRUE,
|
||||
'#element' => [
|
||||
'#default_value' => (isset($element['#default_value'])) ? static::convertOptionsToValues($element['#default_value'], $element['#options_description']) : [],
|
||||
'#error_no_message' => TRUE,
|
||||
'#add_more_input_label' => t('more options'),
|
||||
];
|
||||
|
||||
if ($element['#options_description']) {
|
||||
$element['options']['#element'] = [
|
||||
'value' => [
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Option value'),
|
||||
'#title_display' => t('invisible'),
|
||||
'#placeholder' => t('Enter value'),
|
||||
'#title' => t('@label value', $t_args),
|
||||
'#title_display' => 'invisible',
|
||||
'#placeholder' => t('Enter value…'),
|
||||
'#attributes' => ['class' => ['js-webform-options-sync']],
|
||||
'#maxlength' => $element['#options_value_maxlength'],
|
||||
'#error_no_message' => TRUE,
|
||||
],
|
||||
'option' => [
|
||||
'#type' => 'container',
|
||||
'#title' => t('@label text/description', $t_args),
|
||||
'#title_display' => 'invisible',
|
||||
'text' => [
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('@label text', $t_args),
|
||||
'#title_display' => 'invisible',
|
||||
'#placeholder' => t('Enter text…'),
|
||||
'#maxlength' => $element['#options_text_maxlength'],
|
||||
'#error_no_message' => TRUE,
|
||||
],
|
||||
'description' => [
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('@label description', $t_args),
|
||||
'#title_display' => 'invisible',
|
||||
'#placeholder' => t('Enter description…'),
|
||||
'#rows' => 2,
|
||||
'#maxlength' => $element['#options_description_maxlength'],
|
||||
'#error_no_message' => TRUE,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
else {
|
||||
$element['options']['#element'] = [
|
||||
'value' => [
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('@label value', $t_args),
|
||||
'#title_display' => 'invisible',
|
||||
'#placeholder' => t('Enter value…'),
|
||||
'#attributes' => ['class' => ['js-webform-options-sync']],
|
||||
'#maxlength' => $element['#options_value_maxlength'],
|
||||
'#error_no_message' => TRUE,
|
||||
],
|
||||
'text' => [
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Option text'),
|
||||
'#title_display' => t('invisible'),
|
||||
'#placeholder' => t('Enter text'),
|
||||
'#title' => t('@label text', $t_args),
|
||||
'#title_display' => 'invisible',
|
||||
'#placeholder' => t('Enter text…'),
|
||||
'#maxlength' => $element['#options_text_maxlength'],
|
||||
'#error_no_message' => TRUE,
|
||||
],
|
||||
],
|
||||
'#default_value' => (isset($element['#default_value'])) ? self::convertOptionsToValues($element['#default_value']) : [],
|
||||
];
|
||||
];
|
||||
}
|
||||
|
||||
$element['#attached']['library'][] = 'webform/webform.element.options.admin';
|
||||
return $element;
|
||||
}
|
||||
}
|
||||
|
@ -118,23 +178,16 @@ class WebformOptions extends FormElement {
|
|||
$options = Yaml::decode($options_value);
|
||||
}
|
||||
else {
|
||||
$options = self::convertValuesToOptions($options_value);
|
||||
$options = static::convertValuesToOptions($options_value, $element['#options_description']);
|
||||
}
|
||||
|
||||
// Validate required options.
|
||||
if (!empty($element['#required']) && empty($options)) {
|
||||
if (isset($element['#required_error'])) {
|
||||
$form_state->setError($element, $element['#required_error']);
|
||||
}
|
||||
elseif (isset($element['#title'])) {
|
||||
$form_state->setError($element, t('@name field is required.', ['@name' => $element['#title']]));
|
||||
}
|
||||
else {
|
||||
$form_state->setError($element);
|
||||
}
|
||||
WebformElementHelper::setRequiredError($element, $form_state);
|
||||
return;
|
||||
}
|
||||
|
||||
$element['#value'] = $options;
|
||||
$form_state->setValueForElement($element, $options);
|
||||
}
|
||||
|
||||
|
@ -147,25 +200,32 @@ class WebformOptions extends FormElement {
|
|||
*
|
||||
* @param array $values
|
||||
* An array of values.
|
||||
* @param bool $options_description
|
||||
* Options has description.
|
||||
*
|
||||
* @return array
|
||||
* An array of options.
|
||||
*/
|
||||
public static function convertValuesToOptions(array $values = []) {
|
||||
public static function convertValuesToOptions(array $values = NULL, $options_description = FALSE) {
|
||||
$options = [];
|
||||
foreach ($values as $value) {
|
||||
$option_value = $value['value'];
|
||||
$option_text = $value['text'];
|
||||
if ($values && is_array($values)) {
|
||||
foreach ($values as $value) {
|
||||
$option_value = $value['value'];
|
||||
$option_text = $value['text'];
|
||||
if ($options_description && !empty($value['description'])) {
|
||||
$option_text .= WebformOptionsHelper::DESCRIPTION_DELIMITER . $value['description'];
|
||||
}
|
||||
|
||||
// Populate empty option value or option text.
|
||||
if ($option_value === '') {
|
||||
$option_value = $option_text;
|
||||
}
|
||||
elseif ($option_text === '') {
|
||||
$option_text = $option_value;
|
||||
}
|
||||
// Populate empty option value or option text.
|
||||
if ($option_value === '') {
|
||||
$option_value = $option_text;
|
||||
}
|
||||
elseif ($option_text === '') {
|
||||
$option_text = $option_value;
|
||||
}
|
||||
|
||||
$options[$option_value] = $option_text;
|
||||
$options[$option_value] = $option_text;
|
||||
}
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
@ -175,14 +235,22 @@ class WebformOptions extends FormElement {
|
|||
*
|
||||
* @param array $options
|
||||
* An array of options.
|
||||
* @param bool $options_description
|
||||
* Options has description.
|
||||
*
|
||||
* @return array
|
||||
* An array of values.
|
||||
*/
|
||||
public static function convertOptionsToValues(array $options = []) {
|
||||
public static function convertOptionsToValues(array $options = [], $options_description = FALSE) {
|
||||
$values = [];
|
||||
foreach ($options as $value => $text) {
|
||||
$values[] = ['value' => $value, 'text' => $text];
|
||||
if ($options_description && strpos($text, WebformOptionsHelper::DESCRIPTION_DELIMITER) !== FALSE) {
|
||||
list($text, $description) = explode(WebformOptionsHelper::DESCRIPTION_DELIMITER, $text);
|
||||
$values[] = ['value' => $value, 'text' => $text, 'description' => $description];
|
||||
}
|
||||
else {
|
||||
$values[] = ['value' => $value, 'text' => $text];
|
||||
}
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ use Drupal\Component\Utility\NestedArray;
|
|||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Form\OptGroup;
|
||||
use Drupal\Core\Render\Element\FormElement;
|
||||
use Drupal\webform\Utility\WebformElementHelper;
|
||||
use Drupal\webform\Utility\WebformOptionsHelper;
|
||||
|
||||
/**
|
||||
|
@ -13,6 +14,8 @@ use Drupal\webform\Utility\WebformOptionsHelper;
|
|||
*/
|
||||
abstract class WebformOtherBase extends FormElement {
|
||||
|
||||
use WebformCompositeFormElementTrait;
|
||||
|
||||
/**
|
||||
* Other option value.
|
||||
*/
|
||||
|
@ -31,7 +34,9 @@ abstract class WebformOtherBase extends FormElement {
|
|||
* @var array
|
||||
*/
|
||||
protected static $properties = [
|
||||
'#title',
|
||||
'#required',
|
||||
'#required_error',
|
||||
'#options',
|
||||
'#options_display',
|
||||
'#default_value',
|
||||
|
@ -49,10 +54,15 @@ abstract class WebformOtherBase extends FormElement {
|
|||
[$class, 'processWebformOther'],
|
||||
[$class, 'processAjaxForm'],
|
||||
],
|
||||
'#theme_wrappers' => ['form_element'],
|
||||
'#pre_render' => [
|
||||
[$class, 'preRenderWebformCompositeFormElement'],
|
||||
],
|
||||
'#options' => [],
|
||||
'#other__option_delimiter' => ', ',
|
||||
'#states' => [],
|
||||
// Add '#markup' property to add an 'id' attribute to the form element.
|
||||
// @see template_preprocess_form_element()
|
||||
'#markup' => '',
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -64,7 +74,7 @@ abstract class WebformOtherBase extends FormElement {
|
|||
$type = str_replace('webform_', '', static::$type);
|
||||
|
||||
if ($input === FALSE) {
|
||||
$value = self::convertDefaultValueToElementValue($element);
|
||||
$value = static::convertDefaultValueToElementValue($element);
|
||||
$element[$type]['#default_value'] = $value[$type];
|
||||
if ($value['other'] !== NULL) {
|
||||
$element['other']['#default_value'] = $value['other'];
|
||||
|
@ -90,18 +100,21 @@ abstract class WebformOtherBase extends FormElement {
|
|||
|
||||
$element['#tree'] = TRUE;
|
||||
|
||||
$element['#wrapper_attributes']['class'][] = "js-webform-$type-other";
|
||||
$element['#wrapper_attributes']['class'][] = "webform-$type-other";
|
||||
|
||||
$element[$type]['#type'] = static::$type;
|
||||
$element[$type]['#webform_element'] = TRUE;
|
||||
$element[$type] += array_intersect_key($element, array_combine($properties, $properties));
|
||||
$element[$type]['#title_display'] = 'invisible';
|
||||
if (!isset($element[$type]['#options'][static::OTHER_OPTION])) {
|
||||
$element[$type]['#options'][static::OTHER_OPTION] = (!empty($element['#other__option_label'])) ? $element['#other__option_label'] : t('Other...');
|
||||
$element[$type]['#options'][static::OTHER_OPTION] = (!empty($element['#other__option_label'])) ? $element['#other__option_label'] : t('Other…');
|
||||
}
|
||||
$element[$type]['#error_no_message'] = TRUE;
|
||||
|
||||
// Prevent nested fieldset by removing fieldset theme wrapper around
|
||||
// radios and checkboxes.
|
||||
// @see \Drupal\Core\Render\Element\CompositeFormElementTrait
|
||||
$element[$type]['#pre_render'] = [];
|
||||
|
||||
// Build other textfield.
|
||||
$element['other']['#error_no_message'] = TRUE;
|
||||
foreach ($element as $key => $value) {
|
||||
if (strpos($key, '#other__') === 0) {
|
||||
$other_key = str_replace('#other__', '#', $key);
|
||||
|
@ -112,21 +125,53 @@ abstract class WebformOtherBase extends FormElement {
|
|||
}
|
||||
$element['other'] += [
|
||||
'#type' => 'textfield',
|
||||
'#placeholder' => t('Enter other...'),
|
||||
'#webform_element' => TRUE,
|
||||
'#placeholder' => t('Enter other…'),
|
||||
];
|
||||
if (!isset($element['other']['#title'])) {
|
||||
$element['other'] += [
|
||||
'#title' => $element['other']['#placeholder'] ,
|
||||
'#title_display' => 'invisible',
|
||||
];
|
||||
}
|
||||
|
||||
$element['other']['#wrapper_attributes']['class'][] = "js-webform-$type-other-input";
|
||||
$element['other']['#wrapper_attributes']['class'][] = "webform-$type-other-input";
|
||||
|
||||
if ($element['other']['#type'] == 'datetime') {
|
||||
$element['other']['#prefix'] = '<div class="' . implode(' ', $element['other']['#wrapper_attributes']['class']) . '">';
|
||||
$element['other']['#suffix'] = '</div>';
|
||||
unset($element['other']['#wrapper_attributes']['class']);
|
||||
}
|
||||
|
||||
// Apply #parents to $type and other element.
|
||||
if (isset($element['#parents'])) {
|
||||
$element[$type]['#parents'] = array_merge($element['#parents'], [$type]);
|
||||
$element['other']['#parents'] = array_merge($element['#parents'], ['other']);
|
||||
}
|
||||
|
||||
// Initialize the other element to allow for webform enhancements.
|
||||
/** @var \Drupal\webform\Plugin\WebformElementManagerInterface $element_manager */
|
||||
$element_manager = \Drupal::service('plugin.manager.webform.element');
|
||||
$element_manager->buildElement($element['other'], $complete_form, $form_state);
|
||||
|
||||
// Add attributes to the composite fieldset wrapper.
|
||||
// @see \Drupal\webform\Element\WebformCompositeFormElementTrait
|
||||
|
||||
// Add js trigger to fieldset.
|
||||
$element['#attributes']['class'][] = "js-webform-$type-other";
|
||||
$element['#attributes']['class'][] = "webform-$type-other";
|
||||
|
||||
// Apply the element id to the wrapper so that inline form errors point
|
||||
// to the correct element.
|
||||
$element['#attributes']['id'] = $element['#id'];
|
||||
|
||||
// Remove options.
|
||||
unset($element['#options']);
|
||||
|
||||
// Set validation.
|
||||
if (isset($element['#element_validate'])) {
|
||||
$element['#element_validate'] = array_merge([[get_called_class(), 'validateWebformOther']], $element['#element_validate']);
|
||||
}
|
||||
else {
|
||||
$element['#element_validate'] = [[get_called_class(), 'validateWebformOther']];
|
||||
}
|
||||
// Add validate callback.
|
||||
$element += ['#element_validate' => []];
|
||||
array_unshift($element['#element_validate'], [get_called_class(), 'validateWebformOther']);
|
||||
|
||||
// Attach library.
|
||||
$element['#attached']['library'][] = 'webform/webform.element.other';
|
||||
|
@ -141,66 +186,104 @@ abstract class WebformOtherBase extends FormElement {
|
|||
* Validates an other element.
|
||||
*/
|
||||
public static function validateWebformOther(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
$type = static::getElementType();
|
||||
|
||||
// Determine if the element is visible. (#access !== FALSE)
|
||||
$has_access = (!isset($element['#access']) || $element['#access'] === TRUE);
|
||||
|
||||
// Remove 'webform_' prefix from type.
|
||||
$type = str_replace('webform_', '', static::$type);
|
||||
// Determine if the element has mulitple values.
|
||||
$is_multiple = static::isMultiple($element);
|
||||
|
||||
// Get value.
|
||||
$value = NestedArray::getValue($form_state->getValues(), $element['#parents']);
|
||||
|
||||
// Get return value.
|
||||
$return_value = [];
|
||||
$element_value = $value[$type];
|
||||
$other_value = $value['other'];
|
||||
if (static::isMultiple($element)) {
|
||||
$element_value = array_filter($element_value);
|
||||
$return_value += $element_value;
|
||||
if (isset($element_value[static::OTHER_OPTION])) {
|
||||
unset($return_value[static::OTHER_OPTION]);
|
||||
if ($has_access && $other_value === '') {
|
||||
static::setOtherError($element, $form_state);
|
||||
return;
|
||||
}
|
||||
$return_value += [$other_value => $other_value];
|
||||
}
|
||||
}
|
||||
else {
|
||||
$return_value = $element_value;
|
||||
if ($element_value == static::OTHER_OPTION) {
|
||||
if ($has_access && $other_value === '') {
|
||||
static::setOtherError($element, $form_state);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
$return_value = $other_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
$return_value = static::processValue($element, $value);
|
||||
|
||||
// Determine if the return value is empty.
|
||||
if (static::isMultiple($element)) {
|
||||
if ($is_multiple) {
|
||||
$is_empty = (empty($return_value)) ? TRUE : FALSE;
|
||||
}
|
||||
else {
|
||||
$is_empty = ($return_value === '' || $return_value === NULL) ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
// Handler required validation.
|
||||
if ($element['#required'] && $is_empty && $has_access) {
|
||||
static::setElementError($element, $form_state);
|
||||
// Determine if there is an other value and is the other value empty.
|
||||
$element_value = (array) $value[$type];
|
||||
$other_value = $value['other'];
|
||||
if ($element_value) {
|
||||
$element_value = array_filter($element_value);
|
||||
$element_value = array_combine($element_value, $element_value);
|
||||
}
|
||||
$other_is_empty = (isset($element_value[static::OTHER_OPTION]) && $other_value === '');
|
||||
|
||||
// Display missing other or missing value error.
|
||||
if ($has_access) {
|
||||
$required_error_title = (isset($element['#title'])) ? $element['#title'] : NULL;
|
||||
if ($other_is_empty) {
|
||||
WebformElementHelper::setRequiredError($element['other'], $form_state, $required_error_title);
|
||||
}
|
||||
elseif ($element['#required'] && $is_empty) {
|
||||
WebformElementHelper::setRequiredError($element, $form_state, $required_error_title);
|
||||
$element['other']['#error_no_message'] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
$form_state->setValueForElement($element[$type], NULL);
|
||||
$form_state->setValueForElement($element['other'], NULL);
|
||||
|
||||
$element['#value'] = $return_value;
|
||||
$form_state->setValueForElement($element, $return_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processed other element's submitted value.
|
||||
*
|
||||
* @param array $element
|
||||
* The element.
|
||||
* @param array $value
|
||||
* The submitted value.
|
||||
*
|
||||
* @return array|string
|
||||
* An array of values or a string.
|
||||
*/
|
||||
public static function processValue(array $element, array $value) {
|
||||
$type = static::getElementType();
|
||||
|
||||
$element_value = $value[$type];
|
||||
$other_value = $value['other'];
|
||||
|
||||
if (static::isMultiple($element)) {
|
||||
$return_value = array_filter($element_value);
|
||||
$return_value = array_combine($return_value, $return_value);
|
||||
if (isset($return_value[static::OTHER_OPTION])) {
|
||||
unset($return_value[static::OTHER_OPTION]);
|
||||
if ($other_value !== '') {
|
||||
$return_value += [$other_value => $other_value];
|
||||
}
|
||||
}
|
||||
return $return_value;
|
||||
}
|
||||
else {
|
||||
return ($element_value === static::OTHER_OPTION) ? $other_value : $element_value;
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
// Helper functions.
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* Get the element type.
|
||||
*
|
||||
* @return string
|
||||
* The element type.
|
||||
*/
|
||||
public static function getElementType() {
|
||||
// Remove 'webform_' prefix from type.
|
||||
return str_replace('webform_', '', static::$type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the webform element contains multiple values.
|
||||
*
|
||||
|
@ -223,7 +306,7 @@ abstract class WebformOtherBase extends FormElement {
|
|||
* @return array
|
||||
* An associative array container (element) type and other value.
|
||||
*/
|
||||
protected static function convertDefaultValueToElementValue($element) {
|
||||
protected static function convertDefaultValueToElementValue(array $element) {
|
||||
$type = str_replace('webform_', '', static::$type);
|
||||
|
||||
$default_value = isset($element['#default_value']) ? $element['#default_value'] : NULL;
|
||||
|
@ -253,48 +336,4 @@ abstract class WebformOtherBase extends FormElement {
|
|||
}
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
// Error functions.
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* Set element required error.
|
||||
*
|
||||
* @param array $element
|
||||
* The webform element.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
*/
|
||||
protected static function setElementError(array &$element, FormStateInterface $form_state) {
|
||||
if (isset($element['#required_error'])) {
|
||||
$form_state->setError($element, $element['#required_error']);
|
||||
}
|
||||
elseif (isset($element['#title'])) {
|
||||
$form_state->setError($element, t('@name field is required.', ['@name' => $element['#title']]));
|
||||
}
|
||||
else {
|
||||
$form_state->setError($element);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set element required error.
|
||||
*
|
||||
* @param array $element
|
||||
* The webform element.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
*/
|
||||
protected static function setOtherError(array &$element, FormStateInterface $form_state) {
|
||||
if (isset($element['#required_error'])) {
|
||||
$form_state->setError($element['other'], $element['#required_error']);
|
||||
}
|
||||
elseif (isset($element['#title'])) {
|
||||
$form_state->setError($element['other'], t('@name field is required.', ['@name' => $element['#title']]));
|
||||
}
|
||||
else {
|
||||
$form_state->setError($element['other']);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\Element\Select;
|
||||
use Drupal\webform\Utility\WebformElementHelper;
|
||||
|
||||
/**
|
||||
* Provides a webform roles (select) element.
|
||||
*
|
||||
* @FormElement("webform_permissions")
|
||||
*/
|
||||
class WebformPermissions extends Select {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
$info = parent::getInfo();
|
||||
$class = get_class($this);
|
||||
$info['#element_validate'] = [
|
||||
[$class, 'validateWebformPermissions'],
|
||||
];
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a webform roles (select) element.
|
||||
*/
|
||||
public static function processSelect(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
/** @var \Drupal\user\PermissionHandlerInterface $permission_handler */
|
||||
$permission_handler = \Drupal::service('user.permissions');
|
||||
/** @var \Drupal\Core\Extension\ModuleHandlerInterface $module_handler */
|
||||
$module_handler = \Drupal::service('module_handler');
|
||||
|
||||
// Get list of permissions as options.
|
||||
$options = [];
|
||||
$permissions = $permission_handler->getPermissions();
|
||||
foreach ($permissions as $perm => $perm_item) {
|
||||
$provider = $perm_item['provider'];
|
||||
$display_name = $module_handler->getName($provider);
|
||||
$options[$display_name][$perm] = strip_tags($perm_item['title']);
|
||||
}
|
||||
$element['#options'] = $options;
|
||||
$element['#select2'] = TRUE;
|
||||
|
||||
// Must convert this element['#type'] to a 'select' to prevent
|
||||
// "Illegal choice %choice in %name element" validation error.
|
||||
// @see \Drupal\Core\Form\FormValidator::performRequiredValidation
|
||||
$element['#type'] = 'select';
|
||||
|
||||
WebformElementHelper::process($element);
|
||||
|
||||
return parent::processSelect($element, $form_state, $complete_form);
|
||||
}
|
||||
|
||||
/**
|
||||
* Webform element validation handler for webform roles (select) element.
|
||||
*/
|
||||
public static function validateWebformPermissions(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
if (!empty($element['#multiple'])) {
|
||||
$value = array_values($form_state->getValue($element['#parents'], []));
|
||||
$element['#value'] = $value;
|
||||
$form_state->setValueForElement($element, $value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -2,8 +2,10 @@
|
|||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\Element\Range;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\webform\Utility\WebformElementHelper;
|
||||
|
||||
/**
|
||||
* Provides a webform element for entering a rating.
|
||||
|
@ -23,6 +25,9 @@ class WebformRating extends Range {
|
|||
'#step' => 1,
|
||||
'#star_size' => 'medium',
|
||||
'#reset' => FALSE,
|
||||
'#process' => [
|
||||
[$class, 'processWebformRating'],
|
||||
],
|
||||
'#pre_render' => [
|
||||
[$class, 'preRenderWebformRating'],
|
||||
],
|
||||
|
@ -30,6 +35,15 @@ class WebformRating extends Range {
|
|||
] + parent::getInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand rating elements.
|
||||
*/
|
||||
public static function processWebformRating(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
// Add validate callback.
|
||||
$element['#element_validate'] = [[get_called_class(), 'validateWebformRating']];
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a #type 'webform_rating' render element for input.html.twig.
|
||||
*
|
||||
|
@ -51,7 +65,7 @@ class WebformRating extends Range {
|
|||
$element['#attributes']['value'] = $element['#attributes']['min'];
|
||||
}
|
||||
|
||||
$element['#children']['rateit'] = self::buildRateIt($element);
|
||||
$element['#children']['rateit'] = static::buildRateIt($element);
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
@ -62,8 +76,8 @@ class WebformRating extends Range {
|
|||
* @param array $element
|
||||
* A rating element.
|
||||
*
|
||||
* @return string
|
||||
* The RateIt div tag.
|
||||
* @return array
|
||||
* A renderable array containing the RateIt div tag.
|
||||
*
|
||||
* @see https://github.com/gjunge/rateit.js/wiki
|
||||
*/
|
||||
|
@ -89,9 +103,9 @@ class WebformRating extends Range {
|
|||
'data-rateit-readonly' => $is_readonly ? 'true' : 'false',
|
||||
];
|
||||
|
||||
// Set range element's #id.
|
||||
if (isset($element['#id'])) {
|
||||
$attributes['data-rateit-backingfld'] = '#' . $element['#id'];
|
||||
// Set range element's selector based on its parents.
|
||||
if (isset($element['#attributes']['data-drupal-selector'])) {
|
||||
$attributes['data-rateit-backingfld'] = '[data-drupal-selector="' . $element['#attributes']['data-drupal-selector'] . '"]';
|
||||
}
|
||||
|
||||
// Set value for HTML preview.
|
||||
|
@ -120,7 +134,17 @@ class WebformRating extends Range {
|
|||
'library' => ['webform/webform.element.rating'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a rating element.
|
||||
*/
|
||||
public static function validateWebformRating(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
$value = $element['#value'];
|
||||
$has_access = (!isset($element['#access']) || $element['#access'] === TRUE);
|
||||
if ($has_access && !empty($element['#required']) && ($value === '0' || $value === '')) {
|
||||
WebformElementHelper::setRequiredError($element, $form_state);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use Drupal\Core\Form\FormStateInterface;
|
|||
use Drupal\Core\Render\Element\Checkboxes;
|
||||
|
||||
/**
|
||||
* Provides a roles entity reference webform element.
|
||||
* Provides a webform roles (checkboxes) element.
|
||||
*
|
||||
* @FormElement("webform_roles")
|
||||
*/
|
||||
|
@ -26,27 +26,25 @@ class WebformRoles extends Checkboxes {
|
|||
}
|
||||
|
||||
/**
|
||||
* Processes a checkboxes webform element.
|
||||
* Processes a webform roles (checkboxes) element.
|
||||
*/
|
||||
public static function processCheckboxes(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
$element['#options'] = array_map('\Drupal\Component\Utility\Html::escape', user_role_names());
|
||||
|
||||
// Check if anonymous is included.
|
||||
if (empty($element['#include_anonymous'])) {
|
||||
unset($element['#options']['anonymous']);
|
||||
}
|
||||
|
||||
$membersonly = (empty($element['#include_anonymous'])) ? TRUE : FALSE;
|
||||
$element['#options'] = array_map('\Drupal\Component\Utility\Html::escape', user_role_names($membersonly));
|
||||
$element['#attached']['library'][] = 'webform/webform.element.roles';
|
||||
$element['#attributes']['class'][] = 'js-webform-roles-role';
|
||||
return parent::processCheckboxes($element, $form_state, $complete_form);
|
||||
}
|
||||
|
||||
/**
|
||||
* Webform element validation handler for webform_users elements.
|
||||
* Webform element validation handler for webform roles (checkboxes) element.
|
||||
*/
|
||||
public static function validateWebformRoles(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
$value = $form_state->getValue($element['#parents'], []);
|
||||
$form_state->setValueForElement($element, array_values(array_filter($value)));
|
||||
$value = array_values(array_filter($value));
|
||||
|
||||
$element['#value'] = $value;
|
||||
$form_state->setValueForElement($element, $value);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
33
web/modules/contrib/webform/src/Element/WebformSection.php
Normal file
33
web/modules/contrib/webform/src/Element/WebformSection.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\Core\Render\Element\RenderElement;
|
||||
|
||||
/**
|
||||
* Provides a render element for a section/group of form elements.
|
||||
*
|
||||
* @RenderElement("webform_section")
|
||||
*/
|
||||
class WebformSection extends RenderElement {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
$class = get_class($this);
|
||||
return [
|
||||
'#process' => [
|
||||
[$class, 'processGroup'],
|
||||
[$class, 'processAjaxForm'],
|
||||
],
|
||||
'#pre_render' => [
|
||||
[$class, 'preRenderGroup'],
|
||||
],
|
||||
'#value' => NULL,
|
||||
'#title_tag' => 'h2',
|
||||
'#theme_wrappers' => ['webform_section'],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -21,6 +21,7 @@ class WebformSelectOther extends WebformOtherBase {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $properties = [
|
||||
'#title',
|
||||
'#required',
|
||||
'#options',
|
||||
'#default_value',
|
||||
|
@ -29,6 +30,8 @@ class WebformSelectOther extends WebformOtherBase {
|
|||
'#multiple',
|
||||
'#empty_value',
|
||||
'#empty_option',
|
||||
|
||||
'#ajax',
|
||||
];
|
||||
|
||||
}
|
||||
|
|
|
@ -28,6 +28,9 @@ class WebformSignature extends FormElement {
|
|||
],
|
||||
'#theme' => 'input__webform_signature',
|
||||
'#theme_wrappers' => ['form_element'],
|
||||
// Add '#markup' property to add an 'id' attribute to the form element.
|
||||
// @see template_preprocess_form_element()
|
||||
'#markup' => '',
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\Core\Render\Element\RenderElement;
|
||||
|
||||
/**
|
||||
* Provides a render element to display webform submission information.
|
||||
*
|
||||
* @RenderElement("webform_submission_information")
|
||||
*/
|
||||
class WebformSubmissionInformation extends RenderElement {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
$class = get_class($this);
|
||||
|
||||
return [
|
||||
'#theme' => 'webform_submission_information',
|
||||
'#webform_submission' => NULL,
|
||||
'#source_entity' => NULL,
|
||||
'#pre_render' => [
|
||||
[$class, 'preRenderWebformSubmissionInformation'],
|
||||
],
|
||||
'#theme_wrappers' => ['details'],
|
||||
'#summary_attributes' => [],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create webform submission information for rendering.
|
||||
*
|
||||
* @param array $element
|
||||
* An associative array containing the properties and children of the
|
||||
* element.
|
||||
*
|
||||
* @return array
|
||||
* The modified element with webform submission information.
|
||||
*/
|
||||
public static function preRenderWebformSubmissionInformation(array $element) {
|
||||
/** @var \Drupal\webform\WebformSubmissionInterface $webform_submission */
|
||||
$webform_submission = $element['#webform_submission'];
|
||||
$webform = $webform_submission->getWebform();
|
||||
|
||||
// Add title.
|
||||
$element += [
|
||||
'#title' => t('Submission information'),
|
||||
];
|
||||
|
||||
// Add details attributes.
|
||||
$element['#attributes']['data-webform-element-id'] = $webform->id() . '-submission-information';
|
||||
$element['#attributes']['class'] = ['webform-submission-information'];
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\Core\Render\Element\RenderElement;
|
||||
|
||||
/**
|
||||
* Provides a render element to display webform submission navigation.
|
||||
*
|
||||
* @RenderElement("webform_submission_navigation")
|
||||
*/
|
||||
class WebformSubmissionNavigation extends RenderElement {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
return [
|
||||
'#theme' => 'webform_submission_navigation',
|
||||
'#webform_submission' => NULL,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,190 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\views\Entity\View;
|
||||
|
||||
/**
|
||||
* Provides a form element for selecting webform submission views.
|
||||
*
|
||||
* @FormElement("webform_submission_views")
|
||||
*/
|
||||
class WebformSubmissionViews extends WebformMultiple {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function processWebformMultiple(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
if (!\Drupal::moduleHandler()->moduleExists('views')) {
|
||||
$element['#element_validate'] = [[get_called_class(), 'emptyValue']];
|
||||
return $element;
|
||||
}
|
||||
|
||||
$element['#key'] = 'name';
|
||||
$element['#header'] = TRUE;
|
||||
$element['#empty_items'] = 0;
|
||||
$element['#min_items'] = 1;
|
||||
$element['#add_more_input_label'] = t('more submission views');
|
||||
|
||||
// Build element.
|
||||
$element['#element'] = [];
|
||||
|
||||
// Name / Title / View.
|
||||
$view_options = [];
|
||||
/** @var \Drupal\views\ViewEntityInterface[] $views */
|
||||
$views = View::loadMultiple();
|
||||
foreach ($views as $view) {
|
||||
// Only include webform submission views.
|
||||
if ($view->get('base_table') !== 'webform_submission' || $view->get('base_field') !== 'sid') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$optgroup = $view->label();
|
||||
$displays = $view->get('display');
|
||||
foreach ($displays as $display_id => $display) {
|
||||
// Only include embed displays.
|
||||
if ($display['display_plugin'] === 'embed') {
|
||||
$view_options[$optgroup][$view->id() . ':' . $display_id] = $optgroup . ': ' . $display['display_title'];
|
||||
}
|
||||
}
|
||||
}
|
||||
$element['#element']['name_title_view'] = [
|
||||
'#type' => 'container',
|
||||
'#title' => t('View / Name / Title'),
|
||||
'#help' => '<b>' . t('View') . ':</b> ' . t('A webform submission embed display. The selected view should also include contextual filters. {webform_id}/{source_entity_type}/{source_entity_id}/{account_id}/{in_draft}') .
|
||||
'<hr/>' . '<b>' . t('Name') . ':</b> ' . t('The name to be displayed in the URL when there are multiple submission views available.') .
|
||||
'<hr/>' . '<b>' . t('Options') . ':</b> ' . t('The title to be display in the dropdown menu when there are multiple submission views available.'),
|
||||
'view' => [
|
||||
'#type' => 'select',
|
||||
'#title' => t('View'),
|
||||
'#title_display' => 'invisible',
|
||||
'#empty_option' => t('Select view…'),
|
||||
'#options' => $view_options,
|
||||
'#error_no_message' => TRUE,
|
||||
],
|
||||
'name' => [
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Name'),
|
||||
'#title_display' => 'invisible',
|
||||
'#placeholder' => t('Enter name…'),
|
||||
'#size' => 20,
|
||||
'#pattern' => '^[-_a-z0-9]+$',
|
||||
'#error_no_message' => TRUE,
|
||||
],
|
||||
'title' => [
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Title'),
|
||||
'#title_display' => 'invisible',
|
||||
'#placeholder' => t('Enter title…'),
|
||||
'#size' => 20,
|
||||
'#error_no_message' => TRUE,
|
||||
],
|
||||
];
|
||||
|
||||
// Global routes.
|
||||
if (!empty($element['#global'])) {
|
||||
$global_route_options = [
|
||||
'entity.webform_submission.collection' => t('Submissions'),
|
||||
'entity.webform_submission.user' => t('User'),
|
||||
];
|
||||
$element['#element']['global_routes'] = [
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => t('Apply to global'),
|
||||
'#help' => t('Display the selected view on the below paths') .
|
||||
'<hr/><b>' . t('Submissions') . ':</b><br/>/admin/structure/webform/submissions/manage' .
|
||||
'<hr/><b>' . t('User') . ':</b><br/>/user/{user}/submissions',
|
||||
'#options' => $global_route_options,
|
||||
'#element_validate' => [['\Drupal\webform\Utility\WebformElementHelper', 'filterValues']],
|
||||
'#error_no_message' => TRUE,
|
||||
];
|
||||
}
|
||||
|
||||
// Webform routes.
|
||||
$webform_route_options = [
|
||||
'entity.webform.results_submissions' => t('Submissions'),
|
||||
'entity.webform.user.drafts' => t('User drafts'),
|
||||
'entity.webform.user.submissions' => t('User submissions'),
|
||||
];
|
||||
$element['#element']['webform_routes'] = [
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => t('Apply to webform'),
|
||||
'#help' => t('Display the selected view on the below paths') .
|
||||
'<hr/><b>' . t('Submissions') . ':</b><br/>/admin/structure/webform/manage/{webform}/results/submissions' .
|
||||
'<hr/><b>' . t('User drafts') . ':</b><br/>/webform/{webform}/drafts' .
|
||||
'<hr/><b>' . t('User submissions') . ':</b><br/>/webform/{webform}/submissions',
|
||||
'#options' => $webform_route_options,
|
||||
'#element_validate' => [['\Drupal\webform\Utility\WebformElementHelper', 'filterValues']],
|
||||
'#error_no_message' => TRUE,
|
||||
];
|
||||
|
||||
// Node routes.
|
||||
if (\Drupal::moduleHandler()->moduleExists('webform_node')) {
|
||||
$node_route_options = [
|
||||
'entity.node.webform.results_submissions' => t('Submissions'),
|
||||
'entity.node.webform.user.drafts' => t('User drafts'),
|
||||
'entity.node.webform.user.submissions' => t('User submissions'),
|
||||
];
|
||||
$element['#element']['node_routes'] = [
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => t('Apply to node'),
|
||||
'#help' =>
|
||||
t('Display the selected view on the below paths') .
|
||||
'<hr/><b>' . t('Submissions') . ':</b><br/>/node/{node}/webform/results/submissions' .
|
||||
'<hr/>' . '<b>' . t('User drafts') . ':</b><br/>/node/{node}/webform/drafts' .
|
||||
'<hr/>' . '<b>' . t('User submissions') . ':</b><br/>/node/{node}/webform/submissions',
|
||||
'#options' => $node_route_options,
|
||||
'#element_validate' => [['\Drupal\webform\Utility\WebformElementHelper', 'filterValues']],
|
||||
'#error_no_message' => TRUE,
|
||||
];
|
||||
}
|
||||
|
||||
parent::processWebformMultiple($element, $form_state, $complete_form);
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function validateWebformMultiple(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
if (!\Drupal::moduleHandler()->moduleExists('views')) {
|
||||
$element['#value'] = [];
|
||||
$form_state->setValueForElement($element, []);
|
||||
return;
|
||||
}
|
||||
|
||||
parent::validateWebformMultiple($element, $form_state, $complete_form);
|
||||
$items = NestedArray::getValue($form_state->getValues(), $element['#parents']);
|
||||
foreach ($items as $name => &$item) {
|
||||
// Remove empty view references.
|
||||
if ($name === '' && empty($item['view']) && empty($item['global_routes']) && empty($item['webform_routes']) && empty($item['node_routes'])) {
|
||||
unset($items[$name]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($name === '') {
|
||||
$form_state->setError($element, t('Name is required.'));
|
||||
}
|
||||
if (empty($item['title'])) {
|
||||
$form_state->setError($element, t('Title is required.'));
|
||||
}
|
||||
if (empty($item['view'])) {
|
||||
$form_state->setError($element, t('View name/display id is required.'));
|
||||
}
|
||||
}
|
||||
|
||||
$element['#value'] = $items;
|
||||
$form_state->setValueForElement($element, $items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Form validate callback which clears the submitted value.
|
||||
*/
|
||||
public static function emptyValue(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
$element['#value'] = [];
|
||||
$form_state->setValueForElement($element, []);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\webform\Element;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\Element\FormElement;
|
||||
|
||||
/**
|
||||
* Provides a form element for selecting webform submission views replacement routes.
|
||||
*
|
||||
* @FormElement("webform_submission_views_replace")
|
||||
*/
|
||||
class WebformSubmissionViewsReplace extends FormElement {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
$class = get_class($this);
|
||||
return [
|
||||
'#input' => TRUE,
|
||||
'#process' => [
|
||||
[$class, 'processWebformSubmissionViewsReplace'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
|
||||
if ($input === FALSE) {
|
||||
if (!isset($element['#default_value'])) {
|
||||
$element['#default_value'] = [];
|
||||
}
|
||||
return $element['#default_value'];
|
||||
}
|
||||
else {
|
||||
return $input;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a ng webform submission views replacement element.
|
||||
*/
|
||||
public static function processWebformSubmissionViewsReplace(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
$is_global = (!empty($element['#global'])) ? TRUE : FALSE;
|
||||
$element['#tree'] = TRUE;
|
||||
|
||||
$element['#value'] = (!is_array($element['#value'])) ? [] : $element['#value'];
|
||||
$element['#value'] += [
|
||||
'global_routes' => [],
|
||||
'webform_routes' => [],
|
||||
'node_routes' => [],
|
||||
];
|
||||
|
||||
// Global routes.
|
||||
if ($is_global) {
|
||||
$element['global_routes'] = [
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => t('Replace the global results with submission views'),
|
||||
'#options' => [
|
||||
'entity.webform_submission.collection' => t('Submissions'),
|
||||
'entity.webform_submission.user' => t('User'),
|
||||
],
|
||||
'#default_value' => $element['#value']['global_routes'],
|
||||
'#element_validate' => [['\Drupal\webform\Utility\WebformElementHelper', 'filterValues']],
|
||||
];
|
||||
}
|
||||
|
||||
// Webform routes.
|
||||
$webform_routes_options = [
|
||||
'entity.webform.results_submissions' => t('Submissions'),
|
||||
'entity.webform.user.drafts' => t('User drafts'),
|
||||
'entity.webform.user.submissions' => t('User submissions'),
|
||||
];
|
||||
if (!$is_global) {
|
||||
$default_webform_routes = \Drupal::configFactory()->get('webform.settings')->get('settings.default_submission_views_replace.webform_routes') ?: [];
|
||||
if ($webform_routes_options) {
|
||||
$webform_routes_options = array_diff_key($webform_routes_options, array_flip($default_webform_routes));
|
||||
}
|
||||
}
|
||||
$element['webform_routes'] = [
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => t('Replace the webform results with submission views'),
|
||||
'#options' => $webform_routes_options,
|
||||
'#default_value' => ($webform_routes_options) ? $element['#value']['webform_routes'] : [],
|
||||
'#access' => ($webform_routes_options) ? TRUE : FALSE,
|
||||
'#element_validate' => [['\Drupal\webform\Utility\WebformElementHelper', 'filterValues']],
|
||||
];
|
||||
|
||||
// Node routes.
|
||||
$node_routes_options = [
|
||||
'entity.node.webform.results_submissions' => t('Submissions'),
|
||||
'entity.node.webform.user.drafts' => t('User drafts'),
|
||||
'entity.node.webform.user.submissions' => t('User submissions'),
|
||||
];
|
||||
if (!$is_global) {
|
||||
$default_node_routes = \Drupal::configFactory()->get('webform.settings')->get('settings.default_submission_views_replace.node_routes') ?: [];
|
||||
if ($default_node_routes) {
|
||||
$node_routes_options = array_diff_key($node_routes_options, array_flip($default_node_routes));
|
||||
}
|
||||
}
|
||||
$element['node_routes'] = [
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => t('Replace the node results with submission views'),
|
||||
'#options' => $node_routes_options,
|
||||
'#default_value' => ($node_routes_options) ? $element['#value']['node_routes'] : [],
|
||||
'#access' => ($node_routes_options && \Drupal::moduleHandler()->moduleExists('webform_node')) ? TRUE : FALSE,
|
||||
'#element_validate' => [['\Drupal\webform\Utility\WebformElementHelper', 'filterValues']],
|
||||
];
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue