View source  
  <?php
namespace Drupal\agreement\Form;
use Drupal\agreement\AgreementHandlerInterface;
use Drupal\agreement\Entity\Agreement;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Form\FormInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Mail\MailManagerInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Routing\LocalRedirectResponse;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Symfony\Component\DependencyInjection\ContainerInterface;
class AgreementForm implements FormInterface, ContainerInjectionInterface {
  use StringTranslationTrait;
  
  protected $agreementHandler;
  
  protected $routeMatch;
  
  protected $languageManager;
  
  protected $account;
  
  protected $messenger;
  
  protected $mailManager;
  
  public function __construct(AgreementHandlerInterface $agreementHandler, RouteMatchInterface $routeMatch, LanguageManagerInterface $languageManager, AccountProxyInterface $account, MessengerInterface $messenger, MailManagerInterface $mailManager) {
    $this->agreementHandler = $agreementHandler;
    $this->routeMatch = $routeMatch;
    $this->languageManager = $languageManager;
    $this->account = $account;
    $this->messenger = $messenger;
    $this->mailManager = $mailManager;
  }
  
  public function getFormId() {
    
    $agreement = $this->routeMatch
      ->getParameter('agreement');
    return 'agreement_' . $agreement
      ->id() . '_form';
  }
  
  public function title(RouteMatchInterface $routeMatch, Agreement $agreement) {
    $settings = $agreement
      ->getSettings();
    return $this
      ->t('@title', [
      '@title' => $settings['title'],
    ]);
  }
  
  public function buildForm(array $form, FormStateInterface $form_state, Agreement $agreement = NULL) {
    $settings = $agreement
      ->getSettings();
    $agreed = $this->agreementHandler
      ->hasAgreed($agreement, $this->account);
    $canAgree = $this->agreementHandler
      ->canAgree($agreement, $this->account);
    $canRevoke = $this->account
      ->hasPermission('revoke own agreement');
    $form_state
      ->setStorage([
      'agreement' => $agreement,
      'agreed' => $agreed,
    ]);
    $form['agreement'] = [
      '#type' => 'container',
      '#tree' => TRUE,
      'text' => [
        '#type' => 'processed_text',
        '#text' => $agreement
          ->get('agreement'),
        '#format' => $settings['format'],
        '#language' => $this->languageManager
          ->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)
          ->getId(),
      ],
      'agree' => [
        '#type' => 'checkbox',
        '#title' => $this
          ->t('@agree', [
          '@agree' => $settings['checkbox'],
        ]),
        '#default_value' => $agreed,
        '#access' => (!$agreed || $canRevoke) && $canAgree,
        '#parents' => [
          'agree',
        ],
      ],
    ];
    $form['actions'] = [
      '#type' => 'actions',
      'submit' => [
        '#type' => 'submit',
        '#name' => 'submit',
        '#value' => $this
          ->t('@agree_submit', [
          '@agree_submit' => $settings['submit'],
        ]),
        '#access' => (!$agreed || $canRevoke) && $canAgree,
      ],
    ];
    return $form;
  }
  
  public function validateForm(array &$form, FormStateInterface $form_state) {
    $storage = $form_state
      ->getStorage();
    if (!$form_state
      ->getValue('agree') && !$storage['agreed']) {
      $settings = $storage['agreement']
        ->getSettings();
      $form_state
        ->setErrorByName('agree', $this
        ->t('@agree_error', [
        '@agree_error' => $settings['failure'],
      ]));
    }
  }
  
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $storage = $form_state
      ->getStorage();
    
    $agreement = $storage['agreement'];
    $settings = $agreement
      ->getSettings();
    $destination = '/';
    $agree = $form_state
      ->getValue('agree');
    if ($settings['destination']) {
      $destination = $settings['destination'];
    }
    elseif (isset($_SESSION['agreement_destination'])) {
      $destination = $_SESSION['agreement_destination'];
    }
    $successfulResponse = $this
      ->processAgreement($agreement, $agree, $storage, $destination);
    if ($successfulResponse) {
      $message = $agree ? $settings['success'] : $settings['revoked'];
      $this->messenger
        ->addStatus($this
        ->t('@success', [
        '@success' => $message,
      ]));
      if ($settings['recipient'] && (!$storage['agreed'] || !$agree)) {
        
        $mail_key = $agree ? 'notice' : 'revoked';
        $this
          ->notify($agreement, $this->account, $settings['recipient'], $mail_key);
      }
      $form_state
        ->setResponse($successfulResponse);
    }
    elseif (!$storage['agreed']) {
      $this->messenger
        ->addError($this
        ->t('An error occurred accepting the agreement. Please try again.'));
    }
  }
  
  protected function processAgreement(Agreement $agreement, int $agree, array $storage, string $destination) {
    $successfulResponse = new LocalRedirectResponse($destination);
    if ($this->agreementHandler
      ->isAnonymousAgreement($agreement, $this->account)) {
      $cookie = $this->agreementHandler
        ->agree($agreement, $this->account, $agree);
      if (!$cookie) {
        return FALSE;
      }
      $successfulResponse->headers
        ->setCookie($cookie);
      $successfulResponse
        ->addCacheableDependency((new CacheableMetadata())
        ->addCacheContexts([
        'cookies:' . $cookie
          ->getName(),
      ]));
      return $successfulResponse;
    }
    elseif ($this->agreementHandler
      ->agree($agreement, $this->account, $agree)) {
      return $successfulResponse;
    }
    return FALSE;
  }
  
  protected function notify(Agreement $agreement, AccountInterface $account, $mail = '', $key = 'notice') {
    if ($mail) {
      $params = [
        'context' => [
          'agreement' => $agreement,
        ],
        'account' => $account,
      ];
      $this->mailManager
        ->mail('agreement', $key, $mail, $account
        ->getPreferredAdminLangcode(), $params);
    }
  }
  
  public static function create(ContainerInterface $container) {
    return new static($container
      ->get('agreement.handler'), $container
      ->get('current_route_match'), $container
      ->get('language_manager'), $container
      ->get('current_user'), $container
      ->get('messenger'), $container
      ->get('plugin.manager.mail'));
  }
}