Link from entities to their referenced terms and show entity views on taxonomy terms #458
**Why?** Make it possible for novice users to find data and explore connections between assets/logs without needing to totally grok the farmOS data model first. i.e. * Navigate from a plant asset to a list of all the seed/plant assets with the same crop/variety * Navigate from a plant asset to a list of all the seed/plant assets from the same season * Navigate from a log in the compost category to a list of all the logs in the compost category * Navigate from a material asset to a list of all the materials with the same material type
This commit is contained in:
parent
3cebccc2d1
commit
a760005c7c
|
@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
### Added
|
||||
|
||||
- [Link from entities to their referenced terms and show entity views on taxonomy terms #458](https://github.com/farmOS/farmOS/pull/458).
|
||||
- [Encourage GEOS PHP extension use #521](https://github.com/farmOS/farmOS/pull/521)
|
||||
|
||||
### Changed
|
||||
|
|
|
@ -351,7 +351,7 @@ class FarmFieldFactory implements FarmFieldFactoryInterface {
|
|||
'type' => 'entity_reference_label',
|
||||
'weight' => $options['weight']['view'] ?? 0,
|
||||
'settings' => [
|
||||
'link' => FALSE,
|
||||
'link' => TRUE,
|
||||
],
|
||||
];
|
||||
break;
|
||||
|
|
|
@ -9,6 +9,7 @@ dependencies:
|
|||
- csv_serialization
|
||||
- entity_browser
|
||||
- farm_location
|
||||
- farm_ui_views
|
||||
- image
|
||||
- options
|
||||
- rest
|
||||
|
@ -1593,6 +1594,111 @@ display:
|
|||
- url.query_args
|
||||
- user.permissions
|
||||
tags: { }
|
||||
page_term:
|
||||
display_plugin: page
|
||||
id: page_term
|
||||
display_title: 'By term (page)'
|
||||
position: 4
|
||||
display_options:
|
||||
display_extenders: { }
|
||||
display_description: ''
|
||||
path: taxonomy/term/%taxonomy_term/assets/%entity_bundle
|
||||
arguments:
|
||||
asset_taxonomy_term_reference:
|
||||
id: asset_taxonomy_term_reference
|
||||
table: asset_field_data
|
||||
field: asset_taxonomy_term_reference
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
break_phrase: false
|
||||
default_action: 'not found'
|
||||
exception:
|
||||
value: all
|
||||
title_enable: false
|
||||
title: All
|
||||
title_enable: false
|
||||
title: ''
|
||||
default_argument_type: fixed
|
||||
default_argument_options:
|
||||
argument: ''
|
||||
default_argument_skip_url: false
|
||||
summary_options:
|
||||
base_path: ''
|
||||
count: true
|
||||
items_per_page: 25
|
||||
override: false
|
||||
summary:
|
||||
sort_order: asc
|
||||
number_of_records: 0
|
||||
format: default_summary
|
||||
specify_validation: true
|
||||
validate:
|
||||
type: 'entity:taxonomy_term'
|
||||
fail: 'not found'
|
||||
validate_options:
|
||||
operation: view
|
||||
access: false
|
||||
multiple: 0
|
||||
bundles: {}
|
||||
entity_type: asset
|
||||
plugin_id: entity_taxonomy_term_reference
|
||||
type:
|
||||
id: type
|
||||
table: asset_field_data
|
||||
field: type
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
default_action: 'not found'
|
||||
exception:
|
||||
value: all
|
||||
title_enable: false
|
||||
title: All
|
||||
title_enable: false
|
||||
title: ''
|
||||
default_argument_type: fixed
|
||||
default_argument_options:
|
||||
argument: all
|
||||
default_argument_skip_url: false
|
||||
summary_options:
|
||||
base_path: ''
|
||||
count: true
|
||||
items_per_page: 25
|
||||
override: false
|
||||
summary:
|
||||
sort_order: asc
|
||||
number_of_records: 0
|
||||
format: default_summary
|
||||
specify_validation: true
|
||||
validate:
|
||||
type: 'entity:asset_type'
|
||||
fail: 'not found'
|
||||
validate_options:
|
||||
operation: view
|
||||
multiple: 0
|
||||
access: false
|
||||
bundles: { }
|
||||
glossary: false
|
||||
limit: 0
|
||||
case: none
|
||||
path_case: none
|
||||
transform_dash: false
|
||||
break_phrase: false
|
||||
entity_type: asset
|
||||
entity_field: type
|
||||
plugin_id: string
|
||||
defaults:
|
||||
arguments: false
|
||||
cache_metadata:
|
||||
max-age: 0
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
- url
|
||||
- url.query_args
|
||||
- user.permissions
|
||||
tags: { }
|
||||
page_children:
|
||||
id: page_children
|
||||
display_title: 'Children (page)'
|
||||
|
|
|
@ -2471,7 +2471,7 @@ display:
|
|||
entity_type: log
|
||||
entity_field: type
|
||||
plugin_id: string
|
||||
default_action: ignore
|
||||
default_action: 'not found'
|
||||
exception:
|
||||
value: all
|
||||
title_enable: false
|
||||
|
@ -2521,6 +2521,112 @@ display:
|
|||
- user
|
||||
- user.permissions
|
||||
tags: { }
|
||||
page_term:
|
||||
display_plugin: page
|
||||
id: page_term
|
||||
display_title: 'By term (page)'
|
||||
position: 2
|
||||
display_options:
|
||||
display_extenders: { }
|
||||
display_description: ''
|
||||
path: taxonomy/term/%taxonomy_term/logs/%entity_bundle
|
||||
arguments:
|
||||
log_taxonomy_term_reference:
|
||||
id: log_taxonomy_term_reference
|
||||
table: log_field_data
|
||||
field: log_taxonomy_term_reference
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
break_phrase: false
|
||||
default_action: 'not found'
|
||||
exception:
|
||||
value: all
|
||||
title_enable: false
|
||||
title: All
|
||||
title_enable: false
|
||||
title: ''
|
||||
default_argument_type: fixed
|
||||
default_argument_options:
|
||||
argument: ''
|
||||
default_argument_skip_url: false
|
||||
summary_options:
|
||||
base_path: ''
|
||||
count: true
|
||||
items_per_page: 25
|
||||
override: false
|
||||
summary:
|
||||
sort_order: asc
|
||||
number_of_records: 0
|
||||
format: default_summary
|
||||
specify_validation: true
|
||||
validate:
|
||||
type: 'entity:taxonomy_term'
|
||||
fail: 'not found'
|
||||
validate_options:
|
||||
operation: view
|
||||
access: false
|
||||
multiple: 0
|
||||
bundles: {}
|
||||
entity_type: log
|
||||
plugin_id: entity_taxonomy_term_reference
|
||||
type:
|
||||
id: type
|
||||
table: log_field_data
|
||||
field: type
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
default_action: 'not found'
|
||||
exception:
|
||||
value: all
|
||||
title_enable: false
|
||||
title: All
|
||||
title_enable: false
|
||||
title: ''
|
||||
default_argument_type: fixed
|
||||
default_argument_options:
|
||||
argument: all
|
||||
default_argument_skip_url: false
|
||||
summary_options:
|
||||
base_path: ''
|
||||
count: true
|
||||
items_per_page: 25
|
||||
override: false
|
||||
summary:
|
||||
sort_order: asc
|
||||
number_of_records: 0
|
||||
format: default_summary
|
||||
specify_validation: true
|
||||
validate:
|
||||
type: 'entity:log_type'
|
||||
fail: 'not found'
|
||||
validate_options:
|
||||
operation: view
|
||||
multiple: 0
|
||||
access: false
|
||||
bundles: { }
|
||||
glossary: false
|
||||
limit: 0
|
||||
case: none
|
||||
path_case: none
|
||||
transform_dash: false
|
||||
break_phrase: false
|
||||
entity_type: log
|
||||
entity_field: type
|
||||
plugin_id: string
|
||||
defaults:
|
||||
arguments: false
|
||||
cache_metadata:
|
||||
max-age: 0
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
- url
|
||||
- url.query_args
|
||||
- user
|
||||
- user.permissions
|
||||
tags: { }
|
||||
page_type:
|
||||
id: page_type
|
||||
display_title: 'By type (page)'
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# Add log view tabs to assets.
|
||||
farm.asset.logs:
|
||||
title: 'Logs'
|
||||
route_name: view.farm_log.page_asset
|
||||
|
@ -13,3 +14,21 @@ farm.asset.logs.all:
|
|||
parent_id: farm.asset.logs
|
||||
farm.asset.logs.type:
|
||||
deriver: Drupal\farm_ui_views\Plugin\Derivative\FarmLogViewsTaskLink
|
||||
|
||||
# Add asset/log view tabs to taxonomy terms.
|
||||
farm.taxonomy_term.assets:
|
||||
title: 'Assets'
|
||||
route_name: view.farm_asset.page_term
|
||||
route_parameters:
|
||||
entity_bundle: 'all'
|
||||
base_route: entity.taxonomy_term.canonical
|
||||
weight: 50
|
||||
farm.taxonomy_term.logs:
|
||||
title: 'Logs'
|
||||
route_name: view.farm_log.page_term
|
||||
route_parameters:
|
||||
entity_bundle: 'all'
|
||||
base_route: entity.taxonomy_term.canonical
|
||||
weight: 50
|
||||
farm.taxonomy_term.entities.type:
|
||||
deriver: Drupal\farm_ui_views\Plugin\Derivative\FarmTaxonomyTermViewsTaskLink
|
||||
|
|
|
@ -278,5 +278,8 @@ function farm_ui_views_get_bundle_argument(ViewExecutable $view, string $display
|
|||
elseif ($view->id() == 'farm_log' && $display_id == 'page_asset' && !empty($args[1]) && $args[1] != 'all') {
|
||||
$bundle = $args[1];
|
||||
}
|
||||
elseif (in_array($view->id(), ['farm_asset', 'farm_log']) && $display_id == 'page_term' && !empty($args[1]) && $args[1] != 'all') {
|
||||
$bundle = $args[1];
|
||||
}
|
||||
return $bundle;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,24 @@ services:
|
|||
arguments: [ '@entity_type.manager', '@asset.location' ]
|
||||
tags:
|
||||
- { name: access_check, applies_to: _asset_children_access }
|
||||
farm_ui_views.asset_term_access:
|
||||
class: Drupal\farm_ui_views\Access\FarmTaxonomyTermEntityViewsAccessCheck
|
||||
arguments:
|
||||
- 'asset'
|
||||
- '@entity_type.manager'
|
||||
- '@entity_type.bundle.info'
|
||||
- '@entity_field.manager'
|
||||
tags:
|
||||
- { name: access_check, applies_to: _asset_term_access }
|
||||
farm_ui_views.log_term_access:
|
||||
class: Drupal\farm_ui_views\Access\FarmTaxonomyTermEntityViewsAccessCheck
|
||||
arguments:
|
||||
- 'log'
|
||||
- '@entity_type.manager'
|
||||
- '@entity_type.bundle.info'
|
||||
- '@entity_field.manager'
|
||||
tags:
|
||||
- { name: access_check, applies_to: _log_term_access }
|
||||
farm_ui_views.asset_inventory_access:
|
||||
class: Drupal\farm_ui_views\Access\FarmInventoryAssetViewsAccessCheck
|
||||
arguments: [ '@entity_type.manager' ]
|
||||
|
|
|
@ -20,4 +20,26 @@ function farm_ui_views_views_data_alter(array &$data) {
|
|||
],
|
||||
];
|
||||
}
|
||||
|
||||
// Provide an asset_taxonomy_term_reference argument for views of assets.
|
||||
if (isset($data['asset_field_data'])) {
|
||||
$data['asset_field_data']['asset_taxonomy_term_reference'] = [
|
||||
'title' => t('Asset Taxonomy Term Reference'),
|
||||
'help' => t('Taxonomy Terms that are referenced by the asset.'),
|
||||
'argument' => [
|
||||
'id' => 'entity_taxonomy_term_reference',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
// Provide a log_taxonomy_term_reference argument for views of logs.
|
||||
if (isset($data['log_field_data'])) {
|
||||
$data['log_field_data']['log_taxonomy_term_reference'] = [
|
||||
'title' => t('Log Taxonomy Term Reference'),
|
||||
'help' => t('Taxonomy Terms that are referenced by the log.'),
|
||||
'argument' => [
|
||||
'id' => 'entity_taxonomy_term_reference',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -129,6 +129,41 @@ function farm_ui_views_views_pre_render(ViewExecutable $view) {
|
|||
}
|
||||
}
|
||||
|
||||
// If this is the farm_asset/farm_log View and page_term display, include
|
||||
// the term's name.
|
||||
if (in_array($view->id(), ['farm_asset', 'farm_log']) && $view->current_display == 'page_term') {
|
||||
$term_id = $view->args[0];
|
||||
$entity_bundle = $view->args[1];
|
||||
$term = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->load($term_id);
|
||||
|
||||
if (!empty($term)) {
|
||||
$vocabulary = \Drupal::entityTypeManager()->getStorage('taxonomy_vocabulary')->load($term->bundle());
|
||||
|
||||
$entity_bundle_label = '';
|
||||
if ($entity_bundle != 'all') {
|
||||
$bundles = \Drupal::service('entity_type.bundle.info')->getBundleInfo($view->getBaseEntityType()->id());
|
||||
if (!empty($bundles[$entity_bundle])) {
|
||||
$entity_bundle_label = $bundles[$entity_bundle]['label'] . ' ' . $view->getBaseEntityType()->getPluralLabel();
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($entity_bundle_label)) {
|
||||
$title = t('%bundle with %vocab term %term', [
|
||||
'%bundle' => $entity_bundle_label,
|
||||
'%vocab' => $vocabulary->label(),
|
||||
'%term' => $term->label(),
|
||||
]);
|
||||
}
|
||||
else {
|
||||
$title = t('%base_type with %vocab term %term', [
|
||||
'%base_type' => $view->getBaseEntityType()->getCollectionLabel(),
|
||||
'%vocab' => $vocabulary->label(),
|
||||
'%term' => $term->label(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the title, if so desired.
|
||||
if (!empty($title)) {
|
||||
$view->setTitle($title);
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\farm_ui_views\Access;
|
||||
|
||||
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Routing\Access\AccessInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
|
||||
/**
|
||||
* Checks access for displaying Views of entities that reference taxonomy terms.
|
||||
*/
|
||||
class FarmTaxonomyTermEntityViewsAccessCheck implements AccessInterface {
|
||||
|
||||
/**
|
||||
* The base entity type of the views this access check will be applied to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $baseEntityType;
|
||||
|
||||
/**
|
||||
* The taxonomy term storage.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageInterface
|
||||
*/
|
||||
protected $taxonomyTermStorage;
|
||||
|
||||
/**
|
||||
* The entity type bundle info.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
|
||||
*/
|
||||
protected $entityTypeBundleInfo;
|
||||
|
||||
/**
|
||||
* The entity field manager service.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
|
||||
*/
|
||||
protected $entityFieldManager;
|
||||
|
||||
/**
|
||||
* FarmTaxonomyTermEntityViewsAccessCheck constructor.
|
||||
*
|
||||
* @param string $base_entity_type
|
||||
* The base entity type of the views this access check will be applied to.
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* The entity type manager service.
|
||||
* @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_bundle_info
|
||||
* The entity type bundle info service.
|
||||
* @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
|
||||
* The entity field manager service.
|
||||
*/
|
||||
public function __construct($base_entity_type,
|
||||
EntityTypeManagerInterface $entity_type_manager,
|
||||
EntityTypeBundleInfoInterface $entity_bundle_info,
|
||||
EntityFieldManagerInterface $entity_field_manager) {
|
||||
$this->baseEntityType = $base_entity_type;
|
||||
$this->taxonomyTermStorage = $entity_type_manager->getStorage('taxonomy_term');
|
||||
$this->entityTypeBundleInfo = $entity_bundle_info;
|
||||
$this->entityFieldManager = $entity_field_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* A custom access check to filter out irrelevant entity bundles.
|
||||
*
|
||||
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
|
||||
* The route match.
|
||||
*/
|
||||
public function access(RouteMatchInterface $route_match) {
|
||||
|
||||
// If there is no "taxonomy_term" or "asset_type" parameter, bail.
|
||||
$term_id = $route_match->getParameter('taxonomy_term');
|
||||
$entity_bundle = $route_match->getParameter('entity_bundle');
|
||||
|
||||
if (empty($term_id) || empty($entity_bundle)) {
|
||||
return AccessResult::forbidden();
|
||||
}
|
||||
|
||||
$term = $this->taxonomyTermStorage->load($term_id);
|
||||
|
||||
// Loop through all the entity bundles of the base entity type for the view
|
||||
// and only return AccessResult::allowed() for those which have a taxonomy
|
||||
// term entity reference field referencing the taxonomy term bundle of the
|
||||
// term we loaded above.
|
||||
$bundles = $this->entityTypeBundleInfo->getBundleInfo($this->baseEntityType);
|
||||
foreach (array_keys($bundles) as $type) {
|
||||
// If the route argument is 'all' then we check all the bundles, otherwise
|
||||
// only check the one that matches.
|
||||
if ($entity_bundle == 'all' || $type == $entity_bundle) {
|
||||
$field_definitions = $this->entityFieldManager->getFieldDefinitions($this->baseEntityType, $type);
|
||||
|
||||
foreach (array_values($field_definitions) as $field_definition) {
|
||||
if ($field_definition->getType() == "entity_reference" && $field_definition->getSetting('target_type') == "taxonomy_term") {
|
||||
$handler_settings = $field_definition->getSetting('handler_settings') ?? [];
|
||||
|
||||
if (in_array($term->bundle(), $handler_settings['target_bundles'] ?? [])) {
|
||||
return AccessResult::allowed();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return AccessResult::forbidden();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\farm_ui_views\Plugin\Derivative;
|
||||
|
||||
use Drupal\Component\Plugin\Derivative\DeriverBase;
|
||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
||||
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides task links for farmOS Taxonomy Term Views.
|
||||
*/
|
||||
class FarmTaxonomyTermViewsTaskLink extends DeriverBase implements ContainerDeriverInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* The entity type bundle info.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
|
||||
*/
|
||||
protected $entityTypeBundleInfo;
|
||||
|
||||
/**
|
||||
* Constructs a FarmTaxonomyTermViewsTaskLink instance.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_bundle_info
|
||||
* The entity type bundle info service.
|
||||
*/
|
||||
public function __construct(EntityTypeBundleInfoInterface $entity_bundle_info) {
|
||||
$this->entityTypeBundleInfo = $entity_bundle_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, $base_plugin_id) {
|
||||
return new static(
|
||||
$container->get('entity_type.bundle.info')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDerivativeDefinitions($base_plugin_definition) {
|
||||
$links = [];
|
||||
|
||||
foreach (['asset', 'log'] as $entity_type) {
|
||||
|
||||
$links["farm.taxonomy_term.{$entity_type}s.all"] = [
|
||||
'id' => "farm.taxonomy_term.{$entity_type}s.all",
|
||||
'title' => 'All',
|
||||
'parent_id' => "farm.taxonomy_term.{$entity_type}s",
|
||||
'route_name' => "view.farm_$entity_type.page_term",
|
||||
'route_parameters' => [
|
||||
'entity_bundle' => 'all',
|
||||
],
|
||||
] + $base_plugin_definition;
|
||||
|
||||
// Add links for each entity bundle.
|
||||
$entity_bundles = $this->entityTypeBundleInfo->getBundleInfo($entity_type);
|
||||
foreach ($entity_bundles as $entity_bundle => $info) {
|
||||
|
||||
$links["farm.taxonomy_term.{$entity_type}s.$entity_bundle"] = [
|
||||
'id' => "farm.taxonomy_term.{$entity_type}s.$entity_bundle",
|
||||
'title' => $info['label'],
|
||||
'parent_id' => "farm.taxonomy_term.{$entity_type}s",
|
||||
'route_name' => "view.farm_$entity_type.page_term",
|
||||
'route_parameters' => [
|
||||
'entity_bundle' => $entity_bundle,
|
||||
],
|
||||
] + $base_plugin_definition;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\farm_ui_views\Plugin\views\argument;
|
||||
|
||||
use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
|
||||
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Drupal\taxonomy\Plugin\views\argument\Taxonomy;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Argument handler for taxonomy term references from an arbitrary entity field.
|
||||
*
|
||||
* @ingroup views_argument_handlers
|
||||
*
|
||||
* @ViewsArgument("entity_taxonomy_term_reference")
|
||||
*/
|
||||
class EntityTaxonomyTermReferenceArgument extends Taxonomy {
|
||||
|
||||
/**
|
||||
* The entity type manager service.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* The entity type bundle info.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
|
||||
*/
|
||||
protected $entityTypeBundleInfo;
|
||||
|
||||
/**
|
||||
* The entity field manager service.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
|
||||
*/
|
||||
protected $entityFieldManager;
|
||||
|
||||
/**
|
||||
* EntityTaxonomyTermReferenceArgument constructor.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin ID for the plugin instance.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $term_storage
|
||||
* The taxonomy term storage service.
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* The entity type manager service.
|
||||
* @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_bundle_info
|
||||
* The entity type bundle info service.
|
||||
* @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
|
||||
* The entity field manager service.
|
||||
*/
|
||||
public function __construct(array $configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
EntityStorageInterface $term_storage,
|
||||
EntityTypeManagerInterface $entity_type_manager,
|
||||
EntityTypeBundleInfoInterface $entity_bundle_info,
|
||||
EntityFieldManagerInterface $entity_field_manager) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $term_storage);
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->entityTypeBundleInfo = $entity_bundle_info;
|
||||
$this->entityFieldManager = $entity_field_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static($configuration, $plugin_id, $plugin_definition,
|
||||
$container->get('entity_type.manager')->getStorage('taxonomy_term'),
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('entity_type.bundle.info'),
|
||||
$container->get('entity_field.manager'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query($group_by = FALSE) {
|
||||
// Getting the arguments through views rather than
|
||||
// from the Drupal route is important since it allows
|
||||
// the contextual filter previews in the views UI to
|
||||
// work correctly.
|
||||
$term_id = $this->argument;
|
||||
$entity_bundle = $this->view->args[1] ?: 'all';
|
||||
|
||||
if (empty($term_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$term = $this->termStorage->load($term_id);
|
||||
|
||||
// This is a value like 'asset' or 'log'.
|
||||
$base_entity_type = $this->view->getBaseEntityType()->id();
|
||||
|
||||
$entity_storage = $this->entityTypeManager->getStorage($base_entity_type);
|
||||
|
||||
if (!($entity_storage instanceof SqlContentEntityStorage)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$entity_data_table = $entity_storage->getDataTable();
|
||||
|
||||
$entity_table_mapping = $entity_storage->getTableMapping();
|
||||
|
||||
$conditions = [];
|
||||
|
||||
// Keep track of which field tables we've already joined with since some
|
||||
// assets share the same field e.g. plant and seed assets.
|
||||
$already_joined_term_field_tables = [];
|
||||
|
||||
// Loop through all the bundles of the base entity type for this view.
|
||||
$bundles = $this->entityTypeBundleInfo->getBundleInfo($base_entity_type);
|
||||
foreach (array_keys($bundles) as $type) {
|
||||
// Consider either all of them or just the one matching the
|
||||
// bundle argument.
|
||||
if ($entity_bundle == 'all' || $type == $entity_bundle) {
|
||||
$field_definitions = $this->entityFieldManager->getFieldDefinitions($base_entity_type, $type);
|
||||
|
||||
foreach ($field_definitions as $field_id => $field_definition) {
|
||||
// Look for taxonomy term entity reference fields which reference the
|
||||
// target bundle of the term we loaded above.
|
||||
if ($field_definition->getType() == "entity_reference" && $field_definition->getSetting('target_type') == "taxonomy_term") {
|
||||
$handler_settings = $field_definition->getSetting('handler_settings') ?? [];
|
||||
|
||||
if (in_array($term->bundle(), $handler_settings['target_bundles'] ?? [])) {
|
||||
|
||||
// Now that we have found such a field, get the parameters to
|
||||
// construct a join to allow us to filter only those entities
|
||||
// which actually reference the term we loaded above.
|
||||
$field_table_name = $entity_table_mapping->getFieldTableName($field_id);
|
||||
|
||||
// Don't add the same join more than once.
|
||||
if (array_key_exists($field_table_name, $already_joined_term_field_tables)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$column_names = $entity_table_mapping->getColumnNames($field_id);
|
||||
|
||||
$target_id_column_name = $column_names['target_id'];
|
||||
|
||||
// Join the taxonomy reference field table with the entity.
|
||||
/** @var \Drupal\views\Plugin\views\join\JoinPluginBase $join */
|
||||
$join = Views::pluginManager('join')->createInstance('standard', [
|
||||
'table' => $field_table_name,
|
||||
'field' => 'entity_id',
|
||||
'left_table' => $entity_data_table,
|
||||
'left_field' => 'id',
|
||||
'extra' => [
|
||||
[
|
||||
'field' => 'deleted',
|
||||
'value' => 0,
|
||||
],
|
||||
[
|
||||
'field' => $target_id_column_name,
|
||||
'value' => $term->id(),
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
// Add the relationship.
|
||||
$relationship_alias = $this->query->addRelationship($field_table_name, $join, $entity_data_table);
|
||||
|
||||
// Keep track that we've now joined with that field table.
|
||||
$already_joined_term_field_tables[$field_table_name] = 1;
|
||||
|
||||
// Add a condition to our final WHERE statement that the joined
|
||||
// taxonomy term reference target id is not NULL.
|
||||
$conditions[] = "$relationship_alias.$target_id_column_name IS NOT NULL";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($conditions)) {
|
||||
$combined_conditions = implode(" OR ", $conditions);
|
||||
|
||||
$this->query->addWhereExpression(0, "$entity_data_table.id IS NOT NULL AND ($combined_conditions)");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -28,6 +28,18 @@ class RouteSubscriber extends RouteSubscriberBase {
|
|||
$route->setRequirement('_asset_children_access', 'Drupal\farm_ui_views\Access\FarmAssetChildrenViewsAccessCheck::access');
|
||||
}
|
||||
|
||||
// Add our _asset_term_access requirement to
|
||||
// view.farm_asset.page_term.
|
||||
if ($route = $collection->get('view.farm_asset.page_term')) {
|
||||
$route->setRequirement('_asset_term_access', 'Drupal\farm_ui_views\Access\FarmTaxonomyTermEntityViewsAccessCheck::access');
|
||||
}
|
||||
|
||||
// Add our _log_term_access requirement to
|
||||
// view.farm_log.page_term.
|
||||
if ($route = $collection->get('view.farm_log.page_term')) {
|
||||
$route->setRequirement('_log_term_access', 'Drupal\farm_ui_views\Access\FarmTaxonomyTermEntityViewsAccessCheck::access');
|
||||
}
|
||||
|
||||
// Add our _location_assets_access requirement to
|
||||
// view.farm_asset.page_location.
|
||||
if ($route = $collection->get('view.farm_asset.page_location')) {
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\farm_ui_views\Functional;
|
||||
|
||||
use Drupal\asset\Entity\Asset;
|
||||
use Drupal\Tests\farm_test\Functional\FarmBrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests the farm_ui_views taxonomy views routes.
|
||||
*
|
||||
* @group farm
|
||||
*/
|
||||
class TaxonomyTermTasksTest extends FarmBrowserTestBase {
|
||||
|
||||
/**
|
||||
* Test user.
|
||||
*
|
||||
* @var \Drupal\user\Entity\User
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* Test animal asset.
|
||||
*
|
||||
* @var \Drupal\asset\Entity\Asset
|
||||
*/
|
||||
protected $favaPlantType;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'classy';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'block',
|
||||
'farm_plant',
|
||||
'farm_seed',
|
||||
'farm_ui_views',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalPlaceBlock('local_tasks_block');
|
||||
|
||||
// Create/login a user with permission to access taxonomy pages and assets.
|
||||
$this->user = $this->createUser(['administer taxonomy', 'view any asset']);
|
||||
$this->drupalLogin($this->user);
|
||||
|
||||
$entity_type_manager = $this->container->get('entity_type.manager');
|
||||
$term_storage = $entity_type_manager->getStorage('taxonomy_term');
|
||||
|
||||
// Create a "Oat" plant type term.
|
||||
$oat_plant_type = $term_storage->create([
|
||||
'name' => 'Oat',
|
||||
'vid' => 'plant_type',
|
||||
]);
|
||||
$oat_plant_type->save();
|
||||
|
||||
// Create a oat plant.
|
||||
Asset::create([
|
||||
'name' => 'Pringle\'s Progress Oat Planting',
|
||||
'type' => 'plant',
|
||||
'plant_type' => ['target_id' => $oat_plant_type->id()],
|
||||
])->save();
|
||||
|
||||
// Create a "Fava Bean" plant type term.
|
||||
$this->favaPlantType = $term_storage->create([
|
||||
'name' => 'Fava Bean',
|
||||
'vid' => 'plant_type',
|
||||
]);
|
||||
$this->favaPlantType->save();
|
||||
|
||||
// Create a fava plant.
|
||||
Asset::create([
|
||||
'name' => 'Red Flowering Fava Planting',
|
||||
'type' => 'plant',
|
||||
'plant_type' => ['target_id' => $this->favaPlantType->id()],
|
||||
])->save();
|
||||
|
||||
// Create a fava seed.
|
||||
Asset::create([
|
||||
'name' => 'Red Flowering Fava Seeds',
|
||||
'type' => 'seed',
|
||||
'plant_type' => ['target_id' => $this->favaPlantType->id()],
|
||||
])->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the asset view task links appear on taxonomy term pages.
|
||||
*/
|
||||
public function testTaxonomyTermAssetTaskTabsAppear() {
|
||||
$fava_term_url = 'taxonomy/term/' . $this->favaPlantType->id();
|
||||
|
||||
$this->drupalGet($fava_term_url);
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
$get_array_of_link_text_by_url = function ($elems) {
|
||||
$result = [];
|
||||
foreach ($elems as $elem) {
|
||||
$result[$elem->getAttribute('href')] = $elem->getText();
|
||||
}
|
||||
return $result;
|
||||
};
|
||||
|
||||
$primary_tab_links = $get_array_of_link_text_by_url($this->xpath('//*[contains(@class, :class)]//a', [
|
||||
':class' => 'tabs primary',
|
||||
]));
|
||||
|
||||
$assert_has_link = function ($elements, $url, $label) {
|
||||
$this->assertArrayHasKey($url, $elements, "No link exists with url '$url' among: " . print_r($elements, TRUE));
|
||||
|
||||
$this->assertEquals($label, $elements[$url], "Link label not as expected.");
|
||||
};
|
||||
|
||||
$assert_has_link($primary_tab_links, "/$fava_term_url/assets/all", 'Assets');
|
||||
|
||||
$this->drupalGet("$fava_term_url/assets/all");
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
$secondary_tab_links = $get_array_of_link_text_by_url($this->xpath('//*[contains(@class, :class)]//a', [
|
||||
':class' => 'tabs secondary',
|
||||
]));
|
||||
|
||||
$this->assertCount(3, $secondary_tab_links, 'Only 3 secondary tabs appear.');
|
||||
|
||||
$assert_has_link($secondary_tab_links, "/$fava_term_url/assets/all", 'All(active tab)');
|
||||
$assert_has_link($secondary_tab_links, "/$fava_term_url/assets/plant", 'Plant');
|
||||
$assert_has_link($secondary_tab_links, "/$fava_term_url/assets/seed", 'Seed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the views of assets for terms show the correct assets.
|
||||
*/
|
||||
public function testTaxonomyTermAssetViews() {
|
||||
$fava_term_url = 'taxonomy/term/' . $this->favaPlantType->id();
|
||||
|
||||
$this->drupalGet("$fava_term_url/assets/all");
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
$this->assertSession()->pageTextContains('Red Flowering Fava Planting');
|
||||
$this->assertSession()->pageTextContains('Red Flowering Fava Seeds');
|
||||
$this->assertSession()->pageTextNotContains('Pringle\'s Progress Oat Planting');
|
||||
|
||||
$this->drupalGet("$fava_term_url/assets/plant");
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
$this->assertSession()->pageTextContains('Red Flowering Fava Planting');
|
||||
$this->assertSession()->pageTextNotContains('Red Flowering Fava Seeds');
|
||||
$this->assertSession()->pageTextNotContains('Pringle\'s Progress Oat Planting');
|
||||
|
||||
$this->drupalGet("$fava_term_url/assets/seed");
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
$this->assertSession()->pageTextNotContains('Red Flowering Fava Planting');
|
||||
$this->assertSession()->pageTextContains('Red Flowering Fava Seeds');
|
||||
$this->assertSession()->pageTextNotContains('Pringle\'s Progress Oat Planting');
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue