diff --git a/metadata_profile.module b/metadata_profile.module index 7a5a8cc..505ad51 100644 --- a/metadata_profile.module +++ b/metadata_profile.module @@ -10,6 +10,31 @@ function metadata_profile_entity_type_alter(array &$entity_types) { foreach ($entity_types as $entity_type) { if ($entity_type->getBundleOf() && $entity_type->hasLinkTemplate('edit-form')) { $entity_type->setLinkTemplate('metadata-profile', $entity_type->getLinkTemplate('edit-form') . "/metadata-profile"); + $entity_type->setLinkTemplate('metadata-download', $entity_type->getLinkTemplate('edit-form') . "/metadata-profile/download"); } } } + +/** + * Implements hook_file_download(). + */ +function metadata_profile_file_download($uri){ + $stream_wrapper_manager = \Drupal::service('stream_wrapper_manager'); + $scheme = $stream_wrapper_manager->getScheme($uri); + $target = $stream_wrapper_manager->getTarget($uri); + + if ($scheme == 'temporary' && $target) { + $request = \Drupal::request(); + $route = $request->attributes->get('_route'); + // Check if we were called by Profile download route. + // No additional access checking needed here: route requires + // "export configuration" permission, token is validated by the controller. + // @see \Drupal\features\Controller\FeaturesController::downloadExport() + if (str_ends_with($route, 'metadata_profile.download')) { + return [ + 'Content-disposition' => 'attachment; filename="' . $target . '"', + ]; + } + } + return NULL; +} diff --git a/metadata_profile.permissions.yml b/metadata_profile.permissions.yml new file mode 100644 index 0000000..6d1e21f --- /dev/null +++ b/metadata_profile.permissions.yml @@ -0,0 +1,3 @@ +view metadata profiles: + description: "Access the metadata profiles tab from bundle edit pages." + title: "View metadata profiles" diff --git a/src/Controller/MetadataProfileController.php b/src/Controller/MetadataProfileController.php index 2fedcf1..29020ce 100644 --- a/src/Controller/MetadataProfileController.php +++ b/src/Controller/MetadataProfileController.php @@ -2,29 +2,47 @@ namespace Drupal\metadata_profile\Controller; +use Drupal\Core\Config\Config; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Controller\ControllerBase; +use Drupal\Core\Entity\EntityFieldManagerInterface; use Drupal\Core\Field\BaseFieldDefinition; use Drupal\Core\Field\FieldDefinitionInterface; -use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Field\FieldTypePluginManagerInterface; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Link; use Drupal\Core\Messenger\MessengerTrait; -use Drupal\Core\Config\Config; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Url; -use Drupal\field_permissions\Plugin\FieldPermissionType\Base; +use Drupal\system\FileDownloadController; use Symfony\Component\DependencyInjection\ContainerInterface; -use Drupal\Core\Entity\EntityFieldManagerInterface; +use Symfony\Component\HttpFoundation\Request; + class MetadataProfileController extends ControllerBase { use MessengerTrait; + /** + * The entity field manager. + * + * @var EntityFieldManagerInterface + */ protected EntityFieldManagerInterface $entityFieldManager; + /** + * The Field Type Plugin Manager. + * + * @var FieldTypePluginManagerInterface + */ protected FieldTypePluginManagerInterface $fieldTypePluginManager; + /** + * The config factory. + * + * @var ConfigFactoryInterface + */ protected $configFactory; + /** * The route matcher. * @@ -33,31 +51,46 @@ class MetadataProfileController extends ControllerBase { protected $routeMatch; /** - * The entity type machine name. + * The entity type machine name, e.g. 'node_type'. * * @var string */ protected $entityType; /** - * The bundle machine name. + * The bundle machine name, e.g. 'article'. * * @var string */ protected $entityBundle; /** - * The entity type that our config entity describes bundles of. + * The entity type that our config entity describes bundles of, e.g. 'node'. * * @var string */ protected $entityTypeBundleOf; + /** + * File system interface. + * + * @var \Drupal\Core\File\FileSystemInterface + */ + protected $fileSystem; + /** + * The file download controller. + * + * @var \Drupal\system\FileDownloadController + */ + protected $fileDownloadController; + public function __construct(EntityFieldManagerInterface $entity_field_manager, FieldTypePluginManagerInterface $field_type_plugin_manager, ConfigFactoryInterface $config_factory, - RouteMatchInterface $route_match) { + RouteMatchInterface $route_match, + FileSystemInterface $file_system, + FileDownloadController $file_download_controller) { $this->entityFieldManager = $entity_field_manager; $this->fieldTypePluginManager = $field_type_plugin_manager; $this->configFactory = $config_factory; @@ -68,6 +101,8 @@ class MetadataProfileController extends ControllerBase { $entity_type = $this->routeMatch->getParameter($this->entityType); $this->entityBundle = $entity_type->id(); $this->entityTypeBundleOf = $entity_type->getEntityType()->getBundleOf(); + $this->fileSystem = $file_system; + $this->fileDownloadController = $file_download_controller; } public static function create(ContainerInterface $container) { @@ -75,28 +110,27 @@ class MetadataProfileController extends ControllerBase { $container->get('entity_field.manager'), $container->get('plugin.manager.field.field_type'), $container->get('config.factory'), - $container->get('current_route_match') + $container->get('current_route_match'), + $container->get('file_system'), + FileDownloadController::create($container) ); } /** - * Returns a render array displaying the metadata profile for $content_type. + * Returns a page (render array) displaying the metadata profile. * - * @param $content_type * @return array */ - public function profile($content_type = NULL) { - if (!$content_type) { - $content_type = $this->entityBundle; - } + public function profile() { + // Get core content type information. - $bundle_config = $this->config(str_replace('_', '.', $this->entityType) . '.' . $content_type); + $bundle_config = $this->config(str_replace('_', '.', $this->entityType) . '.' . $this->entityBundle); if (!$bundle_config) { return ['#markup'=> 'Not a valid content type.']; } - $build = $this->format_bundle($bundle_config); + $build = $this->formatBundle($bundle_config); - $metadata_profile = $this->getMetadataProfile($content_type); + $metadata_profile = $this->getMetadataProfile(); $build['summary_table'] = [ '#type' => 'container', @@ -108,19 +142,19 @@ class MetadataProfileController extends ControllerBase { '#type' => 'container', '#weight' => '9', ]; - // Get field information. - $field_definitions = $this->entityFieldManager->getFieldDefinitions($this->entityTypeBundleOf, $content_type); + // Refactor this to not loop; each thing called by displayField should use metadata profile. + $field_definitions = $this->entityFieldManager->getFieldDefinitions($this->entityTypeBundleOf, $this->entityBundle); foreach ($field_definitions as $field_name => $field_definition) { - $build['fields'][$field_name] = $this->display_field($field_name, $field_definition, $metadata_profile[$field_name]); + $build['fields'][$field_name] = $this->buildField($metadata_profile[$field_name]); } return $build; } /** - * Format information about a bundle. + * Format basic information about a bundle. */ - protected function format_bundle(Config $bundle) { + protected function formatBundle(Config $bundle) { if (str_starts_with($bundle->getName(), 'node.type')) { $label = $bundle->get('name'); $machine_name = $bundle->get('type'); @@ -160,13 +194,14 @@ class MetadataProfileController extends ControllerBase { return $build; } - public function getMetadataProfile($bundle) { + public function getMetadataProfile() { // $metadata_profile is an array of arrays. Its keys are the "short" machine names of the fields // such as field_abstract. The values are arrays with the following keys: // * 'label' - text. the bundle's human-readable label. // * 'machine_name' - text. The short machine name such as 'field_abstract'. // * 'id' - text. The long machine name such as 'node.islandora_object.field_abstract'. (same as this array's key) // * 'edit_url' - Url object. The link to the edit page for that field (or to the base field override page). + // * 'description' - the help text for the field. // * 'type' - text. The human readable type of the field. (e.g. "Entity Reference") // * 'type_machine_name' - text. The machine name of the type (e.g. 'entity_reference') // * 'required' - 'Required'|'Not required'. Whether the field is required. @@ -175,84 +210,176 @@ class MetadataProfileController extends ControllerBase { // * 'target_bundles' - array. List of bundle names that are the target of this field. // * 'search_api' - associative array. Has the following keys: // * 'in_search_api' - True|False. True if some field matches. + // * 'has_facet' - True|False. True if some field has a facet. // * 'fields' - array. The 'fields' array is keyed by the machine names of the search api fields, // which are arrays with the following fields: // * 'search_api_field_name' - the machine name of the search api field. // * 'search_api_field_label' - the human readable label of the search api field. // * 'property_path' - the method for getting the values. + // * 'type' = the search api field type. // * 'index_name' - the name of the Search API index (in case there are more than one) // * 'fields_included' - for aggregate and EDTF processor fields, which fields are in that field. // * 'facets' - associative array. has the following keys // * 'has_facet' - True|False. True if there is a facet, even if not showing up (can use with url alias) // * 'facets' - array. the 'facets' array is keyed by the machine name of the facet. each facet is an // array with keys: - // * 'facet_name' - // * 'facet_machine_name' - // * 'facet_source' - // * 'url_alias' - // * 'block_visible' + // * 'facet_name' + // * 'facet_machine_name' + // * 'facet_source' + // * 'url_alias' + // * 'block_visible' $metadata_profile = []; - $field_definitions = $this->entityFieldManager->getFieldDefinitions($this->entityTypeBundleOf, $bundle); + $field_definitions = $this->entityFieldManager->getFieldDefinitions($this->entityTypeBundleOf, $this->entityBundle); foreach ($field_definitions as $field_name => $field_definition) { $metadata_profile[$field_name] = [ 'label' => $field_definition->getLabel(), 'machine_name' => $field_name, 'id' => method_exists($field_definition, 'id') ? $field_definition->id() : $field_definition->getUniqueIdentifier(), 'edit_url' => $this->getFieldEditUrl($field_definition), - 'details_link' => $this->getDetailsFragmentLink($field_name, $field_definition), + 'details_link' => $this->getFieldDetailsFragmentLink($field_name, $field_definition), + 'description' => $field_definition->getDescription(), 'type' => $this->formatType($field_definition), 'type_machine_name' => $field_definition->getType(), 'required' => $this->formatRequired($field_definition), 'repeatable' => $this->formatCardinality($field_definition), 'auto_create' => $this->formatCreateNew($field_definition), 'target_bundles' => $this->formatTargetBundles($field_definition), + 'base_field' => $field_definition->getFieldStorageDefinition() instanceof BaseFieldDefinition, ]; - // Add solr + $metadata_profile[$field_name]['search_api'] = $this->getSearchApi($field_definition); } return $metadata_profile; } + private function getSearchApi($field_definition) { + if (!$this->moduleHandler()->moduleExists('search_api')) { + return FALSE; + } + $search_api = [ + 'in_search_api' => FALSE, + 'fields' => [], + ]; + $storage = $field_definition->getFieldStorageDefinition(); + if ($storage instanceof BaseFieldDefinition) { + $field_id = $this->entityTypeBundleOf . '.' . $storage->getName(); + } + else { + $field_id = 'field.storage.' . $storage->get('id'); + } + $search_api_indexes = $this->configFactory->listAll('search_api.index'); + foreach ($search_api_indexes as $index) { + $index_config = $this->config($index); + + // Loop over fields. + foreach ($index_config->get('field_settings') as $search_api_field_name => $search_api_field_setting) { + $indexed_field_name = $index_config->getName() . '.' .$search_api_field_name; + // Get Aggregated Fields. + if ($search_api_field_setting['property_path'] == 'aggregated_field') { + if (in_array('entity:node/' . $field_definition->getName(), $search_api_field_setting['configuration']['fields'])) { + $search_api['fields'][$indexed_field_name] = $this->getSearchApiField($search_api_field_name, $search_api_field_setting, $index_config); + } + } + // Get EDTF fields. + else if ($field_definition->getType() == 'edtf') { + // Get EDTF year. + if ($search_api_field_setting['property_path'] == 'edtf_year') { + $edtf_year_fields = $index_config->get('processor_settings')['edtf_year_only']['fields'] ?: []; + if (in_array(str_replace('.', '|', $field_definition->id()), $edtf_year_fields)) { + $search_api['fields'][$indexed_field_name] = $this->getSearchApiField($search_api_field_name, $search_api_field_setting, $index_config); + } + } + // Get EDTF Date + else if ($search_api_field_setting['property_path'] == 'edtf_dates') { + $edtf_date_fields = $index_config->get('processor_settings')['edtf_date_processor']['fields'] ?: []; + if (in_array(str_replace('.', '|', $field_definition->id()), $edtf_date_fields)) { + $search_api['fields'][$indexed_field_name] = $this->getSearchApiField($search_api_field_name, $search_api_field_setting, $index_config); + } + } + } + // Check dependencies for a dependency on this field. + if (isset($search_api_field_setting['dependencies']) and isset($search_api_field_setting['dependencies']['config'])) { + $field_dependencies = $search_api_field_setting['dependencies']['config']; + if (in_array($field_id, $field_dependencies)) { + $search_api['fields'][$indexed_field_name] = $this->getSearchApiField($search_api_field_name, $search_api_field_setting, $index_config); + } + } + + // Check if the property path equals this field. + if (isset($search_api_field_setting['datasource_id']) and $search_api_field_setting['property_path'] == $field_definition->getName()) { + $search_api['fields'][$indexed_field_name] = $this->getSearchApiField($search_api_field_name, $search_api_field_setting, $index_config); + } + + } + if (count($search_api['fields']) > 0) { + $search_api['in_search_api'] = TRUE; + } + if (in_array(TRUE, array_column($search_api['fields'], 'has_facet'), true)) { + $search_api['has_facet'] = TRUE; + } + else { + $search_api['has_facet'] = FALSE; + } + + } + return $search_api; + } + protected function buildSummaryTable($metadata_profile) { + $rows = $this->getRows($metadata_profile, TRUE); + return [ + '#type' => 'table', + '#header' => $this->getHeaders(), + '#rows' => $rows, + ]; + + } + + protected function getRows($metadata_profile, $display=NULL) { $rows = []; foreach ($metadata_profile as $field_name => $field_profile) { if (str_starts_with( $field_name, 'field_') or in_array($field_name, ['title', 'name'])) { $rows[] = [ - $field_profile['details_link'], + $display ? $field_profile['details_link'] : $field_profile['label'], $field_profile['machine_name'], + $field_profile['description'], $field_profile['type'], $field_profile['required'], $field_profile['repeatable'], $field_profile['auto_create'], - $this->formatListForTable($field_profile['target_bundles']), - ]; + $display ? $this->formatListForTable($field_profile['target_bundles']) : $field_profile['target_bundles'], + $field_profile['search_api']['in_search_api'] ? 'Indexed' : '', + $field_profile['search_api']['has_facet'] ? 'Facet' : '', + ]; } } + return $rows; + } + + protected function getHeaders() { return [ - '#type' => 'table', - '#header' => [ - $this->t('Field'), - $this->t('Machine name'), - $this->t('Type'), - $this->t('Required'), - $this->t('Repeatable'), - $this->t('Create new'), - $this->t('Target bundles') - // TODO: add more columns - ], - '#rows' => $rows, + $this->t('Field'), + $this->t('Machine name'), + $this->t('Description'), + $this->t('Type'), + $this->t('Required'), + $this->t('Repeatable'), + $this->t('Create new'), + $this->t('Target bundles'), + $this->t('In Search API'), + $this->t('Has Facet'), + // TODO: add more columns ]; - } - protected function display_field(string $field_name, FieldDefinitionInterface $field_definition, array $field_profile) { + protected function buildField(array $field_profile) { $render_array = []; - + $field_name = $field_profile['machine_name']; if (str_starts_with($field_name, 'field_') or in_array($field_name, ['title', 'name'])) { $render_array = [ '#type' => 'container', '#attributes' => ['class' => ['field-info']], - '#weight' => $this->formatWeight($field_definition), // This doesn't work. + // TODO: add weight here from form display defaults. 'title' => [ '#plain_text' => $field_profile['label'], '#prefix' => '

', @@ -272,10 +399,11 @@ class MetadataProfileController extends ControllerBase { ], 'description' => [ '#type' => 'markup', - '#markup' => $field_definition->getDescription() ?: $this->t('[No description]'), + '#markup' => $field_profile['description'] ?: $this->t('[No description]'), '#prefix' => 'Description: ', ], - 'search_api' => $this->formatSearchApi($field_definition), + 'search_api' => $this->formatSearchApi($field_profile), + 'facets' => $this->formatFacets($field_profile), ]; } @@ -297,7 +425,6 @@ class MetadataProfileController extends ControllerBase { else { return NULL; } - } else { return NULL; @@ -312,6 +439,13 @@ class MetadataProfileController extends ControllerBase { } return $edit_url; } + + private function getFieldDetailsFragmentLink(string $field_name, FieldDefinitionInterface $field_definition) + { + $url = Url::fromRoute('', [], ['fragment' => $field_name]); + return Link::fromTextAndUrl($field_definition->getLabel(), $url); + } + protected function formatFieldEditLink(Url $edit_url) { return [ '#type' => 'link', @@ -321,6 +455,8 @@ class MetadataProfileController extends ControllerBase { ]; } + + protected function getFieldTableRows(array $field_profile) { $rows = [ [$this->t('Type:'), $field_profile['type']], @@ -375,8 +511,8 @@ class MetadataProfileController extends ControllerBase { protected function formatCreateNew(FieldDefinitionInterface $field_definition) { if (in_array($field_definition->getType(), ['entity_reference', 'typed_relation'])) { - $create_new_bundle = $field_definition->getSetting('handler_settings')['auto_create_bundle']; - $create_new = $field_definition->getSetting('handler_settings')['auto_create_bundle']; + $create_new_bundle = $field_definition->getSetting('handler_settings')['auto_create_bundle'] || ''; + $create_new = $field_definition->getSetting('handler_settings')['auto_create'] || ''; if ($create_new) { return $create_new_bundle ? 'Create new (' . $create_new_bundle . ')' : 'Create new'; } @@ -426,119 +562,182 @@ class MetadataProfileController extends ControllerBase { '#type' => 'ul', ]]; } - protected function formatSearchApi($field_definition) { - if ($this->moduleHandler()->moduleExists('search_api')) { - $build = [ - '#type' => 'table', - '#header' => [ - 'col1' => $this->t('Search API field name'), - 'col2' => $this->t('Search API machine name'), - 'col3' => $this->t('Search API property path'), - 'col4' => $this->t('Search API field type'), - 'col0' => $this->t('Search API index'), - ], - '#rows' => [], - ]; - $storage = $field_definition->getFieldStorageDefinition(); - if ($storage instanceof BaseFieldDefinition) { - // Use property_path to find related fields. - $field_id = $this->entityTypeBundleOf . '.' . $storage->getName(); - } - else { - $field_id = 'field.storage.' . $storage->get('id'); - } - $search_api_indexes = $this->configFactory->listAll('search_api.index'); - foreach ($search_api_indexes as $index) { - $index_config = $this->config($index); - // Loop over fields. - foreach ($index_config->get('field_settings') as $field_setting_name => $field_setting) { - // Check dependencies. - $field_dependencies = $field_setting['dependencies']['config'] ?: []; - if (in_array($field_id, $field_dependencies)) { - $build['#rows'][$field_setting_name] = $this->format_search_api_field($field_setting_name, $field_setting, $index_config); - } - // Check by property path. - if ($field_setting['datasource_id'] == 'entity:node' and $field_setting['property_path'] == $field_definition->getName()) { - $build['#rows'][$field_setting_name] = $this->format_search_api_field($field_setting_name, $field_setting, $index_config); - } - // Get Aggregated Fields. - if ($field_setting['property_path'] == 'aggregated_field') { - if (in_array('entity:node/' . $field_definition->getName(), $field_setting['configuration']['fields'])) { - $build['#rows'][$field_setting_name] = $this->format_search_api_field($field_setting_name, $field_setting, $index_config); - } - } - if ($field_definition->getType() == 'edtf') { - // Get EDTF year. - if ($field_setting['property_path'] == 'edtf_year') { - $edtf_year_fields = $index_config->get('processor_settings')['edtf_year_only']['fields'] ?: []; - if (in_array(str_replace('.', '|', $field_definition->id()), $edtf_year_fields)) { - $build['#rows'][$field_setting_name] = $this->format_search_api_field($field_setting_name, $field_setting, $index_config); - } - } - // Get EDTF Date - if ($field_setting['property_path'] == 'edtf_dates') { - $edtf_date_fields = $index_config->get('processor_settings')['edtf_date_processor']['fields'] ?: []; - if (in_array(str_replace('.', '|', $field_definition->id()), $edtf_date_fields)) { - $build['#rows'][$field_setting_name] = $this->format_search_api_field($field_setting_name, $field_setting, $index_config); - } - } - } + protected function formatSearchApi($field_profile) { + if (!$field_profile['search_api']['in_search_api']) { + return []; + } + $build = [ + '#type' => 'table', + '#header' => [ + 'col1' => $this->t('Search API field name'), + 'col2' => $this->t('Search API machine name'), + 'col3' => $this->t('Search API property path'), + 'col4' => $this->t('Search API field type'), + 'col5' => $this->t('Aggregate field includes'), + 'col0' => $this->t('Search API index'), + 'col6' => $this->t('Has facet'), + ], + '#rows' => [], + ]; + foreach ($field_profile['search_api']['fields'] as $search_api_field) { + $build['#rows'][] = $this->formatSearchApiField($search_api_field); + } - } - if (count($build['#rows']) == 0) { - return []; - } - } + if (count($build['#rows']) == 0) { + return []; + } + else { return $build; } + } + + + protected function getSearchApiField($field_setting_name, $field_setting, $index_config) { + $search_api_field_array = [ + 'search_api_field_name' => $field_setting_name, + 'search_api_field_label' => $field_setting['label'], + 'property_path' => $field_setting['property_path'], + 'type' => $field_setting['type'], + 'index_name' => $index_config->get('name'), + ]; + // Get fields included for aggregated fields. + if ($field_setting['property_path'] == 'aggregated_field') { + $search_api_field_array['fields_included'] = $field_setting['configuration']['fields']; + } + else if ($field_setting['property_path'] == 'edtf_year') { + $search_api_field_array['fields_included'] = $index_config->get('processor_settings')['edtf_year_only']['fields']; + } + else if ($field_setting['property_path'] == 'edtf') { + $search_api_field_array['fields_included'] = $index_config->get('processor_settings')['edtf_date_processor']['fields']; + } else { - return []; + $search_api_field_array['fields_included'] = []; + } + + // Get facet info. + $search_api_field_array['has_facet'] = FALSE; + if ($this->moduleHandler()->moduleExists('facets')) { + $all_facets = \Drupal::entityTypeManager()->getStorage('facets_facet')->loadMultiple(); + foreach ($all_facets as $facet_id => $facet) { + // If facet uses this solr field... then add it to the array. + if ($facet->get('field_identifier') == $field_setting_name) { + $search_api_field_array['has_facet'] = True; + $search_api_field_array['facets'][] = [ + 'field_identifier' => $facet->get('field_identifier'), + 'facet_name' => $facet->getName(), + 'facet_machine_name' => $facet_id, + 'facet_source' => $facet->getFacetSourceId(), + 'url_alias' => $facet->getUrlAlias(), + 'block_visible' => $this->getBlockVisible($facet), + ]; + } + } } + return $search_api_field_array; } - protected function format_search_api_field($field_setting_name, $field_setting, $index_config) { - $build = [ + protected function getBlockVisible($facet) { + return ''; + } + protected function formatSearchApiField($search_api_field_profile) { + return [ 'data' => [ - $field_setting['label'], - $field_setting_name, - $field_setting['property_path'], - $field_setting['type'], - $index_config->get('name'), + $search_api_field_profile['search_api_field_label'], + $search_api_field_profile['search_api_field_name'], + $search_api_field_profile['property_path'], + $search_api_field_profile['type'], + $this->formatListForTable($search_api_field_profile['fields_included']), + $search_api_field_profile['index_name'], + $search_api_field_profile['has_facet'] ? 'True' : '', ] ]; - if ($field_setting['property_path'] == 'aggregated_field') { - $list_render_array = [ - '#theme' => 'item_list', - '#items' => $field_setting['configuration']['fields'] - ]; - $cell_element = [ 'data' => [ - '#type' => 'container', - 'label' => ['#markup' => $build['data'][2]], - 'list' => $list_render_array, - ]]; + } + + - $build['data'][2] = $cell_element; + + public function sanitizeRowsForCSV($rows) { + foreach ($rows as $row_key => $row) { + foreach ($row as $element_key => $element) { + if ($element instanceof Link) { + $rows[$row_key][$element_key] = $element->getText(); + } + else if (is_array($element)) { + $rows[$row_key][$element_key] = implode('|', $element); + } + } } - if (in_array($field_setting['property_path'], ['edtf_year', 'edtf_dates'])) { - $list_render_array = [ - '#theme' => 'item_list', - '#items' => $index_config->get('processor_settings')['edtf_year_only']['fields'] ?: $this->t('No fields avaailable') - ]; - $cell_element = [ 'data' => [ - '#type' => 'container', - 'label' => ['#markup' => $build['data'][2]], - 'list' => $list_render_array, - ]]; + return $rows; + } + public function download() { + $contentType = $this->entityBundle; + $entityKey = $this->entityTypeBundleOf; + $headers = $this->getHeaders(); + $metadata_profile = $this->getMetadataProfile(); + $rows = $this->getRows($metadata_profile); + $rows = $this->sanitizeRowsForCSV($rows); + $filename_slug = "{$entityKey}__{$contentType}.csv"; + $filename = $this->fileSystem->getTempDirectory() . '/' . $filename_slug; + // Write file to temporary filesystem. + if (file_exists($filename)) { + $this->fileSystem->delete($filename); + } + $fh = fopen($filename, 'w'); + fputcsv($fh, $headers); + foreach ($rows as $row) { + fputcsv($fh, $row); + } + fclose($fh); + $request = new Request(['file' => $filename_slug]); + return $this->fileDownloadController->download($request, 'temporary'); + } - $build['data'][2] = $cell_element; + private function formatFacets(array $field_profile) + { + if (!$field_profile['search_api']['has_facet']) { + return []; + } + $build = [ + '#type' => 'table', + '#header' => [ + $this->t('Search API field'), + $this->t('Facet name'), + $this->t('Facet machine name'), + $this->t('Facet source'), + $this->t('URL fragment alias'), + $this->t('Block is placed'), + ], + '#rows' => [], + ]; + foreach ($field_profile['search_api']['fields'] as $field) { + if ($field['has_facet']) { + foreach ($field['facets'] as $facet) { + $build['#rows'][] = $this->formatFacet($facet); + } + } + + } + if (count($build['#rows']) == 0) { + return []; + } + else { + return $build; } - return $build; } - private function getDetailsFragmentLink(string $field_name, FieldDefinitionInterface $field_definition) + private function formatFacet($facet) { - $url = Url::fromRoute('', [], ['fragment' => $field_name]); - return Link::fromTextAndUrl($field_definition->getLabel(), $url); + return [ + 'data' => [ + $facet['field_identifier'], + $facet['facet_name'], + $facet['facet_machine_name'], + $facet['facet_source'], + $facet['url_alias'], + $facet['block_visible'], + ] + ]; } + } diff --git a/src/Routing/RouteSubscriber.php b/src/Routing/RouteSubscriber.php index 15bf8ee..ec67be5 100644 --- a/src/Routing/RouteSubscriber.php +++ b/src/Routing/RouteSubscriber.php @@ -39,6 +39,9 @@ class RouteSubscriber extends RouteSubscriberBase { if ($route = $this->getEntityProfileRoute($entity_type)) { $collection->add("entity.$entity_type_id.metadata_profile", $route); } + if ($route = $this->getEntityDownloadRoute($entity_type)) { + $collection->add("entity.$entity_type_id.metadata_profile.download", $route); + } } } @@ -60,6 +63,28 @@ class RouteSubscriber extends RouteSubscriberBase { '_controller' => '\Drupal\metadata_profile\Controller\MetadataProfileController::profile', '_title' => 'Metadata Profile', ]) + ->addRequirements([ + '_permission' => 'view metadata profiles', + ]) + ->setOption('_admin_route', TRUE) + ->setOption('parameters', [ + $entity_type_id => ['type' => 'entity:' . $entity_type_id], + ]); + return $route; + } + + return NULL; + } + + protected function getEntityDownloadRoute(EntityTypeInterface $entity_type) { + if ($route_load = $entity_type->getLinkTemplate('metadata-download')) { + $entity_type_id = $entity_type->id(); + $route = new Route($route_load); + $route + ->addDefaults([ + '_controller' => '\Drupal\metadata_profile\Controller\MetadataProfileController::download', + '_title' => 'Metadata Profile Download', + ]) ->addRequirements([ '_permission' => 'administer content', ])