Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
novicell/custom_forms / src / Entity / CustomFormSubmission.php
Size: Mime:
<?php

namespace Drupal\custom_forms\Entity;

use Drupal\Component\Plugin\Exception\PluginException;
use Drupal\Component\Render\MarkupInterface;
use Drupal\Core\Annotation\PluralTranslation;
use Drupal\Core\Annotation\Translation;
use Drupal\Core\Entity\Annotation\ContentEntityType;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityChangedTrait;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Site\Settings;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\custom_forms\AESEncryption;
use Drupal\state_machine\Plugin\Field\FieldType\StateItemInterface;
use Hashids\Hashids;

/**
 * Defines the custom form submission entity class.
 *
 * @package Drupal\custom_forms\Entity
 *
 * @ContentEntityType(
 *   id = "custom_form_submission",
 *   label = @Translation("Custom Form Submission"),
 *   label_collection = @Translation("Custom Form Submissions"),
 *   label_singular = @Translation("Custom Form Submission"),
 *   label_plural = @Translation("Custom Form Submissions"),
 *   label_count = @PluralTranslation(
 *     singular = "@count custom form submission",
 *     plural = "@count custom form submissions"
 *   ),
 *   fieldable = FALSE,
 *   base_table = "custom_forms_submissions",
 *   show_revision_ui = FALSE,
 *   translatable = FALSE,
 *   entity_keys = {
 *     "id" = "id",
 *     "unique_id" = "unique_id",
 *   },
 * )
 */
class CustomFormSubmission extends ContentEntityBase implements CustomFormSubmissionInterface {
  use EntityChangedTrait;
  use StringTranslationTrait;

  /**
   * The factory in charge of CRUD operations for custom form items.
   *
   * @var \Drupal\custom_forms\CustomFormItemFactory
   */
  protected $itemFactory;

  /**
   * The AES encryption functionality.
   *
   * @var \Drupal\custom_forms\AESEncryption
   */
  protected $aes;

  /**
   * The date formatter service.
   *
   * @var \Drupal\Core\Datetime\DateFormatterInterface
   */
  protected $dateFormatter;

  /**
   * @inheritDoc
   */
  public function __construct(array $values, $entity_type, $bundle = FALSE, $translations = []) {
    parent::__construct($values, $entity_type, $bundle, $translations);
    $this->itemFactory = \Drupal::service('custom_forms.factory.item');
    $this->aes = new AESEncryption(Settings::get('custom_forms.aes_key'));
    $this->dateFormatter = \Drupal::service('date.formatter');
  }

  /**
   * {@inheritdoc}
   */
  public function getData() {
    if (!$this->get('data')->isEmpty()) {
      $data = $this->get('data')->first()->getValue();
      $decoded = base64_decode($data['data'], FALSE);
      $decrypted = $this->aes->decrypt($decoded);
      $unserialized = unserialize($decrypted, ['allowed_classes' => FALSE]);
      return $unserialized;
    }

    return [];
  }

  /**
   * {@inheritdoc}
   */
  public function getMappedData($hide_sensitive = TRUE, &$column_headers = []) {
    if (!$this->get('mapped_data')->isEmpty()) {
      $data = $this->get('mapped_data')->first()->getValue();
      $decoded = base64_decode($data['data'], FALSE);
      $decrypted = $this->aes->decrypt($decoded);
      $unserialized = unserialize($decrypted, ['allowed_classes' => FALSE]);


      $mapped_data = [];
      $mapped_data['_id'] = [
        'label' => $this->t('Id')->render(),
        'value' => $this->id(),
        'sensitive' => FALSE,
      ];
      $mapped_data['_unique_id'] = [
        'label' => $this->t('Unique id')->render(),
        'value' => $this->getUniqueId(),
        'sensitive' => FALSE,
      ];

      $form = $this->getForm();

      $form_label       = 'ID:' . $this->get('form')->target_id;
      $form_type_label  = '(' . $this->t('Not found')->render() . ')';
      if ($form !== NULL) {
        $form_label = $form->label();
        /** @var \Drupal\custom_forms\Entity\CustomFormType $form_type */
        $form_type = CustomFormType::load($this->getForm()->bundle());
        if ($form_type !== NULL) {
          $form_type_label = $form_type->label();
        }
      }

      $mapped_data['_form'] = [
        'label' => $this->t('Form')->render(),
        'value' => $form_label,
        'sensitive' => FALSE,
      ];
      $mapped_data['_type'] = [
        'label' => $this->t('Type')->render(),
        'value' => $form_type_label,
        'sensitive' => FALSE,
      ];

      $mapped_data += $unserialized;

      // Add some additional non-field data to mapped data.
      $state_label = $this->getState()->getLabel();
      if ($state_label instanceof MarkupInterface) {
        $state_label = $state_label->render();
      }
      $mapped_data['_state'] = [
        'label' => $this->t('State')->render(),
        'value' => $state_label,
        'sensitive' => FALSE,
      ];
      $mapped_data['_created'] = [
        'label' => $this->t('Created')->render(),
        'value' => $this->getCreatedTime(),
        'sensitive' => FALSE,
      ];
      $mapped_data['_changed'] = [
        'label' => $this->t('Changed')->render(),
        'value' => $this->getChangedTime(),
        'sensitive' => FALSE,
      ];

      foreach ($mapped_data as $key => $value) {
        // Hide sensitive data.
        if ($hide_sensitive && $value['sensitive'] === TRUE) {
          $mapped_data[$key]['value'] = $this->t('Hidden')->render();
        }

        // Add column headers
        $column_headers[$key] = $value['label'];
      }

      return $mapped_data;
    }

    return [];
  }

