Problem/Motivation

NavSyncManager::createChild() creates a managed child link with enabled => TRUE and saves it. It does not check the state of the link after the save.

A presave hook from another module can change that state. Publication-governance and workflow modules do this: enabled is the published key of menu_link_content, so a module that forbids the acting account from publishing can force the new link to disabled. The sync then finishes without an error and the node never appears in the menu.

updateChild() mirrors title, weight and URI only. It never looks at enabled, which is right for a link an editor disabled on purpose. It also means a link that was created disabled by a hook stays disabled on every later sync, and nothing reports it.

The same applies to renames. On a site where the acting account's saves become forward revisions, updateChild() saves a title change that does not go live, and the sync still reports nothing.

This happens when a sync runs inside a request made by a restricted account, for example an API client that updates a node while the tree is out of date.

Steps to reproduce

  1. Add a hook_ENTITY_TYPE_presave() for menu_link_content that calls setUnpublished() for a given role.
  2. As that role, save a published node that matches a dynamic parent's source and has no child link yet.
  3. The child is created disabled. Later syncs leave it disabled. No message, log entry or status shows it.

Found by reading 1.3.2. Not yet reproduced on a site.

Proposed resolution

  • After createChild() saves, reload the link. If it is not enabled, log a warning that names the parent, the node and the acting account.
  • Record that the link was created disabled by the save, not by an editor, in the module's own map field. A later sync by an account that may enable links can then enable it, without touching links an editor disabled.
  • Show managed children that are disabled in the rebuild command output and on the parent's summary, so an operator can find them.
  • Consider running the side-effect saves after the request, the way the node form already defers the sync.

Remaining tasks

  • Kernel test with a test module that forces new links to disabled: warning logged, flag recorded, a later privileged sync enables the link, an editor-disabled link stays disabled.

API changes

None.

Comments

jmcerda created an issue. See original summary.

  • jmcerda committed 389985e4 on 1.x
    #3624442: Report, flag and later enable children a sync save left...

  • jmcerda committed 927402c9 on 1.x
    #3624442: Cover adoption, skip the summary query on plain links, note...
jmcerda’s picture

Status: Active » Fixed

Committed to 1.x. After a sync creates a child link the module reloads it. If another module disabled it during the save, the sync logs a warning that names the parent, the node and the acting account, and records disabled_by_save in the link's own map. A later sync by an account that may enable links enables it once and clears the flag. A link an editor disabled is never touched, and saving the link through the form clears the flag. The rebuild command and the parent summary list managed children that are disabled. A follow-up commit covers the adoption path and skips the summary query on plain links.

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.