For the complete documentation index, see llms.txt. This page is also available as Markdown.

Keeping a custom news URL shape in e107

How to make e107 produce and understand a news URL shape that is not one of the four built-in ones — without redirects, and without editing core files.

The worked example is the shape

/news/view-146-my-article-slug

instead of e107's built-in

/news/view/146/my-article-slug

but the method is the same for any shape you can express as a rule.


When you need this

The usual reason is a site migration. Your old URLs are indexed by search engines, and the new codebase produces a different shape. You have two options:

301 redirects
Custom URL profile

Old URLs keep working

yes, via a redirect hop

yes, natively

Links on the page use

the new shape

the old shape

<link rel="canonical"> points to

the new shape

the old shape

Search engines must

reindex everything

do nothing

Ongoing maintenance

rewrite rules to keep

none

Redirects are fine when you want to move to the new shape. When you want to keep the old shape, a custom URL profile is the better answer: e107 then treats your shape as the real one, and the question of redirects disappears.


How e107 URL profiles work

A URL profile is a PHP class describing one URL shape for one module. It does both directions:

  • parsing — an incoming request is matched against the profile's rules and turned into a route plus variables;

  • creatinge107::url('news', 'view', $row) runs the same rules backwards and emits a URL in that shape.

Because one profile does both, you never end up with pages that link one way and resolve another.

The built-in news profiles live in e107_core/url/news/:

File
Shape

url.php

news.php?extend.1 (no SEF)

sef_full_url.php

/news/news-category/news-title

sef_noid_url.php

/news/news-title

sef_url.php

/news/view/1/news-title

Do not edit these. The next e107 update overwrites them.

The override directory

e107 provides e107_core/override/url/ for exactly this. A profile placed at

takes precedence over e107_core/url/news/sef_url.php. The override directory is not part of the upstream distribution, so updates leave it alone.

Two naming rules, both mandatory:

  1. The file name must match a core profile file name. Here, sef_url.php. A new name such as hyphen_url.php will not be picked up.

  2. The class name is derived from the location. For an override of the news module it is override_news_sef_url, and it extends eUrlConfig.

Your profile does not have to resemble the core file it shadows — only the file name matters. The core profile you shadow becomes unavailable while the override exists, so shadow the one you are least likely to want.


Writing the profile

A profile class has two methods: config() returns the configuration and the rules, admin() returns what the admin screen shows.

The rule syntax

A rule maps a URL pattern to a route:

  • <id:{number}> — a named placeholder and its pattern. {number} matches digits, {sefsecure} matches a slug, {secure} matches a general string.

  • 'view/item' — the route this rule maps to.

  • allowVars — query variables allowed to survive, e.g. ?page=2.

  • mapVars — translates database column names to placeholder names, so e107::url('news', 'view', $newsRow) can fill the pattern straight from a news row.

  • legacyQuery — what the legacy entry point (news.php) is called with. extend.{id} becomes news.php?extend.146.

Order matters. The first matching rule wins, both when parsing and when creating. Put the most specific rules first.

Separators and ambiguity

The interesting part of the hyphen shape is that the separator also occurs inside the slug: view-146-my-article-slug. This works because the placeholder patterns are anchored — {number} matches digits only, so the boundary between 146 and the slug is unambiguous. view-146-2024-annual-report parses as id=146, name=2024-annual-report, not as id=1462024.

Do not rely on this in general. If you design a shape where two adjacent placeholders can both match the same characters, the split becomes arbitrary. Keep at least one anchored placeholder, or a separator that cannot appear in the values.

Empty-value fallbacks

If a news item has no slug, a rule expecting one cannot match. Declare a shorter rule after it:

With 'matchValue' => 'empty' in the config block, a rule whose placeholder value is empty is skipped when creating a URL, and the next rule is tried.

Covering every route

A profile must cover every route the module emits, not just the one you care about. Miss one and those links break. For news that is: index, all, item view, category list, category brief, day archive, month archive, tag and author. Take the core profile you are shadowing as the checklist.

