Replace Move action with Movement quick form action.

This commit is contained in:
Michael Stenta 2023-09-30 14:22:25 -04:00
parent e0ca16cfd2
commit 5296d002e4
16 changed files with 223 additions and 447 deletions

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_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,
];