diff --git a/css/metadata_profile.css b/css/metadata_profile.css
index ca675b4..97c41f5 100644
--- a/css/metadata_profile.css
+++ b/css/metadata_profile.css
@@ -18,3 +18,10 @@
.scrolling-table-container {
overflow-x: auto;
}
+
+.yes-icon {
+ color: #2e7d32;
+}
+.no-icon {
+ color: #c62828;
+}
diff --git a/metadata_profile.module b/metadata_profile.module
index 505ad51..f82e255 100644
--- a/metadata_profile.module
+++ b/metadata_profile.module
@@ -1,5 +1,6 @@
getEntityTypeId() === 'node_type') {
+
+ $operations['metadata_profile'] = [
+ 'title' => t('Metadata profile'),
+ 'weight' => 50,
+ 'url' => Url::fromRoute('entity.node_type.metadata_profile', [
+ 'node_type' => $entity->id(),
+ ]),
+ ];
+ }
+}
diff --git a/src/Controller/MetadataProfileController.php b/src/Controller/MetadataProfileController.php
index 91f0583..d04414a 100644
--- a/src/Controller/MetadataProfileController.php
+++ b/src/Controller/MetadataProfileController.php
@@ -5,7 +5,6 @@ namespace Drupal\metadata_profile\Controller;
use Drupal\Core\Config\Config;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Controller\ControllerBase;
-use Drupal\Core\Entity\ContentEntityForm;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Field\FieldDefinitionInterface;
@@ -15,13 +14,14 @@ use Drupal\Core\Link;
use Drupal\Core\Messenger\MessengerTrait;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url;
-use Drupal\node\NodeForm;
use Drupal\system\FileDownloadController;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
-
+use Drupal\Core\Database\Connection;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
class MetadataProfileController extends ControllerBase {
+
use MessengerTrait;
/**
@@ -79,6 +79,7 @@ class MetadataProfileController extends ControllerBase {
* @var \Drupal\Core\File\FileSystemInterface
*/
protected $fileSystem;
+
/**
* The file download controller.
*
@@ -86,13 +87,30 @@ class MetadataProfileController extends ControllerBase {
*/
protected $fileDownloadController;
+ /**
+ * The Database connection.
+ *
+ * @var \Drupal\Core\Database\Connection
+ */
+ protected Connection $database;
- public function __construct(EntityFieldManagerInterface $entity_field_manager,
- FieldTypePluginManagerInterface $field_type_plugin_manager,
- ConfigFactoryInterface $config_factory,
- RouteMatchInterface $route_match,
- FileSystemInterface $file_system,
- FileDownloadController $file_download_controller) {
+ /**
+ * The Entity type manager.
+ *
+ * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+ */
+ protected EntityTypeManagerInterface $entityTypeManagerService;
+
+ public function __construct(
+ EntityFieldManagerInterface $entity_field_manager,
+ FieldTypePluginManagerInterface $field_type_plugin_manager,
+ ConfigFactoryInterface $config_factory,
+ RouteMatchInterface $route_match,
+ FileSystemInterface $file_system,
+ FileDownloadController $file_download_controller,
+ Connection $database,
+ EntityTypeManagerInterface $entity_type_manager // ✅ ADD THIS
+ ) {
$this->entityFieldManager = $entity_field_manager;
$this->fieldTypePluginManager = $field_type_plugin_manager;
$this->configFactory = $config_factory;
@@ -105,6 +123,8 @@ class MetadataProfileController extends ControllerBase {
$this->entityTypeBundleOf = $entity_type->getEntityType()->getBundleOf();
$this->fileSystem = $file_system;
$this->fileDownloadController = $file_download_controller;
+ $this->database = $database;
+ $this->entityTypeManagerService = $entity_type_manager;
}
public static function create(ContainerInterface $container) {
@@ -114,7 +134,9 @@ class MetadataProfileController extends ControllerBase {
$container->get('config.factory'),
$container->get('current_route_match'),
$container->get('file_system'),
- FileDownloadController::create($container)
+ FileDownloadController::create($container),
+ $container->get('database'),
+ $container->get('entity_type.manager'),
);
}
@@ -124,11 +146,10 @@ class MetadataProfileController extends ControllerBase {
* @return array
*/
public function profile() {
-
// Get core content type information.
- $bundle_config = $this->config(str_replace('_', '.', $this->entityType) . '.' . $this->entityBundle);
+ $bundle_config = $this->config(str_replace('_', '.', $this->entityType) . '.' . $this->entityBundle);
if (!$bundle_config) {
- return ['#markup'=> 'Not a valid content type.'];
+ return ['#markup' => 'Not a valid content type.'];
}
$build = $this->formatBundle($bundle_config);
@@ -136,7 +157,7 @@ class MetadataProfileController extends ControllerBase {
$build['summary_table'] = [
'#type' => 'container',
- '#attributes'=> ['class' => ['scrolling-table-container']],
+ '#attributes' => ['class' => ['scrolling-table-container']],
'data' => $this->buildSummaryTable($metadata_profile),
];
@@ -175,7 +196,7 @@ class MetadataProfileController extends ControllerBase {
'#type' => 'container',
'#attached' => ['library' => ['metadata_profile/metadata_profile']],
- 'title' => [
+ 'title' => [
'#plain_text' => $label,
'#prefix' => '
',
'#suffix' => '
',
@@ -194,40 +215,73 @@ class MetadataProfileController extends ControllerBase {
return $build;
}
+ /**
+ * Builds a metadata profile for the current entity bundle.
+ *
+ * The returned array is keyed by field machine name and contains
+ * structured metadata describing each field.
+ *
+ * Each field profile contains:
+ * - label: string
+ * - machine_name: string
+ * - id: string
+ * - edit_url: \Drupal\Core\Url|null
+ * - details_link: \Drupal\Core\Link
+ * - description: string
+ * - type: string (human readable)
+ * - type_machine_name: string
+ * - required: render array
+ * - repeatable: render array|string
+ * - auto_create: render array|null
+ * - target_bundles: array
+ * - base_field: bool
+ * - usage_count: int
+ * - search_api: array
+ *
+ * @return array
+ * Metadata profile keyed by field machine name.
+ */
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.
- // * '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.
- // * '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' -- not yet implemented.
+ /**
+ * Metadata profile structure.
+ *
+ * $metadata_profile is an associative array keyed by field machine names
+ * (e.g. "field_abstract"). Each value is an array with the following keys:
+ *
+ * - label: (string) Human-readable field label.
+ * - machine_name: (string) Short machine name (e.g. "field_abstract").
+ * - id: (string) Full machine name
+ * (e.g. "node.islandora_object.field_abstract").
+ * - edit_url: (\Drupal\Core\Url) Link to edit the field configuration.
+ * - description: (string) Field help text.
+ * - type: (string) Human-readable field type (e.g. "Entity Reference").
+ * - type_machine_name: (string) Field type machine name
+ * (e.g. "entity_reference").
+ * - required: (string) "Required" or "Not required".
+ * - cardinality: (string) "Not repeatable", "Repeatable",
+ * or "Repeatable, limit N".
+ * - auto_create: (string|null) Whether referenced entities can be created:
+ * "Create new", "Create new (bundle)", or "Do not create new".
+ * - target_bundles: (string[]) List of allowed target bundles.
+ * - search_api: (array) Search API metadata:
+ * - in_search_api: (bool) TRUE if indexed.
+ * - has_facet: (bool) TRUE if any facet exists.
+ * - fields: (array) Indexed by Search API field machine name:
+ * - search_api_field_name: (string)
+ * - search_api_field_label: (string)
+ * - property_path: (string)
+ * - type: (string)
+ * - index_name: (string)
+ * - fields_included: (array) Included fields (aggregated/EDTF).
+ * - facets: (array) Facet definitions:
+ * - has_facet: (bool)
+ * - facets: (array) Each facet contains:
+ * - facet_name: (string)
+ * - facet_machine_name: (string)
+ * - facet_source: (string)
+ * - url_alias: (string)
+ * - block_visible: (string) Not yet implemented.
+ */
$metadata_profile = [];
$field_definitions = $this->entityFieldManager->getFieldDefinitions($this->entityTypeBundleOf, $this->entityBundle);
@@ -246,13 +300,27 @@ class MetadataProfileController extends ControllerBase {
'auto_create' => $this->formatCreateNew($field_definition),
'target_bundles' => $this->formatTargetBundles($field_definition),
'base_field' => $field_definition->getFieldStorageDefinition() instanceof BaseFieldDefinition,
+ 'usage_count' => $this->getUsageCount($field_name),
];
- $metadata_profile[$field_name]['search_api'] = $this->getSearchApi($field_definition);
+ $metadata_profile[$field_name]['search_api'] = $this->getSearchApi($field_definition);
}
$metadata_profile = $this->addWeights($metadata_profile);
return $metadata_profile;
}
+ /**
+ * Builds Search API metadata for a field definition.
+ *
+ * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
+ * The field definition.
+ *
+ * @return array|false
+ * An associative array containing:
+ * - in_search_api: bool
+ * - has_facet: bool
+ * - fields: array
+ * Or FALSE if Search API module is not enabled.
+ */
private function getSearchApi($field_definition) {
if (!$this->moduleHandler()->moduleExists('search_api')) {
return FALSE;
@@ -274,7 +342,7 @@ class MetadataProfileController extends ControllerBase {
// 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;
+ $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'])) {
@@ -282,19 +350,23 @@ class MetadataProfileController extends ControllerBase {
}
}
// 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);
+ 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);
+ // 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);
+ }
+ }
}
}
}
@@ -310,22 +382,29 @@ class MetadataProfileController extends ControllerBase {
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)) {
+ 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;
}
+ /**
+ * Builds a render array for the summary table.
+ *
+ * @param array $metadata_profile
+ * Metadata profile array.
+ *
+ * @return array
+ * Render array for a Drupal table.
+ */
protected function buildSummaryTable($metadata_profile) {
$rows = $this->getRows($metadata_profile, TRUE);
return [
@@ -333,13 +412,43 @@ class MetadataProfileController extends ControllerBase {
'#header' => $this->getHeaders(),
'#rows' => $rows,
];
-
}
- protected function getRows($metadata_profile, $display=NULL) {
+ /**
+ * Builds table rows from a metadata profile array.
+ *
+ * Each row is a numerically indexed array representing a table row:
+ * 0 => Field label or link
+ * 1 => Machine name
+ * 2 => Description
+ * 3 => Field type (human readable)
+ * 4 => Required indicator (render array)
+ * 5 => Repeatable indicator (render array)
+ * 6 => Auto-create indicator (render array|null)
+ * 7 => In Search API (render array)
+ * 8 => Has Facet (render array)
+ * 9 => Target bundles (array|string|render array)
+ * 10 => Weight (int|float)
+ * 11 => Usage count (int)
+ *
+ * Rows are sorted descending by usage count (index 11).
+ *
+ * @param array $metadata_profile
+ * Structured metadata profile keyed by field machine name.
+ * @param bool|null $display
+ * Whether to return renderable values (TRUE) or raw values (FALSE/NULL).
+ *
+ * @return array
+ * A numerically indexed array of row arrays.
+ */
+ 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'])) {
+ if (str_starts_with($field_name, 'field_') or in_array($field_name, [
+ 'title',
+ 'name',
+ ])) {
+ $bundles = $field_profile['target_bundles'] ?? [];
$rows[] = [
$display ? $field_profile['details_link'] : $field_profile['label'],
$field_profile['machine_name'],
@@ -348,13 +457,20 @@ class MetadataProfileController extends ControllerBase {
$field_profile['required'],
$field_profile['repeatable'],
$field_profile['auto_create'],
- $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' : '',
+
+ $this->yesNoIcon($field_profile['search_api']['in_search_api']),
+ $this->YesNoIcon($field_profile['search_api']['has_facet']),
+ $display
+ ? $this->formatListForTable($bundles)
+ : $bundles,
$field_profile['weight'],
- ];
+ $field_profile['usage_count'],
+ ];
}
}
+ usort($rows, function($a, $b) {
+ return $b[11] <=> $a[11];
+ });
return $rows;
}
@@ -367,10 +483,11 @@ class MetadataProfileController extends ControllerBase {
$this->t('Required'),
$this->t('Repeatable'),
$this->t('Create new'),
- $this->t('Target bundles'),
$this->t('In Search API'),
$this->t('Has Facet'),
+ $this->t('Target bundles'),
$this->t('Weight'),
+ $this->t("Usage count"),
// TODO: add more columns
];
@@ -379,7 +496,10 @@ class MetadataProfileController extends ControllerBase {
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'])) {
+ if (str_starts_with($field_name, 'field_') or in_array($field_name, [
+ 'title',
+ 'name',
+ ])) {
$render_array = [
'#type' => 'container',
'#attributes' => ['class' => ['field-info']],
@@ -395,10 +515,20 @@ class MetadataProfileController extends ControllerBase {
'#prefix' => '(',
'#suffix' => ') ',
],
+ 'usage_count' => [
+ '#markup' => $this->formatPlural(
+ $field_profile['usage_count'],
+ "Used 1 time.",
+ "Used @count times."
+ ),
+ '#prefix' => ' br>',
+
+ ],
+
'edit_link' => $field_profile['edit_url'] ? $this->formatFieldEditLink($field_profile['edit_url']) : [],
'table' => [
'#type' => 'table',
- '#header' => ['Field Configuration',''],
+ '#header' => ['Field Configuration', ''],
'#rows' => $this->getFieldTableRows($field_profile),
'#attributes' => ['class' => ['field-info-table']],
'#prefix' => '' . $this->t('Field Information') . '
',
@@ -411,9 +541,6 @@ class MetadataProfileController extends ControllerBase {
'search_api' => $this->formatSearchApi($field_profile),
'facets' => $this->formatFacets($field_profile),
];
-
-
-
}
return $render_array;
@@ -427,7 +554,7 @@ class MetadataProfileController extends ControllerBase {
$edit_url = Url::fromRoute('entity.base_field_override.' . $this->entityTypeBundleOf . '_base_field_override_edit_form', [
$this->entityType => $field_definition->getTargetBundle(),
'base_field_override' => $field_definition->id(),
- 'destination' => $redirect_url->toString()
+ 'destination' => $redirect_url->toString(),
]);
}
else {
@@ -439,7 +566,7 @@ class MetadataProfileController extends ControllerBase {
}
}
else {
- $edit_url = Url::fromRoute('entity.field_config.' . $this->entityTypeBundleOf . '_field_edit_form', [
+ $edit_url = Url::fromRoute('entity.field_config.' . $this->entityTypeBundleOf . '_field_edit_form', [
$this->entityType => $field_definition->getTargetBundle(),
'field_config' => $field_definition->id(),
'destination' => $redirect_url->toString(),
@@ -448,8 +575,7 @@ class MetadataProfileController extends ControllerBase {
return $edit_url;
}
- private function getFieldDetailsFragmentLink(string $field_name, FieldDefinitionInterface $field_definition)
- {
+ private function getFieldDetailsFragmentLink(string $field_name, FieldDefinitionInterface $field_definition) {
$url = Url::fromRoute('', [], ['fragment' => $field_name]);
return Link::fromTextAndUrl($field_definition->getLabel(), $url);
}
@@ -463,20 +589,21 @@ class MetadataProfileController extends ControllerBase {
];
}
-
-
protected function getFieldTableRows(array $field_profile) {
- $rows = [
+ $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']),
];
- 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;
}
@@ -489,24 +616,22 @@ class MetadataProfileController extends ControllerBase {
}
return $type_label;
}
+
protected function formatRequired(FieldDefinitionInterface $field_definition) {
- if ($field_definition->isRequired()) {
- return $this->t('Required');
- }
- else {
- return $this->t('Not required');
- }
+ $cardinality = $field_definition->isRequired();
+ return $this->yesNoIcon($cardinality);
}
/**
- * This does not work - most fields dont have a form weight specified in getDisplayOptions.
+ * This does not work - most fields dont have a form weight specified in
+ * getDisplayOptions.
*/
protected function getWeight(string $field_name, array $form) {
if (isset($form[$field_name])) {
$local_weight = $form[$field_name]['#weight'];
if ($parent = $form['#group_children'][$field_name]) {
$parent_weight = $this->getWeight($parent, $form);
- return $this->combine_weights($parent_weight, $local_weight);
+ return $this->combineWeights($parent_weight, $local_weight);
}
else {
return $local_weight;
@@ -518,32 +643,35 @@ class MetadataProfileController extends ControllerBase {
}
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]);
+ $cardinality = $field_definition
+ ->getFieldStorageDefinition()
+ ->getCardinality();
+
+ if ($cardinality === 1) {
+ return $this->yesNoIcon($cardinality);
}
- return NULL;
+
+ if ($cardinality === -1) {
+ return $this->yesNoIcon($cardinality);
+ }
+
+ return $this->t('Yes (max @limit)', [
+ '@limit' => $cardinality,
+ ]);
}
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_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';
- }
- else {
- return $this->t('Do not create new');
- }
- }
- else {
+ if (!in_array($field_definition->getType(), [
+ 'entity_reference',
+ 'typed_relation',
+ ], TRUE)) {
return NULL;
}
+ $settings = $field_definition->getSetting('handler_settings') ?? [];
+ $create_new = !empty($settings['auto_create']);
+
+ return $this->yesNoIcon($create_new);
}
protected function formatTargetBundles(FieldDefinitionInterface $field_definition) {
@@ -555,10 +683,14 @@ class MetadataProfileController extends ControllerBase {
$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);
+ $bundle_config = $this->entityTypeManager()
+ ->getStorage('taxonomy_vocabulary')
+ ->load($bundle);
}
if ($handler == 'default:node') {
- $bundle_config = $this->entityTypeManager()->getStorage('node_type')->load($bundle);
+ $bundle_config = $this->entityTypeManager()
+ ->getStorage('node_type')
+ ->load($bundle);
}
// TODO: ADD MEDIA, PARAGRAPHS ETC
if ($bundle_config) {
@@ -571,16 +703,18 @@ class MetadataProfileController extends ControllerBase {
'#theme' => 'item_list',
'#title' => $this->t('Target bundles:'),
'#items' => $target_bundles,
- '#type' => 'ul'
- ];
+ '#type' => 'ul',
+ ];
}
protected function formatListForTable(array $list) {
- return ['data' => [
- '#theme' => 'item_list',
- '#items' => array_values($list),
- '#type' => 'ul',
- ]];
+ return [
+ 'data' => [
+ '#theme' => 'item_list',
+ '#items' => array_values($list),
+ '#type' => 'ul',
+ ],
+ ];
}
protected function formatSearchApi($field_profile) {
@@ -613,9 +747,8 @@ class MetadataProfileController extends ControllerBase {
}
}
-
protected function getSearchApiField($field_setting_name, $field_setting, $index_config) {
- $search_api_field_array = [
+ $search_api_field_array = [
'search_api_field_name' => $field_setting_name,
'search_api_field_label' => $field_setting['label'],
'property_path' => $field_setting['property_path'],
@@ -626,24 +759,30 @@ class MetadataProfileController extends ControllerBase {
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 {
- $search_api_field_array['fields_included'] = [];
+ 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 {
+ $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();
+ $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['has_facet'] = TRUE;
$search_api_field_array['facets'][] = [
'field_identifier' => $facet->get('field_identifier'),
'facet_name' => $facet->getName(),
@@ -661,6 +800,7 @@ class MetadataProfileController extends ControllerBase {
protected function getBlockVisible($facet) {
return '';
}
+
protected function formatSearchApiField($search_api_field_profile) {
return [
'data' => [
@@ -671,26 +811,48 @@ class MetadataProfileController extends ControllerBase {
$this->formatListForTable($search_api_field_profile['fields_included']),
$search_api_field_profile['index_name'],
$search_api_field_profile['has_facet'] ? 'True' : '',
- ]
+ ],
];
}
-
-
-
+ /**
+ * Sanitizes table rows for CSV export.
+ *
+ * Converts renderable elements and objects into scalar values:
+ * - Link objects are converted to their text.
+ * - Arrays are flattened into pipe-delimited strings.
+ *
+ * @param array $rows
+ * A numerically indexed array of row arrays.
+ *
+ * @return array
+ * Sanitized rows suitable for fputcsv().
+ */
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);
+ else {
+ if (is_array($element)) {
+ $rows[$row_key][$element_key] = implode('|', $element);
+ }
}
}
}
return $rows;
}
+
+ /**
+ * Generates and returns a CSV download of the metadata profile.
+ *
+ * Writes a temporary CSV file and serves it using Drupal's
+ * file download controller.
+ *
+ * @return \Symfony\Component\HttpFoundation\BinaryFileResponse
+ * The file download response.
+ */
public function download() {
$contentType = $this->entityBundle;
$entityKey = $this->entityTypeBundleOf;
@@ -714,8 +876,7 @@ class MetadataProfileController extends ControllerBase {
return $this->fileDownloadController->download($request, 'temporary');
}
- private function formatFacets(array $field_profile)
- {
+ private function formatFacets(array $field_profile) {
if (!$field_profile['search_api']['has_facet']) {
return [];
}
@@ -738,7 +899,6 @@ class MetadataProfileController extends ControllerBase {
$build['#rows'][] = $this->formatFacet($facet);
}
}
-
}
if (count($build['#rows']) == 0) {
return [];
@@ -748,8 +908,7 @@ class MetadataProfileController extends ControllerBase {
}
}
- private function formatFacet($facet)
- {
+ private function formatFacet($facet) {
return [
'data' => [
$facet['field_identifier'],
@@ -758,49 +917,95 @@ class MetadataProfileController extends ControllerBase {
$facet['facet_source'],
$facet['url_alias'],
$facet['block_visible'],
- ]
+ ],
];
}
- private function addWeights(array $metadata_profile)
- {
+ private function addWeights(array $metadata_profile) {
if ($this->entityTypeBundleOf == 'node') {
$node = \Drupal\node\Entity\Node::create(['type' => $this->entityBundle]);
$form = \Drupal::service('entity.form_builder')->getForm($node);
- foreach($metadata_profile as $field_name => $field_profile) {
+ foreach ($metadata_profile as $field_name => $field_profile) {
$metadata_profile[$field_name]['weight'] = $this->getWeight($field_name, $form);
}
$weights = array_column($metadata_profile, 'weight');
array_multisort($weights, SORT_ASC, $metadata_profile);
-
}
return $metadata_profile;
}
- private function combine_weights($parent_weight, $local_weight)
- {
- $parent_int = (string)floor($parent_weight);
- $parent_fraction = (string)($parent_weight - $parent_int);
+ private function combineWeights($parent_weight, $local_weight) {
+ $parent_int = (string) floor($parent_weight);
+ $parent_fraction = (string) ($parent_weight - $parent_int);
$parent_fraction = str_replace('0.', '', $parent_fraction);
- $local_int = (string)floor($local_weight);
- $local_fraction = (string)($local_weight - $local_int);
+ $local_int = (string) floor($local_weight);
+ $local_fraction = (string) ($local_weight - $local_int);
$combined_weight = $parent_int;
$combined_weight .= '.';
if ($parent_fraction) {
# pad so its length is a multiple of 2
$length = mb_strlen($parent_fraction);
- if (fmod($length, 2) != 0){
+ if (fmod($length, 2) != 0) {
$parent_fraction .= '0';
}
$combined_weight .= $parent_fraction;
}
$combined_weight .= str_pad($local_int, 2, '0', STR_PAD_LEFT);
if ($local_fraction) {
- $a=2;
+ $a = 2;
}
- return (float)$combined_weight;
+ return (float) $combined_weight;
+ }
+
+ /**
+ * Gets usage count for a field.
+ *
+ * For configurable fields, counts rows in the field table.
+ * For base fields, counts nodes of the current bundle.
+ *
+ * @param string $field_name
+ * The field machine name.
+ *
+ * @return int
+ * Number of usages.
+ */
+ private function getUsageCount($field_name) {
+ if (str_starts_with($field_name, 'field_')) {
+ $connection = $this->database;
+ $count = $connection->select("node__{$field_name}", 'f')
+ ->countQuery()
+ ->execute()
+ ->fetchField();
+ return $count;
+ }
+ return $this->entityTypeManagerService
+ ->getStorage('node')
+ ->getQuery()
+ ->accessCheck(FALSE)
+ ->condition('type', $this->entityBundle)
+ ->count()
+ ->execute();
+ }
+
+ /**
+ * Returns a render array representing a yes/no icon.
+ *
+ * @param bool $value
+ * TRUE for yes, FALSE for no.
+ *
+ * @return array
+ * Render array containing markup for the icon.
+ */
+ protected function yesNoIcon(bool $value): array {
+ return [
+ 'data' => [
+ '#markup' => $value
+ ? '✔'
+ : '✖',
+ ],
+ ];
}
}