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 for asset, log, organization, and plan entity 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 upon.
  • Visiting 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_action
    • asset_csv_action
    • asset_kml_action
    • asset_farm_action
    • log_clone_action
    • log_csv_action
    • log_kml_action
    • log_mark_as_done_action
    • log_reschedule_action
    • log_quantity_csv_action
    • organization_clone_action
    • quick_group
    • quick_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

m.stenta’s picture

Version: 7.x-1.x-dev » 2.0.x-dev

Moving 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.

m.stenta’s picture

Title: Display log action buttons on log view page » Display action buttons on log/asset view page
Project: Log entity » farmOS
Version: 2.0.x-dev » 8.x-2.x-dev

Moving 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.

m.stenta’s picture

Version: 8.x-2.x-dev » 2.x-dev
m.stenta’s picture

paul121’s picture

A related project that we might be able to use or draw inspiration from: https://www.drupal.org/project/expose_actions

m.stenta’s picture

Version: 2.x-dev » 3.x-dev
m.stenta’s picture

Version: 3.x-dev » 4.x-dev
m.stenta’s picture

A related project that we might be able to use or draw inspiration from: https://www.drupal.org/project/expose_actions

Thanks @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.

m.stenta’s picture

We may also want to be more discerning about which actions we make available as action links. I alluded to this in comment #2 above:

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.

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...

m.stenta’s picture

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.

Ah 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_actions module 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.

m.stenta’s picture

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.

We'll need to make sure all of these redirect back to the entity afterwards...

Actually 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.

m.stenta’s picture

Here is a quick list of all actions by entity type:

Asset

  • Add log
  • Archive asset
  • Assign owners
  • Clone asset
  • Export CSV
  • Delete asset
  • Flag asset
  • Export KML
  • Assign asset parent
  • Unarchive asset
  • Assign group membership
  • Record movement
  • Assign asset to farm

Log

  • Categorize
  • Clone
  • Export CSV
  • Delete log
  • Flag log
  • Export KML
  • Mark as done
  • Mark as pending
  • Export quantities CSV
  • Reschedule

Organization

  • Archive organization
  • Clone organization
  • Delete organization
  • Unarchive organization

Plan

  • Archive plan
  • Assign owners
  • Delete plan
  • Flag plan
  • Unarchive plan
m.stenta’s picture

One 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.

m.stenta’s picture

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.

  • Add log

If 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.

m.stenta’s picture

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

It 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)?

m.stenta’s picture

  • Export CSV
  • Export KML

These 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.

  • Reschedule

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.

m.stenta’s picture

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

  • Assign group membership

The "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...

m.stenta’s picture

OK, 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

  • Add log* (could replace existing "Add Log: [type]" links)
  • Clone asset (but should redirect to cloned asset edit form)
  • Export CSV*
  • Export KML
  • Assign group membership*
  • Record movement* (could replace "Move" button on assets)
  • Assign asset to farm*

Log

  • Clone (but should redirect to cloned log edit form)
  • Export CSV*
  • Export KML
  • Mark as done (but only show if log is not done)
  • Export quantities CSV*
  • Reschedule*

Organization

  • Clone organization (but should redirect to cloned organization edit form)

Plan

(none!)

m.stenta’s picture

Another 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's access() 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...

m.stenta’s picture

Status: Active » Needs work

I've started working on this in a 4.x-action-actions branch of my fork on GitHub: https://github.com/farmOS/farmOS/compare/4.x...mstenta:farmOS:4.x-action...

What is done:

  • New routes are added for asset, log, organization, and plan entity 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 upon.
  • Visiting 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.

What still needs to be done:

  • Redirects are not working as desired after executing actions with a confirmation form. For example, using the "Add log" action on an asset redirects nicely to the confirmation form, but then sends you to a log edit form with a URL of http://localhost/log/add/activity?destination=http%3A//localhost/assets&asset%5B0%5D=1. The destination of ?destination=http%3A//localhost/assets&asset%5B0%5D=1 should be ?destination=asset/[id].
  • 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.
  • Decide if we want to replace the "Move" button on assets with the "Record movement" quick form action link.
  • Figure out how we want to filter which actions we expose (see "these are the actions that would be valuable to expose" in my comment #18 above).
  • Figure out how to hide "Mark as done" action on logs that are already done.
  • 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.
m.stenta’s picture

Issue summary: View changes

Updated 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.

m.stenta’s picture

Issue summary: View changes

Redirects are not working as desired after executing actions with a confirmation form. For example, using the "Add log" action on an asset redirects nicely to the confirmation form, but then sends you to a log edit form with a URL of http://localhost/log/add/activity?destination=http%3A//localhost/assets&asset%5B0%5D=1. The destination of ?destination=http%3A//localhost/assets&asset%5B0%5D=1 should be ?destination=asset/[id].

I added a ?destination parameter 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 ?destination logic 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.

Figure out how we want to filter which actions we expose (see "these are the actions that would be valuable to expose" in my comment #18 above).

I decided to go with a simple hook-based approach to this. The farm_ui_action module 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.

m.stenta’s picture

Issue summary: View changes
m.stenta’s picture

Issue summary: View changes

Expose specific farmOS core actions:

I exposed all of the ones listed above. Tested and they all seem to be working well!

Decide if we want to replace the "Move" button on assets with the "Record movement" quick form action link.

I went ahead and removed this, since it is now redundant.

Aside from the redundancy, other justifications for removing this include:

  • It's a weird custom design pattern that we don't use anywhere else.
  • We didn't have any automated tests for it. We will have tests for these action links.
  • Less code! Two fewer classes!
m.stenta’s picture

Issue summary: View changes

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?

I'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.

m.stenta’s picture

Figure out how to customize redirects for some actions (eg: redirect to cloned entity edit form instead of original entity canonical page).

I'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.

m.stenta’s picture

Issue summary: View changes
m.stenta’s picture

Figure out how to hide "Mark as done" action on logs that are already done.

I 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:

    // If the state field is already in the target state, return early.
    // The workflow will not allow a transition to the same state but the
    // action itself does not need to fail.

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...

m.stenta’s picture

So we should review that and assess whether or not the logic can be changed...

I 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:

No access to execute Mark as done on the Log Transplant 2026 Lettuce - Green.

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.

m.stenta’s picture

I'm going to open an issue in the Log module to suggest this change.

See: #3576009: Deny access to state change actions that wouldn't change the state

m.stenta’s picture

Title: Display action buttons on log/asset view page » Show entity action buttons on canonical entity pages as action links
m.stenta’s picture

Title: Show entity action buttons on canonical entity pages as action links » Display action buttons on log/asset view page

Reviewed 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:

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 ?destination logic 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.

m.stenta’s picture

how well Gin handles many action links on mobile devices.

Oh 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!

m.stenta’s picture

Title: Display action buttons on log/asset view page » Show entity action buttons on canonical entity pages as action links

Oops accidentally reverted the title.

m.stenta’s picture

how well Gin handles many action links on mobile devices.

Oh 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.

m.stenta’s picture

hide "Mark as done" action on logs that are already done

I'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.

m.stenta’s picture

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!

Opened a follow-up: #3576215: Replace canonical entity action links with a single "Actions" modal

m.stenta’s picture

Redirects are not working as desired after executing actions with a confirmation form. For example, using the "Add log" action on an asset redirects nicely to the confirmation form, but then sends you to a log edit form with a URL of http://localhost/log/add/activity?destination=http%3A//localhost/assets&asset%5B0%5D=1. The destination of ?destination=http%3A//localhost/assets&asset%5B0%5D=1 should be ?destination=asset/[id].

Ah 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.

m.stenta’s picture

Issue summary: View changes