<?php

/**
 * @file
 * Provides a webform content type which allows webforms to be integrated into a website as nodes.
 */

use Drupal\Core\Access\AccessResult;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url;
use Drupal\node\Entity\Node;
use Drupal\node\NodeInterface;
use Drupal\webform\Element\WebformMessage;
use Drupal\webform\Entity\Webform;

/**
 * Implements hook_entity_type_alter().
 */
function webform_node_entity_type_alter(array &$entity_types) {
  if (isset($entity_types['webform'])) {
    /** @var \Drupal\Core\Entity\ContentEntityTypeInterface $webform_entity_type */
    $webform_entity_type = $entity_types['webform'];
    $webform_entity_type->setLinkTemplate('references', '/admin/structure/webform/manage/{webform}/references');
  }
}

/**
 * Implements hook_help().
 */
function webform_node_help($route_name, RouteMatchInterface $route_match) {
  if ($route_name != 'node.add') {
    return NULL;
  }

  // Don't show the warning is the user can't create webforms.
  if (!\Drupal::currentUser()->hasPermission('create webform')) {
    return NULL;
  }

  /** @var \Drupal\node\NodeTypeInterface $node_type */
  $node_type = $route_match->getParameter('node_type');

  // Determine if the node type has webform (entity reference) field.
  $has_webform_field = FALSE;
  $field_configs = \Drupal::entityTypeManager()->getStorage('field_config')->loadByProperties(['entity_type' => 'node', 'bundle' => $node_type->id()]);
  foreach ($field_configs as $field_config) {
    if ($field_config->get('field_type') === 'webform') {
      $has_webform_field = TRUE;
      break;
    }
  }

  // Display warning message if webform query string parameter is missing.
  if ($has_webform_field && !\Drupal::request()->get('webform_id')) {
    $build = [
      '#type' => 'webform_message',
      '#message_type' => 'warning',
      '#message_close' => TRUE,
      '#message_id' => 'webform_node.references',
      '#message_storage' => WebformMessage::STORAGE_USER,
      '#message_message' => t('Webforms must first be <a href=":href">created</a> before referencing them in the below form.', [':href' => Url::fromRoute('entity.webform.collection')->toString()]),
      '#cache' => ['max-age' => 0],
    ];
  }
  elseif (\Drupal::request()->get('webform_id')) {
    // If there is a webform query string parameter, then disable caching.
    $build = ['#cache' => ['max-age' => 0]];
  }
  else {
    $build = [];
  }

  // Makes sure if a webform field is added to a node type it is accounted for.
  \Drupal::service('renderer')->addCacheableDependency($build, $node_type);
  return $build;
}

/**
 * Implements hook_node_access().
 */
function webform_node_node_access(NodeInterface $node, $operation, AccountInterface $account) {
  if (strpos($operation, 'webform_submission_') !== 0) {
    return AccessResult::neutral();
  }
  else {
    /** @var \Drupal\webform\WebformEntityReferenceManagerInterface $entity_reference_manager */
    $entity_reference_manager = \Drupal::service('webform.entity_reference_manager');

    // Check that the node has a webform field that has been populated.
    $webform = $entity_reference_manager->getWebform($node);
    if (!$webform) {
      return AccessResult::forbidden();
    }

    // Check administer webform submissions.
    if ($account->hasPermission('administer webform submission')) {
      return AccessResult::allowed();
    }

    // Change access to ANY submission.
    $operation = str_replace('webform_submission_', '', $operation);
    $any_permission = "$operation webform submissions any node";
    if ($account->hasPermission($any_permission)) {
      return AccessResult::allowed();
    }

    // Change access to submission associated with the node's webform.
    $own_permission = "$operation webform submissions own node";
    if ($account->hasPermission($own_permission) && $node->getOwnerId() === $account->id()) {
      return AccessResult::allowed();
    }

    return AccessResult::forbidden();
  }
}

/**
 * Implements hook_node_prepare_form().
 *
 * Prepopulate a node's webform field target id.
 *
 * @see \Drupal\webform_node\Controller\WebformNodeReferencesListController::render
 */
function webform_node_node_prepare_form(NodeInterface $node, $operation, FormStateInterface $form_state) {
  // Only prepopulate new nodes.
  if (!$node->isNew()) {
    return;
  }

  /** @var \Drupal\webform\WebformEntityReferenceManagerInterface $entity_reference_manager */
  $entity_reference_manager = \Drupal::service('webform.entity_reference_manager');

  // Make the node has a webform (entity reference) field.
  $field_name = $entity_reference_manager->getFieldName($node);
  if (!$field_name) {
    return;
  }

  // Populate the node's title and webform field.
  $webform_id = \Drupal::request()->query->get('webform_id');
  if ($webform_id && ($webform = Webform::load($webform_id))) {
    $node->title->value = $webform->label();
    $node->$field_name->target_id = $webform_id;
  }
}

/**
 * Implements hook_node_delete().
 *
 * Remove user specified entity references.
 */
function webform_node_node_delete(NodeInterface $node) {
  /** @var \Drupal\webform\WebformEntityReferenceManagerInterface $entity_reference_manager */
  $entity_reference_manager = \Drupal::service('webform.entity_reference_manager');
  $entity_reference_manager->deleteUserWebformId($node);
}

/**
 * Implements hook_preprocess_HOOK() for page title templates.
 */
function webform_node_preprocess_page_title(&$variables) {
  $node = \Drupal::routeMatch()->getParameter('node');
  if ($node && is_string($node)) {
    $node = Node::load($node);
  }

  if (!$node) {
    return;
  }

  /** @var \Drupal\webform\WebformEntityReferenceManagerInterface $entity_reference_manager */
  $entity_reference_manager = \Drupal::service('webform.entity_reference_manager');

  // Only allow user to change webform for specific routes.
  if (!$entity_reference_manager->isUserWebformRoute($node)) {
    return;
  }

  $webforms = $entity_reference_manager->getWebforms($node);
  if (count($webforms) > 1) {
    $route_options = ['query' => \Drupal::destination()->getAsArray()];

    $operations = [];

    // Add current webform first.
    $current_webform = $entity_reference_manager->getWebform($node);
    $operations[$current_webform->id()] = [
      'title' => $current_webform->label(),
      'url' => Url::fromRoute('entity.node.webform.entity_reference.set', ['node' => $node->id(), 'webform' => $current_webform->id()], $route_options),
    ];

    // Add remaining webforms.
    foreach ($webforms as $webform) {
      $operations[$webform->id()] = [
        'title' => $webform->label(),
        'url' => Url::fromRoute('entity.node.webform.entity_reference.set', ['node' => $node->id(), 'webform' => $webform->id()], $route_options),
      ];
    }

    $variables['title_prefix']['webform_node'] = [
      '#type' => 'operations',
      '#links' => $operations,
      '#prefix' => '<div class="webform-dropbutton webform-node-entity-references">',
      '#suffix' => '</div>',
      '#attached' => [
        'library' => [
          'webform_node/webform_node.entity_references',
        ],
      ],
    ];
  }
}
