Problem/Motivation
Since #3295483: Flagged message not showing for anonymous user (commit bc25d062, first released in 5.1.0), ActionLinkTypeBase::getAsFlagLink() renders the flag element for every anonymous user who is denied access, including anonymous users who have no permission to use the flag at all:
// If anonymous user does not have permission to unflag, render the flag
// so a flagged message can be displayed.
elseif ($this->currentUser->isAnonymous()) {
$render['#access'] = !$access->isAllowed();
}
The intent of #3295483 is to keep the element for anonymous users who can flag but cannot unflag, so the flag message can be shown after flagging. Its test grants the anonymous role the flag permission first. However, the condition only checks isAnonymous(). When the anonymous role has no permission for the flag, #title and the href are never set (they are only added when access is allowed), yet #access is now set to TRUE.
As a result, every flaggable entity shown to anonymous users renders an empty flag element:
- With the default
flag.html.twig: an empty<div class="flag flag-[id] js-flag-[id]-[entity id] action-flag"><span></span></div>, and theflag/flag.linklibrary is attached. - With themes whose flag template has no
titlean empty, styled AJAX link, e.g.<span class="flag flag-subscribe-node ..."><a class="use-ajax btn ..." rel="nofollow"></a></span?. These templates worked correctly up to 5.0.3, because the element was never rendered without a title.
In 5.0.3 and earlier this case fell through to $render = []; and nothing was output.
Steps to reproduce
- Install Flag 5.1.0 on a standard site (reproduced on Drupal 11.4.7).
- Create a flag for the Article content type with the AJAX link type, shown in the full view mode.
- Grant
flag [id]andunflag [id]to the Authenticated user role only; the anonymous role gets no flag permissions. - Log out and view an article.
- Expected: no flag markup. Actual: an empty flag wrapper is rendered (see above).
Proposed resolution
Only render the element for anonymous users who are allowed to flag, which is the scenario #3295483 targets. Add the access result as a cacheable dependency so the render cache varies correctly:
elseif ($this->currentUser->isAnonymous()) {
$flag_access = $flag->actionAccess('flag', $this->currentUser, $entity);
CacheableMetadata::createFromRenderArray($render)
->addCacheableDependency($flag_access)
->applyTo($render);
if ($flag_access->isAllowed()) {
$render['#access'] = TRUE;
}
else {
$render = [];
}
}
A permission-based check is preferred over $flag->isFlagged(), which needs the anonymous session and can make pages uncacheable (see #2894095).
Optionally, the default flag.html.twig could also skip the wrapper when there is neither a title nor unflag_denied_text.
Remaining tasks
- Create a merge request with the fix.
- Add test coverage: an anonymous user without flag permission sees no flag markup, while the scenario covered by
AjaxLinkTest::testAnonymousMessage()keeps passing. - Review.
User interface changes
Empty flag markup is no longer output for anonymous users without permission to use the flag.
API changes
None.
Data model changes
None.
Issue fork flag-3630623
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments