webform_access.module in Webform 6.x
Same filename and directory in other branches
Provides webform access controls for webform nodes.
File
modules/webform_access/webform_access.moduleView source
<?php
/**
* @file
* Provides webform access controls for webform nodes.
*/
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Database\Query\AlterableInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\webform\Utility\WebformElementHelper;
use Drupal\webform\WebformInterface;
use Drupal\webform\WebformSubmissionInterface;
use Drupal\webform_access\Entity\WebformAccessGroup;
/**
* Implements hook_webform_help_info().
*/
function webform_access_webform_help_info() {
$help = [];
// Access group.
$help['webform_access_group'] = [
'group' => 'access',
'title' => t('Webform Access: Group'),
'content' => t('The <strong>Access group</strong> page lists reusable groups used to access webform source entity and users.'),
'video_id' => 'access',
'routes' => [
// @see /admin/structure/webform/access/group/manage
'entity.webform_access_group.collection',
],
];
// Access type.
$help['webform_access_type'] = [
'type' => 'access',
'title' => t('Webform Access: Type'),
'content' => t('The <strong>Access type</strong> page lists types of groups used to send email notifications to users.'),
'video_id' => 'access',
'routes' => [
// @see /admin/structure/webform/access/type/manage
'entity.webform_access_type.collection',
],
];
return $help;
}
/******************************************************************************/
// Delete relationship hooks.
/******************************************************************************/
/**
* Implements hook_user_delete().
*/
function webform_access_user_delete(EntityInterface $entity) {
\Drupal::database()
->delete('webform_access_group_user')
->condition('uid', $entity
->id())
->execute();
}
/**
* Implements hook_node_delete().
*/
function webform_access_node_delete(EntityInterface $entity) {
\Drupal::database()
->delete('webform_access_group_entity')
->condition('entity_type', 'node')
->condition('entity_id', $entity
->id())
->execute();
}
/**
* Implements hook_field_config_delete().
*/
function webform_access_field_config_delete(EntityInterface $entity) {
/** @var Drupal\field\Entity\FieldConfig $definition */
if ($entity
->getType() === 'webform' && $entity
->getEntityTypeId() === 'node') {
$entity_ids = \Drupal::entityQuery('webform_access_group')
->condition('type', $entity
->getTargetBundle())
->execute();
if ($entity_ids) {
\Drupal::database()
->delete('webform_access_group_entity')
->condition('entity_type', 'node')
->condition('entity_id', $entity_ids, 'IN')
->condition('field_name', $entity
->getName())
->execute();
}
}
}
/**
* Implements hook_field_storage_config_delete().
*/
function webform_access_field_storage_config_delete(EntityInterface $entity) {
/** @var Drupal\field\Entity\FieldStorageConfig $entity */
if ($entity
->getType() === 'webform') {
\Drupal::database()
->delete('webform_access_group_entity')
->condition('entity_type', $entity
->getEntityTypeId())
->condition('field_name', $entity
->getName())
->execute();
}
}
/******************************************************************************/
// Access checking.
/******************************************************************************/
/**
* Implements hook_menu_local_tasks_alter().
*
* Add webform access group to local task cacheability.
*
* @see \Drupal\Core\Menu\Plugin\Block\LocalTasksBlock::build
*/
function webform_access_menu_local_tasks_alter(&$data, $route_name) {
// Change config entities 'Translate *' tab to be just label 'Translate'.
$webform_entities = [
'webform_access_group',
'webform_access_type',
];
foreach ($webform_entities as $webform_entity) {
if (isset($data['tabs'][0]["config_translation.local_tasks:entity.{$webform_entity}.config_translation_overview"]['#link']['title'])) {
$data['tabs'][0]["config_translation.local_tasks:entity.{$webform_entity}.config_translation_overview"]['#link']['title'] = t('Translate');
}
}
$route_name = \Drupal::routeMatch()
->getRouteName();
if ($route_name !== 'entity.node.canonical' && strpos($route_name, 'entity.node.webform.') !== 0) {
return;
}
/** @var \Drupal\webform\WebformRequestInterface $request_handler */
$request_handler = \Drupal::service('webform.request');
$account = \Drupal::currentUser();
$webform = $request_handler
->getCurrentWebform();
$source_entity = $request_handler
->getCurrentSourceEntity();
if (!$webform || $source_entity) {
return;
}
/** @var \Drupal\webform_access\WebformAccessGroupStorageInterface $webform_access_group */
$webform_access_group_storage = \Drupal::entityTypeManager()
->getStorage('webform_access_group');
$webform_access_groups = $webform_access_group_storage
->loadByEntities($webform, $source_entity, $account);
if (empty($webform_access_groups)) {
return;
}
/** @var \Drupal\Core\Cache\CacheableMetadata $cacheability */
$cacheability = $data['cacheability'];
foreach ($webform_access_groups as $webforn_access_group) {
$cacheability
->addCacheableDependency($webforn_access_group);
}
}
/**
* Implements hook_ENTITY_TYPE_access() for webform entities.
*/
function webform_access_webform_access(WebformInterface $webform, $operation, AccountInterface $account) {
// Prevent recursion when a webform is being passed as the source entity
// via the URL.
// @see \Drupal\webform\Plugin\WebformSourceEntity\QueryStringWebformSourceEntity::getSourceEntity
if (\Drupal::request()->query
->get('source_entity_type') === 'webform') {
return AccessResult::neutral();
}
/** @var \Drupal\webform\WebformRequestInterface $request_handler */
$request_handler = \Drupal::service('webform.request');
$source_entity = $request_handler
->getCurrentSourceEntity([
'webform_submission',
]);
if (!$source_entity) {
return AccessResult::neutral();
}
/** @var \Drupal\webform_access\WebformAccessGroupStorageInterface $webform_access_group */
$webform_access_group_storage = \Drupal::entityTypeManager()
->getStorage('webform_access_group');
$webform_access_groups = $webform_access_group_storage
->loadByEntities($webform, $source_entity, $account);
if (empty($webform_access_groups)) {
return AccessResult::neutral();
}
$permission = str_replace('submission_', '', $operation);
foreach ($webform_access_groups as $webforn_access_group) {
$permissions = $webforn_access_group
->get('permissions');
if (in_array('administer', $permissions) || in_array($permission, $permissions)) {
return AccessResult::allowed()
->cachePerUser()
->addCacheableDependency($webforn_access_group);
}
}
// No opinion.
return AccessResult::neutral();
}
/**
* Implements hook_ENTITY_TYPE_access() for webform_submission entities.
*/
function webform_access_webform_submission_access(WebformSubmissionInterface $webform_submission, $operation, AccountInterface $account) {
if (!in_array($operation, [
'view',
'update',
'delete',
])) {
return AccessResult::neutral();
}
$webform = $webform_submission
->getWebform();
$source_entity = $webform_submission
->getSourceEntity([
'webform_submission',
]);
if (!$source_entity) {
return AccessResult::neutral();
}
/** @var \Drupal\webform_access\WebformAccessGroupStorageInterface $webform_access_group */
$webform_access_group_storage = \Drupal::entityTypeManager()
->getStorage('webform_access_group');
$webform_access_groups = $webform_access_group_storage
->loadByEntities($webform, $source_entity, $account);
if (empty($webform_access_groups)) {
return AccessResult::neutral();
}
foreach ($webform_access_groups as $webforn_access_group) {
$permissions = $webforn_access_group
->get('permissions');
if (in_array('administer', $permissions) || in_array($operation . '_any', $permissions) || in_array($operation . '_own', $permissions) && $webform_submission
->getOwnerId() === $account
->id()) {
return AccessResult::allowed()
->cachePerUser()
->addCacheableDependency($webforn_access_group);
}
}
// No opinion.
return AccessResult::neutral();
}
/**
* Implements hook_webform_submission_query_access_alter().
*/
function webform_access_webform_submission_query_access_alter(AlterableInterface $query, array $webform_submission_tables) {
$account = $query
->getMetaData('account') ?: \Drupal::currentUser();
// Collect access group ids with 'view_any' or 'administer' permissions.
/** @var \Drupal\webform_access\WebformAccessGroupStorageInterface $access_group_storage */
$access_group_storage = \Drupal::entityTypeManager()
->getStorage('webform_access_group');
/** @var \Drupal\webform_access\WebformAccessGroupInterface $access_group */
$access_groups = $access_group_storage
->loadByEntities(NULL, NULL, $account);
$access_any_group_ids = [];
$access_own_group_ids = [];
foreach ($access_groups as $access_group) {
$access_group_permissions = $access_group
->get('permissions');
$access_group_permissions = array_combine($access_group_permissions, $access_group_permissions);
if (isset($access_group_permissions['view_any']) || isset($access_group_permissions['administer'])) {
$access_any_group_ids[] = $access_group
->id();
}
elseif (isset($access_group_permissions['view_own'])) {
$access_own_group_ids[] = $access_group
->id();
}
}
if ($access_any_group_ids) {
// Add access group entity type, entity id, and webform id to the query.
$result = \Drupal::database()
->select('webform_access_group_entity', 'ge')
->fields('ge', [
'entity_type',
'entity_id',
'webform_id',
])
->condition('group_id', $access_any_group_ids, 'IN')
->execute();
while ($record = $result
->fetchAssoc()) {
foreach ($webform_submission_tables as $table) {
/** @var \Drupal\Core\Database\Query\SelectInterface $query */
$condition = $query
->andConditionGroup();
$condition
->condition($table['alias'] . '.entity_type', $record['entity_type']);
$condition
->condition($table['alias'] . '.entity_id', (string) $record['entity_id']);
$condition
->condition($table['alias'] . '.webform_id', $record['webform_id']);
$table['condition']
->condition($condition);
}
}
}
if ($access_own_group_ids) {
// Add access group entity type, entity id, and webform id to the query.
$result = \Drupal::database()
->select('webform_access_group_entity', 'ge')
->fields('ge', [
'entity_type',
'entity_id',
'webform_id',
])
->condition('group_id', $access_own_group_ids, 'IN')
->execute();
while ($record = $result
->fetchAssoc()) {
foreach ($webform_submission_tables as $table) {
/** @var \Drupal\Core\Database\Query\SelectInterface $query */
$condition = $query
->andConditionGroup();
$condition
->condition($table['alias'] . '.uid', $account
->id());
$condition
->condition($table['alias'] . '.entity_type', $record['entity_type']);
$condition
->condition($table['alias'] . '.entity_id', (string) $record['entity_id']);
$condition
->condition($table['alias'] . '.webform_id', $record['webform_id']);
$table['condition']
->condition($condition);
}
}
}
}
/******************************************************************************/
// Webform access groups (node) entity.
/******************************************************************************/
/**
* Implements hook_field_widget_form_alter().
*/
function webform_access_field_widget_form_alter(&$element, FormStateInterface $form_state, $context) {
/** @var \Drupal\Core\Field\FieldItemListInterface $items */
$items = $context['items'];
$field_definition = $items
->getFieldDefinition();
if ($field_definition
->getType() !== 'webform') {
return;
}
// Get the target entity.
$entity = $items
->getEntity();
// Only nodes are currently supported.
if ($entity
->getEntityTypeId() !== 'node') {
return;
}
$default_value = $entity
->id() ? \Drupal::database()
->select('webform_access_group_entity', 'ge')
->fields('ge', [
'group_id',
])
->condition('entity_type', $entity
->getEntityTypeId())
->condition('entity_id', $entity
->id())
->condition('webform_id', $element['target_id']['#default_value'])
->condition('field_name', $field_definition
->getName())
->execute()
->fetchCol() : ($items->webform_access_group ?: []);
$element['settings']['webform_access_group'] = _webform_access_group_build_element($default_value, [], $form_state);
}
/**
* Implements hook_form_BASE_FORM_ID_alter().
*/
function webform_access_form_node_form_alter(&$form, FormStateInterface $form_state, $form_id) {
/** @var \Drupal\webform\WebformEntityReferenceManagerInterface $entity_reference_manager */
$entity_reference_manager = \Drupal::service('webform.entity_reference_manager');
$node = $form_state
->getFormObject()
->getEntity();
$field_names = $entity_reference_manager
->getFieldNames($node);
if ($field_names) {
$form['actions']['submit']['#submit'][] = '_webform_access_form_node_form_submit';
}
}
/**
* Webform access group submit handler.
*/
function _webform_access_form_node_form_submit(&$form, FormStateInterface $form_state) {
/** @var \Drupal\webform\WebformEntityReferenceManagerInterface $entity_reference_manager */
$entity_reference_manager = \Drupal::service('webform.entity_reference_manager');
$node = $form_state
->getFormObject()
->getEntity();
$field_names = $entity_reference_manager
->getFieldNames($node);
$record = [
'entity_type' => 'node',
'entity_id' => $node
->id(),
];
foreach ($field_names as $field_name) {
$value = $form_state
->getValue($field_name);
// Handle hidden webform fields.
if ($value === NULL) {
continue;
}
$record['field_name'] = $field_name;
// Delete all existing records.
\Drupal::database()
->delete('webform_access_group_entity')
->condition('entity_type', $record['entity_type'])
->condition('entity_id', $record['entity_id'])
->condition('field_name', $record['field_name'])
->execute();
foreach ($value as $item) {
$record['webform_id'] = $item['target_id'];
foreach ($item['settings']['webform_access_group'] as $group_id) {
$record['group_id'] = $group_id;
// Insert new record.
\Drupal::database()
->insert('webform_access_group_entity')
->fields([
'group_id',
'entity_type',
'entity_id',
'field_name',
'webform_id',
])
->values($record)
->execute();
// Invalidate cache tags.
WebformAccessGroup::load($group_id)
->invalidateTags();
}
}
}
}
/******************************************************************************/
// Webform access group users.
/******************************************************************************/
/**
* Implements hook_form_FORM_ID_alter() for user form.
*
* Add the webform access group to an individual user's account page.
*/
function webform_access_form_user_form_alter(&$form, FormStateInterface $form_state) {
// Make sure some webform access groups exist before displaying
// the webform access details widget.
if (!WebformAccessGroup::loadMultiple()) {
return;
}
// Only display the webform access detail widget if the current user can
// administer webform and users.
if (!\Drupal::currentUser()
->hasPermission('administer webform') || !\Drupal::currentUser()
->hasPermission('administer users')) {
return;
}
$account = $form_state
->getFormObject()
->getEntity();
$default_value = \Drupal::database()
->select('webform_access_group_user', 'gu')
->fields('gu', [
'group_id',
])
->condition('uid', $account
->id())
->execute()
->fetchCol();
$form['webform_access'] = [
'#type' => 'details',
'#title' => t('Webform access'),
'#open' => TRUE,
'#weight' => 5,
];
$form['webform_access']['webform_access_group'] = _webform_access_group_build_element($default_value, $form, $form_state);
$form['actions']['submit']['#submit'][] = '_webform_access_user_profile_form_submit';
}
/**
* Submit callback for the user profile form to save the webform_access user setting.
*/
function _webform_access_user_profile_form_submit($form, FormStateInterface $form_state) {
$account = $form_state
->getFormObject()
->getEntity();
// Delete all existing records.
\Drupal::database()
->delete('webform_access_group_user')
->condition('uid', $account
->id())
->execute();
$record = [
'uid' => $account
->id(),
];
$value = $form_state
->getValue('webform_access_group');
foreach ($value as $group_id) {
$record['group_id'] = $group_id;
// Insert new record.
\Drupal::database()
->insert('webform_access_group_user')
->fields([
'group_id',
'uid',
])
->values($record)
->execute();
WebformAccessGroup::load($group_id)
->invalidateTags();
}
// Invalidate 'webform_submission_list' cache tag when user is updated.
// @see _webform_clear_webform_submission_list_cache_tag()
Cache::invalidateTags([
'webform_submission_list',
]);
}
/******************************************************************************/
// Webform access group helper functions.
/******************************************************************************/
/**
* Build element used to select webform access groups.
*
* @param array $default_value
* Array of default group ids.
* @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
* Element used to select webform access groups.
*/
function _webform_access_group_build_element(array $default_value, array $form, FormStateInterface $form_state) {
$element = [
'#type' => 'webform_entity_select',
'#title' => 'Access group',
'#target_type' => 'webform_access_group',
'#selection_handler' => 'default:webform_access_group',
'#multiple' => TRUE,
'#select2' => TRUE,
'#default_value' => $default_value,
'#access' => \Drupal::currentUser()
->hasPermission('administer webform'),
];
return WebformElementHelper::process($element);
}