diff --git a/islandora.routing.yml b/islandora.routing.yml index 9215a360..e2cb7d35 100644 --- a/islandora.routing.yml +++ b/islandora.routing.yml @@ -37,16 +37,6 @@ islandora.add_member_to_node_page: _entity_create_any_access: 'node' islandora.upload_children: - path: '/node/{node}/members/upload_old' - defaults: - _form: '\Drupal\islandora\Form\AddChildrenForm' - _title: 'Upload Children' - options: - _admin_route: 'TRUE' - requirements: - _custom_access: '\Drupal\islandora\Form\AddChildrenForm::access' - -islandora.upload_children_wizard: path: '/node/{node}/members/upload/{step}' defaults: _wizard: '\Drupal\islandora\Form\AddChildrenWizard\Form' @@ -55,8 +45,7 @@ islandora.upload_children_wizard: options: _admin_route: 'TRUE' requirements: - #_custom_access: '\Drupal\islandora\Form\AddChildrenWizard\Form::access' - _custom_access: '\Drupal\islandora\Form\AddChildrenForm::access' + _custom_access: '\Drupal\islandora\Form\AddChildrenWizard\Access::checkAccess' islandora.add_media_to_node_page: path: '/node/{node}/media/add' diff --git a/src/Form/AddChildrenForm.php b/src/Form/AddChildrenForm.php index 528b4283..adad9dae 100644 --- a/src/Form/AddChildrenForm.php +++ b/src/Form/AddChildrenForm.php @@ -12,6 +12,8 @@ use Symfony\Component\HttpKernel\Exception\HttpException; /** * Form that lets users upload one or more files as children to a resource node. + * + * @deprecated Replaced with the "wizard" appraach. */ class AddChildrenForm extends AddMediaForm { diff --git a/src/Form/AddChildrenWizard/Access.php b/src/Form/AddChildrenWizard/Access.php new file mode 100644 index 00000000..2a5cdbe2 --- /dev/null +++ b/src/Form/AddChildrenWizard/Access.php @@ -0,0 +1,63 @@ +<?php + +namespace Drupal\islandora\Form\AddChildrenWizard; + +use Drupal\Core\Access\AccessResult; +use Drupal\Core\Access\AccessResultInterface; +use Drupal\Core\DependencyInjection\ContainerInjectionInterface; +use Drupal\Core\Routing\RouteMatch; +use Drupal\islandora\IslandoraUtils; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Access checker. + * + * The _wizard/_form route enhancers do not really allow for access checking + * things, so let's roll it separately for now. + */ +class Access implements ContainerInjectionInterface { + + /** + * The Islandora utils service. + * + * @var \Drupal\islandora\IslandoraUtils + */ + protected IslandoraUtils $utils; + + /** + * Constructor. + */ + public function __construct(IslandoraUtils $utils) { + $this->utils = $utils; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) : self { + return new static( + $container->get('islandora.utils') + ); + } + + /** + * Check if the user can create any "Islandora" nodes and media. + * + * @param \Drupal\Core\Routing\RouteMatch $route_match + * The current routing match. + * + * @return \Drupal\Core\Access\AccessResultInterface + * Whether we can or cannot show the "thing". + */ + public function checkAccess(RouteMatch $route_match) : AccessResultInterface { + $can_create_media = $this->utils->canCreateIslandoraEntity('media', 'media_type'); + $can_create_node = $this->utils->canCreateIslandoraEntity('node', 'node_type'); + + if ($can_create_media && $can_create_node) { + return AccessResult::allowed(); + } + + return AccessResult::forbidden(); + } + +} diff --git a/src/Form/AddChildrenWizard/FileSelectionForm.php b/src/Form/AddChildrenWizard/FileSelectionForm.php index 5fb29c43..8d29bab3 100644 --- a/src/Form/AddChildrenWizard/FileSelectionForm.php +++ b/src/Form/AddChildrenWizard/FileSelectionForm.php @@ -7,48 +7,66 @@ use Drupal\Core\Batch\BatchBuilder; use Drupal\Core\Database\Connection; use Drupal\Core\Entity\EntityFieldManagerInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; -use Drupal\Core\Field\Entity\BaseFieldOverride; use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Field\FieldItemList; use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Field\WidgetInterface; -use Drupal\Core\Field\WidgetPluginManager; use Drupal\Core\Form\FormBase; -use Drupal\Core\Form\FormState; use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Queue\QueueFactory; -use Drupal\Core\Queue\QueueInterface; use Drupal\Core\Session\AccountProxyInterface; use Drupal\Core\Url; -use Drupal\file\FileInterface; use Drupal\islandora\IslandoraUtils; -use Drupal\media\MediaInterface; -use Drupal\media\MediaSourceInterface; use Drupal\media\MediaTypeInterface; use Drupal\node\NodeInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpKernel\Exception\HttpException; use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; +/** + * Children addition wizard's second step. + */ class FileSelectionForm extends FormBase { + /** + * The entity type manager service. + * + * @var \Drupal\Core\Entity\EntityTypeManagerInterface|null + */ protected ?EntityTypeManagerInterface $entityTypeManager; /** * The widget plugin manager service. * - * @var WidgetPluginManager + * @var \Drupal\Core\Field\WidgetPluginManager|null */ protected ?PluginManagerInterface $widgetPluginManager; + /** + * The entity field manager service. + * + * @var \Drupal\Core\Entity\EntityFieldManagerInterface|null + */ protected ?EntityFieldManagerInterface $entityFieldManager; + /** + * The database connection serivce. + * + * @var \Drupal\Core\Database\Connection|null + */ protected ?Connection $database; + /** + * The current user. + * + * @var \Drupal\Core\Session\AccountProxyInterface|null + */ protected ?AccountProxyInterface $currentUser; + /** + * {@inheritdoc} + */ public static function create(ContainerInterface $container): self { - $instance = parent::create($container); + $instance = parent::create($container); $instance->entityTypeManager = $container->get('entity_type.manager'); $instance->widgetPluginManager = $container->get('plugin.manager.field.widget'); @@ -59,20 +77,57 @@ class FileSelectionForm extends FormBase { return $instance; } + /** + * {@inheritdoc} + */ public function getFormId() { return 'islandora_add_children_wizard_file_selection'; } - protected function getMediaType(FormStateInterface $form_state) : MediaTypeInterface { + /** + * Helper; get the media type, based off discovering from form state. + * + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The form state. + * + * @return \Drupal\media\MediaTypeInterface + * The target media type. + * + * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException + * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException + */ + protected function getMediaType(FormStateInterface $form_state): MediaTypeInterface { return $this->doGetMediaType($form_state->getTemporaryValue('wizard')); } - protected function doGetMediaType(array $values) : MediaTypeInterface { - /** @var MediaTypeInterface $media_type */ - return $this->entityTypeManager->getStorage('media_type')->load($values['media_type']); + /** + * Helper; get media type, given our required values. + * + * @param array $values + * An associative array which must contain at least: + * - media_type: The machine name of the media type to load. + * + * @return \Drupal\media\MediaTypeInterface + * The loaded media type. + * + * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException + * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException + */ + protected function doGetMediaType(array $values): MediaTypeInterface { + /** @var \Drupal\media\MediaTypeInterface $media_type */ + return $this->entityTypeManager->getStorage('media_type')->load($values['media_type']); } - protected function getField(FormStateInterface $form_state) : FieldDefinitionInterface { + /** + * Helper; get field instance, based off discovering from form state. + * + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The form state. + * + * @return \Drupal\Core\Field\FieldDefinitionInterface + * The field definition. + */ + protected function getField(FormStateInterface $form_state): FieldDefinitionInterface { $cached_values = $form_state->getTemporaryValue('wizard'); $field = $this->doGetField($cached_values); @@ -81,23 +136,49 @@ class FileSelectionForm extends FormBase { return $field; } - protected function doGetField(array $values) : FieldDefinitionInterface { + /** + * Helper; get field instance, given our required values. + * + * @param array $values + * See ::doGetMediaType() for which values are required. + * + * @return \Drupal\Core\Field\FieldDefinitionInterface + * The target field. + */ + protected function doGetField(array $values): FieldDefinitionInterface { $media_type = $this->doGetMediaType($values); $media_source = $media_type->getSource(); $source_field = $media_source->getSourceFieldDefinition($media_type); $fields = $this->entityFieldManager->getFieldDefinitions('media', $media_type->id()); - return isset($fields[$source_field->getFieldStorageDefinition()->getName()]) ? - $fields[$source_field->getFieldStorageDefinition()->getName()] : + return $fields[$source_field->getFieldStorageDefinition()->getName()] ?? $media_source->createSourceField(); } - protected function getWidget(FormStateInterface $form_state) : WidgetInterface { + /** + * Helper; get widget for the field, based on discovering from form state. + * + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The form state. + * + * @return \Drupal\Core\Field\WidgetInterface + * The widget. + */ + protected function getWidget(FormStateInterface $form_state): WidgetInterface { return $this->doGetWidget($this->getField($form_state)); } - protected function doGetWidget(FieldDefinitionInterface $field) : WidgetInterface { + /** + * Helper; get the base widget for the given field. + * + * @param \Drupal\Core\Field\FieldDefinitionInterface $field + * The field for which get obtain the widget. + * + * @return \Drupal\Core\Field\WidgetInterface + * The widget. + */ + protected function doGetWidget(FieldDefinitionInterface $field): WidgetInterface { return $this->widgetPluginManager->getInstance([ 'field_definition' => $field, 'form_mode' => 'default', @@ -105,11 +186,13 @@ class FileSelectionForm extends FormBase { ]); } - public function buildForm(array $form, FormStateInterface $form_state) : array { - // TODO: Using the media type selected in the previous step, grab the + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state): array { + // Using the media type selected in the previous step, grab the // media bundle's "source" field, and create a multi-file upload widget // for it, with the same kind of constraints. - $field = $this->getField($form_state); $items = FieldItemList::createInstance($field, $field->getName(), $this->getMediaType($form_state)->getTypedData()); @@ -131,11 +214,6 @@ class FileSelectionForm extends FormBase { public function submitForm(array &$form, FormStateInterface $form_state) { $cached_values = $form_state->getTemporaryValue('wizard'); - dsm($form); - dsm($form_state); - dsm($this->doGetField($cached_values)); - dsm($this->doGetField($cached_values)->getName()); - $widget = $this->getWidget($form_state); $builder = (new BatchBuilder()) ->setTitle($this->t('Creating children...')) @@ -143,24 +221,26 @@ class FileSelectionForm extends FormBase { ->setFinishCallback([$this, 'batchProcessFinished']); $values = $form_state->getValue($this->doGetField($cached_values)->getName()); $massaged_values = $widget->massageFormValues($values, $form, $form_state); - dsm($values); - dsm($massaged_values); foreach ($massaged_values as $delta => $file) { - dsm($file); - $builder->addOperation([$this, 'batchProcess'], [$delta, $file, $form, $form_state, $cached_values]); + $builder->addOperation( + [$this, 'batchProcess'], + [$delta, $file, $cached_values] + ); } batch_set($builder->toArray()); $form_state->setRedirectUrl(Url::fromUri("internal:/node/{$cached_values['node']}/members")); } - public function batchProcess($delta, $info, array $form, FormStateInterface $form_state, array $values, &$context) { - $transaction = \Drupal::database()->startTransaction(); + /** + * Implements callback_batch_operation() for our child addition batch. + */ + public function batchProcess($delta, $info, array $values, &$context) { + $transaction = $this->database->startTransaction(); try { $taxonomy_term_storage = $this->entityTypeManager->getStorage('taxonomy_term'); - dsm(func_get_args()); - /** @var FileInterface $file */ + /** @var \Drupal\file\FileInterface $file */ $file = $this->entityTypeManager->getStorage('file')->load($info['target_id']); $file->setPermanent(); if ($file->save() !== SAVED_UPDATED) { @@ -170,36 +250,35 @@ class FileSelectionForm extends FormBase { $node_storage = $this->entityTypeManager->getStorage('node'); $parent = $node_storage->load($values['node']); - // Create a node (with the filename?) (and also belonging to the target node). + // Create a node (with the filename?) (and also belonging to the target + // node). + /** @var \Drupal\node\NodeInterface $node */ $node = $node_storage->create([ 'type' => $values['bundle'], 'title' => $file->getFilename(), IslandoraUtils::MEMBER_OF_FIELD => $parent, 'uid' => $this->currentUser->id(), 'status' => NodeInterface::PUBLISHED, - IslandoraUtils::MODEL_FIELD => $values['model'] ? + IslandoraUtils::MODEL_FIELD => ($values['model'] ? $taxonomy_term_storage->load($values['model']) : - NULL, + NULL), ]); + if ($node->save() !== SAVED_NEW) { throw new \Exception("Failed to create node for file '{$file->id()}'."); } // Create a media with the file attached and also pointing at the node. $field = $this->doGetField($values); - $widget = $this->doGetWidget($field); - $items = FieldItemList::createInstance($field, $field->getName(), $this->getMediaType($form_state)->getTypedData()); - $items->setValue([0 => $info]); - //$items->setValue([$delta => $info]); $media_values = array_merge( [ 'bundle' => $values['media_type'], 'name' => $file->getFilename(), IslandoraUtils::MEDIA_OF_FIELD => $node, - IslandoraUtils::MEDIA_USAGE_FIELD => $values['use'] ? + IslandoraUtils::MEDIA_USAGE_FIELD => ($values['use'] ? $taxonomy_term_storage->loadMultiple($values['use']) : - NULL, + NULL), 'uid' => $this->currentUser->id(), // XXX: Published... no constant? 'status' => 1, @@ -210,11 +289,19 @@ class FileSelectionForm extends FormBase { ], ] ); - dsm($media_values); $media = $this->entityTypeManager->getStorage('media')->create($media_values); if ($media->save() !== SAVED_NEW) { throw new \Exception("Failed to create media for file '{$file->id()}."); } + + $context['results'] = array_merge_recursive($context['results'], [ + 'validation_violations' => $this->validationClassification([ + $file, + $media, + $node, + ]), + ]); + $context['results']['count'] += 1; } catch (HttpExceptionInterface $e) { $transaction->rollBack(); @@ -226,8 +313,52 @@ class FileSelectionForm extends FormBase { } } - public function batchProcessFinished() { - // TODO: Dump out status message of some sort? + /** + * @param array $entities + * + * @return array + */ + protected function validationClassification(array $entities) { + $violations = []; + + foreach ($entities as $entity) { + $entity_violations = $entity->validate(); + if ($entity_violations->count() > 0) { + $violations[$entity->getEntityTypeId()][$entity->id()] = $entity_violations->count(); + } + } + + return $violations; + } + + /** + * Implements callback_batch_finished() for our child addition batch. + */ + public function batchProcessFinished($success, $results, $operations): void { + if ($success) { + $this->messenger()->addMessage($this->formatPlural( + $results['count'], + 'Added 1 child node.', + 'Added @count child nodes.' + )); + foreach ($results['validation_violations'] ?? [] as $entity_type => $info) { + foreach ($info as $id => $count) { + $this->messenger()->addWarning($this->formatPlural( + $count, + '1 validation error present in <a href=":uri">bulk created entity of type %type, with ID %id</a>.', + '@count validation errors present in <a href=":uri">bulk created entity of type %type, with ID %id</a>.', + [ + '%type' => $entity_type, + ':uri' => Url::fromRoute("entity.{$entity_type}.canonical", [$entity_type => $id])->toString(), + '%id' => $id, + ] + )); + } + } + } + else { + $this->messenger()->addError($this->t('Encountered an error when adding children.')); + } } } diff --git a/src/Form/AddChildrenWizard/Form.php b/src/Form/AddChildrenWizard/Form.php index bd2b92ae..68489f18 100644 --- a/src/Form/AddChildrenWizard/Form.php +++ b/src/Form/AddChildrenWizard/Form.php @@ -5,8 +5,6 @@ namespace Drupal\islandora\Form\AddChildrenWizard; use Drupal\Core\Access\AccessResult; use Drupal\Core\DependencyInjection\ClassResolverInterface; use Drupal\Core\Form\FormBuilderInterface; -use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Render\RendererInterface; use Drupal\Core\Routing\RouteMatch; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Session\AccountProxyInterface; @@ -14,14 +12,38 @@ use Drupal\Core\TempStore\SharedTempStoreFactory; use Drupal\ctools\Wizard\FormWizardBase; use Drupal\islandora\IslandoraUtils; use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\RequestStack; +/** + * Bulk children addition wizard base form. + */ class Form extends FormWizardBase { + /** + * The Islandora Utils service. + * + * @var \Drupal\islandora\IslandoraUtils + */ protected IslandoraUtils $utils; + + /** + * The current node ID. + * + * @var string|mixed|null + */ protected string $nodeId; + + /** + * The current route match. + * + * @var \Drupal\Core\Routing\RouteMatchInterface + */ protected RouteMatchInterface $currentRoute; + + /** + * The current user. + * + * @var \Drupal\Core\Session\AccountProxyInterface + */ protected AccountProxyInterface $currentUser; /** @@ -58,7 +80,6 @@ class Form extends FormWizardBase { [ 'utils' => \Drupal::service('islandora.utils'), 'tempstore_id' => 'islandora.upload_children', - //'machine_name' => 'islandora_add_children_wizard', 'current_route_match' => \Drupal::service('current_route_match'), 'current_user' => \Drupal::service('current_user'), ] @@ -79,35 +100,38 @@ class Form extends FormWizardBase { * {@inheritdoc} */ public function getOperations($cached_values) { - return [ - 'child_type' => [ - 'title' => $this->t('Type of children'), - 'form' => TypeSelectionForm::class, - 'values' => [ - 'node' => $this->nodeId, - ] + $ops = []; + + $ops['child_type'] = [ + 'title' => $this->t('Type of children'), + 'form' => TypeSelectionForm::class, + 'values' => [ + 'node' => $this->nodeId, + ], + ]; + $ops['child_files'] = [ + 'title' => $this->t('Files for children'), + 'form' => FileSelectionForm::class, + 'values' => [ + 'node' => $this->nodeId, ], - 'child_files' => [ - 'title' => $this->t('Files for children'), - 'form' => FileSelectionForm::class, - 'values' => [ - 'node' => $this->nodeId, - ] - ] ]; + + return $ops; } + /** + * {@inheritdoc} + */ public function getNextParameters($cached_values) { return parent::getNextParameters($cached_values) + ['node' => $this->nodeId]; } + /** + * {@inheritdoc} + */ public function getPreviousParameters($cached_values) { return parent::getPreviousParameters($cached_values) + ['node' => $this->nodeId]; } - public function finish(array &$form, FormStateInterface $form_state) { - parent::finish($form, $form_state); // TODO: Change the autogenerated stub - dsm($form_state->getTemporaryValue('wizard')); - } - } diff --git a/src/Form/AddChildrenWizard/TypeSelectionForm.php b/src/Form/AddChildrenWizard/TypeSelectionForm.php index fffb2ae9..68237711 100644 --- a/src/Form/AddChildrenWizard/TypeSelectionForm.php +++ b/src/Form/AddChildrenWizard/TypeSelectionForm.php @@ -3,25 +3,50 @@ namespace Drupal\islandora\Form\AddChildrenWizard; use Drupal\Core\Cache\CacheableMetadata; -use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Entity\EntityFieldManagerInterface; use Drupal\Core\Entity\EntityTypeBundleInfoInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormStateInterface; use Drupal\islandora\IslandoraUtils; -use Drupal\taxonomy\TermInterface; use Symfony\Component\DependencyInjection\ContainerInterface; -use function Symfony\Component\DependencyInjection\Loader\Configurator\iterator; +/** + * Children addition wizard's first step. + */ class TypeSelectionForm extends FormBase { + /** + * Cacheable metadata that is instantiated and used internally. + * + * @var \Drupal\Core\Cache\CacheableMetadata|null + */ protected ?CacheableMetadata $cacheableMetadata = NULL; + + /** + * The entity type bundle info service. + * + * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface|null + */ protected ?EntityTypeBundleInfoInterface $entityTypeBundleInfo; + + /** + * The entity type manager service. + * + * @var \Drupal\Core\Entity\EntityTypeManagerInterface|null + */ protected ?EntityTypeManagerInterface $entityTypeManager; - protected ?EntityFieldManagerInterface $entityFieldManager; + /** + * The entity field manager service. + * + * @var \Drupal\Core\Entity\EntityFieldManagerInterface|null + */ + protected ?EntityFieldManagerInterface $entityFieldManager; + /** + * {@inheritdoc} + */ public static function create(ContainerInterface $container) { $instance = parent::create($container); @@ -39,14 +64,33 @@ class TypeSelectionForm extends FormBase { return 'islandora_add_children_type_selection'; } + /** + * Memoization for ::getNodeBundleOptions(). + * + * @var array|null + */ protected ?array $nodeBundleOptions = NULL; + + /** + * Indicate presence of model field on node bundles. + * + * Populated as a side effect of ::getNodeBundleOptions(). + * + * @var array|null + */ protected ?array $nodeBundleHasModelField = NULL; - //protected ?array $nodeBundleHasMemberOfField = NULL; + + /** + * Helper; get the node bundle options available to the current user. + * + * @return array + * An associative array mapping node bundle machine names to their human- + * readable labels. + */ protected function getNodeBundleOptions() : array { if ($this->nodeBundleOptions === NULL) { $this->nodeBundleOptions = []; $this->nodeBundleHasModelField = []; - //$this->nodeBundleHasMemberOfField = []; $access_handler = $this->entityTypeManager->getAccessControlHandler('node'); foreach ($this->entityTypeBundleInfo->getBundleInfo('node') as $bundle => $info) { @@ -62,7 +106,6 @@ class TypeSelectionForm extends FormBase { } $this->nodeBundleOptions[$bundle] = $info['label']; $fields = $this->entityFieldManager->getFieldDefinitions('node', $bundle); - //$this->nodeBundleHasMemberOfField[$bundle] = array_key_exists(IslandoraUtils::MEMBER_OF_FIELD, $fields); $this->nodeBundleHasModelField[$bundle] = array_key_exists(IslandoraUtils::MODEL_FIELD, $fields); } } @@ -70,7 +113,16 @@ class TypeSelectionForm extends FormBase { return $this->nodeBundleOptions; } - protected function getModelOptions() : \Traversable { + /** + * Generates a mapping of taxonomy term IDs to their names. + * + * @return \Generator + * The mapping of taxonomy term IDs to their names. + * + * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException + * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException + */ + protected function getModelOptions() : \Generator { $terms = $this->entityTypeManager->getStorage('taxonomy_term') ->loadTree('islandora_models', 0, NULL, TRUE); foreach ($terms as $term) { @@ -78,15 +130,43 @@ class TypeSelectionForm extends FormBase { } } - protected function mapModelStates() : \Traversable { + /** + * Helper; map node bundles supporting the "has model" field, for #states. + * + * @return \Generator + * Yields associative array mapping the string 'value' to the bundles which + * have the given field. + */ + protected function mapModelStates() : \Generator { $this->getNodeBundleOptions(); foreach (array_keys(array_filter($this->nodeBundleHasModelField)) as $bundle) { yield ['value' => $bundle]; } } + /** + * Memoization for ::getMediaBundleOptions(). + * + * @var array|null + */ protected ?array $mediaBundleOptions = NULL; + + /** + * Indicate presence of usage field on media bundles. + * + * Populated as a side effect in ::getMediaBundleOptions(). + * + * @var array|null + */ protected ?array $mediaBundleUsageField = NULL; + + /** + * Helper; get options for media types. + * + * @return array + * An associative array mapping the machine name of the media type to its + * human-readable label. + */ protected function getMediaBundleOptions() : array { if ($this->mediaBundleOptions === NULL) { $this->mediaBundleOptions = []; @@ -113,16 +193,33 @@ class TypeSelectionForm extends FormBase { return $this->mediaBundleOptions; } + /** + * Helper; list the terms of the "islandora_media_use" vocabulary. + * + * @return \Generator + * Generates term IDs as keys mapping to term names. + * + * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException + * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException + */ protected function getMediaUseOptions() { - /** @var TermInterface[] $terms */ - $terms = $this->entityTypeManager->getStorage('taxonomy_term') + /** @var \Drupal\taxonomy\TermInterface[] $terms */ + $terms = $this->entityTypeManager->getStorage('taxonomy_term') ->loadTree('islandora_media_use', 0, NULL, TRUE); foreach ($terms as $term) { yield $term->id() => $term->getName(); } } - protected function mapUseStates() { + + /** + * Helper; map media types supporting the usage field for use with #states. + * + * @return \Generator + * Yields associative array mapping the string 'value' to the bundles which + * have the given field. + */ + protected function mapUseStates(): \Generator { $this->getMediaBundleOptions(); foreach (array_keys(array_filter($this->mediaBundleUsageField)) as $bundle) { yield ['value' => $bundle]; @@ -165,7 +262,7 @@ class TypeSelectionForm extends FormBase { 'required' => [ ':input[name="bundle"]' => $model_states, ], - ] + ], ]; $form['media_type'] = [ '#type' => 'select',