XF 2.2 link function not generating proper path

stromb0li

Active member
I'm trying to add a new sub-navigation item to the user account section. Whenever I add the following template modification, the link is generated as /account vs /account/my-custom-account-section. Interestingly, if I browse the page directly, my logic to select the sub-navigation item works properly, but the link still only generates as /account.

Is there something else I need to do to generate the path properly? I am extending the xfcp_account class in my controller, so it should have context to account.

Code:
<?xml version="1.0" encoding="utf-8"?>
<template_modifications>
    <modification type="public" template="account_wrapper" modification_key="my_template_modification" description="Add nav item" execution_order="10" enabled="1" action="preg_replace">
        <find><![CDATA[/\<xf:if is="\$xf\.app\.connectedAccountCount\"\>/]]></find>
        <replace><![CDATA[<a class="blockLink {{ $pageSelected == 'my_custom_account_section' ? 'is-selected' : '' }}" href="{{ link('account/my-custom-account-section') }}">
            {{ phrase('my_custom_account_section') }}
        </a>
        $0]]></replace>
    </modification>
</template_modifications>

Cheers!
 
I can't reproduce this, the href is generated as /account/my-custom-account-section. It might be worth making sure no other add-ons or route filters are interfering.
 
It looks like it is the route entry I've added for my add-on. If I remove the route, it works well, but that then prevents me from having additional routes for account/my-custom-account-section/item/1 for example. What is the proper way to specify the route so it is not hidden when trying to extend the account routes?

Here is what I have now.
Code:
<?xml version="1.0" encoding="utf-8"?>
<routes>
      <route route_type="public" route_prefix="account" sub_name="my-custom-account-section" controller="XF:Account" context="CustomAccountSection"/>
</routes>
 
Last edited:
Sorry for double reply, but I think I'm getting really close now.

In my route file for the add-on, I have:
XML:
      <route route_type="public" route_prefix="account" sub_name="my-custom-account-section" format="my-custom-account-section/:+int&lt;section_id&gt;/" controller="XF:Account" context="account"/>

This now allows link('account/my-custom-account-section') to display properly, but I'm not able to catch /account/my-custom-account-section/1/edit for example to process the request. Any thoughts on how I can achieve this?

Thank you!
 
That would correspond to the actionEdit method of the controller. For creating sub-routes you would generally either create a new controller or set an action prefix. For example, if you set an action prefix myCustomAccountSection, it would correspond to the actionMyCustomAccountSectionEdit method.
 
I found if I use actionEdit it works, but actionMyCustomAccountSectionEdit does not work (which actionMyCustomAccountSection is what I am using for /account/my-custom-account-section). I also cannot seem to get the parameterbag $params to populate with the section_id, in either case as well.
 
For creating sub-routes you would generally either create a new controller or set an action prefix.
If you create a new controller, account/my-custom-account-section corresponds to the actionIndex method. If you set an action prefix, it corresponds to the actionMyCustomAccountSectionIndex method.
 
I'm really sorry, I don't understand.

Since this is trying to extend account, I currently have the controller in \src\upload\myaddon\XF\Pub\Controller as Account.php;
class Account extends XFCP_Account. Then I have actionMyCustomAccountSection() and that serves /account/my-custom-account-section well. I was assuming I'd be able to use actionMyCustomAccountSectionEdit(), but instead it seems to be actionEdit in the same account class to serve the expected view.

Is the ask to create \src\upload\myaddon\XF\Public\Controller\my-custom-account-section\MyCustomAccountSection.php or \src\upload\myaddon\XF\Public\Controller\MyCustomAccountSection.php?
 
You're creating a new route, and a route maps a base path to a controller (and optional action prefix). The action method that a URL maps to is based on its sub-path.

For example, if you create a route to map the account/notes base path to the XF:AccountNote controller, then the account/notes URL will map to \XF\Pub\Controller\AccountNote::actionIndex and the account/notes/edit URL will map to \XF\Pub\Controller\AccountNote::actionEdit.

Alternatively if you create a route to map the account/notes base path to the XF:Account controller with the action prefix note, then the account/notes URL will map to \XF\Pub\Controller\Account::actionNoteIndex and the account/notes/edit URL will map to \XF\Pub\Controller\Account::actionNoteEdit.

If you do neither then the account/notes URL will map to \XF\Pub\Controller\Account::actionNotes because it will match the account route, where the base path is account and the sub-path is notes.

What you've done currently is create a duplicate route which maps a new base path to the same controller. This means that account/my-custom-account-section/username will route to the same place as account/username (\XF\Pub\Controller\Account::actionUsername), which is likely to yield unexpected behavior.

The ask is to either create a new controller or define an action prefix and update your action methods so the sub-paths map to them accordingly.
 
Last edited:
I'm getting really frustrated at myself that I cannot figure this out.....

So let's say I follow this approach:
For example, if you create a route to map the account/notes base path to the XF:AccountNote controller, then the account/notes URL will map to \XF\Pub\Controller\AccountNote::actionIndex and the account/notes/edit URL will map to \XF\Pub\Controller\AccountNote::actionEdit.

In my add-on, I'd still use XF\Pub\Controller\AccountNote.php and then declare the following?
Code:
namespace Notes\Pub\Controller;
use XF\Mvc\ParameterBag;
use XF\Pub\Controller\AbstractController;

class Notes extends AbstractController

In routes, I'd have?
Code:
<route route_type="public" route_prefix="account" sub_name="notes" format="notes/:+int&lt;section_id&gt;/" controller="XF:AccountNote" />

Extending account is what's confusing me. If this was all separate, I think I'd be having much more success.
 
Close! My example of \XF\Pub\Controller\AccountNote may have been misleading since it does not actually exist in the core and your add-on would create a new controller in its own namespace rather than the XF namespace.

In your example above, you're creating a new controller class \Notes\Pub\Controller\Notes (in src/addons/Notes/Pub/Controller/Notes.php):
PHP:
<?php

namespace Notes\Pub\Controller;

use XF\Mvc\ParameterBag;
use XF\Pub\Controller\AbstractController;

class Notes extends AbstractController
{
    // your actions
}

This means your route controller would be Notes:Notes (rather than XF:AccountNote). The : expands to Pub\Controller to match the class and file.

Alternatively you could continue extending \XF\Pub\Controller\Account, but set your route action prefix to note. Then rename your actions to be actionNoteIndex to match /account/notes and actionNoteEdit to match /account/notes/edit, etc.
 
I still cannot figure this out... It seems like I cannot properly use the route as I can't use a route_prefix of "account/notes" which would be my base prefix.

If I use:
Code:
<route route_type="public" route_prefix="account" sub_name="notes" format="notes/:+int&lt;note_id&gt;" controller="XF:AccountNotes" />

Then it still seems like the original account route is being used:
The requested page could not be found. (Code: invalid_action, controller: XF:Account, action: Notes)

If I use:
Code:
<route route_type="public" route_prefix="account" sub_name="notes" controller="XF:AccountNotes" />

I see it is going to my controller, but it still is expecting "Notes" vs actionIndex as the function name:
The requested page could not be found. (Code: invalid_controller, controller: XF:AccountNotes, action: notes)

I've also tried moving my controller to /upload/src/addons/Notes/Pub/Controller/AccountNotes.php with the following route:
Code:
<route route_type="public" route_prefix="account" sub_name="notes" controller="Notes:AccountNotes" />

But receive the following error:
The requested page could not be found. (Code: invalid_action, controller: Notes:AccountNotes, action: Notes)

Here is a copy of my controller in /upload/src/addons/Notes/XF/Pub/Controller/AccountNotes.php
PHP:
<?php

namespace Notes\XF\Pub\Controller;

use XF\Mvc\ParameterBag;
use XF\Pub\Controller\AbstractController;

class AccountNotes extends AbstractController
{
    public function actionIndex(){
        $visitor = \XF::visitor();
        $auth = $visitor->Auth->getAuthenticationHandler();
        if (!$auth)
        {
            return $this->noPermission();
        }

        return $this->message('Index View.');
    }
 
    public function actionEdit(ParameterBag $params){
        $visitor = \XF::visitor();
        $auth = $visitor->Auth->getAuthenticationHandler();
        if (!$auth)
        {
            return $this->noPermission();
        }

        return $this->message('Edit View.');

    }
}

I'm really sorry I cannot seem to grasp this. Everything else seems to be going well, I just cannot figure out how to build upon this account path...
 
Last edited:
Start with the namespace, class name, and file location of your controller. Since you're creating a new controller, it does not need to reside in the XF sub-namespace. Your namespace should be Notes\Pub\Controller. Your class name can stay as AccountNotes. The file location must correspond exactly with the namespace and class name, so it should be located at src/addons/Notes/Pub/Controller/AccountNotes.php.

Remember that when defining public routes, the : in the controller expands to \Pub\Controller\. Since your fully qualified class name (that is, the namespace and class name together) is \Notes\Pub\Controller\AccountNotes, the controller for the route should then be set to Notes:AccountNotes.
 
I tried moving the controller to /Pub/Controller/AccountNotes.php, but I receive the following:
The requested page could not be found. (Code: invalid_action, controller: Notes:AccountNotes, action: Notes)

Route:
<route route_type="public" route_prefix="account" sub_name="notes" controller="Notes:AccountNotes" />

Given the above explanation, I thought I'd reference actionIndex() for account/notes?
 
Last edited:
Ensure your route format matches the sub-name. Using your code (with a fixed namespace) and the following route configuration and browsing to account/notes:

1693186424406.png

1693186457692.png

When setting a sub-name, the route format needs to be set to match. I had overlooked that detail in your previous message.
 
That works, but then I cannot catch any value after account/notes. I.e. if I want account/notes/1/edit, using the following route will cause me to not match

Route:
The requested page could not be found. (Code: invalid_action, controller: XF:Account, action: Notes)
<route route_type="public" route_prefix="account" sub_name="notes" format="notes/:+int&lt;note_id&gt;/" controller="Notes:AccountNotes" context="account"/>


While it works for account/notes/1/edit, for account/notes, I'm back to being caught by the account route:
The requested page could not be found. (Code: invalid_action, controller: XF:Account, action: Notes)
 
That would be because the format has a + in front of the note_id parameter, which means the note_id must be present for the route to match. If you remove the + then the note_id is optional.
 
That would be because the format has a + in front of the note_id parameter, which means the note_id must be present for the route to match. If you remove the + then the note_id is optional.

Face Palm No GIF


Thank you!!! I really really really appreciate all the help! I think I finally got a handle on this now.
 
No worries, a lot of this stuff should be properly documented to avoid the headaches. Glad you got it working.
 
Top Bottom