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,11 @@
langcode: en
status: true
dependencies:
module:
- asset
- farm_owner
id: asset_assign_action
label: 'Assign owners'
type: asset
plugin: asset_assign_action
configuration: { }

View File

@@ -0,0 +1,11 @@
langcode: en
status: true
dependencies:
module:
- farm_owner
- log
id: log_assign_action
label: 'Assign owners'
type: log
plugin: 'log_assign_action'
configuration: { }

View File

@@ -0,0 +1,7 @@
# Schema for actions.
action.configuration.asset_assign_action:
type: action_configuration_default
label: 'Configuration for the asset assign action'
action.configuration.log_assign_action:
type: action_configuration_default
label: 'Configuration for the log assign action'

View File

@@ -0,0 +1,10 @@
name: farmOS Owner
description: Provides an Owner field for farmOS records.
type: module
package: farmOS
core_version_requirement: ^10
dependencies:
- drupal:user
- farm:farm_field
- log:log
- asset:asset

View File

@@ -0,0 +1,34 @@
<?php
/**
* @file
* Contains farm_owner.module.
*/
use Drupal\Core\Entity\EntityTypeInterface;
/**
* Implements hook_entity_base_field_info().
*/
function farm_owner_entity_base_field_info(EntityTypeInterface $entity_type) {
$fields = [];
// Add owner field to logs and assets.
if (in_array($entity_type->id(), ['asset', 'log'])) {
$field_info = [
'type' => 'entity_reference',
'label' => t('Owners'),
'description' => t('Assign ownership to one or more users.'),
'target_type' => 'user',
'multiple' => TRUE,
'weight' => [
'form' => -70,
'view' => -70,
],
];
$fields['owner'] = \Drupal::service('farm_field.factory')->baseFieldDefinition($field_info);
}
return $fields;
}

View File

@@ -0,0 +1,47 @@
<?php
/**
* @file
* Updates farm_owner module.
*/
use Drupal\system\Entity\Action;
/**
* Add 'owner' field to assets.
*/
function farm_owner_post_update_add_asset_owner(&$sandbox = NULL) {
$entity_type = 'asset';
$module_name = 'farm_owner';
$field_name = 'owner';
$field_info = [
'type' => 'entity_reference',
'label' => t('Owner'),
'description' => t('Optionally specify an owner for this asset.'),
'target_type' => 'user',
'multiple' => TRUE,
'weight' => [
'form' => -70,
'view' => -70,
],
];
$field_definition = \Drupal::service('farm_field.factory')->baseFieldDefinition($field_info);
\Drupal::entityDefinitionUpdateManager()
->installFieldStorageDefinition($field_name, $entity_type, $module_name, $field_definition);
// Update the label of the log_assign_action config.
$action = Action::load('log_assign_action');
$action->set('label', t('Assign owners'));
$action->save();
// Create action for assigning assets to users.
$action = Action::create([
'id' => 'asset_assign_action',
'label' => t('Assign owners'),
'type' => 'asset',
'plugin' => 'asset_assign_action',
'configuration' => [],
]);
$action->save();
}

View File

@@ -0,0 +1,14 @@
farm_owner.log_assign_action_form:
path: '/log/assign'
defaults:
_form: 'Drupal\farm_owner\Form\AssignActionForm'
entity_type: 'log'
requirements:
_user_is_logged_in: 'TRUE'
farm_owner.asset_assign_action_form:
path: '/asset/assign'
defaults:
_form: 'Drupal\farm_owner\Form\AssignActionForm'
entity_type: 'asset'
requirements:
_user_is_logged_in: 'TRUE'

View File

@@ -0,0 +1,7 @@
services:
farm_owner.log_event_subscriber:
class: Drupal\farm_owner\EventSubscriber\LogEventSubscriber
arguments:
[ '@current_user' ]
tags:
- { name: 'event_subscriber' }

View File

