Permanent Tourist

The personal website of Mark Howells-Mead

Some people write documentation; some people keep Wikis containing stuff that they need to refer back to. I blog, because I’ve been using WordPress since 2003.

This time, the topic is how to set permalinks for news pages in WordPress as a user would expect them to be.

Modify the permalinks

The standard behaviour is that posts have permalinks outside the normal hierarchical structure: for example, instead of the URL containing /hotel/news/2024/04/my-article/, it’s usually /2024/04/my-article/. This can be illogical for a website owner who wants the single news view URLs to be logically structured “within” the news section of the website.

For this first step, override the prefix in the permalink settings, choosing /hotel/news/%year%/%monthnum%/%postname%/ as an example. Save the values and flush any cache you might have in place for the website, and off you go.

Next up, the list page. This will need to be created as a regular page, in this example /hotel/news/, by adding a subpage “News” to the top-level page “Hotel”.

Change the Reading Settings in WordPress and select this new page as the “Posts page”. Assuming you’re using the site editor with HTML-based templates in your theme, this page will then draw its content from the index.html template (or one of the archive templates). Note here that the slug of this page must match the prefix you added manually to the permalink settings.

This doesn’t actually move the posts into the news section of the site; it just pretends to by modifying the URL.

Highlighting the menu entries in the navigation

Add a navigation block to the relevant place in the site editor. (I prefer to use my custom block which uses wp_nav_menu to render using PHP.) Add your news page to the menu or to the navigation and it will be highlighted as expected when you’re on the news list page.

Next up, you’ll want that top-level menu entry to be highlighted when you’re on the single view page too. This is where things start to get tricky. This isn’t how WordPress menus usually work, so you’ll need to add a CSS class name to the menu entry yourself.

In the site I’m working on, the “News” page is a subpage of “Hotel”. When I’m on the post single view, I want the top-level menu entry “Hotel” and the “News” entry to be highlighted. WordPress already recognises “News” as the designated post list page, so it gets the current_page_parent class name automatically when viewing the single post view.

However, it doesn’t know that “Hotel” is the “News” page ancestor. (It does on the list view, just not on the single view.)

Add a CSS class manually

Using the nav_menu_css_class filter in PHP, we’ll add the necessary class ourselves. The logical semantic core class name for this is current-menu-ancestor. We just need to work out how and when to add it.

The logic is simple enough: if we’re on a single post view, and if the menu item is the top-level ancestor of the post list page, then add the class name. First of all, we get the page ID of the “News” page’s top-level ancestor with the function getPostListRootId.

Because the function will be called once for every menu item on the entire site, we want to avoid calling get_option('page_for_posts') more than once, so it’s saved as a class variable $post_list_root_id if it’s not been defined yet.

private $post_list_root_id = 0;

…

private function getPostListRootId()
	{
		if ($this->post_list_root_id) {
			return (int) $this->post_list_root_id;
		}

		$post_archive_page_id = get_option('page_for_posts');

		if ((int) $post_archive_page_id) {
			$post_archive_page_ancestors = get_post_ancestors($post_archive_page_id);
			$this->post_list_root_id = $post_archive_page_ancestors[0];
		}

		return (int) $this->post_list_root_id;
	}

Now, we can use that function in the method we’re applying to the menu items using the nav_menu_css_class hook.

add_filter('nav_menu_css_class', [$this, 'menuItemClasses'], 10, 3);

…

public function menuItemClasses($classes, $item, $args)
	{
		if (!is_single()) {
			return $classes;
		}

		if ((int) ($item->object_id ?? null) === $this->getPostListRootId()) {
			$classes[] = 'current-menu-ancestor';
		}

		return $classes;
	}

This will now ensure that the class names are in place, so that we can style them using our own CSS.

Translating the URLs using Polylang Pro

Navigate to the Languages » Translations screen in WordPress admin and search for the slug of your news page (“/news”). Then switch languages using the language selector in the black toolbar at the top of the page. Translate the URL structure, being careful to retain the structure and the slashes, which must be the same in all languages. (Make sure that the pages which correspond to the URL here actually exist.)

Once all of these values are modified, return to the Settings » Permalinks screen and re-save so that the changes are recognised. If the permalinks still don’t work, copy the current value of the permalink structure from the Custom Structure field—you set this before—then choose a standard option and click save. Then paste the custom structure value back into the field and save again. This will force the permalinks to be hard-reset.

…and don’t forget to clear the entire site cache!

Example site

You can check out the website of the Hotel Aare in Thun, for which I embarked on this adventure most recently.

Leave a Reply

Your email address will not be published. Required fields are marked *

For security, use of Google’s reCAPTCHA service is required which is subject to the Google Privacy Policy and Terms of Use.

This site uses Akismet to reduce spam. Learn how your comment data is processed.