• This forum has been archived. New threads and replies may not be made. All add-ons/resources that are active should be migrated to the Resource Manager. See this thread for more information.

[GUIDE] Custom Pages with Navigation Tabs

Jaxel

Well-known member
This guide will explain how to make custom pages for your mods. The first thing you need to do is create your mod with a unique addon ID, and then create a directory in the /library/ folder to follow that ID. The name of your addon ID should begin with a letter; I tried to begin with a number, but it created programs as PHP doesn't like classes that start with numbers. For this example, I am using an addon ID I created called "World"... this will be a Hello World mod!

Once your mod is created, you will need to create a routing class. Based on the name of my mod, I created the class "World_Route_Prefix_Portal" at "/library/World/Route/Prefix/Portal.php".
Code:
<?php

class World_Route_Prefix_Index implements XenForo_Route_Interface
{
	public function match($routePath, Zend_Controller_Request_Http $request, XenForo_Router $router)
	{
		return $router->getRouteMatch('World_ControllerPublic_Index', 'index', 'world', $routePath);
	}
}
This one is simple, the class contains a function that matches the URL a user inputs and sends it to the appropriate location. This function assumes the user will be going to the /portal/ URL and it will forward the user to /portal/index. For getRouteMatch, we are passing 4 parameters:
  • World_ControllerPublic_Index - this is the function we will be sending the user to
  • index - you could put $routePath here, in order to have nested actionFunctions depending on if a user puts in /world/subdir or something along those lines. In this case, I am making it so that no matter what a user inputs, they will always be sent to /world/index.
  • portal - this is a variable defining what 'navigation tab' the user is on. Just like the index field, you can customize this, but then you will need to make a navigation tab for each and every portal page. This is what is called the "Major Section"
  • $routePath - this is what is called the "Minor Section". This field will store additional URL information beyond the /world/. So if a user goes to /world/edit, this field will contain "edit". You could use this information to redirect the user depending on whats in the Minor Section.
The next thing you need to do is create the routing prefix in XenForo so that it knows the routing class we just created exists. You can do this at the "admin.php?route-prefixes/" link on your XF installation. Be warned that you must be in debug mode to create modifications through XenForo's control panel!
  • Route Prefix: world
  • Route Type: public
  • Route Class: World_Route_Prefix_Index
Next we are going to create a navigation tab so that users cant click a link and head to /world/. The first thing we need to do is create the class which processes the navtab. Based on the name of my mod, I created the class "World_Listeners_Navigation" at "/library/World/Listeners/Navigation.php".
Code:
<?php

class World_Listeners_Navigation
{
	public static function navtabs(array &$extraTabs, $selectedTabId)
	{
		$extraTabs['world'] = array(
			'title' => 'Hello World',
			'href' => 'world/',
			'selected' => ($selectedTabId == 'world'),
			'linksTemplate' => 'World_Navtabs',
		);
	}
}

This function creates the second row of links for when you click on the Hello World primary tab. The primary tab is generated in a Template called "World_Navtabs"... which must be created.
Code:
<ul class="secondaryContent blockLinksList">
	<li><a href="{$xenOptions.boardUrl}/world/">Hello World</a></li>
</ul>

In order to continue, we need to use XenForo's hook system called "Code Listeners". You can create new Code Listeners at the "admin.php?code-event-listeners/" link on your XF installation.
  • Listen to Event: navigation_tabs
  • Execute Callback: World_Listeners_Navigation::navtabs
Now we have a Routing Class, and a Navigation Tab Class... whats left? Well the actual page! We'll keep this next part simple. Based on the name of my mod, I created the class "World_ControllerPublic_Index" at "/library/World/ControllerPublic/Index.php".
Code:
<?php