@@ -0,0 +1,69 @@
<?php
namespace Drupal\farm_owner\EventSubscriber;
use Drupal\Core\Session\AccountInterface;
use Drupal\log\Event\LogEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Perform actions on log presave.
*/
class LogEventSubscriber implements EventSubscriberInterface {
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $currentUser;
/**
* LogEventSubscriber constructor.
*
* @param \Drupal\Core\Session\AccountInterface $current_user
* The current user.
*/
public function __construct(AccountInterface $current_user) {
$this->currentUser = $current_user;
}
/**
* {@inheritdoc}
*
* @return array
* The event names to listen for, and the methods that should be executed.
*/
public static function getSubscribedEvents() {
return [
LogEvent::PRESAVE => 'setLogOwner',
];
}
/**
* Set the log owner to the current user, if an owner isn't specified.
*
* @param \Drupal\log\Event\LogEvent $event
* The log event.
*/
public function setLogOwner(LogEvent $event): void {
// Get the log entity from the event.
$log = $event->log;
// If there is no currently logged in user, bail.
if (empty($this->currentUser->id())) {
return;
}
// If the log already has an owner, bail.
$owners = $log->get('owner')->referencedEntities();
if (!empty($owners)) {
return;
}
// Add the current user to the log's owners.
$log->owner[] = ['target_id' => $this->currentUser->id()];
}
}

View File

