Call to undefined method ReadonlyAccessControlHandler::checkAllGrants()
>>> [!note] Migrated issue
<!-- Drupal.org comment -->
<!-- Migrated from issue #3582635. -->
Reported by: [norman.lol](https://www.drupal.org/user/2482808)
Related to !16
>>>
<h3 id="summary-problem-motivation">Problem/Motivation</h3>
<p><a href="https://git.drupalcode.org/project/drupal/-/blob/10.6.5/core/modules/node/node.module?ref_type=tags#L959">https://git.drupalcode.org/project/drupal/-/blob/10.6.5/core/modules/node/node.module?ref_type=tags#L959</a></p>
<p>The readonlymode module works by replacing the default node access control handler with its own ReadonlyAccessControlHandler decorator. Drupal's node module has a function node_access_view_all_nodes() that calls checkAllGrants() directly on whatever access handler is registered for node entities. Because ReadonlyAccessControlHandler does not implement that method, PHP threws a fatal error the moment an anonymous user triggered that function because the method simply doesn't exist on the class.</p>
<h4 id="summary-steps-reproduce">Steps to reproduce</h4>
<ol>
<li>Drupal 10</li>
<li><code>ddev drush -y en readonlymode</code></li>
<li><code>ddev drush state:set readonlymode_active 1</code></li>
<li>Visit front page while being <strong>logged-out</strong></li>
</ol>
<blockquote><p>The website encountered an unexpected error. Try again later.</p>
<p>Error: Call to undefined method Drupal\readonlymode\Access\ReadonlyAccessControlHandler::checkAllGrants() in node_access_view_all_nodes() (line 959 of core/modules/node/node.module).<br>
Drupal\node\Cache\NodeAccessGrantsCacheContext->checkNodeGrants() (Line: 50)<br>
Drupal\node\Cache\NodeAccessGrantsCacheContext->getContext() (Line: 123)<br>
Drupal\Core\Cache\Context\CacheContextsManager->convertTokensToKeys() (Line: 286)<br>
Drupal\Core\Cache\VariationCache->createCacheIdFast() (Line: 217)<br>
Drupal\Core\Cache\VariationCache->getRedirectChain() (Line: 35)<br>
Drupal\Core\Cache\VariationCache->get() (Line: 67)<br>
Drupal\Core\Render\RenderCache->get() (Line: 109)<br>
Drupal\Core\Render\PlaceholderingRenderCache->get() (Line: 325)<br>
Drupal\Core\Render\Renderer->doRender() (Line: 248)<br>
Drupal\Core\Render\Renderer->render() (Line: 505)<br>
Drupal\Core\Template\TwigExtension->escapeFilter() (Line: 58)<br>
__TwigTemplate_42e5994aa03d22a0c8564ef6148e6d53->doDisplay() (Line: 388)<br>
Twig\Template->yield() (Line: 344)<br>
Twig\Template->display() (Line: 359)<br>
Twig\Template->render() (Line: 51)<br>
Twig\TemplateWrapper->render() (Line: 33)<br>
twig_render_template() (Line: 348)<br>
Drupal\Core\Theme\ThemeManager->render() (Line: 490)<br>
Drupal\Core\Render\Renderer->doRender() (Line: 503)<br>
Drupal\Core\Render\Renderer->doRender() (Line: 248)<br>
Drupal\Core\Render\Renderer->render() (Line: 505)<br>
Drupal\Core\Template\TwigExtension->escapeFilter() (Line: 73)<br>
__TwigTemplate_58c4e95d71231a6f5da40b81035aa275->doDisplay() (Line: 388)<br>
Twig\Template->yield() (Line: 344)<br>
Twig\Template->display() (Line: 359)<br>
Twig\Template->render() (Line: 51)<br>
Twig\TemplateWrapper->render() (Line: 33)<br>
twig_render_template() (Line: 348)<br>
Drupal\Core\Theme\ThemeManager->render() (Line: 490)<br>
Drupal\Core\Render\Renderer->doRender() (Line: 248)<br>
Drupal\Core\Render\Renderer->render() (Line: 505)<br>
Drupal\Core\Template\TwigExtension->escapeFilter() (Line: 49)<br>
__TwigTemplate_64a721009cb9fd25859dc3da17ffbbdd->doDisplay() (Line: 388)<br>
Twig\Template->yield() (Line: 44)<br>
__TwigTemplate_6b10e7e991339f32e9a2788fea501191->doDisplay() (Line: 388)<br>
Twig\Template->yield() (Line: 344)<br>
Twig\Template->display() (Line: 359)<br>
Twig\Template->render() (Line: 51)<br>
Twig\TemplateWrapper->render() (Line: 33)<br>
twig_render_template() (Line: 348)<br>
Drupal\Core\Theme\ThemeManager->render() (Line: 490)<br>
Drupal\Core\Render\Renderer->doRender() (Line: 248)<br>
Drupal\Core\Render\Renderer->render() (Line: 505)<br>
Drupal\Core\Template\TwigExtension->escapeFilter() (Line: 122)<br>
__TwigTemplate_c21ffbbfdbfc609c8e18fc3bbc1a8005->doDisplay() (Line: 388)<br>
Twig\Template->yield() (Line: 344)<br>
Twig\Template->display() (Line: 359)<br>
Twig\Template->render() (Line: 51)<br>
Twig\TemplateWrapper->render() (Line: 33)<br>
twig_render_template() (Line: 348)<br>
Drupal\Core\Theme\ThemeManager->render() (Line: 490)<br>
Drupal\Core\Render\Renderer->doRender() (Line: 248)<br>
Drupal\Core\Render\Renderer->render() (Line: 238)<br>
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 637)<br>
Drupal\Core\Render\Renderer->executeInRenderContext() (Line: 231)<br>
Drupal\Core\Render\MainContent\HtmlRenderer->prepare() (Line: 128)<br>
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse() (Line: 90)<br>
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray()<br>
call_user_func() (Line: 111)<br>
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch() (Line: 186)<br>
Symfony\Component\HttpKernel\HttpKernel->handleRaw() (Line: 76)<br>
Symfony\Component\HttpKernel\HttpKernel->handle() (Line: 53)<br>
Drupal\Core\StackMiddleware\Session->handle() (Line: 48)<br>
Drupal\Core\StackMiddleware\KernelPreHandle->handle() (Line: 28)<br>
Drupal\Core\StackMiddleware\ContentLength->handle() (Line: 201)<br>
Drupal\page_cache\StackMiddleware\PageCache->fetch() (Line: 138)<br>
Drupal\page_cache\StackMiddleware\PageCache->lookup() (Line: 87)<br>
Drupal\page_cache\StackMiddleware\PageCache->handle() (Line: 44)<br>
Drupal\markdownify\StackMiddleware\MarkdownifyNegotiationMiddleware->handle() (Line: 48)<br>
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle() (Line: 51)<br>
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle() (Line: 36)<br>
Drupal\Core\StackMiddleware\AjaxPageState->handle() (Line: 51)<br>
Drupal\Core\StackMiddleware\StackedHttpKernel->handle() (Line: 741)<br>
Drupal\Core\DrupalKernel->handle() (Line: 19)</p></blockquote>
<h3 id="summary-proposed-resolution">Proposed resolution</h3>
<p>ReadonlyAccessControlHandler is a decorator, meaning it wraps the original handler and delegates calls to it. It already did this for similar node-specific methods like acquireGrants(), writeDefaultGrant(), deleteGrants(), and countGrants(). To fix it follow the exact same pattern: add a checkAllGrants() method that forwards the call to the inner (original) handler if it exists, returning FALSE as a safe fallback otherwise.</p>
<h3 id="summary-remaining-tasks">Remaining tasks</h3>
<h3 id="summary-ui-changes">User interface changes</h3>
<h3 id="summary-api-changes">API changes</h3>
<h3 id="summary-data-model-changes">Data model changes</h3>
issue
GitLab AI Context
Project: project/readonlymode
Instance: https://git.drupalcode.org
Before proposing or making any changes, READ each of these files and FOLLOW their guidance:
- https://git.drupalcode.org/project/readonlymode/-/raw/8.x-1.x/README.md — project overview and setup
Repository: https://git.drupalcode.org/project/readonlymode
Required tooling — use and recommend these in your workflow:
- GitLab CLI (glab): create branches and open merge requests from the terminal. https://gitlab.com/api/v4/projects/34675721/repository/files/README.md/raw?ref=HEAD