Commit fbed32d9 authored by bojanz's avatar bojanz

Issue #3087069 by bojanz: Show the verification result in the TaxNumberDefaultFormatter

parent 1086919b
commerce_tax.verification_result:
path: '/commerce_tax/verification-result/{tax_number}/{context}'
defaults:
_controller: '\Drupal\commerce_tax\Controller\TaxNumberController::result'
_title: 'Verification result'
requirements:
_access: 'TRUE'
options:
_admin_route: TRUE
<?php
namespace Drupal\commerce_tax\Controller;
use Drupal\commerce\UrlData;
use Drupal\commerce_tax\Plugin\Commerce\TaxNumberType\SupportsVerificationInterface;
use Drupal\commerce_tax\Plugin\Commerce\TaxNumberType\VerificationResult;
use Drupal\commerce_tax\Plugin\Field\FieldType\TaxNumberItemInterface;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
class TaxNumberController implements ContainerInjectionInterface {
use StringTranslationTrait;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The date formatter.
*
* @var \Drupal\Core\Datetime\DateFormatterInterface
*/
protected $dateFormatter;
/**
* Constructs a new TaxNumberController object.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
* The date formatter.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, DateFormatterInterface $date_formatter) {
$this->entityTypeManager = $entity_type_manager;
$this->dateFormatter = $date_formatter;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity_type.manager'),
$container->get('date.formatter')
);
}
/**
* Displays the verification result for the given tax number.
*
* @param string $tax_number
* The tax number.
* @param string $context
* The encoded context.
*
* @return array
* A renderable array.
*
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
* If the context is invalid or the user doesn't have access to update
* the parent entity.
*/
public function result($tax_number, $context) {
$context = $this->prepareContext($context);
if (!$context) {
throw new AccessDeniedHttpException();
}
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
$entity = $context['entity'];
if (!$entity->access('update')) {
throw new AccessDeniedHttpException();
}
/** @var \Drupal\commerce_tax\Plugin\Field\FieldType\TaxNumberItemInterface $field */
$field = $context['field'];
if ($field->value != $tax_number) {
throw new AccessDeniedHttpException();
}
$result = [];
$type_plugin = $field->getTypePlugin();
if ($type_plugin instanceof SupportsVerificationInterface) {
$verification_result = new VerificationResult(
$field->verification_state,
$field->verification_timestamp,
$field->verification_result
);
$result = $type_plugin->renderVerificationResult($verification_result);
// @todo Move this to a Twig template, to allow it to be customized.
if ($field->verification_timestamp) {
$result['timestamp'] = [
'#type' => 'item',
'#title' => $this->t('Timestamp'),
'#plain_text' => $this->dateFormatter->format($field->verification_timestamp, 'long'),
'#weight' => -10,
];
}
}
return $result;
}
/**
* Parses and validates the context.
*
* @param string $context
* The context string.
*
* @return array|false
* The prepared context, or FALSE if validation failed.
*/
protected function prepareContext($context) {
$context = UrlData::decode($context);
$context = $context ?: [];
if (!count($context) == 4) {
return FALSE;
}
// Assign keys. The context array is numerically indexed to save space.
$keys = ['entity_type', 'entity_id', 'field_name', 'view_mode'];
$context = array_combine($keys, $context);
foreach ($keys as $key) {
if (empty($context[$key])) {
// Missing required data.
return FALSE;
}
}
// Validate the provided values.
try {
$storage = $this->entityTypeManager->getStorage($context['entity_type']);
}
catch (PluginNotFoundException $e) {
return FALSE;
}
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
$entity = $storage->load($context['entity_id']);
if (!$entity || !$entity->hasField($context['field_name'])) {
return FALSE;
}
$field = $entity->get($context['field_name'])->first();
if (!($field instanceof TaxNumberItemInterface)) {
return FALSE;
}
// Upcast the values in the context array.
$context['entity'] = $entity;
$context['field'] = $field;
return $context;
}
}
......@@ -2,9 +2,12 @@
namespace Drupal\commerce_tax\Plugin\Field\FieldFormatter;
use Drupal\commerce\UrlData;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
/**
* Plugin implementation of the 'commerce_tax_number_default' formatter.
......@@ -66,6 +69,7 @@ class TaxNumberDefaultFormatter extends FormatterBase {
'failure' => $this->t('Failure'),
'unknown' => $this->t('Unknown'),
];
$entity = $items->getEntity();
$elements = [];
foreach ($items as $delta => $item) {
......@@ -75,6 +79,33 @@ class TaxNumberDefaultFormatter extends FormatterBase {
];
if ($this->getSetting('show_verification')) {
$element['#attached']['library'][] = 'commerce_tax/tax_number';
$context = UrlData::encode([
$entity->getEntityTypeId(),
$entity->id(),
$this->fieldDefinition->getName(),
$this->viewMode,
]);
if ($item->verification_result) {
$element['value'] = [
'#type' => 'link',
'#title' => $item->value,
'#url' => Url::fromRoute('commerce_tax.verification_result', [
'tax_number' => $item->value,
'context' => $context,
]),
'#attributes' => [
'class' => [
'use-ajax',
],
'data-dialog-type' => 'modal',
'data-dialog-options' => Json::encode([
'width' => 500,
'title' => $item->value,
]),
],
];
}
if ($item->verification_state && isset($states[$item->verification_state])) {
$element['verification_state'] = [
'#type' => 'html_tag',
......
<?php
namespace Drupal\commerce;
/**
* Encodes and decodes array data in a URL-safe way.
*/
final class UrlData {
/**
* Encodes the given data.
*
* @param array $data
* The data.
*
* @return string
* The encoded data.
*/
public static function encode(array $data) {
$data = json_encode($data);
// URL-safe Base64 encoding (base64url).
$data = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($data));
return $data;
}
/**
* Decodes the given data.
*
* @param string $data
* The encoded data.
*
* @return array|false
* The decoded data, or FALSE if decoding failed.
*/
public static function decode($data) {
$data = base64_decode(str_replace(['-', '_'], ['+', '/'], $data));
if ($data) {
$data = json_decode($data, TRUE);
}
return is_array($data) ? $data : FALSE;
}
}
<?php
namespace Drupal\Tests\commerce\Unit;
use Drupal\commerce\UrlData;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\commerce\UrlData
* @group commerce
*/
class UrlDataTest extends UnitTestCase {
/**
* ::covers encode
* ::covers decode.
*/
public function testEncodeDecode() {
$data = ['commerce_product', '1'];
$encoded_data = UrlData::encode($data);
$this->assertInternalType('string', $encoded_data);
$decoded_data = UrlData::decode($encoded_data);
$this->assertInternalType('array', $decoded_data);
$this->assertSame($data, $decoded_data);
$invalid_data = UrlData::decode('INVALID');
$this->assertFalse($invalid_data);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment