This is a very vague idea I've been turning over for ages.

The motivation is this:

A ton of support requests in the issue queue are due to people using Flag in totally bizarre and insane ways. Generally, they add a flag to something that they don't actually care about being flagged. What they actually want to happen when the user flags is completely different. They then use Rules to detect the user flagging the entity, and then cause the thing they actually want to happen -- and throw away or ignore the actual Flagging.

Some recent examples:

- using Flag to add products to the cart with Commerce
- using Flag to publish/unpublish a node
- wanting the flagged entities to end up as values in an entityreference field, so that the end result is an orderable list of things. This might be, for example, a playlist or a wishlist.

The reason for doing this convoluted crazy stuff is simple: people want the nice JS AJAXy toggle link. Using Flag gives them a very nice UI where the user sees 'Add to cart', clicks it, sees the link update itself and turn into 'Remove from cart'.

This leads me to consider the following question:

Could we decouple the link part of Flag to a general 'Action Link' module?

This would provide generic links that 'do stuff'. A link would have a particular entity as its subject, and be aware of the current user. We'd have different types of link, to match the current different types of flag link.

- instead of the path being flag/ACTION/FLAGNAME/ENTITY_ID we'd have action_list/ACTION_LINK_NAME/TOGGLE_STATUS/ENTITY_TYPE/ENTITY_ID

- ACTION_LINK_NAME would be analogous to the flag name. We'd prefix with 'flag', so here it would be 'flag_bookmarks'.
- TOGGLE_STATUS would 0 or 1 (or preferably something human-readable, but using 0 and 1 would allow us to extend from binary toggling if desired)
- ENTITY_TYPE isn't necessary for flags, but nice to have if other consumer modules need to know this.

The link types would be, as currently on Flag:

- AJAX link, updates magically
- Normal link, reloads the page
- Confirmation form

Things to figure out:

- how to tell the Action Link system how to attach the Flagging entity that we now have on the Flagging form.
- how do we account for the Flagging Form module's flag link, which allows editing of the flagging once it's been made? This might be a general concept that you can return to the confirmation form and adjust the current state of the toggle. But then:
- how do we account that for Flag, when the toggle is 'off' there is nothing to actually look at, as there is no flagging? That suggests that an 'adjust current state' form is per-state
- Flags are per-entity type. An action link presumably does not need this restriction. But individual ones -- such as those provided by flag -- would need to be aware that they are restricted in this way.

Applications of this beyond Flag:

- nodequeue, which currently has a 'normal link' on nodes
- OG: join/leave group
- Commerce: Add to basket
- Workbench: publish / mark for review
- Rules: triggers a Rules event, allowing custom behaviour
- making/breaking entityreference relations (as in, 'add this entity as a value to a set field on a set entity'. This would cover things like 'Add to my playlist', where a playlist is an entity with a multivalued field so that items can be ordered)

How this would look to consumers:

I imagine that to declare an action link, you need to specify:

- its type: whether it's AJAX, reload, confirm form, etc
- its machine name: eg 'flag_bookmarks'
- various strings for the different states, like we have for Flag at the moment.
- various options such as:
- whether the action is reversible
- whether the action link is replaced with the reverse link, or a confirmation message
- a callback for performing the actual action. This would need standard parameters, presumably something like:
- the action link machine name (from which we deduce the flag)
- the toggle state (maps to 'flag' / 'unflag')
- entity type
- entity ID
- the account performing the action
- an array of extra options from, say, the query string-
- a callback for access, which would get the same sort of parameters

How this would work for users:

We'd want to keep the setup tasks to a minimum, and not end up with the sort of situation with D7 taxonomy (where it's a lot more complicated to add taxonomy terms to a node type).

On Flag we now have flag links exposable as:

- entity links, set per view mode. This is in the style of D6-era hook_links(), alongside the 'read more' link. This is obviously mainly suited to nodes and users.
- pseudofields.

Ideally, we'd keep everything in the Flag admin form, but allow the creation of multiple Action Links per flag. Each action link can be set to display as entity links or pseudofields, or be output with a theme call.

Consequences for Flag:

- extra dependency :(
- more users and eyeballs on our JS code, which really needs someone who knows their JS to take care of it
- slower code :( We need to load two pieces of configuration: the Flag itself and the Flag link. But it's highly likely that we'd want Flag Links to become their own plugin type on D8 anyway, so the only extra bit of indirection is that the Flag Link plugin would need to know its callbacks, rather than those being hardcoded.

Interesting ways to take this further

- make the link type dependent on the state, rather than for the whole action link plugin. This is a current feature request on flag: have a confirm form for flagging, but just AJAX for unflagging.
- have more than 2 states: cycle through A -> B -> C -> A rather than just A -> B -> A.


andypost’s picture

The idea to use real entity fields for links when flags are used for them, d8 have hook_entity_field_info() and alter

joachim’s picture

Can you explain more how using hook_entity_field_info() would work?

I had a look at it, but as far as I can tell, you still need to use hook_field_extra_fields().

andypost’s picture

My vision is:
- new flag configured|attached to entity type (maybe bundle like content translation in d8 core)
- then its field exposed via hook_entity_field_info() for entity
- so flag needs to expose configurable field for entity

PS: I'm keeping in mind to replace sticky|promoted node options with flag in d9

joachim’s picture

> PS: I'm keeping in mind to replace sticky|promoted node options with flag in d9

I'm not sure flag is that good a fit for those.

Flag is good when the flags are per-user. If you use Flag for global flags, then it's just a fancy UI for a boolean field, with the expense of a whole extra entity. (Admittedly, you get to know WHEN the boolean was last set...)

However, this issue is about splitting off the fancy UI so that it actually would be usable for things like a boolean field on an entity.

The Action Link plugin is basically going to be in charge of two things:

- creating output for a link
- creating output for returning when a link has clicked (such as AJAX response, confirm form, etc)

A single Flag config entity would have configuration for:

- a Flag plugin
- multiple Action Link plugins (same sort of pattern as image styles having multiple image effects)

I'm not yet sure though of who handles output onto entities.

I had a tinker with hook_field_extra_fields() / hook_entity_view(), which AFAIK is still the way to get extra bits onto an entity.

Can you explain a bit more about how you'd do it as a configurable field instead?

joachim’s picture

Proof of concept work for this on github:

joachim’s picture

Issue summary: View changes

Updated issue summary.

joachim’s picture

Turns out this exists already:

socketwench’s picture

Yep, although I don't believe there's a D8 version. For now, I'm proceeding with Action Links as an integral part of Flag. I do have some ideas to create greater decoupling between the two through careful application of OOP in the case FastToggle doesn't make it to 8.

joachim’s picture

Hmm I've had another look at, and it's rather limited in its scope. It can really only work with boolean properties of an entity. That's something that's baked deep in the way it operates. Furthermore, it's currently still in a pre-entities frame of mind: it works on nodes, comments, and users, but by handling each one separately. That part at least could be improved, but take it beyond boolean object properties would mean a complete rewrite, I think.

joachim’s picture

Title: [D8] Decoupling flag links from Flag » [meta] Decoupling flag links from Flag
Version: 7.x-3.x-dev » 8.x-4.x-dev
Category: Task » Plan
socketwench’s picture

Status: Active » Closed (works as designed)