diff --git a/config/install/entity_mesh.settings.yml b/config/install/entity_mesh.settings.yml index d8c47d16a8788816b63f9d4903a25d95567e4eae..7ec07f2b4c8a66cf35a45073295b33516f49b88f 100644 --- a/config/install/entity_mesh.settings.yml +++ b/config/install/entity_mesh.settings.yml @@ -1,2 +1,3 @@ external: true internal: true +process_mesh_using_queue: false diff --git a/config/optional/views.view.entity_mesh.yml b/config/optional/views.view.entity_mesh.yml index 9cb65244c781735c65728c4379c049219285848b..d227da80defc9b68a13e2f5ed4cf2584c8e1300f 100644 --- a/config/optional/views.view.entity_mesh.yml +++ b/config/optional/views.view.entity_mesh.yml @@ -120,6 +120,55 @@ display: hide_empty: false empty_zero: false hide_alter_empty: true + subcategory: + id: subcategory + table: entity_mesh + field: subcategory + relationship: none + group_type: group + admin_label: '' + plugin_id: standard + label: Subcategory + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: false + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true source_entity_type: id: source_entity_type table: entity_mesh @@ -793,6 +842,48 @@ display: default_group: All default_group_multiple: { } group_items: { } + subcategory: + id: subcategory + table: entity_mesh + field: subcategory + relationship: none + group_type: group + admin_label: '' + plugin_id: subcategory_filter + operator: in + value: { } + group: 1 + exposed: true + expose: + operator_id: subcategory_op + label: Subcategory + description: '' + use_operator: false + operator: subcategory_op + operator_limit_selection: false + operator_list: { } + identifier: subcategory + required: false + remember: false + multiple: true + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + editor: '0' + reduce: 0 + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } source_entity_bundle: id: source_entity_bundle table: entity_mesh @@ -1515,6 +1606,55 @@ display: hide_empty: false empty_zero: false hide_alter_empty: true + subcategory: + id: subcategory + table: entity_mesh + field: subcategory + relationship: none + group_type: group + admin_label: '' + plugin_id: standard + label: Subcategory + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: false + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true source_entity_type: id: source_entity_type table: entity_mesh @@ -2475,6 +2615,55 @@ display: hide_empty: false empty_zero: false hide_alter_empty: true + subcategory: + id: subcategory + table: entity_mesh + field: subcategory + relationship: none + group_type: group + admin_label: '' + plugin_id: standard + label: Subcategory + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true source_entity_type: id: source_entity_type table: entity_mesh diff --git a/config/optional/views.view.entity_mesh_node.yml b/config/optional/views.view.entity_mesh_node.yml index d80333303483a07a5d3fa700f73bd635f347d4da..7a6c88e8a017099c602a81a790b544834007ecc1 100644 --- a/config/optional/views.view.entity_mesh_node.yml +++ b/config/optional/views.view.entity_mesh_node.yml @@ -709,6 +709,55 @@ display: hide_empty: false empty_zero: false hide_alter_empty: true + subcategory: + id: subcategory + table: entity_mesh + field: subcategory + relationship: none + group_type: group + admin_label: '' + plugin_id: standard + label: Subcategory + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: false + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true pager: type: full options: @@ -797,6 +846,48 @@ display: default_group: All default_group_multiple: { } group_items: { } + subcategory: + id: subcategory + table: entity_mesh + field: subcategory + relationship: none + group_type: group + admin_label: '' + plugin_id: subcategory_filter + operator: in + value: { } + group: 1 + exposed: true + expose: + operator_id: subcategory_op + label: Subcategory + description: '' + use_operator: false + operator: subcategory_op + operator_limit_selection: false + operator_list: { } + identifier: subcategory + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + editor: '0' + reduce: 0 + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } source_entity_bundle: id: source_entity_bundle table: entity_mesh diff --git a/config/schema/entity_mesh.schema.yml b/config/schema/entity_mesh.schema.yml index 52c07fba27748aa097759c3b0ddff9dd5a1b287e..97ff42b012a8bc8be920dd7861296c6bf02786a2 100644 --- a/config/schema/entity_mesh.schema.yml +++ b/config/schema/entity_mesh.schema.yml @@ -8,3 +8,6 @@ entity_mesh.settings: internal: type: boolean label: 'Track internal' + process_mesh_using_queue: + type: boolean + label: 'Update table using queue when CRUD operations are performed' diff --git a/entity_mesh.install b/entity_mesh.install index 3dda0263cf17810788d36468fe0bdce281b730bc..1b657c5d73efd86ac0d4a68bc1944e5058a3b4dc 100644 --- a/entity_mesh.install +++ b/entity_mesh.install @@ -46,6 +46,12 @@ function entity_mesh_schema() { 'not null' => TRUE, 'description' => 'Mesh Category', ], + 'subcategory' => [ + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + 'description' => 'Mesh Subcategory', + ], 'source_hash_id' => [ 'type' => 'varchar', 'length' => 64, @@ -203,3 +209,32 @@ function entity_mesh_update_10003() { $config_source = new FileStorage($config_path); \Drupal::service('config.installer')->installOptionalConfig($config_source); } + +/** + * Adding new field subcategory. + */ +function entity_mesh_update_10004() { + // Update data base schema. + $schema = \Drupal::database()->schema(); + $schema_definition = entity_mesh_schema(); + $schema->dropTable('entity_mesh'); + $schema->createTable('entity_mesh', $schema_definition['entity_mesh']); + + // Update views. + \Drupal::configFactory()->getEditable('views.view.entity_mesh')->delete(); + \Drupal::configFactory()->getEditable('views.view.entity_mesh_node')->delete(); + + $path_to_module = \Drupal::service('extension.path.resolver')->getPath('module', 'entity_mesh'); + $config_path = $path_to_module . '/config/optional'; + $config_source = new FileStorage($config_path); + \Drupal::service('config.installer')->installOptionalConfig($config_source); +} + +/** + * Set new field configuration "process_mesh_using_queue". + */ +function entity_mesh_update_10005() { + $config = \Drupal::configFactory()->getEditable('entity_mesh.settings'); + $config->set('process_mesh_using_queue', FALSE); + $config->save(); +} diff --git a/entity_mesh.module b/entity_mesh.module index 2e30134e1e4266931787765a62ae65ba6312b465..6497455d64c905e4adf8a3be07e5022e1e148104 100644 --- a/entity_mesh.module +++ b/entity_mesh.module @@ -12,46 +12,21 @@ use Drupal\node\NodeInterface; * Implements hook_entity_insert(). */ function entity_mesh_entity_insert(EntityInterface $entity) { - if (entity_mesh_is_render_entity($entity)) { - \Drupal::queue('entity_mesh_queue_worker')->createItem([ - 'entity' => $entity->getEntityTypeId(), - 'id' => $entity->id(), - 'service' => 'entity_mesh.entity_render', - 'service_method' => 'processEntity', - 'task_type' => 'process_item', - ]); - } + entity_mesh_process_entity($entity, 'process'); } /** * Implements hook_entity_update(). */ function entity_mesh_entity_update(EntityInterface $entity) { - if (entity_mesh_is_render_entity($entity)) { - \Drupal::queue('entity_mesh_queue_worker')->createItem([ - 'entity' => $entity->getEntityTypeId(), - 'id' => $entity->id(), - 'service' => 'entity_mesh.entity_render', - 'service_method' => 'processEntity', - 'task_type' => 'process_item', - ]); - } + entity_mesh_process_entity($entity, 'process'); } /** * Implements hook_entity_delete(). */ function entity_mesh_entity_delete(EntityInterface $entity) { - if (entity_mesh_is_render_entity($entity)) { - \Drupal::queue('entity_mesh_queue_worker')->createItem([ - 'entity' => $entity->getEntityTypeId(), - 'id' => $entity->id(), - 'service' => 'entity_mesh.entity_render', - 'service_method' => 'deleteItem', - 'task_type' => 'delete_item', - 'type' => 'entity_render', - ]); - } + entity_mesh_process_entity($entity, 'delete'); } /** @@ -66,3 +41,56 @@ function entity_mesh_entity_delete(EntityInterface $entity) { function entity_mesh_is_render_entity(EntityInterface $entity) { return $entity instanceof NodeInterface; } + +/** + * Process entity. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity to process. + * @param string $operation + * The operation to perform. + */ +function entity_mesh_process_entity(EntityInterface $entity, $operation) { + // Check it the entity has to be precessed. + if (!entity_mesh_is_render_entity($entity)) { + return; + } + + $service = 'entity_mesh.entity_render'; + + $use_queue = \Drupal::config('entity_mesh.settings')->get('process_mesh_using_queue'); + + // Create or update the entity in the entity mesh. + if ($operation === 'process') { + if ($use_queue) { + \Drupal::queue('entity_mesh_queue_worker')->createItem([ + 'entity' => $entity->getEntityTypeId(), + 'id' => $entity->id(), + 'service' => $service, + 'service_method' => 'processEntity', + 'task_type' => 'process_item', + ]); + return; + } + $entity_mesh = \Drupal::service($service); + $entity_mesh->processEntity($entity); + return; + } + + // Remove the entity from the mesh. + if ($operation === 'delete') { + if ($use_queue) { + \Drupal::queue('entity_mesh_queue_worker')->createItem([ + 'entity' => $entity->getEntityTypeId(), + 'id' => $entity->id(), + 'service' => $service, + 'service_method' => 'deleteItem', + 'task_type' => 'delete_item', + ]); + return; + } + $entity_mesh = \Drupal::service($service); + $entity_mesh->deleteItem($entity->getEntityTypeId(), $entity->getEntityTypeId()); + } + +} diff --git a/entity_mesh.views.inc b/entity_mesh.views.inc index d71e22df9b754dad0c52cb17c06b2fbcdaa49df0..76f8c7ed5dce9844de65074cd41977b6984e4abc 100644 --- a/entity_mesh.views.inc +++ b/entity_mesh.views.inc @@ -76,6 +76,24 @@ function entity_mesh_views_data() { ], ]; + $data['entity_mesh']['subcategory'] = [ + 'title' => t('Sub Category'), + 'help' => t('Sub Category field.'), + 'field' => [ + 'id' => 'standard', + 'click sortable' => TRUE, + ], + 'sort' => [ + 'id' => 'standard', + ], + 'filter' => [ + 'id' => 'subcategory_filter', + ], + 'argument' => [ + 'id' => 'standard', + ], + ]; + $data['entity_mesh']['source_hash_id'] = [ 'title' => t('Source hash ID'), 'help' => t('Source hash ID field.'), diff --git a/src/Batches/GeneralBatch.php b/src/Batches/GeneralBatch.php index 1fa07b4cb24f3d570ca3bd715ad387b8692d0322..ae64f0e4c2fd82170d86afdcb5c7760d6527936a 100644 --- a/src/Batches/GeneralBatch.php +++ b/src/Batches/GeneralBatch.php @@ -59,7 +59,7 @@ class GeneralBatch { * Generate batch. * * @param array $types - * Mesg types. + * Mesh types. * * @return \Drupal\Core\Batch\BatchBuilder * Batch object. diff --git a/src/Commands/EntityMeshCommands.php b/src/Commands/EntityMeshCommands.php index 35ce4d9dda21b194be58a760da4736aeed2464ec..c862fde4af29297bd3089263ce3d3041aa74af77 100644 --- a/src/Commands/EntityMeshCommands.php +++ b/src/Commands/EntityMeshCommands.php @@ -8,7 +8,7 @@ use Drush\Commands\DrushCommands; use Drush\Log\DrushLoggerManager; /** - * A Drush commandfile. + * A Drush command file. * * In addition to this file, you need a drush.services.yml * in root of your module, and a composer.json file that provides the name diff --git a/src/Entity.php b/src/Entity.php index c37c45752dfc486d256616e40cd63158fdc1759a..fd9c065d3c6f3baafe075d05ba09aa54e397b1b0 100644 --- a/src/Entity.php +++ b/src/Entity.php @@ -22,7 +22,7 @@ abstract class Entity { protected $type; /** - * Entity Mish Repository. + * Entity Mesh Repository. * * @var \Drupal\entity_mesh\RepositoryInterface */ @@ -121,14 +121,14 @@ abstract class Entity { /** * Delete an item from the database. * - * @param string $type - * The type of the source. * @param string $entity_type * The entity type of the source. * @param string $entity_id * The entity id of the source. + * @param string $type + * The type of the source. */ - public function deleteItem(string $type, string $entity_type, string $entity_id) { + public function deleteItem(string $entity_type, string $entity_id, string $type = '') { $source = $this->entityMeshRepository->instanceEmptySource(); $source->setType($type); $source->setSourceEntityType($entity_type); diff --git a/src/EntityRender.php b/src/EntityRender.php index 68a9238201006f507362ff10662b9bc42b6ec8e4..5e3671fd21b7e4a609e29b275532f17c886e3345 100644 --- a/src/EntityRender.php +++ b/src/EntityRender.php @@ -251,15 +251,20 @@ class EntityRender extends Entity { * The language code. */ protected function processInternalHref(TargetInterface $target, string $langcode) { + $found_data = FALSE; // Remove the langcode to find the alias. // @todo take into account other languages also to support cross-language paths. // @todo this is the default URL creation, change with the real one. $alias = str_replace('/' . $langcode, '', $target->getPath() ?? ''); + $found_data = $this->setDataIfRedirection($alias, $langcode, $target); + // Get the info from alias. // This method is quicker than using the router service, // so firstly apply this system. - $found_data = $this->setDataTargetFromAliasIfExists($alias, $langcode, $target); + if (!$found_data) { + $found_data = $this->setDataTargetFromAliasIfExists($alias, $langcode, $target); + } // If this method not found the data, use the router service. if (!$found_data) { @@ -267,7 +272,7 @@ class EntityRender extends Entity { } // If the target is set as link broken maybe is because is a file url. - if ($target->getEntityType() === 'broken-link') { + if ($target->getSubcategory() === 'broken-link') { $this->setDataTargetIfFileUrl($langcode, $target); } @@ -301,6 +306,61 @@ class EntityRender extends Entity { $target->setEntityBundle($entity->bundle()); } + /** + * Get the data if it is a redirection. + * + * @param string $alias + * The alias. + * @param string $langcode + * The language code. + * @param TargetInterface $target + * The target. + * + * @return bool + * If is a redirection. + */ + protected function setDataIfRedirection(string &$alias, string $langcode, TargetInterface $target): bool { + // Check if is a redirected link. + $uri = $this->entityMeshRepository->ifRedirectionForPath($alias, $langcode); + if ($uri === NULL) { + return FALSE; + } + + $type = ''; + $target->setSubcategory('redirected-link'); + + // Check if it starts with "internal:". + if (str_starts_with($uri, "internal:")) { + $uri = substr($uri, strlen("internal:")); + $type = 'internal'; + } + elseif (str_starts_with($uri, "entity:")) { + $uri = substr($uri, strlen("entity:")); + $type = 'entity'; + } + elseif (str_starts_with($uri, "http")) { + $target->setLinkType('external'); + $target->setPath('External redirection: ' . $uri); + return TRUE; + } + + if (str_starts_with($uri, "/$langcode")) { + $uri = substr($uri, strlen("/$langcode")); + } + + if ($type === '') { + $alias = $uri; + return FALSE; + } + + // The internal can be an alias. + if ($type === 'internal' && $possible_alias = $this->getPathFromAliasTables($uri, $langcode)) { + $uri = $possible_alias->path; + } + + return $this->processInternalPaths($uri, $langcode, $target); + } + /** * Set data about the target from alias if exists. * @@ -320,8 +380,24 @@ class EntityRender extends Entity { if (!$record) { return FALSE; } + return $this->processInternalPaths($record->path, $langcode, $target); + } - $path = explode('/', ltrim($record->path, '/')); + /** + * Process internal paths. + * + * @param string $path + * The path. + * @param string $langcode + * The language code. + * @param TargetInterface $target + * The target. + * + * @return bool + * If the path is processed. + */ + protected function processInternalPaths(string $path, string $langcode, TargetInterface $target) { + $path = explode('/', ltrim($path, '/')); if (count($path) < 2) { return FALSE; } @@ -360,7 +436,7 @@ class EntityRender extends Entity { $route_match = \Drupal::service('router.no_access_checks')->match($target->getPath()); } catch (\Exception $e) { - $target->setEntityType('broken-link'); + $target->setSubcategory('broken-link'); return; } @@ -407,7 +483,9 @@ class EntityRender extends Entity { return; } - // Try get he media that referred to the file. + $target->setSubcategory($target->getCategory() ?? ''); + + // Try get the media that referred to the file. $media = $this->entityMeshRepository->getMediaFileByEntityFile($file); // If we do not have a media, set information of the entity file. @@ -450,4 +528,11 @@ class EntityRender extends Entity { return $result->fetchObject(); } + /** + * {@inheritDoc} + */ + public function deleteItem(string $entity_type, string $entity_id, string $type = '') { + parent::deleteItem('entity_render', $entity_type, $entity_id); + } + } diff --git a/src/Form/SettingsForm.php b/src/Form/SettingsForm.php index c7b002fda6109e575d1cf1790fcbe35b96f08cbc..e507f6a02242af8de3a8cf8c78f72c607a8a6610 100644 --- a/src/Form/SettingsForm.php +++ b/src/Form/SettingsForm.php @@ -128,6 +128,15 @@ class SettingsForm extends ConfigFormBase { '#title' => $this->t('Internal URLs'), '#default_value' => $config->get('internal'), ]; + $form['global']['process_mesh_using_queue'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Recalculated dependencies using queue'), + '#description' => $this->t('When an operation is performed on an entity, + such as a node, its dependencies are recalculated. There is the possibility + that this recalculation is executed within the same workflow as the operation + or added to a queue to be executed later.'), + '#default_value' => $config->get('process_mesh_using_queue'), + ]; return parent::buildForm($form, $form_state); } diff --git a/src/Plugin/QueueWorker/EntityMeshQueueWorker.php b/src/Plugin/QueueWorker/EntityMeshQueueWorker.php index 0ec560f62b71a0556b7e0ac1e223039c31b791b6..4d9a6c17a9a4994a9bb7479feff155b389ab41a0 100644 --- a/src/Plugin/QueueWorker/EntityMeshQueueWorker.php +++ b/src/Plugin/QueueWorker/EntityMeshQueueWorker.php @@ -68,7 +68,7 @@ final class EntityMeshQueueWorker extends QueueWorkerBase implements QueueWorker */ protected function deleteItem(array $data) { $entity_mesh_service = $this->container->get($data['service']); - $entity_mesh_service->{$data['service_method']}($data['type'], $data['entity'], $data['id']); + $entity_mesh_service->{$data['service_method']}($data['entity'], $data['id']); } /** diff --git a/src/Plugin/views/filter/SubcategoryFilter.php b/src/Plugin/views/filter/SubcategoryFilter.php new file mode 100644 index 0000000000000000000000000000000000000000..ebdb937121837627f9f782c7af3e8997adefb5e5 --- /dev/null +++ b/src/Plugin/views/filter/SubcategoryFilter.php @@ -0,0 +1,27 @@ +<?php + +namespace Drupal\entity_mesh\Plugin\views\filter; + +/** + * Provides a custom filter for a specific column. + * + * @ViewsFilter("subcategory_filter") + */ +class SubcategoryFilter extends BaseSelectFilter { + + /** + * {@inheritdoc} + */ + protected $tableColumn = 'subcategory'; + + /** + * {@inheritdoc} + */ + protected $databaseTable = 'entity_mesh'; + + /** + * {@inheritdoc} + */ + protected $filterLabel = 'Subcategory'; + +} diff --git a/src/Plugin/views/filter/TargetHtrefFilter.php b/src/Plugin/views/filter/TargethrefFilter.php similarity index 81% rename from src/Plugin/views/filter/TargetHtrefFilter.php rename to src/Plugin/views/filter/TargethrefFilter.php index 3d6e208093bce4b9f5bae037d803d46b16e3fd71..d0499b2dc9f3ea5cf3429204498227b353769060 100644 --- a/src/Plugin/views/filter/TargetHtrefFilter.php +++ b/src/Plugin/views/filter/TargethrefFilter.php @@ -7,9 +7,9 @@ use Drupal\views\Plugin\views\filter\StringFilter; /** * Provides a custom filter for a specific column. * - * @ViewsFilter("target_htref_filter") + * @ViewsFilter("target_href_filter") */ -class TargetHtrefFilter extends StringFilter { +class TargethrefFilter extends StringFilter { /** * {@inheritdoc} diff --git a/src/ProcessDataForD3Trait.php b/src/ProcessDataForD3Trait.php index 3c3e55ac9df1afc003fbc27af6c4c3234ab22c2d..6f007d4201faccdbbb8b03113d023ac502939eef 100644 --- a/src/ProcessDataForD3Trait.php +++ b/src/ProcessDataForD3Trait.php @@ -75,7 +75,7 @@ trait ProcessDataForD3Trait { $target_type = NULL; $target_label = NULL; - $type = $this->getValueFromObjetc($row, "type"); + $type = $this->getValueFromObject($row, "type"); $this->setSourceData($source_id, $source_type, $source_label, $row); $this->setTargetData($target_id, $target_type, $target_label, $row); @@ -120,26 +120,26 @@ trait ProcessDataForD3Trait { * Process the raw data for source. */ protected function setSourceData(&$source_id, &$source_type, &$source_label, $row) { - $source_id = $this->getValueFromObjetc($row, "source_hash_id"); - $source_type = $this->getValueFromObjetc($row, "source_entity_type") . '-' . $this->getValueFromObjetc($row, "source_entity_bundle"); - $source_label = $this->getValueFromObjetc($row, "source_title"); + $source_id = $this->getValueFromObject($row, "source_hash_id"); + $source_type = $this->getValueFromObject($row, "source_entity_type") . '-' . $this->getValueFromObject($row, "source_entity_bundle"); + $source_label = $this->getValueFromObject($row, "source_title"); } /** * Process the raw data for target. */ protected function setTargetData(&$target_id, &$target_type, &$target_label, $row) { - $target_id = $this->getValueFromObjetc($row, "target_hash_id"); + $target_id = $this->getValueFromObject($row, "target_hash_id"); // Target data. - if ($this->getValueFromObjetc($row, "target_link_type") === "external") { + if ($this->getValueFromObject($row, "target_link_type") === "external") { $target_type = 'external'; - $target_label = $this->getValueFromObjetc($row, "target_title"); + $target_label = $this->getValueFromObject($row, "target_title"); } else { - $target_type = $this->getValueFromObjetc($row, "target_entity_type"); - $target_type .= !empty($this->getValueFromObjetc($row, "target_entity_bundle")) ? '-' . $this->getValueFromObjetc($row, "target_entity_bundle") : ''; - $target_label = $this->getValueFromObjetc($row, "target_title"); + $target_type = $this->getValueFromObject($row, "target_entity_type"); + $target_type .= !empty($this->getValueFromObject($row, "target_entity_bundle")) ? '-' . $this->getValueFromObject($row, "target_entity_bundle") : ''; + $target_label = $this->getValueFromObject($row, "target_title"); } } @@ -154,7 +154,7 @@ trait ProcessDataForD3Trait { * @return mixed * The value. */ - protected function getValueFromObjetc($row, $field) { + protected function getValueFromObject($row, $field) { if ($this->fieldPrefix !== '') { $field = $this->fieldPrefix . $field; } @@ -174,11 +174,11 @@ trait ProcessDataForD3Trait { * TRUE if the row is excluded, FALSE otherwise. */ protected function ifExcludedRow($row): bool { - $target_link_type = $this->getValueFromObjetc($row, "target_link_type"); - $target_entity_type = $this->getValueFromObjetc($row, "target_entity_type"); + $target_link_type = $this->getValueFromObject($row, "target_link_type"); + $target_entity_type = $this->getValueFromObject($row, "target_entity_type"); - $source_id = $this->getValueFromObjetc($row, "source_hash_id"); - $target_id = $this->getValueFromObjetc($row, "target_hash_id"); + $source_id = $this->getValueFromObject($row, "source_hash_id"); + $target_id = $this->getValueFromObject($row, "target_hash_id"); if ($source_id === $target_id) { return TRUE; diff --git a/src/Repository.php b/src/Repository.php index dbe40176a02fa5954e6f9a8296cd8108314d5c43..c734722c3024a8849a6b45ccfaa8d1f1ae4bb61d 100644 --- a/src/Repository.php +++ b/src/Repository.php @@ -381,4 +381,58 @@ class Repository implements RepositoryInterface { return $medias_type_reference_to_file; } + /** + * {@inheritdoc} + */ + public function ifRedirectionForPath($path, $langcode, $count = 0) { + $service_redirect = 'redirect.repository'; + // @phpstan-ignore-next-line + $container = \Drupal::getContainer(); + if (!$container->has($service_redirect)) { + return NULL; + } + $path = trim($path, '/'); + $redirect_repository = $container->get($service_redirect); + /** @var \Drupal\redirect\Entity\Redirect $redirect_object */ + $redirect_object = $redirect_repository->findMatchingRedirect($path, [], $langcode); + if ($redirect_object === NULL) { + return NULL; + } + + $redirect = $redirect_object->getRedirect(); + $uri = $redirect['uri'] ?? NULL; + if ($uri === NULL) { + return NULL; + } + + // Check if te redirection has a redirection and avoid infinite loop. + if ($count > 5) { + return $uri; + } + $filtered_uri = $this->handlePrefixFromRedirection($uri, $langcode); + $new_redirection = $this->ifRedirectionForPath($filtered_uri, $langcode, $count++); + + return $new_redirection ?? $uri; + } + + /** + * Handle prefix from redirection. + * + * @param string $uri + * The uri. + * @param string $langcode + * The langcode. + * + * @return string + * The uri. + */ + public function handlePrefixFromRedirection($uri, $langcode = '') { + $prefixes = ['internal:', 'entity:', $langcode]; + foreach ($prefixes as $prefix) { + $uri = preg_replace('/^' . $prefix . '/', '', $uri); + $uri = trim($uri, '/'); + } + return $uri; + } + } diff --git a/src/RepositoryInterface.php b/src/RepositoryInterface.php index 484a81a4a6b3b208aad1c95b10b99550c07d727c..5d56a2d8d2531d1563a8d1d6121bf9ac4508c0e2 100644 --- a/src/RepositoryInterface.php +++ b/src/RepositoryInterface.php @@ -128,4 +128,19 @@ interface RepositoryInterface { */ public function getMediaFileByEntityFile(FileInterface $file): ?MediaInterface; + /** + * Check if exists a redirection for this path. + * + * @param string $path + * Path to check. + * @param string $langcode + * Language code. + * @param int $count + * Number of redirection to check. + * + * @return mixed + * The redirection or NULL if it does not exist. + */ + public function ifRedirectionForPath(string $path, string $langcode, int $count = 0); + } diff --git a/src/Target.php b/src/Target.php index 7f8818c44e0903704983466cc6f3cc846847b8a5..6208e42cc0e607989135f9e7c76cda27eeb55786 100644 --- a/src/Target.php +++ b/src/Target.php @@ -18,6 +18,13 @@ class Target implements TargetInterface { */ protected $category; + /** + * Category. + * + * @var string|null + */ + protected $subCategory; + /** * Hash ID. * @@ -118,6 +125,20 @@ class Target implements TargetInterface { $this->category = $category; } + /** + * {@inheritdoc} + */ + public function getSubcategory(): ?string { + return $this->subCategory ?? $this->getCategory(); + } + + /** + * {@inheritdoc} + */ + public function setSubcategory($sub_category) { + $this->subCategory = $sub_category; + } + /** * {@inheritdoc} */ @@ -320,6 +341,7 @@ class Target implements TargetInterface { public function toArray(): array { return [ 'category' => $this->getCategory(), + 'subcategory' => $this->getSubcategory(), 'target_link_type' => $this->getLinkType(), 'target_href' => $this->getHref(), 'target_path' => $this->getPath(), diff --git a/src/TargetInterface.php b/src/TargetInterface.php index e8280646c665a3f836b3be5f33ac5b8ecacd49d4..eec26b382101bc1e735665bc42b6b48c23cf6657 100644 --- a/src/TargetInterface.php +++ b/src/TargetInterface.php @@ -25,6 +25,22 @@ interface TargetInterface { */ public function setCategory($category); + /** + * Get the Subcategory. + * + * @return string|null + * The subcategory. + */ + public function getSubcategory(): ?string; + + /** + * Set the Subcategory. + * + * @param string $sub_category + * The subcategory. + */ + public function setSubcategory($sub_category); + /** * Get the href. *