clean install

This commit is contained in:
2024-12-10 15:08:16 +01:00
commit e14eb2d8fd
31193 changed files with 3555714 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
langcode: en
status: true
dependencies:
enforced:
module:
- farm_flag
id: monitor
label: Monitor
entity_types: null

View File

@@ -0,0 +1,9 @@
langcode: en
status: true
dependencies:
enforced:
module:
- farm_flag
id: priority
label: Priority
entity_types: null

View File

@@ -0,0 +1,9 @@
langcode: en
status: true
dependencies:
enforced:
module:
- farm_flag
id: review
label: Needs review
entity_types: null

View File

@@ -0,0 +1,11 @@
langcode: en
status: true
dependencies:
module:
- asset
- farm_flag
id: asset_flag_action
label: 'Flag asset'
type: asset
plugin: entity:flag_action:asset
configuration: { }

View File

@@ -0,0 +1,11 @@
langcode: en
status: true
dependencies:
module:
- farm_flag
- log
id: log_flag_action
label: 'Flag log'
type: log
plugin: entity:flag_action:log
configuration: { }

View File

@@ -0,0 +1,11 @@
langcode: en
status: true
dependencies:
module:
- farm_flag
- plan
id: plan_flag_action
label: 'Flag plan'
type: plan
plugin: entity:flag_action:plan
configuration: { }

View File

@@ -0,0 +1,21 @@
# Schema for flag config entity.
farm_flag.flag.*:
type: config_entity
label: 'Flag'
mapping:
id:
type: string
label: 'ID'
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

@@ -0,0 +1,8 @@
name: farmOS Flags
description: Provides a general purpose record flagging system.
type: module
package: farmOS
core_version_requirement: ^10
dependencies:
- drupal:text
- farm:farm_field

View File

@@ -0,0 +1,3 @@
farm_flag:
default_permissions:
- view flag

View File

