entityFieldManager = $entity_field_manager; $this->fieldTypePluginManager = $field_type_plugin_manager; $this->configFactory = $config_factory; $this->routeMatch = $route_match; $route_options = $this->routeMatch->getRouteObject()->getOptions(); $array_keys = array_keys($route_options['parameters']); $this->entityType = array_shift($array_keys); $entity_type = $this->routeMatch->getParameter($this->entityType); $this->entityBundle = $entity_type->id(); $this->entityTypeBundleOf = $entity_type->getEntityType()->getBundleOf(); } public static function create(ContainerInterface $container) { return new static( $container->get('entity_field.manager'), $container->get('plugin.manager.field.field_type'), $container->get('config.factory'), $container->get('current_route_match') ); } /** * Returns a render array displaying the metadata profile for $content_type. * * @param $content_type * @return array */ public function profile($content_type = NULL) { if (!$content_type) { $content_type = $this->entityBundle; } // Get core content type information. $node_type = $this->config('node.type.' . $content_type); if (!$node_type) { return ['#markup'=> 'Not a valid content type.']; } $build = $this->format_bundle($node_type); $metadata_profile = $this->getMetadataProfile($content_type); $build['summary_table'] = [ '#type' => 'container', '#attributes'=> ['class' => ['scrolling-table-container']], 'data' => $this->buildSummaryTable($metadata_profile), ]; $build['fields'] = [ '#type' => 'container', '#weight' => '9', ]; // Get field information. $field_definitions = $this->entityFieldManager->getFieldDefinitions('node', $content_type); foreach ($field_definitions as $field_name => $field_definition) { $build['fields'][$field_name] = $this->display_field($field_name, $field_definition, $metadata_profile[$field_name]); } return $build; } /** * Format information about a bundle. */ protected function format_bundle(Config $bundle) { $build = [ '#type' => 'container', '#attached' => ['library' => ['metadata_profile/metadata_profile']], 'title' => [ '#plain_text' => $bundle->get('name'), '#prefix' => '

', '#suffix' => '

', ], 'machine_name' => [ '#plain_text' => $bundle->get('type'), '#prefix' => ' (', '#suffix' => ')', ], 'description' => [ '#plain_text' => $bundle->get('description'), '#prefix' => '

', '#suffix' => '

', ], ]; return $build; } public function getMetadataProfile($bundle) { // $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). // * '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. // * 'cardinality' - 'Not repeatable'|'Repeatable'|'Repeatable, limit N' // * 'auto_create' - 'Create new'|'Create new #bundle'|'Do not create new'|NULL Whether a relationship field allows new entity creation on the fly. // * '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. // * '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. // * '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' $metadata_profile = []; $field_definitions = $this->entityFieldManager->getFieldDefinitions('node', $bundle); 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), '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), ]; // Add solr } return $metadata_profile; } protected function buildSummaryTable($metadata_profile) { $rows = []; foreach ($metadata_profile as $field_name => $field_profile) { if (str_starts_with( $field_name, 'field_') or $field_name == 'title') { $rows[] = [ $field_profile['details_link'], $field_profile['machine_name'], $field_profile['type'], $field_profile['required'], $field_profile['repeatable'], $field_profile['auto_create'], $this->formatListForTable($field_profile['target_bundles']), ]; } } 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, ]; } protected function display_field(string $field_name, FieldDefinitionInterface $field_definition, array $field_profile) { $render_array = []; if (str_starts_with($field_name, 'field_') or $field_name == 'title') { $render_array = [ '#type' => 'container', '#attributes' => ['class' => ['field-info']], '#weight' => $this->formatWeight($field_definition), // This doesn't work. 'title' => [ '#plain_text' => $field_profile['label'], '#prefix' => '

', '#suffix' => '

', ], 'machine_name' => [ '#plain_text' => $field_name, '#prefix' => '(', '#suffix' => ') ', ], 'edit_link' => $field_profile['edit_url'] ? $this->formatFieldEditLink($field_profile['edit_url']) : [], 'table' => [ '#type' => 'table', '#header' => ['Field Configuration',''], '#rows' => $this->getFieldTableRows($field_profile), '#attributes' => ['class' => ['field-info-table']], ], 'description' => [ '#type' => 'markup', '#markup' => $field_definition->getDescription() ?: $this->t('[No description]'), '#prefix' => 'Description: ', ], 'search_api' => $this->formatSearchApi($field_definition), ]; } return $render_array; } protected function getFieldEditUrl(FieldDefinitionInterface $field_definition) { $redirect_url = Url::fromRoute('', ['fragment' => $field_definition->getName()]); if ($field_definition->getFieldStorageDefinition() instanceof BaseFieldDefinition) { if ($this->moduleHandler()->moduleExists('base_field_override_ui')) { if (method_exists($field_definition, 'id')) { $edit_url = Url::fromRoute('entity.base_field_override.node_base_field_override_edit_form', [ 'node_type' => $field_definition->getTargetBundle(), 'base_field_override' => $field_definition->id(), 'destination' => $redirect_url->toString() ]); } else { return NULL; } } else { return NULL; } } else { $edit_url = Url::fromRoute('entity.field_config.node_field_edit_form', [ 'node_type' => $field_definition->getTargetBundle(), 'field_config' => $field_definition->id(), 'destination' => $redirect_url->toString(), ]); } return $edit_url; } protected function formatFieldEditLink(Url $edit_url) { return [ '#type' => 'link', '#url' => $edit_url, '#title' => $this->t('Edit'), '#attributes' => ['class' => ['edit-link']], ]; } protected function getFieldTableRows(array $field_profile) { $rows = [ [$this->t('Type:'), $field_profile['type']], [$this->t('Required:'), $field_profile['required']], [$this->t('Repeatable:'), $field_profile['repeatable']], ]; if ($field_profile['auto_create']) { $rows[] = [$this->t('Create new:'), $field_profile['auto_create']]; } if ($field_profile['target_bundles']) { $rows[] = [$this->t('Target bundles:'), $this->formatListForTable($field_profile['target_bundles'])]; } return $rows; } protected function formatType(FieldDefinitionInterface $field_definition) { $type = $field_definition->getType(); $type_label = $this->fieldTypePluginManager->getDefinition($type)['label']; if (in_array($type, ['entity_reference', 'typed_relation'])) { $type_label = $type_label . ' > ' . $field_definition->getSetting('target_type'); } return $type_label; } protected function formatRequired(FieldDefinitionInterface $field_definition) { if ($field_definition->isRequired()) { return $this->t('Required'); } else { return $this->t('Not required'); } } /** * This does not work - most fields dont have a form weight specified in getDisplayOptions. */ protected function formatWeight(FieldDefinitionInterface $field_definition) { return $field_definition->getDisplayOptions('form')['weight']; } protected function formatCardinality(FieldDefinitionInterface $field_definition) { $cardinality = $field_definition->getFieldStorageDefinition()->getCardinality(); if ($cardinality == 1) { return $this->t('Not repeatable'); } elseif ($cardinality < 0) { return $this->t('Repeatable'); } elseif ($cardinality > 1) { return $this->t('Repeatable, limit :limit', [':limit' => $cardinality]); } return NULL; } 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']; if ($create_new) { return $create_new_bundle ? 'Create new (' . $create_new_bundle . ')' : 'Create new'; } else { return $this->t('Do not create new'); } } else { return NULL; } } protected function formatTargetBundles(FieldDefinitionInterface $field_definition) { $handler = $field_definition->getSetting('handler'); $setting = $field_definition->getSetting('handler_settings'); if (!$setting) { return []; } $target_bundles = $setting['target_bundles']; foreach ($target_bundles as $index => $bundle) { if ($handler == 'default:taxonomy_term') { $bundle_config = $this->entityTypeManager()->getStorage('taxonomy_vocabulary')->load($bundle); } if ($handler == 'default:node') { $bundle_config = $this->entityTypeManager()->getStorage('node_type')->load($bundle); } // TODO: ADD MEDIA, PARAGRAPHS ETC if ($bundle_config) { $target_bundles[$index] = $bundle_config->get('name') . ' (' . $bundle . ')'; } } return $target_bundles; return [ '#theme' => 'item_list', '#title' => $this->t('Target bundles:'), '#items' => $target_bundles, '#type' => 'ul' ]; } protected function formatListForTable(array $list) { return ['data' => [ '#theme' => 'item_list', '#items' => array_values($list), '#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 = 'node.' . $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); } } } } if (count($build['#rows']) == 0) { return []; } } return $build; } else { return []; } } protected function format_search_api_field($field_setting_name, $field_setting, $index_config) { $build = [ 'data' => [ $field_setting['label'], $field_setting_name, $field_setting['property_path'], $field_setting['type'], $index_config->get('name'), ] ]; 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; } 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, ]]; $build['data'][2] = $cell_element; } return $build; } private function getDetailsFragmentLink(string $field_name, FieldDefinitionInterface $field_definition) { $url = Url::fromRoute('', [], ['fragment' => $field_name]); return Link::fromTextAndUrl($field_definition->getLabel(), $url); } }