  /**
   * {@inheritdoc}
   */
  public function setDataArray(array $data) : CustomFormSubmissionInterface {
    $serialized = serialize($data);
    $encrypted = $this->aes->encrypt($serialized);
    $encoded = base64_encode($encrypted);
    $this->set('data', ['data' => $encoded]);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function setMappedDataArray(array $data) : CustomFormSubmissionInterface {
    $serialized = serialize($data);
    $encrypted = $this->aes->encrypt($serialized);
    $encoded = base64_encode($encrypted);
    $this->set('mapped_data', ['data' => $encoded]);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function setFormId($form_id) : CustomFormSubmissionInterface {
    $this->set('form', $form_id);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function setErrors(array $errors) : CustomFormSubmissionInterface {
    $this->set('errors', $errors);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getUniqueId() : ?string {
    return $this->get('unique_id')->value;
  }

  /**
   * {@inheritdoc}
   */
  public function getForm() : ?CustomForm {
    return $this->get('form')->entity;
  }

  /**
   * {@inheritdoc}
   */
  public function getState() : StateItemInterface {
    return $this->get('state')->first();
  }

  /**
   * {@inheritdoc}
   */
  public function getErrors() : array {
    $errors = [];
    if (!$this->get('errors')->isEmpty()) {
      $errors = $this->get('errors')->first()->getValue();
    }
    return $errors;
  }

  /**
   * {@inheritdoc}
   */
  public function getCreatedTime() : int {
    return $this->get('created')->value;
  }

  /**
   * @return \Drupal\Core\Field\FieldItemListInterface|mixed
   */
  public function getCreated() {
    return $this->get('created');
  }

  /**
   * {@inheritdoc}
   */
  public function generateReceiptData() : array {
    /** @var \Drupal\custom_forms\Entity\CustomForm $form */
    $form            = $this->getForm();
    $submission_data = $this->getData();
    $data            = [];
    $dateFormatter   = \Drupal::service('date.formatter');

    $data['submission_date'] = [
      'label' => t('Date'),
      'value' => $dateFormatter->format($this->getCreatedTime(), 'medium'),
    ];

    $fields = $this->itemFactory->getFormItems($form, TRUE);
    $exclude_plugins = [
      'submit' // Exclude submit button from appearing on receipt.
    ];

    /** @var \Drupal\custom_forms\CustomFormItem $field */
    foreach ($fields as $field) {
      if (!empty($submission_data) && $field->getType() === 'field' && !in_array($field->getPluginDefinition()['id'], $exclude_plugins, FALSE)) {
        $field_plugin = $field->getPlugin();
        $formatted_data = $field_plugin->formatReceiptValue($submission_data);
        if (!empty($formatted_data)) {
          $data[$field->getMachineName()] = $formatted_data;
        }
      }
    }

    return $data;
  }

  /**
   * {@inheritdoc}
   */
  public static function loadByUniqueId($id) : ?CustomFormSubmissionInterface {
    $query = \Drupal::entityQuery('custom_form_submission');
    $query->condition('unique_id', $id);
    $query->accessCheck(TRUE);
    $result = $query->execute();
    // Get the first match.
    $id = reset($result);

    // Only load the submission if we found a match.
    if (!empty($id)) {
      return self::load($id);
    }
  }

  /**
   * {@inheritdoc}
   */
  public function preSave(EntityStorageInterface $storage) {
    parent::preSave($storage);

    // Set the state to draft if it's not been set
    if ($this->get('state')->isEmpty()) {
      $this->set('state', 'draft');
    }

    // Generate a unique ID if it's empty.
    if ($this->get('unique_id')->isEmpty()) {
      $config = \Drupal::config('custom_forms.settings');
      // Generate and save the unique ID.
      $hashIds = new Hashids($config->get('submission_salt'), 8);
      $unique_id = $hashIds->encode($this->getCreatedTime() . $this->id());
      $this->set('unique_id', $unique_id);
    }

    // Set errors if it's empty.
    if ($this->get('errors')->isEmpty()) {
      $this->setErrors([]);
    }
  }

  /**
   * @inheritDoc
   */
  public function postSave(EntityStorageInterface $storage, $update = TRUE) {
    parent::postSave($storage, $update);
    $this->setMappedDataArray($this->mapData(FALSE));
  }

  /**
   * {@inheritdoc}
   */
  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
    $fields =  parent::baseFieldDefinitions($entity_type);

    $fields['unique_id'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Unique id'))
      ->setDescription(t('The unique id for the submission.'))
      ->setRequired(TRUE)
      ->setSetting('max_length', 255)
      ->setDisplayConfigurable('form', FALSE)
      ->setDisplayConfigurable('view', FALSE);

    $fields['form'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(t('Custom Form'))
      ->setDescription(t('The custom form which the submission belongs to.'))
      ->setCardinality(1)
      ->setRequired(TRUE)
      ->setSetting('target_type', 'custom_form')
      ->setSetting('handler', 'default')
      ->setDisplayConfigurable('form', FALSE)
      ->setDisplayConfigurable('view', FALSE);

    $fields['state'] = BaseFieldDefinition::create('state')
      ->setLabel(t('State'))
      ->setDescription(t('The submission state.'))
      ->setDefaultValue('draft')
      ->setRequired(TRUE)
      ->setSetting('max_length', 255)
      ->setDisplayOptions('view', [
        'label' => 'hidden',
        'type' => 'state_transition_form',
        'weight' => 10,
      ])
      ->setDisplayConfigurable('form', FALSE)
      ->setDisplayConfigurable('view', FALSE)
      ->setSetting('workflow', 'submission');

    $fields['data'] = BaseFieldDefinition::create('map')
      ->setLabel(t('Data'))
      ->setDescription(t('A serialized array of submission data.'))
      ->setDefaultValue([]);

    $fields['mapped_data'] = BaseFieldDefinition::create('map')
      ->setLabel(t('Mapped data'))
      ->setDescription(t('A serialized array of mapped submission data.'))
      ->setDefaultValue([]);

    $fields['created'] = BaseFieldDefinition::create('created')
      ->setLabel(t('Created'))
      ->setDescription(t('The time when the submission was created.'));

    $fields['changed'] = BaseFieldDefinition::create('changed')
      ->setLabel(t('Changed'))
      ->setDescription(t('The time when the submission was last edited.'));

    $fields['errors'] = BaseFieldDefinition::create('map')
      ->setLabel(t('Errors'))
      ->setDescription(t('A serialized array of submission errors.'));

    return $fields;
  }

  /**
   * Map the submitted data.
   *
   * @param bool $hide_sensitive
   *   Whether to hide sensitive values or not, defaults to TRUE.
   *
   * @return array
   *   Returns the mapped data.
   */
  private function mapData($hide_sensitive = TRUE) : array {
    /** @var \Drupal\custom_forms\Entity\CustomForm $form */
    $form            = $this->getForm();
    $submission_data = $this->getData();
    /** @var \Drupal\custom_forms\CustomFormItem[] $items */
    $items           = $this->itemFactory->getFormItems($form);

    // If the form and items failed to load, we return nothing.
    // This is because it means the submission was saved outside the flow
    // and thus ended in a situation where the associated form or items have
    // been deleted.
    if ($form === NULL && $items === NULL) {
      return [];
    }

    // Remove internal values from array
    unset(
      $submission_data['_custom_form_detector'],
      $submission_data['_finalize'],
      $submission_data['_receipt_page_id'],
      $submission_data['_process_file_usage']
    );

    $mapped_data = [];
    foreach ($items as $item) {
      if ($item->getType() === 'field') {
        try {
          /** @var \Drupal\custom_forms\Plugin\CustomForms\FieldMapping\CustomFormsFieldMappingInterface $mapping_plugin */
          $mapping_plugin = $this->itemFactory->getItemPlugin('mapping', $item->getMappingId());

          // Get all mapping ids and labels so we can loop through them.
          $mappings = $mapping_plugin->getMappingIds($item, $mapped_data, FALSE);

          // Get the mapped submission data.
          $mapped_field = $mapping_plugin->mapValue($submission_data, $mapped_data, $item, $hide_sensitive);

          /**
           * @var string $mapping_id
           * @var \Drupal\Core\StringTranslation\TranslatableMarkup $mapping_label
           */
          foreach ($mappings as $mapping_id => $mapping_label) {
            if ($mapping_label instanceof MarkupInterface) {
              $mapping_label = $mapping_label->render();
            }
            $mapped_data[$mapping_id] = [
              'label' => $mapping_label,
              'value' => $mapped_field[$mapping_id],
              'sensitive' => $mapping_plugin->isSensitive(),
            ];
          }

        } catch (PluginException $e) {
          $error_message = t('Failed during data mapping. Message = %message',
            [
              '%message' => $e->getMessage(),
            ]
          );
          \Drupal::logger('Custom Forms')->error($error_message);
        }
      }
    }

    return $mapped_data;
  }

}