Refactor Move and Group actions into quick forms #736

This commit is contained in:
Michael Stenta 2023-10-25 15:41:24 -04:00
commit 40d9f2a7d0
28 changed files with 327 additions and 828 deletions

View File

@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Changed
- [Refactor Move and Group actions into quick forms #736](https://github.com/farmOS/farmOS/pull/736)
### Fixed
- [Fix planting quick form creating empty quantities #737](https://github.com/farmOS/farmOS/pull/737)

View File

@ -456,10 +456,12 @@ selected entities can then be used in the quick form code in various ways.
### Providing a quick form action
To add a quick form action, two additional files are added to the module:
To add a quick form action, three additional files are added to the module:
1. a PHP class in `src/Plugin/Action` that extends from `QuickFormActionBase`
2. an action config entity in `config/install/system.action.*.yml`
3. a `config/schema/[mymodule].schema.yml` file that describes action schema
(see example below).
For example, an action that redirects to the "Harvest" quick form defined above
for prepopulating the "Asset" field would be provided as follows:
@ -511,6 +513,15 @@ plugin: harvest
configuration: { }
```
`/config/schema/farm_quick_harvest.schema.yml`:
```yml
# Schema for actions.
action.configuration.harvest:
type: action_configuration_default
label: 'Configuration for the harvest action'
```
Note that config entities are only created when the module is installed. In
order to add a config entity to a module that is already installed, an update
hook must be used to manually create the config entity.

View File

@ -1,12 +0,0 @@
langcode: en
status: true
dependencies:
module:
- asset
- farm_group
- farm_observation
id: asset_group_action
label: 'Group asset'
type: asset
plugin: 'asset_group_action'
configuration: { }

View File

@ -1,3 +0,0 @@
action.configuration.asset_group_action:
type: action_configuration_default
label: 'Configuration for the asset group action'

View File

@ -0,0 +1,16 @@
<?php
/**
* @file
* Post update hooks for the farm_group module.
*/
use Drupal\system\Entity\Action;
/**
* Uninstall system.action.asset_group_action.
*/
function farm_group_post_update_uninstall_asset_group_action(&$sandbox) {
$config = Action::load('asset_group_action');
$config->delete();
}

View File

@ -1,259 +0,0 @@
<?php
namespace Drupal\farm_group\Form;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Markup;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Drupal\Core\Url;
use Drupal\log\Entity\Log;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
/**
* Provides an asset group confirmation form.
*/
class AssetGroupActionForm extends ConfirmFormBase {
/**
* The tempstore factory.
*
* @var \Drupal\Core\TempStore\SharedTempStore
*/
protected $tempStore;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $user;
/**
* The entity type.
*
* @var \Drupal\Core\Entity\EntityTypeInterface
*/
protected $entityType;
/**
* The assets to group.
*
* @var \Drupal\Core\Entity\EntityInterface[]
*/
protected $entities;
/**
* Constructs an AssetGroupActionForm 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\Session\AccountInterface $user
* The current user.
*/
public function __construct(PrivateTempStoreFactory $temp_store_factory, EntityTypeManagerInterface $entity_type_manager, AccountInterface $user) {
$this->tempStore = $temp_store_factory->get('asset_group_confirm');
$this->entityTypeManager = $entity_type_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('current_user')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'asset_group_action_confirm_form';
}
/**
* {@inheritdoc}
*/
public function getQuestion() {
return $this->formatPlural(count($this->entities), 'Are you sure you want to group this @item?', 'Are you sure you want to group 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('Group');
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$this->entityType = $this->entityTypeManager->getDefinition('asset');
$this->entities = $this->tempStore->get($this->user->id());
if (empty($this->entityType) || empty($this->entities)) {
return new RedirectResponse($this->getCancelUrl()
->setAbsolute()
->toString());
}
$form['date'] = [
'#type' => 'datetime',
'#title' => $this->t('Date'),
'#default_value' => new DrupalDateTime('midnight', $this->user->getTimeZone()),
'#required' => TRUE,
];
$form['group'] = [
'#type' => 'entity_autocomplete',
'#title' => $this->t('Group'),
'#description' => $this->t('The groups to assign the asset to. Leave blank to un-assign an asset from groups.'),
'#target_type' => 'asset',
'#selection_handler' => 'views',
'#selection_settings' => [
'view' => [
'view_name' => 'farm_group_reference',
'display_name' => 'entity_reference',
],
'match_operator' => 'CONTAINS',
'match_limit' => 10,
],
'#tags' => TRUE,
'#validate_reference' => FALSE,
'#maxlength' => 1024,
];
$form['done'] = [
'#type' => 'checkbox',
'#title' => $this->t('This membership change has taken place (mark the log as done)'),
];
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;
}
// Create an observation log to group the assets.
if ($form_state->getValue('confirm') && !empty($accessible_entities)) {
// Load group assets.
$groups = [];
$group_ids = array_column($form_state->getValue('group', []) ?? [], 'target_id');
if (!empty($group_ids)) {
$groups = $this->entityTypeManager->getStorage('asset')->loadMultiple($group_ids);
}
$done = (bool) $form_state->getValue('done', FALSE);
// Generate a name for the log.
// @phpstan-ignore-next-line
$asset_names = farm_log_asset_names_summary($accessible_entities);
// @phpstan-ignore-next-line
$group_names = farm_log_asset_names_summary($groups);
$log_name = $this->t('Clear group membership of @assets', ['@assets' => Markup::create($asset_names)]);
if (!empty($group_names)) {
$log_name = $this->t('Group @assets into @groups', ['@assets' => Markup::create($asset_names), '@groups' => Markup::create($group_names)]);
}
// Create the log.
$log = Log::create([
'name' => $log_name,
'type' => 'observation',
'timestamp' => $form_state->getValue('date')->getTimestamp(),
'asset' => $accessible_entities,
'is_group_assignment' => TRUE,
'group' => $groups,
]);
// Mark as done.
if ($done !== FALSE) {
$log->get('status')->first()->applyTransitionById('done');
}
// Validate the log before saving.
$violations = $log->validate();
if ($violations->count() > 0) {
$this->messenger()->addWarning(
$this->t('Could not group assets: @bundle @entity_type validation failed.',
[
'@bundle' => $log->getBundleLabel(),
'@entity_type' => $log->getEntityType()->getSingularLabel(),
],
),
);
$this->tempStore->delete($this->currentUser()->id());
$form_state->setRedirectUrl($this->getCancelUrl());
return;
}
$log->save();
$this->messenger()->addMessage($this->t('Log created: <a href=":uri">%log_label</a>', [':uri' => $log->toUrl()->toString(), '%log_label' => $log->label()]));
}
// Add warning message for inaccessible entities.
if (!empty($inaccessible_entities)) {
$inaccessible_count = count($inaccessible_entities);
$this->messenger()->addWarning($this->formatPlural($inaccessible_count, 'Could not group @count @item because you do not have the necessary permissions.', 'Could not group @count @items because you do not have the necessary permissions.', [
'@item' => $this->entityType->getSingularLabel(),
'@items' => $this->entityType->getPluralLabel(),
]));
}
$this->tempStore->delete($this->currentUser()->id());
$form_state->setRedirectUrl($this->getCancelUrl());
}
}

View File

@ -1,106 +0,0 @@
<?php
namespace Drupal\farm_group\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 groups assets with an observation log.
*
* @Action(
* id = "asset_group_action",
* label = @Translation("Assign assets to a group with an observation log."),
* type = "asset",
* confirm_form_route_name = "farm_group.asset_group_action_form"
* )
*/
class AssetGroup 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 AssetGroup 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('asset_group_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 calculateDependencies() {
$dependencies = parent::calculateDependencies();
// Add dependency on farm_observation for observation logs.
$dependencies['module'][] = 'farm_observation';
return $dependencies;
}
/**
* {@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

@ -1,12 +0,0 @@
langcode: en
status: true
dependencies:
module:
- asset
- farm_activity
- farm_location
id: asset_move_action
label: 'Move asset'
type: asset
plugin: 'asset_move_action'
configuration: { }

View File

@ -18,8 +18,3 @@ log.type.*.third_party.farm_location:
is_movement:
type: boolean
label: 'Is a movement'
# Schema for actions.
action.configuration.asset_move_action:
type: action_configuration_default
label: 'Configuration for the asset move action'

View File

@ -29,7 +29,6 @@ function farm_location_asset_base_fields() {
'settings' => [
'link' => TRUE,
'render_without_location' => TRUE,
'move_asset_button' => TRUE,
],
'weight' => 95,
],

View File

@ -0,0 +1,16 @@
<?php
/**
* @file
* Post update hooks for the farm_location module.
*/
use Drupal\system\Entity\Action;
/**
* Uninstall system.action.asset_move_action.
*/
function farm_location_post_update_uninstall_asset_move_action(&$sandbox) {
$config = Action::load('asset_move_action');
$config->delete();
}

View File

@ -1,6 +0,0 @@
farm_location.asset_move_action_form:
path: '/asset/move'
defaults:
_form: 'Drupal\farm_location\Form\AssetMoveActionForm'
requirements:
_user_is_logged_in: 'TRUE'

View File

@ -1,289 +0,0 @@
<?php
namespace Drupal\farm_location\Form;
use Drupal\asset\Entity\AssetInterface;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Markup;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Drupal\Core\Url;
use Drupal\log\Entity\Log;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
/**
* Provides an asset move confirmation form.
*/
class AssetMoveActionForm extends ConfirmFormBase {
/**
* The tempstore factory.
*
* @var \Drupal\Core\TempStore\SharedTempStore
*/
protected $tempStore;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $user;
/**
* The entity type.
*
* @var \Drupal\Core\Entity\EntityTypeInterface
*/
protected $entityType;
/**
* The assets to move.
*
* @var \Drupal\Core\Entity\EntityInterface[]
*/
protected $entities;
/**
* The current Request object.
*
* @var \Symfony\Component\HttpFoundation\Request
*/
protected $request;
/**
* Constructs an AssetMoveActionForm 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\Session\AccountInterface $user
* The current user.
* @param \Symfony\Component\HttpFoundation\Request $request
* The current Request object.
*/
public function __construct(PrivateTempStoreFactory $temp_store_factory, EntityTypeManagerInterface $entity_type_manager, AccountInterface $user, Request $request) {
$this->tempStore = $temp_store_factory->get('asset_move_confirm');
$this->entityTypeManager = $entity_type_manager;
$this->user = $user;
$this->request = $request;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('tempstore.private'),
$container->get('entity_type.manager'),
$container->get('current_user'),
$container->get('request_stack')->getCurrentRequest(),
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'asset_move_action_confirm_form';
}
/**
* {@inheritdoc}
*/
public function getQuestion() {
return $this->formatPlural(count($this->entities), 'Are you sure you want to move this @item?', 'Are you sure you want to move 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('Move');
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
// Check if asset IDs were provided in the asset query param.
if ($asset_ids = $this->request->get('asset')) {
// Wrap in an array, if necessary.
if (!is_array($asset_ids)) {
$asset_ids = [$asset_ids];
}
// Add each asset the user has view access to.
$this->entities = array_filter($this->entityTypeManager->getStorage('asset')->loadMultiple($asset_ids), function (AssetInterface $asset) {
return $asset->access('view', $this->user);
});
}
// Else load entities from the tempStore state.
else {
$this->entities = $this->tempStore->get($this->user->id());
}
$this->entityType = $this->entityTypeManager->getDefinition('asset');
if (empty($this->entityType) || empty($this->entities)) {
return new RedirectResponse($this->getCancelUrl()
->setAbsolute()
->toString());
}
$form['date'] = [
'#type' => 'datetime',
'#title' => $this->t('Date'),
'#default_value' => new DrupalDateTime('midnight', $this->user->getTimeZone()),
'#required' => TRUE,
];
$form['location'] = [
'#type' => 'entity_autocomplete',
'#title' => $this->t('Location'),
'#target_type' => 'asset',
'#selection_handler' => 'views',
'#selection_settings' => [
'view' => [
'view_name' => 'farm_location_reference',
'display_name' => 'entity_reference',
],
'match_operator' => 'CONTAINS',
'match_limit' => 10,
],
'#tags' => TRUE,
'#validate_reference' => FALSE,
'#maxlength' => 1024,
];
$form['done'] = [
'#type' => 'checkbox',
'#title' => $this->t('This movement has taken place (mark the log as done)'),
];
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;
}
// Create an activity log to move the assets.
if ($form_state->getValue('confirm') && !empty($accessible_entities)) {
// Load location assets.
$locations = [];
$location_ids = array_column($form_state->getValue('location', []) ?? [], 'target_id');
if (!empty($location_ids)) {
$locations = $this->entityTypeManager->getStorage('asset')->loadMultiple($location_ids);
}
$done = (bool) $form_state->getValue('done', FALSE);
// Generate a name for the log.
// @phpstan-ignore-next-line
$asset_names = farm_log_asset_names_summary($accessible_entities);
// @phpstan-ignore-next-line
$location_names = farm_log_asset_names_summary($locations);
$log_name = $this->t('Clear location of @assets', ['@assets' => Markup::create($asset_names)]);
if (!empty($location_names)) {
$log_name = $this->t('Move @assets to @locations', ['@assets' => Markup::create($asset_names), '@locations' => Markup::create($location_names)]);
}
// Create the log.
$log = Log::create([
'name' => $log_name,
'type' => 'activity',
'timestamp' => $form_state->getValue('date')->getTimestamp(),
'asset' => $accessible_entities,
'is_movement' => TRUE,
'location' => $locations,
]);
// Mark as done.
if ($done !== FALSE) {
$log->get('status')->first()->applyTransitionById('done');
}
// Validate the log before saving.
$violations = $log->validate();
if ($violations->count() > 0) {
$this->messenger()->addWarning(
$this->t('Could not move assets: @bundle @entity_type validation failed.',
[
'@bundle' => $log->getBundleLabel(),
'@entity_type' => $log->getEntityType()->getSingularLabel(),
],
),
);
$this->tempStore->delete($this->currentUser()->id());
$form_state->setRedirectUrl($this->getCancelUrl());
return;
}
$log->save();
$this->messenger()->addMessage($this->t('Log created: <a href=":uri">%log_label</a>', [':uri' => $log->toUrl()->toString(), '%log_label' => $log->label()]));
}
// Add warning message for inaccessible entities.
if (!empty($inaccessible_entities)) {
$inaccessible_count = count($inaccessible_entities);
$this->messenger()->addWarning($this->formatPlural($inaccessible_count, 'Could not move @count @item because you do not have the necessary permissions.', 'Could not move @count @items because you do not have the necessary permissions.', [
'@item' => $this->entityType->getSingularLabel(),
'@items' => $this->entityType->getPluralLabel(),
]));
}
$this->tempStore->delete($this->currentUser()->id());
$form_state->setRedirectUrl($this->getCancelUrl());
}
}

View File

@ -1,106 +0,0 @@
<?php
namespace Drupal\farm_location\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 moves assets with an activity log.
*
* @Action(
* id = "asset_move_action",
* label = @Translation("Move assets with an activity log."),
* type = "asset",
* confirm_form_route_name = "farm_location.asset_move_action_form"
* )
*/
class AssetMove 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 AssetMove 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('asset_move_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 calculateDependencies() {
$dependencies = parent::calculateDependencies();
// Add dependency on farm_activity for activity logs.
$dependencies['module'][] = 'farm_activity';
return $dependencies;
}
/**
* {@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

@ -5,7 +5,6 @@ namespace Drupal\farm_location\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\Plugin\Field\FieldFormatter\EntityReferenceLabelFormatter;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
/**
* Field formatter for the current location asset field.
@ -27,7 +26,6 @@ class AssetCurrentLocationFormatter extends EntityReferenceLabelFormatter {
public static function defaultSettings() {
return [
'render_without_location' => FALSE,
'move_asset_button' => FALSE,
] + parent::defaultSettings();
}
@ -42,12 +40,6 @@ class AssetCurrentLocationFormatter extends EntityReferenceLabelFormatter {
'#type' => 'checkbox',
'#default_value' => $this->getSetting('render_without_location'),
];
$elements['move_asset_button'] = [
'#title' => $this->t('Move asset button'),
'#description' => $this->t('Include a button to move the asset.'),
'#type' => 'checkbox',
'#default_value' => $this->getSetting('move_asset_button'),
];
return $elements;
}
@ -57,7 +49,6 @@ class AssetCurrentLocationFormatter extends EntityReferenceLabelFormatter {
public function settingsSummary() {
$summary = parent::settingsSummary();
$summary[] = $this->getSetting('render_without_location') ? $this->t('Render without current location') : $this->t('Do not render without current location');
$summary[] = $this->getSetting('move_asset_button') ? $this->t('Include move asset button') : $this->t('No move asset button');
return $summary;
}
@ -89,25 +80,6 @@ class AssetCurrentLocationFormatter extends EntityReferenceLabelFormatter {
$elements[] = ['#markup' => 'N/A'];
}
// Add the move asset button if configured.
if ($this->getSetting('move_asset_button')) {
// Append a "Move asset" link.
$options = [
'query' => [
'asset' => $asset->id(),
'destination' => $asset->toUrl()->toString(),
],
];
$elements[] = [
'#type' => 'link',
'#title' => $this->t('Move asset'),
'#url' => Url::fromRoute('farm_location.asset_move_action_form', [], $options),
'#attributes' => [
'class' => ['button', 'button--small'],
],
];
}
return $elements;
}

View File

@ -0,0 +1,11 @@
langcode: en
status: true
dependencies:
module:
- asset
- farm_quick_group
id: quick_group
label: 'Assign group membership'
type: asset
plugin: quick_group
configuration: { }

View File

@ -0,0 +1,4 @@
# Schema for actions.
action.configuration.quick_group:
type: action_configuration_default
label: 'Configuration for the quick group action'

View File

@ -0,0 +1,27 @@
<?php
/**
* @file
* Post update hooks for the farm_quick_group module.
*/
use Drupal\system\Entity\Action;
/**
* Install system.action.quick_group.
*/
function farm_quick_group_post_update_install_quick_group_action(&$sandbox) {
$config = Action::create([
'id' => 'quick_group',
'label' => 'Assign group membership',
'type' => 'asset',
'plugin' => 'quick_group',
'dependencies' => [
'module' => [
'asset',
'farm_quick_group',
],
],
]);
$config->save();
}

View File

@ -0,0 +1,26 @@
<?php
namespace Drupal\farm_quick_group\Plugin\Action;
use Drupal\farm_quick\Plugin\Action\QuickFormActionBase;
/**
* Action for recording group membership assignment.
*
* @Action(
* id = "quick_group",
* label = @Translation("Assign group membership"),
* type = "asset",
* confirm_form_route_name = "farm.quick.group"
* )
*/
class Group extends QuickFormActionBase {
/**
* {@inheritdoc}
*/
public function getQuickFormId(): string {
return 'group';
}
}

View File

@ -14,6 +14,7 @@ use Drupal\farm_quick\Plugin\QuickForm\QuickFormBase;
use Drupal\farm_quick\Plugin\QuickForm\QuickFormInterface;
use Drupal\farm_quick\Traits\QuickFormElementsTrait;
use Drupal\farm_quick\Traits\QuickLogTrait;
use Drupal\farm_quick\Traits\QuickPrepopulateTrait;
use Drupal\farm_quick\Traits\QuickStringTrait;
use Psr\Container\ContainerInterface;
@ -34,6 +35,7 @@ class Group extends QuickFormBase implements QuickFormInterface {
use QuickLogTrait;
use QuickFormElementsTrait;
use QuickPrepopulateTrait;
use QuickStringTrait;
/**
@ -112,6 +114,7 @@ class Group extends QuickFormBase implements QuickFormInterface {
];
// Assets.
$prepopulated_assets = $this->getPrepopulatedEntities('asset', $form_state);
$form['asset'] = [
'#type' => 'entity_autocomplete',
'#title' => $this->t('Assets'),
@ -126,6 +129,7 @@ class Group extends QuickFormBase implements QuickFormInterface {
'#maxlength' => 1024,
'#tags' => TRUE,
'#required' => TRUE,
'#default_value' => $prepopulated_assets,
];
// Groups.

View File

@ -0,0 +1,11 @@
langcode: en
status: true
dependencies:
module:
- asset
- farm_quick_movement
id: quick_movement
label: 'Record movement'
type: asset
plugin: quick_movement
configuration: { }

View File

@ -0,0 +1,4 @@
# Schema for actions.
action.configuration.quick_movement:
type: action_configuration_default
label: 'Configuration for the quick movement action'

View File

@ -0,0 +1,24 @@
<?php
/**
* @file
* Contains farm_quick_movement.module.
*/
use Drupal\Core\Entity\EntityTypeInterface;
/**
* Implements hook_entity_base_field_info_alter().
*/
function farm_quick_movement_entity_base_field_info_alter(&$fields, EntityTypeInterface $entity_type) {
/** @var \Drupal\Core\Field\BaseFieldDefinition[] $fields */
// Add "Move" button to asset "Current location" field formatter which
// redirects to the Movement quick form.
if ($entity_type->id() == 'asset' && !empty($fields['location'])) {
$display_options = $fields['location']->getDisplayOptions('view');
$display_options['type'] = 'asset_current_location_move';
$display_options['settings']['move_asset_button'] = TRUE;
$fields['location']->setDisplayOptions('view', $display_options);
}
}

View File

@ -0,0 +1,27 @@
<?php
/**
* @file
* Post update hooks for the farm_quick_movement module.
*/
use Drupal\system\Entity\Action;
/**
* Install system.action.quick_movement.
*/
function farm_quick_movement_post_update_install_quick_movement_action(&$sandbox) {
$config = Action::create([
'id' => 'quick_movement',
'label' => 'Record movement',
'type' => 'asset',
'plugin' => 'quick_movement',
'dependencies' => [
'module' => [
'asset',
'farm_quick_movement',
],
],
]);
$config->save();
}

View File

@ -8,6 +8,11 @@
color: 'blue',
};
instance.currentLocationLayer = instance.addLayer('vector', opts);
// If an asset geometry was pre-populated, add it to the layer.
if (instance.farmMapSettings.behaviors.quick_movement.asset_geometry) {
this.updateAssetGeometry(instance, instance.farmMapSettings.behaviors.quick_movement.asset_geometry)
}
},
// When updating asset geometry, update the current location layer.

View File

@ -0,0 +1,26 @@
<?php
namespace Drupal\farm_quick_movement\Plugin\Action;
use Drupal\farm_quick\Plugin\Action\QuickFormActionBase;
/**
* Action for recording movements.
*
* @Action(
* id = "quick_movement",
* label = @Translation("Record movement"),
* type = "asset",
* confirm_form_route_name = "farm.quick.movement"
* )
*/
class Movement extends QuickFormActionBase {
/**
* {@inheritdoc}
*/
public function getQuickFormId(): string {
return 'movement';
}
}

View File

@ -0,0 +1,99 @@
<?php
namespace Drupal\farm_quick_movement\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\farm_location\Plugin\Field\FieldFormatter\AssetCurrentLocationFormatter;
/**
* Field formatter for the current location asset field with a move button.
*
* @FieldFormatter(
* id = "asset_current_location_move",
* label = @Translation("Asset current location (with Move button)"),
* description = @Translation("Display the label of the referenced entities with a button to move."),
* field_types = {
* "entity_reference"
* }
* )
*/
class AssetCurrentLocationMoveFormatter extends AssetCurrentLocationFormatter {
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
return [
'move_asset_button' => FALSE,
] + parent::defaultSettings();
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$elements = parent::settingsForm($form, $form_state);
$elements['move_asset_button'] = [
'#title' => $this->t('Move asset button'),
'#description' => $this->t('Include a button to move the asset.'),
'#type' => 'checkbox',
'#default_value' => $this->getSetting('move_asset_button'),
];
return $elements;
}
/**
* {@inheritdoc}
*/
public function settingsSummary() {
$summary = parent::settingsSummary();
$summary[] = $this->getSetting('move_asset_button') ? $this->t('Include move asset button') : $this->t('No move asset button');
return $summary;
}
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items, $langcode) {
// Build labels in parent.
$elements = parent::viewElements($items, $langcode);
// Get the asset.
$asset = $items->getEntity();
// If the asset is fixed don't render additional information.
if ($asset->get('is_fixed')->value) {
return $elements;
}
// If there are no current locations only render if configured to.
if (empty($elements) && !$this->getSetting('render_without_location')) {
return $elements;
}
// Add the move asset button if configured.
if ($this->getSetting('move_asset_button')) {
// Append a "Move asset" link.
$options = [
'query' => [
'asset' => $asset->id(),
'destination' => $asset->toUrl()->toString(),
],
];
$elements[] = [
'#type' => 'link',
'#title' => $this->t('Move asset'),
'#url' => Url::fromRoute('farm.quick.movement', [], $options),
'#attributes' => [
'class' => ['button', 'button--small'],
],
];
}
return $elements;
}
}

View File

@ -15,6 +15,7 @@ use Drupal\farm_quick\Plugin\QuickForm\QuickFormBase;
use Drupal\farm_quick\Plugin\QuickForm\QuickFormInterface;
use Drupal\farm_quick\Traits\QuickFormElementsTrait;
use Drupal\farm_quick\Traits\QuickLogTrait;
use Drupal\farm_quick\Traits\QuickPrepopulateTrait;
use Drupal\farm_quick\Traits\QuickStringTrait;
use Psr\Container\ContainerInterface;
@ -35,6 +36,7 @@ class Movement extends QuickFormBase implements QuickFormInterface {
use QuickLogTrait;
use QuickFormElementsTrait;
use QuickPrepopulateTrait;
use QuickStringTrait;
use WktTrait;
@ -114,6 +116,7 @@ class Movement extends QuickFormBase implements QuickFormInterface {
];
// Assets.
$prepopulated_assets = $this->getPrepopulatedEntities('asset', $form_state);
$form['asset'] = [
'#type' => 'entity_autocomplete',
'#title' => $this->t('Assets'),
@ -133,6 +136,7 @@ class Movement extends QuickFormBase implements QuickFormInterface {
'wrapper' => 'asset-geometry',
'event' => 'autocompleteclose change',
],
'#default_value' => $prepopulated_assets,
];
// Locations.
@ -167,6 +171,13 @@ class Movement extends QuickFormBase implements QuickFormInterface {
'#behaviors' => [
'quick_movement',
],
'#map_settings' => [
'behaviors' => [
'quick_movement' => [
'asset_geometry' => $this->combinedAssetGeometries($prepopulated_assets),
],
],
],
'#display_raw_geometry' => TRUE,
];