@@ -0,0 +1,205 @@
<?php
/**
* @file
* The farmOS Flags module.
*/
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\farm_flag\Form\EntityFlagActionForm;
use Drupal\farm_flag\Routing\EntityFlagActionRouteProvider;
/**
* Implements hook_entity_base_field_info().
*/
function farm_flag_entity_base_field_info(EntityTypeInterface $entity_type) {
$fields = [];
// Add flag field to farmOS entities.
if (in_array($entity_type->id(), ['asset', 'log', 'plan'])) {
$field_info = [
'type' => 'list_string',
'label' => t('Flags'),
'description' => t('Add flags to enable better sorting and filtering of records.'),
'allowed_values_function' => 'farm_flag_field_allowed_values',
'multiple' => TRUE,
'weight' => [
'form' => -75,
'view' => -75,
],
];
$fields['flag'] = \Drupal::service('farm_field.factory')->baseFieldDefinition($field_info);
}
return $fields;
}
/**
* Flag options helper.
*
* @param string|null $entity_type
* The entity type. Returns all flags if NULL.
* @param string[] $bundles
* Array of bundle ids to limit to. An empty array loads all bundles.
* @param bool $intersection
* A flag indicating to return an intersection of the allowed options.
*
* @return array
* Returns an array of flags for use in form select options.
*/
function farm_flag_options(?string $entity_type = NULL, array $bundles = [], bool $intersection = FALSE) {
/** @var \Drupal\farm_flag\Entity\FarmFlagInterface[] $flags */
$flags = \Drupal::entityTypeManager()->getStorage('flag')->loadMultiple();
// If an entity type is provided, begin the filtering process...
if (!empty($entity_type)) {
// If no bundles are specified, load all bundles of the entity type.
if (empty($bundles) && $bundle_entity_type = \Drupal::entityTypeManager()->getDefinition($entity_type)->getBundleEntityType()) {
$bundles = array_keys(\Drupal::entityTypeManager()
->getStorage($bundle_entity_type)
->loadMultiple());
}
// Find only the flags that apply to the entity type and bundles.
$flags = array_filter($flags, function ($flag) use ($entity_type, $bundles, $intersection) {
$flag_entity_types = $flag->getEntityTypes();
// The flag applies if no entity type is specified.
if (empty($flag_entity_types)) {
return TRUE;
}
// Otherwise the flag must specify the entity type.
if (!array_key_exists($entity_type, $flag_entity_types)) {
return FALSE;
}
// The flag applies to the bundle if:
// Case 1: The flag specifies 'all' bundles of the entity type.
$bundle_applies = in_array('all', $flag_entity_types[$entity_type]);
// Case 2: No intersection.
// The flag applies if any of the requested bundles are supported.
$bundle_applies |= !$intersection && !empty(array_intersect($bundles, $flag_entity_types[$entity_type]));
// Case 3: Intersection.
// The flag only applies if all the requested bundles are supported.
$bundle_applies |= $intersection && empty(array_diff($bundles, $flag_entity_types[$entity_type]));
return $bundle_applies;
});
}
// Assemble the options.
$options = [];
foreach ($flags as $id => $flag) {
$options[$id] = $flag->label();
}
return $options;
}
/**
* Allowed values callback function for the flags field.
*
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface $definition
* The field storage definition.
* @param \Drupal\Core\Entity\ContentEntityInterface|null $entity
* The entity being created if applicable.
* @param bool $cacheable
* Boolean indicating if the allowed values can be cached. Defaults to TRUE.
*
* @return array
* Returns an array of allowed values for use in form select options.
*/
function farm_flag_field_allowed_values(FieldStorageDefinitionInterface $definition, ?ContentEntityInterface $entity = NULL, bool &$cacheable = TRUE) {
$entity_type = NULL;
$bundles = [];
if (!empty($entity)) {
$cacheable = FALSE;
$entity_type = $entity->getEntityTypeId();
$bundles = [$entity->bundle()];
}
return farm_flag_options($entity_type, $bundles);
}
/**
* Implements hook_farm_ui_theme_region_items().
*/
function farm_flag_farm_ui_theme_region_items(string $entity_type) {
// Define common asset, log, and plan region items on behalf of core modules.
switch ($entity_type) {
case 'asset':
case 'log':
case 'plan':
return [
'second' => [
'flag',
],
];
default:
return [];
}
}
/**
* Implements hook_theme().
*/
function farm_flag_theme() {
return [
'field__flag' => [
'base hook' => 'field',
],
];
}
/**
* Prepares variables for field--flag templates.
*
* Adds classes to each flag wrapper.
*
* Default template: field--flag.html.twig.
*
* @param array $variables
* An associative array containing:
* - element: An associative array containing render arrays for the list of
* flags.
*/
function template_preprocess_field__flag(array &$variables) {
// Preprocess list_string flag fields.
if ($variables['element']['#field_type'] == 'list_string') {
/** @var \Drupal\Core\Field\FieldItemListInterface $items */
$items = $variables['element']['#items'];
// Add classes to each flag.
foreach ($items as $key => $list_item) {
$classes = ['flag', 'flag--' . $list_item->getString()];
$variables['items'][$key]['attributes']->addClass($classes);
}
}
}
/**
* Implements hook_entity_type_build().
*/
function farm_flag_entity_type_build(array &$entity_types) {
/** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */
// Enable the entity flag action on entity types with a flag field.
foreach (['asset', 'log', 'plan'] as $entity_type) {
if (!empty($entity_types[$entity_type])) {
$route_providers = $entity_types[$entity_type]->getRouteProviderClasses();
$route_providers['flag'] = EntityFlagActionRouteProvider::class;
$entity_types[$entity_type]->setHandlerClass('route_provider', $route_providers);
$entity_types[$entity_type]->setLinkTemplate('flag-action-form', '/' . $entity_type . '/flag');
$entity_types[$entity_type]->setFormClass('flag-action-form', EntityFlagActionForm::class);
}
}
}

View File

