Skip to main content

FIXED – Menu settings: custom links can no longer use `#user_url` / `#logout_url` placeholders

Environment

  • FluentCommunity 2.7.7 (Core + Pro)
  • WordPress 7.0.4
  • PHP 8.3

Summary

The link validation in the menu settings form rejects any permalink that does not start with http://, https:// or /. This makes it impossible to create or edit a custom menu item that uses FluentCommunity's own placeholder tokens, such as #user_url/... or #logout_url.

The plugin uses these placeholders for its own built-in items, so the validator contradicts the plugin's own data model.

Steps to reproduce

  1. Go to Settings > Menu Settings
  2. Under "Profile Dropdown Items", add a custom link, or edit an existing custom one
  3. Enter #user_url/scheduled-posts as the permalink
  4. Try to save

Expected: the item is saved.

Actual: validation fails with "URL must start with http:// or https://" and saving is blocked.

Why this is inconsistent

The built-in items ship with exactly these values:

  • my_spaces uses #user_url/spaces
  • logout uses #logout_url

They only survive because validation is skipped for system items (assets/admin_app.js):

if (this.editingItem.is_system !== "yes") {
    const t = Q1(this.editingItem.permalink);
    t.valid || this.urlErrors.push(...t.errors)
}

So the placeholder syntax is valid and fully functional at runtime. It is simply no longer reachable for user-created items.

Root cause

In assets/admin_app.js the validator dispatches on the first character:

Q1 = t => (t = t.trim(), t.startsWith("/") ? Ske(t) : HF(t))

HF, the URL branch, requires a scheme before doing anything else:

if (!/^https?:\/\//i.test(t))
    return e.push(Vi("URL must start with http:// or https://")),
           {valid: false, type: "url", errors: e};

Any value starting with # therefore lands in the URL branch and can never pass.

The server side is not the problem. CustomSanitizer::sanitizeMenuLink() passes the value through sanitize_url(), which preserves #... values unchanged:

$item['permalink'] = sanitize_url(Arr::get($item, 'permalink', ''));

The restriction is purely client-side.

Workaround, and why it is harmful

The only way to push such a link past the form is to prefix it with //, because the path branch Ske then accepts it. The stored value becomes:

//#user_url/scheduled-posts

That is a protocol-relative URL, so the browser reads #user_url as a hostname and the link is dead. We had exactly this in production and had to correct the value directly in the database, in the serialized fluent_community_menu_groups option inside the fcom_meta table.

Suggested fix

Let the validator recognise values starting with #, or more specifically the supported placeholder tokens, and route them past the http/https requirement, the same way paths starting with / are already handled.

Relative paths are accepted today. Placeholders should be too, especially since the plugin ships them in its own default menu items.

Thanks for the post, we are investigating the matter.

Md. Ariful Islam

Fixed in dev. Thanks.

Fixed in 2.8.0. Thank you!