class World_ControllerPublic_Index extends XenForo_ControllerPublic_Abstract
{
	public function actionIndex()
	{
		$text = 'Hello World!';

		$viewParams = array(
			'text' => $text,
		);

		return $this->responseView('World_ViewPublic_Index', 'World_Index', $viewParams);
	}
}
This one is simple, declare the action function for when someone goes to /world/index, store Hello World into a string, add the string to passable parameters, then call up the XenForo template generator called responseView. You'll notice a class called "World_ViewPublic_Index", thankfully you dont actually have to make this class, as it will be generated automatically. The second parameter however "World_Index" is referencing a template which must be created.
Code:
<xen:if hascontent="true">
<div class="sectionMain">
	<xen:contentcheck>
		{xen:raw $text}
	</xen:contentcheck>
</div>
</xen:if>

Not so simple is it eh? I made this guide off the top of my head, so if anything is wrong, please tell me.

Remember! I'm an unemployed programmer who enjoys donations!
 
Can you post some screenshots?

I'm very interested to see what can be done with Pages.
 
Code:
	<li><a href="{$xenOptions.boardUrl}/world/">Hello World</a></li>
This would best be replaced with:
Code:
	<li><a href="{xen:link world/}">Hello World</a></li>

Besides that, good tutorial. Was about to make it myself, haha.

One question:
Here's my navtab template:
Code:
<ul class="secondaryContent blockLinksList">
<li><a href="{xen:link ezirc/options/}">{xen:phrase ezirc_options}</a></li>
</ul>
The list / subnav options do not show up on the page itself, but does show on the dropdown from a different page. Any ideas?
 
Yes...
Code:
<?php

class World_Route_Prefix_Index implements XenForo_Route_Interface
{
	public function match($routePath, Zend_Controller_Request_Http $request, XenForo_Router $router)
	{
		return $router->getRouteMatch('World_ControllerPublic_Index', 'index', 'world', $routePath);
	}
}
Code:
<?php

class World_Listeners_Navigation
{
	public static function navtabs(array &$extraTabs, $selectedTabId)
	{
		$extraTabs['world'] = array(
			'title' => 'Hello World',
			'href' => 'world/',
			'selected' => ($selectedTabId == 'world'),
			'linksTemplate' => 'World_Navtabs',
		);
	}
}

There are three instances of 'world' in the code above. All 3 instances must match, otherwise your subnav will break.
 
Yes...
-snip-
There are three instances of 'world' in the code above. All 3 instances must match, otherwise your subnav will break.
My code is based along those lines, not exactly as my code did not come from this tutorial. Very similar though.
So, you're saying the href array value must be the same as the identifier?


Edit: That's odd, changing the identifiers to the original href value worked .. not sure why it's like that, but works for me. Thanks :)
 
Code:
<?php

class World_Listeners_Navigation
{
	public static function navtabs(array &$extraTabs, $selectedTabId)
	{
		$extraTabs['world'] = array(
			'title' => 'Hello World',
			'href' => 'world/',
			'selected' => ($selectedTabId == 'world'),
			'linksTemplate' => 'World_Navtabs',
		);
	}
}

There are three instances of 'world' in the code above. All 3 instances must match, otherwise your subnav will break.
Almost there - you need to use the link builder in the PHP code as well as the templates, and it's good practice to use phrases:
PHP:
public static function navtabs(array &$extraTabs, $selectedTabId)
{
	$extraTabs['world'] = array(
		'title' => new XenForo_Phrase('hello_world'),
		'href' => XenForo_Link::buildPublicLink('full:world'),
		'selected' => ($selectedTabId == 'world'),
		'linksTemplate' => 'World_Navtabs',
	);
}
 
$routePath - array or string?

I am asking because if I have something like /world/edit/1 - how do get the '1'?
 