@@ -0,0 +1,271 @@
<?php
namespace Drupal\farm_owner\Form;
use Drupal\Component\Plugin\Exception\PluginException;
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 Drupal\farm_role\ManagedRolePermissionsManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
/**
* Provides an assign confirmation form.
*/
class AssignActionForm extends ConfirmFormBase {
/**
* The tempstore factory.
*
* @var \Drupal\Core\TempStore\SharedTempStore
*/
protected $tempStore;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The managed role permissions manager.
*
* @var \Drupal\farm_role\ManagedRolePermissionsManagerInterface
*/
protected $managedRolePermissionsManager;
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $user;
/**
* The entity type.
*
* @var \Drupal\Core\Entity\EntityTypeInterface
*/
protected $entityType;
/**
* The entities to assign.
*
* @var \Drupal\Core\Entity\EntityInterface[]
*/
protected $entities;
/**
* Constructs an AssignActionForm 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\farm_role\ManagedRolePermissionsManagerInterface $managed_role_permissions_manager
* The managed role permissions manager.
* @param \Drupal\Core\Session\AccountInterface $user
* The current user.
*/
public function __construct(PrivateTempStoreFactory $temp_store_factory, EntityTypeManagerInterface $entity_type_manager, ManagedRolePermissionsManagerInterface $managed_role_permissions_manager, AccountInterface $user) {
$this->tempStore = $temp_store_factory->get('entity_assign_confirm');
$this->entityTypeManager = $entity_type_manager;
$this->managedRolePermissionsManager = $managed_role_permissions_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('plugin.manager.managed_role_permissions'),
$container->get('current_user')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'assign_action_confirm_form';
}
/**
* {@inheritdoc}
*/
public function getQuestion() {
return $this->formatPlural(count($this->entities), 'Are you sure you want to update assignment of this @item?', 'Are you sure you want to update assignment of 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('Assign');
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, ?string $entity_type = NULL) {
// Only allow asset and log entities.
if (!in_array($entity_type, ['asset', 'log'])) {
throw new PluginException('Unsupported entity type given when building form to assign entity');
}
// Load the entity type definition.
$this->entityType = $this->entityTypeManager->getDefinition($entity_type);
// Load saved entities.
$this->entities = $this->tempStore->get($this->user->id());
// If there are no entities, or if the entity type definition didn't load,
// redirect the user to the cancel URL.
if (empty($this->entityType) || empty($this->entities)) {
return new RedirectResponse($this->getCancelUrl()
->setAbsolute()
->toString());
}
// Load active users.
$active_users = $this->entityTypeManager->getStorage('user')->loadByProperties([
'status' => TRUE,
]);
// Build options for form select.
$user_options = array_map(function ($user) {
return $user->label();
}, $active_users);
$form['users'] = [
'#type' => 'select',
'#title' => $this->t('Owners'),
'#description' => $this->t('Assign ownership to one or more users.'),
'#options' => $user_options,
'#multiple' => TRUE,
];
$form['operation'] = [
'#type' => 'radios',
'#title' => $this->t('Append or replace'),
'#description' => $this->t('Select "Append" if you want to add owners, but keep the existing assignments. Select "Replace" if you want to replace existing assignments with the people 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 user assignment on accessible entities.
$total_count = 0;
foreach ($accessible_entities as $entity) {
/** @var \Drupal\Core\Field\FieldItemListInterface $owner_field */
if ($owner_field = $entity->get('owner')) {
// Save existing users if appending.
$existing_owners = [];
if ($form_state->getValue('operation') === 'append') {
$existing_owners = array_column($owner_field->getValue(), 'target_id');
}
// Empty the owner field.
$owner_field->setValue([]);
// Build list of owners.
$new_owners = array_unique(array_merge($existing_owners, $form_state->getValue('users')));
foreach ($new_owners as $owner) {
$owner_field->appendItem($owner);
}
// Validate the entity before saving.
$violations = $entity->validate();
if ($violations->count() > 0) {
$this->messenger()->addWarning(
$this->t('Could not assign <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 update assignment of @count @item because you do not have the necessary permissions.', 'Could not update assignment of @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, 'Updated assignment of @count @item.', 'Updated assignment of @count @items', [
'@item' => $this->entityType->getSingularLabel(),
'@items' => $this->entityType->getPluralLabel(),
]));
}
$this->tempStore->delete($this->currentUser()->id());
$form_state->setRedirectUrl($this->getCancelUrl());
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace Drupal\farm_owner\Plugin\Action;
/**
* Action that assigns users to assets.
*
* @Action(
* id = "asset_assign_action",
* label = @Translation("Assign assets to users."),
* type = "asset",
* confirm_form_route_name = "farm_owner.asset_assign_action_form"
* )
*/
class AssetAssign extends AssignBase {
}

View File

@@ -0,0 +1,89 @@
<?php
namespace Drupal\farm_owner\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;
/**
* Action that assigns users to entities.
*/
abstract class AssignBase 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 AssignBase 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_assign_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(), $entities);
}
/**
* {@inheritdoc}
*/
public function execute($object = NULL) {
$this->executeMultiple([$object]);
}
/**
* {@inheritdoc}
*/
public function access($object, ?AccountInterface $account = NULL, $return_as_object = FALSE) {
return $object->access('update', $account, $return_as_object);
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace Drupal\farm_owner\Plugin\Action;
/**
* Action that assigns users to logs.
*
* @Action(
* id = "log_assign_action",
* label = @Translation("Assign logs to users."),
* type = "log",
* confirm_form_route_name = "farm_owner.log_assign_action_form"
* )
*/
class LogAssign extends AssignBase {
}

View File

@@ -0,0 +1,8 @@
langcode: en
status: true
id: test
label: Test
description: ''
name_pattern: 'Test log [log:id]'
workflow: log_default
new_revision: true

View File

@@ -0,0 +1,7 @@
name: farmOS Owner Tests
description: 'Support module for owner testing.'
type: module
package: Testing
core_version_requirement: ^10
dependencies:
- farm:farm_owner

View File

@@ -0,0 +1,81 @@
<?php
namespace Drupal\Tests\farm_owner\Kernel;
use Drupal\KernelTests\KernelTestBase;
use Drupal\Tests\user\Traits\UserCreationTrait;
use Drupal\log\Entity\Log;
/**
* Tests for farmOS log owner logic.
*
* @group farm
*/
class LogOwnerTest extends KernelTestBase {
use UserCreationTrait;
/**
* {@inheritdoc}
*/
protected static $modules = [
'log',
'farm_field',
'farm_owner',
'farm_owner_test',
'state_machine',
'system',
'user',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->installSchema('system', 'sequences');
$this->installEntitySchema('log');
$this->installEntitySchema('user');
$this->installConfig(['farm_owner_test']);
}
/**
* Test that saving a log sets its owner.
*/
public function testLogOwner() {
// Create two users.
$user1 = $this->createUser();
$user2 = $this->createUser();
// Test that a new log does not have an owner, if no one is logged in.
$log = Log::create([
'type' => 'test',
]);
$log->save();
$this->assertEmpty($log->get('owner')->referencedEntities());
// Log in the first user.
$this->setCurrentUser($user1);
// Test that creating a log without any owners results in the current user
// becoming an owner.
$log = Log::create([
'type' => 'test',
]);
$log->save();
$this->assertNotEmpty($log->get('owner')->referencedEntities());
$this->assertEquals($user1->id(), $log->get('owner')->referencedEntities()[0]->id());
// Test that creating a log with an owner does not override that owner.
$log = Log::create([
'type' => 'test',
'owner' => [['target_id' => $user2->id()]],
]);
$log->save();
$this->assertNotEmpty($log->get('owner')->referencedEntities());
$this->assertEquals(1, count($log->get('owner')->referencedEntities()));
$this->assertEquals($user2->id(), $log->get('owner')->referencedEntities()[0]->id());
}
}