media_mpx-8.x-1.x-dev/src/Form/AccountForm.php
src/Form/AccountForm.php
<?php
namespace Drupal\media_mpx\Form;
use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Path\CurrentPathStack;
use Drupal\Core\Url;
use Drupal\media_mpx\DataObjectFactoryCreator;
use Drupal\media_mpx\MpxLogger;
use GuzzleHttp\Exception\TransferException;
use Lullabot\Mpx\DataService\ObjectListQuery;
use Lullabot\Mpx\Exception\ClientException;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* The mpx Account form.
*
* @property \Drupal\media_mpx\AccountInterface $entity
*/
class AccountForm extends EntityForm {
/**
* The current path service.
*
* @var \Drupal\Core\Path\CurrentPathStack
*/
protected $currentPathStack;
/**
* The factory used to load mpx objects.
*
* @var \Drupal\media_mpx\DataObjectFactoryCreator
*/
protected $dataObjectFactory;
/**
* The system logger for mpx errors.
*
* @var \Drupal\media_mpx\MpxLogger
*/
private $mpxLogger;
/**
* AccountForm constructor.
*
* @param \Drupal\Core\Path\CurrentPathStack $currentPathStack
* The current path service.
* @param \Drupal\media_mpx\DataObjectFactoryCreator $dataObjectFactory
* The factory used to load mpx objects.
* @param \Drupal\media_mpx\MpxLogger $mpxLogger
* The system logger for mpx errors.
*/
public function __construct(CurrentPathStack $currentPathStack, DataObjectFactoryCreator $dataObjectFactory, MpxLogger $mpxLogger) {
$this->currentPathStack = $currentPathStack;
$this->dataObjectFactory = $dataObjectFactory;
$this->mpxLogger = $mpxLogger;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('path.current'),
$container->get('media_mpx.data_object_factory_creator'),
$container->get('media_mpx.exception_logger')
);
}
/**
* {@inheritdoc}
*/
public function form(array $form, FormStateInterface $form_state) {
$form = parent::form($form, $form_state);
$form = $this->idLabelForm($form);
$users = $this->loadMpxUsers($form_state);
if (empty($users)) {
$url = Url::fromRoute('entity.media_mpx_user.add_form')->toString() . '?destination=' . \Drupal::service('path.current')->getPath();
$this->messenger()->addError($this->t('<a href="@add-user">Create at least one mpx user</a> before creating accounts.', [
'@add-user' => $url,
]));
return [];
}
$form = $this->userAccountForm($form, $form_state, $users);
$form['status'] = [
'#type' => 'checkbox',
'#title' => $this->t('Enabled'),
'#default_value' => $this->entity->status(),
];
return $form;
}
/**
* Ajax callback to fetch the list of mpx accounts.
*
* @param array &$form
* The form being rendered.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*/
public function fetchAccounts(array &$form, FormStateInterface $form_state) {
list($options, $account_pids) = $this->accountOptions($form_state);
if (empty($options)) {
return;
}
$form['account_pids'] = [
'#type' => 'value',
'#value' => $account_pids,
];
$form['accounts_container']['account'] = [
// @todo Change to radios when
// https://www.drupal.org/project/drupal/issues/2758631 is fixed.
'#type' => 'select',
'#title' => $this->t('mpx account'),
'#options' => $options,
'#default_value' => $this->entity->get('account'),
];
}
/**
* {@inheritdoc}
*/
public function save(array $form, FormStateInterface $form_state) {
$this->entity->set('public_id', $form_state->getValue('account_pids')[$this->entity->get('account')]);
$result = parent::save($form, $form_state);
$message_args = ['%label' => $this->entity->label()];
$message = $result == SAVED_NEW
? $this->t('Created new mpx account %label.', $message_args)
: $this->t('Updated mpx account %label.', $message_args);
$this->messenger()->addStatus($message);
$form_state->setRedirectUrl($this->entity->toUrl('collection'));
return $result;
}
/**
* Return the mpx user and account selection form.
*
* @param array $form
* The current form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param array $users
* An array of mpx User options.
*
* @return array
* The complete form.
*/
private function userAccountForm(array $form, FormStateInterface $form_state, array $users): array {
$form['user'] = [
'#type' => 'select',
'#title' => $this->t('mpx user'),
'#description' => $this->t('Select an mpx user to see what accounts are available.'),
'#options' => $users,
'#default_value' => $this->entity->get('user'),
'#ajax' => [
'callback' => [$this, 'fetchAccounts'],
'event' => 'change',
'wrapper' => 'media-mpx-accounts',
'progress' => [
'type' => 'throbber',
'message' => $this->t('Fetching mpx accounts…'),
],
],
];
$form['accounts_container'] = [
'#type' => 'container',
'#attributes' => [
'id' => 'media-mpx-accounts',
],
];
$this->fetchAccounts($form, $form_state);
return $form;
}
/**
* Return the id and label fields.
*
* @param array $form
* The current form.
*
* @return array
* The complete form.
*/
private function idLabelForm(array $form): array {
$form['label'] = [
'#type' => 'textfield',
'#title' => $this->t('Label'),
'#maxlength' => 255,
'#default_value' => $this->entity->label(),
'#description' => $this->t('Label for the mpx account.'),
'#required' => TRUE,
];
$form['id'] = [
'#type' => 'machine_name',
'#default_value' => $this->entity->id(),
'#machine_name' => [
'exists' => '\Drupal\media_mpx\Entity\Account::load',
],
'#disabled' => !$this->entity->isNew(),
];
return $form;
}
/**
* Return all configured mpx user names, keyed by their config entity ID.
*
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*
* @return array
* An array of user names, keyed by the config entity ID.
*/
private function loadMpxUsers(FormStateInterface $form_state): array {
$users = array_map(function ($entity) {
/** @var \Drupal\media_mpx\Entity\UserInterface $entity */
return $entity->label();
}, $this->entityTypeManager->getStorage('media_mpx_user')->loadMultiple());
// Set the currently selected user on the initial load.
if (!$form_state->hasValue('user')) {
reset($users);
$form_state->setValue('user', key($users));
}
return $users;
}
/**
* Return the account options and their public IDs.
*
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*
* @return array
* An array with:
* - The account options.
* - The account public IDs.
*/
protected function accountOptions(FormStateInterface $form_state): array {
try {
try {
return $this->fetchAccountOptions($form_state);
}
catch (ClientException $e) {
$this->displayCredentialError($e);
}
}
catch (TransferException $e) {
// Something went very wrong, so we log the whole exception for reference.
$this->mpxLogger->logException($e);
$this->messenger()->addError($this->t('An unexpected error occurred. The full error has been logged. %error',
[
'%error' => $e->getMessage(),
])
);
}
return [[], []];
}
/**
* Display an access denied error.
*
* @param \Lullabot\Mpx\Exception\ClientException $e
* The mpx client exception.
*/
private function displayCredentialError(ClientException $e) {
// First, we have special handling for credential errors.
if ($e->getCode() == 401 || $e->getCode() == 403) {
$this->messenger()->addError($this->t('Access was denied connecting to mpx. %error',
[
'%error' => $e->getMessage(),
])
);
return;
}
// This is a client exception, but not an authentication error so we
// throw this up to the "unexpected error" case.
throw $e;
}
/**
* Fetch the account options from mpx.
*
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*
* @return array
* An array with:
* - The account options.
* - The account public IDs.
*/
private function fetchAccountOptions(FormStateInterface $form_state): array {
$options = [];
$account_pids = [];
$user_entity_id = $form_state->getValue('user');
/** @var \Drupal\media_mpx\Entity\UserInterface $user */
$user = $this->entityTypeManager->getStorage('media_mpx_user')
->load($user_entity_id);
$accountFactory = $this->dataObjectFactory->forObjectType($user, 'Access Data Service', 'Account', '1.0');
$query = new ObjectListQuery();
$accounts = $accountFactory->select($query);
/** @var \Lullabot\Mpx\DataService\Access\Account $account */
foreach ($accounts as $account) {
$path_parts = explode('/', $account->getId()->getPath());
$options[(string) $account->getId()] = $this->t('@title (@id)', [
'@title' => $account->getTitle(),
'@id' => end($path_parts),
]);
$account_pids[(string) $account->getId()] = $account->getPid();
}
return [$options, $account_pids];
}
}