The tab highlight is not working for me:(

router
PHP:
class Ragtek_Linklist_Route_Prefix_Index implements XenForo_Route_Interface
{
    public function match($routePath, Zend_Controller_Request_Http $request, XenForo_Router $router)
    {
               $action = $router->resolveActionWithIntegerParam($routePath, $request, 'link_id');
        return $router->getRouteMatch('Ragtek_Linklist_ControllerPublic_Index', $action, 'links');
    }
}

Navtab:
PHP:
class Ragtek_Linklist_StaticMethods
{
    static public function addTab(array &$extraTabs, $selected)
    {
        $extraTabs[] =array(
            'title' => new XenForo_Phrase('ragtekLinklist'),
            'selected' => ($selected == 'links'),
            'href'   => XenForo_Link::buildPublicLink('links'),
        );
    }
The tab is in the navbar and when i click on it, it loads the right controller, but it's not highlighted.
 
class Ragtek_Linklist_StaticMethods
{
static public function addTab(array &$extraTabs, $selected)
{
$extraTabs[] =array(
'title' => new XenForo_Phrase('ragtekLinklist'),
'selected' => ($selected == 'links'),
'href' => XenForo_Link::buildPublicLink('links'),
);
}
[/php]
The tab is in the navbar and when i click on it, it loads the right controller, but it's not highlighted.

Try using 'selected' => ($selectedTabId == 'links'),

Set the selected tab ID to match the name of the extra tab, ie:

$extraTabs['my_cool_tab'] =array(
'title' => new XenForo_Phrase('coolTab'),
'selected' => ($selectedTabId == 'my_cool_tab'),
'href' => XenForo_Link::buildPublicLink('coollinks'),
);
 
thx

$extraTabs['links'] =array(

was missing but it still not works:(

I've started debugging and here's the strange thing:
PHP:
XenForo_CodeEvent::fire('navigation_tabs', array(&$extraTabs, $selectedTabId));



        if (!empty($extraTabs[$selectedTabId]))
        {
             Ragtek_Debug::show($extraTabs[$selectedTabId]);
            $selectedTab = $extraTabs[$selectedTabId];
        }

[/php]
$xtraTabs[$selectedTabId] is a Array
Code:
Array
(
    [title] => XenForo_Phrase Object
        (
            [_phraseName:protected] => ragtekLinklist
            [_params:protected] => Array
                (
                )

            [_insertParamsEscaped:protected] => 1
        )

    [selected] => 1
    [href] => links/
)
1
 
In the example above, it seems some of the code says "portal" while the instructions say "world" or visa versa...
I think...
 
Code:
<?php

class World_ControllerPublic_Index extends XenForo_ControllerPublic_Abstract
{
public function actionIndex()
{
$text = 'Hello World!';

$viewParams = array(
'text' => $text,
);

return $this->responseView('World_ViewPublic_Index', 'World_Index', $viewParams);
}
}
This one is simple, declare the action function for when someone goes to /world/index, store Hello World into a string, add the string to passable parameters, then call up the XenForo template generator called responseView. You'll notice a class called "World_ViewPublic_Index", thankfully you dont actually have to make this class, as it will be generated automatically. The second parameter however "World_Index" is referencing a template which must be created.
Code:
<xen:if hascontent="true">
<div class="sectionMain">
<xen:contentcheck>
{xen:raw $text}
</xen:contentcheck>
</div>
</xen:if>

Hmm I can't get this part to work
It will not use the template

PHP:
<?php

class VKmanga_ControllerPublic_Index extends XenForo_ControllerPublic_Abstract
{
    public function actionIndex()
    {
        $text = 'Hello World!';

        $viewParams = array(
            'text' => $text,
        );

        return $this->responseView('VKmanga_ViewPublic_Index', 'VKmanga_Main', $viewParams);
    }
}

Has something changed? as this was posted last year.

Thanks

Edit = Np got it working :)
 
Arrr problem found ;)

@sifuhall: Maybe you did the same mistake like me. In Prefix/Portal.php the first line has to be:
class Texturen_Route_Prefix_Portal, not Index!

Cheers, now it works. Thank you for this tutorial.
 
Top Bottom