Problem/Motivation
Steps to reproduce:
Install, add a link to Tools menu and go to admin/structure/menu/manage/tools.
There's no link to delete the menu link. There's a reset link, but following that leads to an error:
Recoverable fatal error: Argument 1 passed to Drupal\Core\Menu\MenuTreeStorage::save() must be of the type array, null given, called in /home/sb56c5d1abc73ea6/www/core/lib/Drupal/Core/Menu/MenuLinkManager.php on line 405 and defined in Drupal\Core\Menu\MenuTreeStorage->save() (line 256 of /home/sb56c5d1abc73ea6/www/core/lib/Drupal/Core/Menu/MenuTreeStorage.php).
Notice: Undefined index: menu_link_content:749ebca4-77db-43af-9706-06d286d0479c in Drupal\Core\Menu\MenuLinkManager->resetInstance() (line 403 of /home/sb56c5d1abc73ea6/www/core/lib/Drupal/Core/Menu/MenuLinkManager.php).
Discovered in #2313263: Page not found after adding, editing or deleting a menu link:
In MenuForm.php we have:
// Links can either be reset or deleted, not both.
if ($link->isResettable()) {
$operations['reset'] = array(
'title' => $this->t('Reset'),
'url' => Url::fromRoute('menu_ui.link_reset', ['menu_link_plugin' => $link->getPluginId()]),
);
}
elseif ($delete_link = $link->getDeleteRoute()) {
Where isResettable() is:
interface MenuLinkInterface extends PluginInspectionInterface, DerivativeInspectionInterface {
...
/**
* Returns whether this link can be reset.
*
* In general, only links that store overrides using the
* menu_link.static.overrides service should return TRUE for this method.
*
* @return bool
* TRUE if it can be reset, FALSE otherwise.
*/
public function isResettable();
In MenuLinkBase.php:
abstract class MenuLinkBase extends PluginBase implements MenuLinkInterface {
...
/**
* {@inheritdoc}
*/
public function isResettable() {
return AccessResult::forbidden();
}
This makes that if ($link->isResettable()) above always true.
Another usage sample from MenuLinkResetForm.php:
/**
* Checks access based on whether the link can be reset.
*
* @param \Drupal\Core\Menu\MenuLinkInterface $menu_link_plugin
* The menu link plugin being checked.
*
* @return \Drupal\Core\Access\AccessResultInterface
* The access result.
*/
public function linkIsResettable(MenuLinkInterface $menu_link_plugin) {
return AccessResult::allowedIf($menu_link_plugin->isResettable())->setCacheable(FALSE);
}
Where allowedIf() is:
abstract class AccessResult implements AccessResultInterface, CacheableInterface {
...
/**
* Creates an allowed or neutral access result.
*
* @param bool $condition
* The condition to evaluate.
*
* @return \Drupal\Core\Access\AccessResult
* If $condition is TRUE, isAllowed() will be TRUE, otherwise isNeutral()
* will be TRUE.
*/
public static function allowedIf($condition) {
return $condition ? static::allowed() : static::neutral();
}
Unless I'm missing something this turns AccessResult::forbidden() to AccessResult::allowed().
Proposed resolution
Make isResettable() return bool
or
Rename isResettable() to getSomething() or checkResetAccess() and fix usages?
Remaining tasks
User interface changes
API changes
Beta phase evaluation
| Issue category | Clear bug |
|---|---|
| Issue priority | Major because it potentially affects quite some site builders. |
| Disruption | Not disruptive |
| Comment | File | Size | Author |
|---|---|---|---|
| #12 | 2373017-11.patch | 3.45 KB | olli |
| #10 | interdiff.txt | 1.17 KB | olli |
| #10 | 2373017-10.patch | 3.46 KB | olli |
| #10 | 2373017-10-test_only.patch | 1.37 KB | olli |
Comments
Comment #1
olli commentedComment #2
olli commentedComment #3
olli commentedComment #5
olli commentedComment #6
olli commentedComment #7
olli commentedComment #9
olli commentedThis should have failed in test_only patch...
Comment #10
olli commentedComment #12
olli commentedreroll
I wonder if I should move this @todo somewhere else.
Comment #13
olli commentedComment #14
olli commentedComment #15
olli commented#12 Just a guess: move it to MenuLinkResetForm::linkIsResettable()?
Comment #18
dawehner... I guess its fine to not pretend to make the admin listing cacheable.
Comment #19
catchCommitted/pushed to 8.0.x, thanks!