Problem/Motivation
We provide a number of helpful "bulk actions" for entities in farmOS (assets, logs, organizations, plans), eg: "Clone [asset/log]", "Export [CSV/KML]", "Reschedule" (log), "Mark as done", etc.
These are only available from lists of entities, however, and cannot be triggered from individual canonical entity pages. It would be nice to offer these as action links alongside the existing links provided by our farm_ui_action module.
Original summary (from 2015 / farmOS v1):
Display log action buttons on log view pages. Similar to how they are displayed in farmOS in the Views Bulk Operations lists.
Proposed resolution
Add action links for entity actions on canonical entity pages.
Remaining tasks
New routes are added forasset,log,organization, andplanentity actions, with paths like:/asset/{entity}/action/{action}.The routes have numerous access requirements, including a custom access callback that runs the.access()method on the action plugin itself with the specific entity it is acting uponVisiting that route will execute the action on that entity. If the action has a confirmation form, it will then redirect there. Otherwise, it will redirect back to the entity that the action was performed on.Action link buttons are added to canonical entity pages for all actions that are available to their entity type. These are conditionally displayed based on access to the route. Users should only see buttons for actions that they have confirmed access to perform.Redirects back to the entity after executing actions with a confirmation form.Figure out how to customize redirects for some actions (eg: redirect to cloned entity edit form instead of original entity canonical page).- Decide if we want to replace the existing "Add Log: [type]" action links with the "Add log" action.
Remove the "Move" button added to asset "Current location" field formatter. The "Record movement" quick form action link makes it redundant.Provide hooks for exposing entity actions, and altering actions exposed by other modules.-
Expose specific farmOS core actions:asset_clone_actionasset_csv_actionasset_kml_actionasset_farm_actionlog_clone_actionlog_csv_actionlog_kml_actionlog_mark_as_done_actionlog_reschedule_actionlog_quantity_csv_actionorganization_clone_actionquick_groupquick_movement
- Figure out how to hide "Mark as done" action on logs that are already done. (See #3576009: Deny access to state change actions that wouldn't change the state)
Confirm that it's safe to execute actions before redirecting to confirmation forms. This works for farmOS's actions, I think, but is that the right pattern generally?- Confirm that button caching works as expected. (If one user does has access to perform an action on an entity, and loads the entity page first, does that button still appear for a user that does not have access to perform the action?)
- Automated tests.
User interface changes
Additional action links will be available on canonical entity pages for performing entity actions on individual entities, which are currently only available to apply in bulk from entity list pages.
API changes
None.
Data model changes
None.
Comments
Comment #1
m.stentaMoving this to 2.0.x - but it may be something we defer to downstream applications to implement (eg: farmOS). Worth exploring what is involved here first, in case it makes sense to do it in a more general way.
Comment #2
m.stentaMoving this to the farmOS issue queue, and generalizing it for assets as well. It makes more sense to have this logic downstream in the application that needs it, because there will be some decisions about what actions to include/exclude.
Comment #3
m.stentaComment #4
m.stentaComment #5
paul121 commentedA related project that we might be able to use or draw inspiration from: https://www.drupal.org/project/expose_actions
Comment #6
m.stentaComment #7
m.stentaComment #8
m.stentaThanks @paul121! I took a look at this today and the way it exposes actions as action links is extremely simple.
It doesn't work out of the box, unfortunately, because it assumes that every action confirmation form accepts
?entity_type=[type]and?entity_id=[id]query parameters, and includes that in all the links it generates. It also includes a?action=[id]query parameter, which feels unnecessary.The other thing it adds is permissions for each action, so they can be turned on/off via roles.
It does not include any automated tests.
I think we could replicate what we need from this pretty easily without adding a dependency on
expose_actions.Comment #9
m.stentaWe may also want to be more discerning about which actions we make available as action links. I alluded to this in comment #2 above:
The consideration there is: by what mechanism do we filter them? The easy approach is to hard-code which actions we will create links for. The disadvantage is that actions provided by other modules will not be included. We could consider adding a hook to allow modules to opt in their actions...
As I'm looking at the asset/log actions, many of them can be omitted because they don't add anything to the existing entity edit form. Or, in the case of the "Record movement" action (which redirects to the movement quick form), we already provide a button for that on asset pages, so it would be redundant.
Actions that would be very helpful, though, are ones like "Clone asset/log" because there is no way to do that from the entity edit form.
Other actions that do not have a confirmation step, like "Archive/Unarchive asset" and "Mark as done/Mark as pending" (for logs), would also be nice to expose, even though the same can be accomplished via the entity edit form, because they are common tasks and adding an action link for them saves a click.
We'll need to make sure all of these redirect back to the entity afterwards...
Comment #10
m.stentaAh hmm, these might be tricky, actually.
All action links need to have a
route_name(where the link brings you). In the case of actions with a confirmation form, they can go there. But actions without confirmation forms don't have a route to point to.The way the
expose_actionsmodule handles this is by providing a default confirmation form for actions that don't have one. This form executes the action on submit. That means an additional step, which I was hoping to avoid.Perhaps we could provide a default route that simply executes the action without confirmation, and then redirects back to the entity.
Comment #11
m.stentaActually for the clone action, we probably want to redirect to the new cloned asset. Better yet, maybe we want to redirect to the cloned asset's edit form, so that it can be customized after cloning.
Comment #12
m.stentaHere is a quick list of all actions by entity type:
Asset
Log
Organization
Plan
Comment #13
m.stentaOne thing to note: the gin theme can only display so many action links in the dropdown before they get cut off at the bottom of the page. There is no way to scroll.
On a fresh farmOS install, with ALL core modules installed, there are 21 action links added to asset pages. Only the first 15 show on a 768px tall screen at 100% zoom.
Comment #14
m.stentaIf we could replace all of the existing "Add Log: [type]" action links with just "Add log", then that would cut the total number of action links on an asset page down to 11, which is under the 15 maximum (all other screen size, filtered actions, etc variables considered).
The "Add log" action has a confirmation form step, however, so we would need to be able to support confirmation forms to do that.
Comment #15
m.stentaIt would be weird to show "Mark as done" on a log already marked as done.
Can we filter out actions based on entity status (and/or other logic)?
Comment #16
m.stentaThese would be nice to have as well, since it's not possible to do this from the entity edit form. These require confirmation forms, however.
I think this would also be nice to have (with confirmation form), because the confirmation form offers the ability to reschedule by a relative period, whereas the entity edit form requires an exact date.
Comment #17
m.stentaThe "Assign group membership" action could also be useful, and we don't provide a button for that on asset pages.
Makes me wonder... maybe we could remove the "Move" button if we expose the "Record movement" action...
Comment #18
m.stentaOK, so just to organize my stream of thoughts... these are the actions that would be valuable to expose, in my opinion. I have marked the ones that use a confirmation form with an asterisk (*) and made a few notes. I removed "Archive/Unarchive/Mark as pending", but left "Mark as done" for logs, since that is the most useful IMO.
Asset
Log
Organization
Plan
(none!)
Comment #19
m.stentaAnother consideration here is access/permissions. Actions links should only be shown if the user has permission to use them.
This is tricky, because Drupal core displays action links if the user has access to the route that they point to.
Actions without confirmation forms don't have a route. And the ones that do often set
_user_is_logged_in: 'TRUE'and then (I think) they check access using the action plugin'saccess()method, but I'm not sure if this results in a 403, which would determine whether the action link is displayed.I wonder if we could provide a custom route for all actions with an access callback that calls individual action plugin
access()methods...Comment #20
m.stentaI've started working on this in a
4.x-action-actionsbranch of my fork on GitHub: https://github.com/farmOS/farmOS/compare/4.x...mstenta:farmOS:4.x-action...What is done:
asset,log,organization, andplanentity actions, with paths like:/asset/{entity}/action/{action}.access()method on the action plugin itself with the specific entity it is acting upon.What still needs to be done:
http://localhost/log/add/activity?destination=http%3A//localhost/assets&asset%5B0%5D=1. Thedestinationof?destination=http%3A//localhost/assets&asset%5B0%5D=1should be?destination=asset/[id].Comment #21
m.stentaUpdated issue summary to use the standard template, copied my "What is done" and "What still needs to be done" bullet points from comment #20 above into the "Remaining tasks", and crossed off the ones that are done.
Comment #22
m.stentaI added a
?destinationparameter when redirecting to the confirmation form which redirects back to the entity's canonical page after execution.This seems to work fine on the actions that we want to expose. It does not work on the "Add log" action, as I noted earlier, because that action has its own
?destinationlogic built-in.So if we do want to expose "Add log" (and replace the existing "Add Log: [type]" action links), then we'll need to sort that out. For now, I'm going to punt on that, and leave the existing "Add Log: [type]" action links in place.
I decided to go with a simple hook-based approach to this. The
farm_ui_actionmodule will provide two hooks: one for exposing entity actions (hook_farm_exposed_entity_actions()), and one for altering exposed entity actions (hook_farm_exposed_entity_actions_alter()).Still working on exposing specific core farmOS actions...
One nice side-effect of using this hook-based approach is that we can also use it to order the entity actions in a logical manner. And I'm giving them all a default weight of 10 so that they appear under the other actions we provide.
Comment #23
m.stentaComment #24
m.stentaI exposed all of the ones listed above. Tested and they all seem to be working well!
I went ahead and removed this, since it is now redundant.
Aside from the redundancy, other justifications for removing this include:
Comment #25
m.stentaI've looked into some of Drupal core's actions and it seems like this is the standard pattern.
And given that we are explicitly opting actions in to this, we don't need to worry about weird ones being added automatically. It will be important to test any new ones that are added in the future, but that's best practice either way.
Comment #26
m.stentaI'm only focusing on the "clone entity" action for this... but it is challenging.
In order to redirect to the cloned entity, we need to know the ID of that new entity. This isn't available outside of the action plugin's
execute()method currently. And we can't add the redirect inside the action itself, because we don't know if it's being run on an individual entity (via this new action link), or multiple (via a list of entities). We obviously can't redirect if multiple entities are being cloned.So I think we need to just let this work as-is for the first pass. It's still adding value, even if it requires an extra click to get to the cloned entity (a link to the entity does get shown to the user in a status message after cloning).
If we want to pick this pack up in the future (if there is high demand for it from the community), then perhaps a better approach would be to create a dedicated action link + router item for cloning individual entities, and hide the clone entity action links. This would give us more control, and would allow us to redirect very easily.
Comment #27
m.stentaComment #28
m.stentaI looked into this, and it seems like it would almost work out-of-the-box, but not quite!
This action is provided by the Log module (so it is technically outside of the farmOS codebase, but I maintain both):
https://git.drupalcode.org/project/log/-/blob/3.x/src/Plugin/Action/LogM...
https://git.drupalcode.org/project/log/-/blob/3.x/src/Plugin/Action/LogS...
The
access()method on that action does perform a check to see if it would be a "valid state transition", and returns a forbidden access result if it isn't. In theory, this would hide the action link for us if a log is already "Done", because it would be an invalid transition to change from "Done" to "Done".However, the
access()method performs another check right before that one, in which it asks "is the new state already equal to the old state?" And if it is, then it returns an allowed access result:https://git.drupalcode.org/project/log/-/blob/2bbb45e9c97dca0af0d07efd21...
This logic was added in #3244472: Log actions error when transitioning to the same state and do not support transitions in other workflows .
So we should review that and assess whether or not the logic can be changed...
Comment #29
m.stentaI tested simply removing that condition, and it does exactly what we want! The "Mark as done" button only shows on logs that are not already "Done".
I also tested the action on a list of logs, and it still works. However, it means that now if you try to change logs that are already done, it will show an error message for each. For example:
I can see why we might have decided to add this logic to hide those earlier, but maybe that wasn't the right approach overall. Other actions also show errors like this if they fail the access check, and we don't hide those.
I'm going to open an issue in the Log module to suggest this change.
Comment #30
m.stentaSee: #3576009: Deny access to state change actions that wouldn't change the state
Comment #31
m.stentaComment #32
m.stentaReviewed some of this on the dev call yesterday. @symbioquine asked how well Gin handles many action links on mobile devices.
I installed all modules locally and opened an asset page in my browser. There are 15 action links, which fit perfectly on my 768 px tall laptop screen (one more and it would be cut off). When I simulate a mobile device in my browser (iPhone 12/13 and Galaxy S20+) they fit nicely with extra room on the bottom in portrait mode (both of those devices have a pixel height greater than 768px). But in landscape mode things get a little funky. They are cut off on the bottom, and when I try to scroll the action links strangely jump UP above the first button, and go off the top of the screen. I don't know if this is a bug with Gin, or if its my browser simulation. I'll need to test on an actual mobile device...
Worth reiterating, the number of action links in this test is mainly due to the existing "Add Log: [type]" buttons, not the new ones we're adding. So this is an existing issue. But we could consider removing those "Add Log: [type]" buttons and using the single "Add log" action instead, as described above. This would replace 10 of the action links with 1 (in a farmOS instance with ALL core log types enabled).
The other advantage of using the "Add log" action is it removes potentially nonsensical buttons from certain contexts. For example, right now if you have the seeding log module enabled and the animal asset module enabled, you will see "Add Log: Seeding" on your Sheep asset. If we make the change described here, then you will only see "Add log" on your Sheep, which takes you to a page with a dropdown of log types. Seeding will still be an option in that page, but I think that's better than showing it on the animal asset page.
Still need to sort out this issue, if we want to go that route, though:
Comment #33
m.stentaOh also worth noting, I found this issue in the Gin queue, which was found previously and commented on by @paul121... :-)
#3211772: Improve "Add Content" on admin overview pages
That's a proof of concept which proposes using a modal popup to display actions. This approach could have a lot of benefits, including the ability to add descriptions or other content alongside each action.
@paul121 commented to request that it be made generalizable so that we could use it in our contexts: https://www.drupal.org/project/gin/issues/3211772#comment-14539415
It doesn't seem like anything has moved forward in the meantime.
But... it gives me ideas... and maybe we can implement our own modal approach. I'm imagining a route that is provided by our module, which essentially works the same as our "index" routes (eg: index of quick forms, reports, etc), where each item is an action that is available to a given entity. We could replace all of our action links with a single "Actions" button that opens this index up in a modal dialog. That wouldn't be too hard... and would make these actions a lot more scalable.
This idea is outside the scope of this issue, of course, but something to consider next!
Comment #34
m.stentaOops accidentally reverted the title.
Comment #35
m.stentaOh dang. I just remembered: WE (farmOS) are overriding Gin's default local actions styling to display them as a "dropbutton" (the dropdown). So it's not really Gin's fault if these don't work on mobile devices. It's ours. :-/
https://github.com/farmOS/farmOS/blob/5f36bf6dd372868ea7fe9a6c95c928a33b...
(Thanks to @paul121's comment for reminding me of this!)
If we took the single-button+modal idea described above, it might allow us to remove that override.
Comment #36
m.stentaI'm going to hide this action for now, pending #3576009: Deny access to state change actions that wouldn't change the state. We can expose it if/when that change is made in the Log module.
Comment #37
m.stentaOpened a follow-up: #3576215: Replace canonical entity action links with a single "Actions" modal
Comment #38
m.stentaAh I just tested this again and it's actually working perfectly! Originally when I tested this I wasn't adding the redirect correctly.
I will remove the existing "Add Log: [type]" links and replace them with that.
Comment #39
m.stenta