Issue #3243383: Allow flags to be limited to specific entity types and bundles

This commit is contained in:
Michael Stenta 2021-10-14 11:37:45 -04:00
parent 95bedca516
commit 97309754c1
9 changed files with 157 additions and 10 deletions

View File

@ -94,7 +94,7 @@ function mymodule_farm_entity_bundle_field_info(EntityTypeInterface $entity_type
Certain fields on assets and logs include a list of options to select from.
These include:
- **Flags** (on assets and logs)
- **Flags** (on assets, logs, and plans)
- Monitor (`monitor`)
- Needs review (`needs_review`)
- Priority (`priority`)
@ -142,16 +142,41 @@ dependencies:
- my_module
id: monitor
label: Monitor
entity_types: null
```
The most important parts are the `id`, which is a unique machine name for
the flag, and `label`, which is the human readable/translatable label that
will be shown in the select field and other parts of the UI.
the flag, `label`, which is the human readable/translatable label that will be
shown in the select field and other parts of the UI, and `entity_types`, which
can optionally specify the entity types and bundles that this flag applies to.
The `langcode` and `status` and `dependencies` are standard configuration
entity properties. By putting the module's name in "enforced modules" it will
ensure that the flag is removed when the module is uninstalled.
Flags can be limited to certain entity types and bundles via an optional
`entity_types` property. This accepts a set of entity types with arrays of
bundles that the flag applies to (or `all` to apply to all bundles). For
example, to create a flag that only applies to Animal assets:
```yaml
entity_types:
asset:
- animal
```
To create a flag that applies to all asset types and log types, but not plans,
specify `all` for the `asset` and `log` bundles, but omit the `plan` entity
type:
```yaml
entity_types:
asset:
- all
log:
- all
```
#### Land type
The "Land" module in farmOS provides a "Field" type like this:
@ -205,9 +230,9 @@ label: Soil test
#### ID tag type
ID tag types are similar to Flags, in that they have an `id` and `label`, but
they also have an additional property: `bundle`. This allows the tag type to
be limited to certain types of assets.
ID tag types are similar to Flags, in that they have an `id` and `label`. They
also have an additional `bundle` property, which allows them to be limited to
certain types of assets.
For example, an "Ear tag" type, provided by the "Animal asset" module, only
applies to "Animal" assets:

View File

@ -6,3 +6,4 @@ dependencies:
- farm_flag
id: monitor
label: Monitor
entity_types: null

View File

@ -6,3 +6,4 @@ dependencies:
- farm_flag
id: priority
label: Priority
entity_types: null

View File

@ -6,3 +6,4 @@ dependencies:
- farm_flag
id: review
label: Needs review
entity_types: null

View File

@ -9,3 +9,13 @@ farm_flag.flag.*:
label:
type: label
label: 'Label'
entity_types:
type: sequence
label: 'Entity types'
nullable: true
sequence:
type: sequence
label: 'Entity type'
sequence:
type: string
label: 'Bundle'

View File

@ -5,7 +5,10 @@
* The farmOS Flags module.
*/
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\farm_flag\Entity\FarmFlagInterface;
use Drupal\farm_flag\Form\EntityFlagActionForm;
use Drupal\farm_flag\Routing\EntityFlagActionRouteProvider;
@ -37,19 +40,72 @@ function farm_flag_entity_base_field_info(EntityTypeInterface $entity_type) {
/**
* Allowed values callback function for the flags field.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $definition
* The field definition.
* @param \Drupal\Core\Entity\ContentEntityInterface|null $entity
* The entity being created if applicable.
*
* @return array
* Returns an array of allowed values for use in form select options.
*/
function farm_flag_field_allowed_values() {
function farm_flag_field_allowed_values(FieldDefinitionInterface $definition, ContentEntityInterface $entity = NULL) {
/** @var \Drupal\farm_flag\Entity\FarmFlagInterface[] $flags */
$flags = \Drupal::entityTypeManager()->getStorage('flag')->loadMultiple();
$allowed_values = [];
$entity_type = NULL;
$bundle = NULL;
if (!empty($entity)) {
$entity_type = $entity->getEntityTypeId();
$bundle = $entity->bundle();
}
foreach ($flags as $id => $flag) {
$allowed_values[$id] = $flag->getLabel();
if (farm_flag_applies($flag, $entity_type, $bundle)) {
$allowed_values[$id] = $flag->getLabel();
}
}
return $allowed_values;
}
/**
* Check to see if a flag applies to an entity type + bundle.
*
* @param \Drupal\farm_flag\Entity\FarmFlagInterface $flag
* The flag object.
* @param string|null $entity_type
* The entity type machine name.
* @param string|null $bundle
* The bundle name.
*
* @return bool
* Returns TRUE if the flag applies, FALSE otherwise.
*/
function farm_flag_applies(FarmFlagInterface $flag, $entity_type = NULL, $bundle = NULL) {
// If no entity type is specified, we assume the flag applies. This ensures
// it shows in lists/filters where the entity type may not be known.
if (empty($entity_type)) {
return TRUE;
}
// Load applicable entity types.
$entity_types = $flag->getEntityTypes();
// The flag applies if there are no allowed entity types specified.
if (empty($entity_types)) {
return TRUE;
}
// The flag applies if the entity type is in the list of applicable entity
// types, and the bundle is in the list of applicable bundles (or the flag
// applies to "all" bundles).
if (array_key_exists($entity_type, $entity_types) && (in_array($bundle, $entity_types[$entity_type]) || in_array('all', $entity_types[$entity_type]))) {
return TRUE;
}
// Otherwise, assume the flag does not apply.
return FALSE;
}
/**
* Implements hook_theme().
*/

View File

@ -22,6 +22,7 @@ use Drupal\Core\Config\Entity\ConfigEntityBase;
* config_export = {
* "id",
* "label",
* "entity_types",
* },
* )
*
@ -43,6 +44,13 @@ class FarmFlag extends ConfigEntityBase implements FarmFlagInterface {
*/
protected $label;
/**
* The entity types and bundles that this flag applies to.
*
* @var array
*/
protected $entity_types;
/**
* {@inheritdoc}
*/
@ -50,4 +58,11 @@ class FarmFlag extends ConfigEntityBase implements FarmFlagInterface {
return $this->label;
}
/**
* {@inheritdoc}
*/
public function getEntitytypes() {
return $this->entity_types;
}
}

View File

@ -19,4 +19,13 @@ interface FarmFlagInterface extends ConfigEntityInterface {
*/
public function getLabel();
/**
* Returns the entity types and bundles that this flag applies to.
*
* @return array
* An array of arrays, keyed by entity type machine name, listing bundles
* (or `all`) that this flag applies to.
*/
public function getEntityTypes();
}

View File

@ -2,6 +2,7 @@
namespace Drupal\farm_flag\Form;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
@ -33,6 +34,13 @@ class EntityFlagActionForm extends ConfirmFormBase {
*/
protected $entityTypeManager;
/**
* The entity field manager.
*
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
*/
protected $entityFieldManager;
/**
* The current user.
*
@ -68,12 +76,15 @@ class EntityFlagActionForm extends ConfirmFormBase {
* The tempstore factory.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
* The entity field manager.
* @param \Drupal\Core\Session\AccountInterface $user
* The current user.
*/
public function __construct(PrivateTempStoreFactory $temp_store_factory, EntityTypeManagerInterface $entity_type_manager, AccountInterface $user) {
public function __construct(PrivateTempStoreFactory $temp_store_factory, EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, AccountInterface $user) {
$this->tempStore = $temp_store_factory->get('entity_flag_confirm');
$this->entityTypeManager = $entity_type_manager;
$this->entityFieldManager = $entity_field_manager;
$this->user = $user;
}
@ -84,6 +95,7 @@ class EntityFlagActionForm extends ConfirmFormBase {
return new static(
$container->get('tempstore.private'),
$container->get('entity_type.manager'),
$container->get('entity_field.manager'),
$container->get('current_user')
);
}
@ -146,11 +158,28 @@ class EntityFlagActionForm extends ConfirmFormBase {
->toString());
}
// Get allowed values for the selected entities.
// We find the intersection of all the allowed values to ensure that
// disallowed flags cannot be assigned.
$allowed_values = [];
$base_field_definitions = $this->entityFieldManager->getBaseFieldDefinitions($entity_type_id);
if (!empty($base_field_definitions['flag'])) {
foreach ($this->entities as $entity) {
$entity_allowed_values = farm_flag_field_allowed_values($base_field_definitions['flag'], $entity);
if (empty($allowed_values)) {
$allowed_values = $entity_allowed_values;
}
else {
$allowed_values = array_intersect_assoc($allowed_values, $entity_allowed_values);
}
}
}
$form['flags'] = [
'#type' => 'select',
'#title' => $this->t('Flag'),
'#description' => $this->t('Select the flags that should be attached to the record(s).'),
'#options' => farm_flag_field_allowed_values(),
'#options' => $allowed_values,
'#multiple' => TRUE,
];