The admin block

label and description are what you see in the admin dropdown — make them distinguishable from the core profile you are shadowing, or you will not be able to tell which one is selected.

generate drives the Rebuild button, which regenerates slugs from titles. On a migrated site this is dangerous — see below.


Installing it

  1. Create e107_core/override/url/news/ and put the profile file in it.

  2. Go to Admin → Settings → URLs.

  3. Select your profile in the News row.

  4. Press Update.

  5. Confirm that e107_system/cache/url/config.php has been regenerated: the news section should name your file in configPath and your class in configClass, and rules should hold your rule set.

If you edit the profile afterwards, delete e107_system/cache/url/config.php. The compiled rule set is cached, and an edited profile that "does nothing" is almost always a stale cache.

If your profile does not appear in the dropdown

At the time of writing, an override for a module whose name is also a plugin folder — which includes news — is discarded before it reaches the admin screen. See Known issue below.


Testing

Test each route in both directions: open the URL (parsing) and check a link on a page that points to it (creating). Parsing alone is not enough — a profile can resolve a URL correctly and still emit a different shape in links.

  • item, with and without a slug

  • item whose slug starts with digits — confirms the separator logic

  • item with a non-ASCII slug

  • category list, category brief

  • day and month archives

  • tag and author lists

  • second page of a paginated list (?page=2)

  • the site's news index

Then view the page source of an article and check <link rel="canonical"> carries your shape, not the built-in one.

Trailing slashes

e107 strips the trailing slash from every URL it generates. If your indexed URLs have one, they still resolve — matching tolerates it — but new links and the canonical tag will not have it. Search engines consolidate on the canonical form over time. Nothing breaks; expect the shape in search results to change eventually.


Migrating an existing site

The profile handles URL shape. It does not handle the values inside them.

The slugs must survive the migration. Your URLs embed news_sef and category_sef values. If those columns do not come across intact, the profile will happily generate correctly-shaped URLs containing different slugs, and every indexed URL becomes wrong. Migrate those columns 1:1 and verify a few rows by hand before switching the profile on.

Do not press "Rebuild". The Rebuild button in URL configuration regenerates slugs from titles. On a site whose URLs are already indexed this silently rewrites them. If a title has been edited since publication — or the slug was ever adjusted by hand — the regenerated slug differs from the indexed one.

With an ID in the URL nothing 404s, because the ID is what the lookup uses. That is the trap: the site looks fine while the canonical tag now points somewhere other than the indexed URL. Leave Rebuild alone unless you are deliberately regenerating slugs and prepared to redirect.


Known issue: override not listed for news

eRouter::adminReadModules() scans the override directory, then runs a clean-up pass meant to remove overrides belonging to plugins that are not installed:

$plugins is the directory listing of e107_plugins/. Because news is both a core URL module and a bundled plugin folder, it matches — and it can never be in $ret['plugin'], because the plugin loop skips anything already registered as a core module. The news override is therefore removed from the list, and the admin dropdown never offers it.

The runtime is not affected. eRouter::buildGlobalConfig() reads the preference url_config['news'] directly, and any readable value containing a slash is preserved. Only the admin screen cannot offer the choice.

Working around it

Set the preference directly, from a script run as the main admin:

override/sef is the location value for e107_core/override/url/news/sef_url.php. Setting e_url_list as well mirrors what a normal admin save does.

Delete the script afterwards. It grants a settings change to whoever can reach the URL, and the permission check is the only thing standing in the way.

After running it, do not press Update on Admin → Settings → URLs. That form submits whatever the dropdown shows, and the dropdown cannot show your profile — so saving that page silently reverts the preference. If this is a site you maintain long-term, put the snippet behind a button in a small admin plugin, so restoring it is one click rather than a file upload.

Verify by opening e107_system/cache/url/config.php: the news section must name your override file, and rules must not be empty.


Checklist

Last updated