@@ -0,0 +1,34 @@
<?php
/**
* @file
* Provides Views data for farm_flag.module.
*/
/**
* Implements hook_views_data_alter().
*/
function farm_flag_views_data_alter(array &$data) {
// Because Drupal core does not provide full Views integration for base fields
// we must manually specify the list_field views filter for the flag field.
// Define the views filter settings.
$flag_filter = [
'id' => 'list_field',
'field_name' => 'flag',
'allow_empty' => TRUE,
];
$tables = [
'asset__flag',
'asset_revision__flag',
'log__flag',
'log_revision__flag',
'plan__flag',
'plan_revision__flag',
];
foreach ($tables as $table) {
if (!empty($data[$table]['flag_value'])) {
$data[$table]['flag_value']['filter'] = $flag_filter;
}
}
}

View File

@@ -0,0 +1,68 @@
<?php
namespace Drupal\farm_flag\Entity;
use Drupal\Core\Config\Entity\ConfigEntityBase;
/**
* Defines the FarmFlag entity.
*
* @ConfigEntityType(
* id = "flag",
* label = @Translation("Flag"),
* label_collection = @Translation("Flags"),
* handlers = {
* "access" = "\Drupal\entity\EntityAccessControlHandler",
* "permission_provider" = "\Drupal\entity\EntityPermissionProvider",
* },
* entity_keys = {
* "id" = "id",
* "label" = "label",
* },
* config_export = {
* "id",
* "label",
* "entity_types",
* },
* )
*
* @ingroup farm
*/
class FarmFlag extends ConfigEntityBase implements FarmFlagInterface {
/**
* The flag ID.
*
* @var string
*/
protected $id;
/**
* The flag label.
*
* @var string
*/
protected $label;
/**
* The entity types and bundles that this flag applies to.
*
* @var array
*/
protected $entity_types;
/**
* {@inheritdoc}
*/
public function getLabel() {
return $this->label;
}
/**
* {@inheritdoc}
*/
public function getEntitytypes() {
return $this->entity_types;
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace Drupal\farm_flag\Entity;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
/**
* Provides an interface for defining FarmFlag config entities.
*
* @ingroup farm
*/
interface FarmFlagInterface extends ConfigEntityInterface {
/**
* Returns the flag label.
*
* @return string
* The flag label.
*/
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

@@ -0,0 +1,267 @@
<?php
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;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
/**
* Provides an entity flag action form.
*
* @see \Drupal\farm_flag\Plugin\Action\EntityFlag
* @see \Drupal\Core\Entity\Form\DeleteMultipleForm
*/
class EntityFlagActionForm extends ConfirmFormBase {
/**
* The tempstore factory.
*
* @var \Drupal\Core\TempStore\SharedTempStore
*/
protected $tempStore;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The entity field manager.
*
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
*/
protected $entityFieldManager;
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $user;
/**
* The entity type.
*
* @var \Drupal\Core\Entity\EntityTypeInterface
*/
protected $entityType;
/**
* The entities to flag.
*
* @var \Drupal\Core\Entity\EntityInterface[]
*/
protected $entities;
/**
* The entity flag field name.
*
* @var string
*/
protected $flagFieldName = 'flag';
/**
* Constructs an EntityFlagActionForm form object.
*
* @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory
* 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, 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;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('tempstore.private'),
$container->get('entity_type.manager'),
$container->get('entity_field.manager'),
$container->get('current_user')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
// Get entity type ID from the route because ::buildForm has not yet been
// called.
$entity_type_id = $this->getRouteMatch()->getParameter('entity_type_id');
return $entity_type_id . '_flag_action_confirm_form';
}
/**
* {@inheritdoc}
*/
public function getQuestion() {
return $this->formatPlural(count($this->entities), 'Are you sure you want to flag this @item?', 'Are you sure you want to flag these @items?', [
'@item' => $this->entityType->getSingularLabel(),
'@items' => $this->entityType->getPluralLabel(),
]);
}
/**
* {@inheritdoc}
*/
public function getCancelUrl() {
if ($this->entityType->hasLinkTemplate('collection')) {
return new Url('entity.' . $this->entityType->id() . '.collection');
}
else {
return new Url('<front>');
}
}
/**
* {@inheritdoc}
*/
public function getDescription() {
return '';
}
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return $this->t('Flag');
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, $entity_type_id = NULL) {
$this->entityType = $this->entityTypeManager->getDefinition($entity_type_id);
$this->entities = $this->tempStore->get($this->user->id() . ':' . $entity_type_id);
if (empty($entity_type_id) || empty($this->entities)) {
return new RedirectResponse($this->getCancelUrl()
->setAbsolute()
->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.
$entity_bundles = array_unique(array_map(function ($entity) {
return $entity->bundle();
}, $this->entities));
$allowed_values = farm_flag_options($entity_type_id, $entity_bundles, TRUE);
$form['flags'] = [
'#type' => 'select',
'#title' => $this->t('Flags'),
'#description' => $this->t('Add flags to enable better sorting and filtering of records.'),
'#options' => $allowed_values,
'#multiple' => TRUE,
];
$form['operation'] = [
'#type' => 'radios',
'#title' => $this->t('Append or replace'),
'#description' => $this->t('Select "Append" if you want to add flags to the records, but keep the existing flags. Select "Replace" if you want to replace existing flags with the ones specified above.'),
'#options' => [
'append' => $this->t('Append'),
'replace' => $this->t('Replace'),
],
'#default_value' => 'append',
'#required' => TRUE,
];
return parent::buildForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
// Filter out entities the user doesn't have access to.
$inaccessible_entities = [];
$accessible_entities = [];
foreach ($this->entities as $entity) {
if (!$entity->access('update', $this->currentUser())) {
$inaccessible_entities[] = $entity;
continue;
}
$accessible_entities[] = $entity;
}
// Update flags on accessible entities.
$total_count = 0;
foreach ($accessible_entities as $entity) {
/** @var \Drupal\Core\Field\FieldItemListInterface $flag_field */
if ($flag_field = $entity->get($this->flagFieldName)) {
// Save existing flags if appending.
$existing_flags = [];
if ($form_state->getValue('operation') === 'append') {
$existing_flags = array_column($flag_field->getValue(), 'value');
}
// Empty the flag field.
$flag_field->setValue([]);
$new_flags = array_unique(array_merge($existing_flags, $form_state->getValue('flags')));
foreach ($new_flags as $flag) {
$flag_field->appendItem($flag);
}
// Validate the entity before saving.
$violations = $entity->validate();
if ($violations->count() > 0) {
$this->messenger()->addWarning(
$this->t('Could not flag <a href=":entity_link">%entity_label</a>: validation failed.',
[
':entity_link' => $entity->toUrl()->setAbsolute()->toString(),
'%entity_label' => $entity->label(),
],
),
);
continue;
}
$entity->save();
$total_count++;
}
}
// Add warning message for inaccessible entities.
if (!empty($inaccessible_entities)) {
$inaccessible_count = count($inaccessible_entities);
$this->messenger()->addWarning($this->formatPlural($inaccessible_count, 'Could not flag @count @item because you do not have the necessary permissions.', 'Could not flag @count @items because you do not have the necessary permissions.', [
'@item' => $this->entityType->getSingularLabel(),
'@items' => $this->entityType->getPluralLabel(),
]));
}
// Add confirmation message.
if (!empty($total_count)) {
$this->messenger()->addStatus($this->formatPlural($total_count, 'Flagged @count @item.', 'Flagged @count @items', [
'@item' => $this->entityType->getSingularLabel(),
'@items' => $this->entityType->getPluralLabel(),
]));
}
$this->tempStore->delete($this->currentUser()->id() . ':' . $this->entityType->id());
$form_state->setRedirectUrl($this->getCancelUrl());
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace Drupal\farm_flag\Plugin\Action\Derivative;
use Drupal\Core\Action\Plugin\Action\Derivative\EntityActionDeriverBase;
use Drupal\Core\Entity\EntityTypeInterface;
/**
* Provides an action deriver that finds entity types with a flag form.
*
* @see \Drupal\farm_flag\Plugin\Action\EntityFlag
*/
class EntityFlagDeriver extends EntityActionDeriverBase {
/**
* {@inheritdoc}
*/
public function getDerivativeDefinitions($base_plugin_definition) {
if (empty($this->derivatives)) {
$definitions = [];
foreach ($this->getApplicableEntityTypes() as $entity_type_id => $entity_type) {
$definition = $base_plugin_definition;
$definition['type'] = $entity_type_id;
$definition['label'] = $this->t('Flag @entity_type', ['@entity_type' => $entity_type->getSingularLabel()]);
$definition['confirm_form_route_name'] = 'entity.' . $entity_type->id() . '.flag_form';
$definitions[$entity_type_id] = $definition;
}
$this->derivatives = $definitions;
}
return $this->derivatives;
}
/**
* {@inheritdoc}
*/
protected function isApplicable(EntityTypeInterface $entity_type) {
return $entity_type->hasLinkTemplate('flag-action-form');
}
}

View File

@@ -0,0 +1,98 @@
<?php
namespace Drupal\farm_flag\Plugin\Action;
use Drupal\Core\Action\Plugin\Action\EntityActionBase;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Redirects to a form to add flags to the entity.
*
* @Action(
* id = "entity:flag_action",
* action_label = @Translation("Flag an entity"),
* deriver = "Drupal\farm_flag\Plugin\Action\Derivative\EntityFlagDeriver",
* )
*/
class EntityFlag extends EntityActionBase {
/**
* The tempstore object.
*
* @var \Drupal\Core\TempStore\SharedTempStore
*/
protected $tempStore;
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $currentUser;
/**
* Constructs a new EntityFlag object.
*
* @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\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory
* The tempstore factory.
* @param \Drupal\Core\Session\AccountInterface $current_user
* Current user.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, PrivateTempStoreFactory $temp_store_factory, AccountInterface $current_user) {
$this->currentUser = $current_user;
$this->tempStore = $temp_store_factory->get('entity_flag_confirm');
parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_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'),
$container->get('tempstore.private'),
$container->get('current_user')
);
}
/**
* {@inheritdoc}
*/
public function executeMultiple(array $entities) {
/** @var \Drupal\Core\Entity\EntityInterface[] $entities */
$this->tempStore->set($this->currentUser->id() . ':' . $this->getPluginDefinition()['type'], $entities);
}
/**
* {@inheritdoc}
*/
public function execute($object = NULL) {
$this->executeMultiple([$object]);
}
/**
* {@inheritdoc}
*/
public function access($object, ?AccountInterface $account = NULL, $return_as_object = FALSE) {
$result = $object->get('flag')->access('edit', $account, TRUE)
->andIf($object->access('update', $account, TRUE));
return $return_as_object ? $result : $result->isAllowed();
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace Drupal\farm_flag\Routing;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\Routing\EntityRouteProviderInterface;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
/**
* Provides routes for the entity flag action.
*/
class EntityFlagActionRouteProvider implements EntityRouteProviderInterface {
/**
* {@inheritdoc}
*/
public function getRoutes(EntityTypeInterface $entity_type) {
$collection = new RouteCollection();
$entity_type_id = $entity_type->id();
if ($flag_route = $this->getEntityFlagFormRoute($entity_type)) {
$collection->add("entity.$entity_type_id.flag_form", $flag_route);
}
return $collection;
}
/**
* Gets the entity flag form route.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type.
*
* @return \Symfony\Component\Routing\Route|null
* The generated route, if available.
*/
protected function getEntityFlagFormRoute(EntityTypeInterface $entity_type) {
if ($entity_type->hasLinkTemplate('flag-action-form')) {
$route = new Route($entity_type->getLinkTemplate('flag-action-form'));
$route->setDefault('_form', $entity_type->getFormClass('flag-action-form'));
$route->setDefault('entity_type_id', $entity_type->id());
$route->setRequirement('_user_is_logged_in', 'TRUE');
return $route;
}
}
}

View File

@@ -0,0 +1 @@
{% extends 'field.html.twig' %}

View File

@@ -0,0 +1,125 @@
<?php
namespace Drupal\Tests\farm_flag\Kernel;
use Drupal\KernelTests\KernelTestBase;
use Drupal\farm_flag\Entity\FarmFlag;
/**
* Tests for farm_flag logic.
*
* @group farm_flag
*/
class FlagTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'farm_field',
'farm_flag',
'log',
'asset',
'state_machine',
];
/**
* Test farm flag options logic.
*/
public function testFarmFlagOptions() {
// Create a general flag that applies to all entity types.
$general_flag = FarmFlag::create([
'id' => 'general',
'label' => 'General',
'entity_types' => NULL,
]);
$general_flag->save();
// Create bundles and flags for testing.
$test_entity_types = [
'log' => ['activity', 'input', 'observation'],
'asset' => [],
];
foreach ($test_entity_types as $entity_type => $bundles) {
$entity_type_id = $entity_type . '_type';
// Create a flag for all bundles of the entity type.
$flag = FarmFlag::create([
'id' => $entity_type . '_flag',
'entity_types' => [
$entity_type => ['all'],
],
]);
$flag->save();
// Create bundles and a flag for each bundle.
foreach ($bundles as $bundle_id) {
// Create the bundle.
$bundle = \Drupal::entityTypeManager()->getStorage($entity_type_id)->create([
'id' => $bundle_id,
'workflow' => $entity_type . '_default',
]);
$bundle->save();
// Create a flag that only applies for the bundle.
$flag = FarmFlag::create([
'id' => $bundle_id . '_flag',
'entity_types' => [
$entity_type => [$bundle_id],
],
]);
$flag->save();
}
}
// Create a special flag that only applies to activity logs.
$flag = FarmFlag::create([
'id' => 'special_flag',
'entity_types' => [
'log' => ['activity'],
],
]);
$flag->save();
// Load all flag options.
$all_flags = \Drupal::entityTypeManager()->getStorage('flag')->loadMultiple();
$all_flag_ids = array_keys($all_flags);
// 1. With default parameters all flag options are returned.
$expected_flag_ids = array_keys(farm_flag_options());
$this->assertEmpty(array_diff($expected_flag_ids, $all_flag_ids), 'All flag options are returned.');
// 2. Flags applying to any asset type are returned.
$flag_ids = array_keys(farm_flag_options('asset'));
$expected_flag_ids = ['general', 'asset_flag'];
$this->assertEmpty(array_diff($expected_flag_ids, $flag_ids));
// 3. Flags applying to any log type are returned.
$flag_ids = array_keys(farm_flag_options('log'));
$expected_flag_ids = ['general', 'log_flag', 'special_flag', 'activity_flag', 'input_flag', 'observation_flag'];
$this->assertEmpty(array_diff($expected_flag_ids, $flag_ids));
// 4. Flags applying to every log type are returned.
$flag_ids = array_keys(farm_flag_options('log', [], TRUE));
$expected_flag_ids = ['general', 'log_flag'];
$this->assertEmpty(array_diff($expected_flag_ids, $flag_ids));
// 5. Flags applying to either activity or input log types are returned.
$flag_ids = array_keys(farm_flag_options('log', ['activity', 'input']));
$expected_flag_ids = ['general', 'log_flag', 'special_flag', 'activity_flag', 'input_flag'];
$this->assertEmpty(array_diff($expected_flag_ids, $flag_ids));
// 6. Flags applying to both activity and input log types are returned.
$flag_ids = array_keys(farm_flag_options('log', ['activity', 'input'], TRUE));
$expected_flag_ids = ['general', 'log_flag'];
$this->assertEmpty(array_diff($expected_flag_ids, $flag_ids));
// 7. Flags applying to only the activity log types are returned.
$flag_ids = array_keys(farm_flag_options('log', ['activity'], TRUE));
$expected_flag_ids = ['general', 'log_flag', 'special_flag', 'activity_flag'];
$this->assertEmpty(array_diff($expected_flag_ids, $flag_ids));
}
}