• 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.

[Tutorial] Building a site using XenForo

Robbo

Well-known member
Building a site using (around) XenForo

I need to create a proper site for MercenaryDesign and have decided while I do it I will write down what I do to help other people as I have seen a lot of people ask about this on these forums. This will be a basic set up, ready to expand, with a home page, different container template to make the style look different for the main site and setting a route for the forum index. This will also give people an in-site into some of the thinking that is behind making things for XenForo and how you have to think of each little part and design in your head before coding. Good programmers spend more time designing code than actually writing it.

Note: I will be using XenPlus for this. A developers library I have made. I will attach it to this post but use with care as it isn't in a release state yet. It will just be for listeners in this tutorial so you can just use the traditional way instead if you want.

Initial Setup
First we need to create a library and add-on. I recommend naming the library {abbr}Site incase you have more than one site running from the same files. I will be using MercSite and using that name throughout this tutorial. Navigate into /your/install/path/library/ and create a folder called MercSite. Then create a few folders we will need at some point later. ControllerPublic, Route, Route/Prefix, Listener.
Now go into ACP -> Development -> Create Add-on. Fill this out however you want, doesn't really matter unless you want to export your site at a later date (cool that you can do that, huh? <3 XenForo). Now we are ready to add our code and ACP changes.

Note: For the remainder of this tutorial we will use your/install/path/library/MercSite as the root directory.

Home Page
Now so we have something to look at later on when testing we will create the home page. First thing to do is make a route prefix. That is, to go to a certain class which will direct to the correct controller if there is a certain word in your URL. For this we will use home (later on we will set this route as the default one). This route prefix class will be called when you go to yourwebsite.com/home. Create a file called Home.php in Route/Prefix. File contents...
Code:
<?php
 
class MercSite_Route_Prefix_Home implements XenForo_Route_Interface
{
    public function match($routePath, Zend_Controller_Request_Http $request, XenForo_Router $router)
    {
        return $router->getRouteMatch('MercSite_ControllerPublic_Home', $routePath, 'home');
    }
}
This is the most basic of route prefixes. This return line specifies 3 simple things:
- the controller we want to use (which we will create later)
- the route path for input processing later in your controller
- the navigation tab this section belongs to so it can be shown as selected. For example if you set this to 'help' then when at this route the help navigation tab would show as selected.

Now that we have our route class we can create the actual route in the ACP to use it. Go into ACP -> Development -> Route Prefixes -> Create new Route Prefix.
Set...
Route Prefix: home
Route Type: Public
Route Class: MercSite_Route_Prefix_Home
Use class to build link: Never
Add-on: MercSite

Ok if you navigate to yoursitename.com/home you should get this error: The controller MercSite_ControllerPublic_Home does not define an action called Index.
This is simply because we haven't created our Controller yet, let's do that now.

Create a file called Home.php in ControllerPublic. Give it the following contents.
Code:
<?php
 
class MercSite_ControllerPublic_Home extends XenForo_ControllerPublic_Abstract
{
    public function actionIndex()
    {
        return $this->responseView('MercSite_ViewPublic_Home', 'home');
    }
}
Another simple little class. After routing XenForo will call the method here called actionIndex(). All we are doing with it is returning a View to display a page. First argument sent is to a View class which is optional and the second is the public template name, in this case I have just used 'home'.
Now that we have our Controller setup you should see that the home page you got the error on earlier is simply blank. To solve this we need to create the home template we specified.

Go into ACP -> Appearance -> Create New Template. Set the template name to home, add-on to MercSite and whatever HTML contents you want for your home page. Here is a simple little thing I added for testing.
Code:
<xen:title>Home</xen:title>
 
<div class="section">
    <div class="primaryContent">Welcome to MercenaryDesign.</div>
</div>
Home page done!
 
Putting the Home Page in Place
Now that we have the home page we need to make it the default route prefix so that you see it when going to yourwebsite.com and anything that was there (forum index and category hashes) go to yourwebsite.com/forums. forums is already used and what I am doing is making it so if it doesn't have a forum specified it will show the normal forum index.

Let's start by extending the index route prefix so that when going to the index the home page that we made is shown and make it so it builds a link to the forums prefix. This is an unconventional (read hacky) prefix however it will mean any links XenForo makes to index to go to the forum index will now point to yourwebsite.com/forums and when you go to yourwebsite.com it will show the home page. Create a file in Route/Prefix called Forums.php. File contents...
Code:
<?php
 
class MercSite_Route_Prefix_Index implements XenForo_Route_Interface
{
  public function match($routePath, Zend_Controller_Request_Http $request, XenForo_Router $router)
  {
      return $router->getRouteMatch('MercSite_ControllerPublic_Home', 'index', 'home');
  }
 
  public function buildLink($originalPrefix, $outputPrefix, $action, $extension, $data, array &$extraParams)
  {
      return XenForo_Link::buildBasicLink('forums', $action, $extension);
  }
}
The match method here works the same as the home prefixes version. The buildLink method simply creates an empty link to yourwebsite.com/forums. However this buildLink method will not be called because the index route prefix set in the ACP is set to never build links. So navigate to ACP -> Development -> Route Prefixes and find the index prefix in the public list. Go into it and set Use class to build link to Always. Note that if you want to import this to a new forum you will have to do this again or do it in an install script.

Now don't click the forums tab as it will create a infinite loop and the world will implode!! This is happening because when no forum is specified it will redirect to the forum index which is now the Forums Controller. So it is redirecting to itself and causing universe problems. To stop this we extend the Forum Controller and instead of having it redirect at this point we make it reroute to the forum index. Create a file called Forum.php in ControllerPublic. Set file contents to...
Code:
<?php
 
class MercSite_ControllerPublic_Forum extends XFCP_MercSite_ControllerPublic_Forum
{
    public function actionIndex()
    {
        $forumId = $this->_input->filterSingle('node_id', XenForo_Input::UINT);
        $forumName = $this->_input->filterSingle('node_name', XenForo_Input::STRING);
        if (!$forumId && !$forumName && $this->_routeMatch->getResponseType() != 'rss')
        {
            return $this->responseReroute('XenForo_ControllerPublic_Index', 'index');
        }
 
        return parent::actionIndex();
    }
}
This simply does the same check as its parent but reroutes instead. If it doesn't meet the check then it falls back to the parent Controller.

Now we are ready to test this all out however we don't have a listener to use these files. Create a file called LoadClass.php in Listener. Give it the following contents...
Code:
<?php
 
class MercSite_Listener_LoadClass extends XenPlus_Listener_LoadClass
{
    protected function _extend()
    {
          $extend = array(
              array('MercSite_ControllerPublic_Forum', 'XenForo_ControllerPublic_Forum'),
              array('MercSite_Route_Prefix_Index', 'XenForo_Route_Prefix_Index')
          );
 
          if (!$this->_options->categoryOwnPage)
              $extend[] = array('MercSite_Route_Prefix_Categories', 'XenForo_Route_Prefix_Categories');
 
          return $extend;
    }
}
This uses my unfinished XenPlus library which will be attached here in its current state. Meant for use by people who know what they are doing. The method in this class returns arrays of what to extend. The line with if (!$this->_options->categoryOwnPage) is for category hash links which we will create shortly.

Now we need to tell XenForo to use this listener. XenPlus listeners all use Your_Class_Name::listen for simplicity. Navigate to ACP -> Development -> Code Event Listeners and create a new listener.
Listen to Event: load_class_controller
Execute Callback: MercSite_Listener_LoadClass :: listen
Add-on: MercSite
Then do the same but for the load_class_route_prefix event instead.

Now you can make sure everything works by going to your index and to the forums. We are almost done with this section. We have to fix the category linking when they use a hash (forums/#categoryname.1 for example) and make the home tab show as selected when you are at the home page.

To fix the category linking we just have to extend the categories prefix like we have already specified in our listener. Create a file called Categories.php in Route/Prefix. Contents...
Code:
<?php
 
class MercSite_Route_Prefix_Categories extends XFCP_MercSite_Route_Prefix_Categories implements XenForo_Route_BuilderInterface
{
    public function buildLink($originalPrefix, $outputPrefix, $action, $extension, $data, array &$extraParams)
    {
        return XenForo_Link::buildBasicLink('forums', $action, $extension) . parent::buildLink($originalPrefix, $outputPrefix, $action, $extension, $data, $extraParams);
    }
}
All this does is put the new forums prefix we use for the forum index in front of these hash links which are created by the parent Class.

XenForo adds the default home tab without any code to select it without template edits. To get around this what we will do is use the homePageUrl option to create our own home tab and then empty it from there to remove the default XenForo home tab. We do this by adding another listener. Create the file NavigationTabs.php in Listener and give it the following contents...
Code:
<?php
 
class MercSite_Listener_NavigationTabs extends XenPlus_Listener_NavigationTabs
{
    public function execute(array &$extraTabs, $selectedTabId)
    {
        if (!$this->_options->homePageUrl)
            return;
 
        $extraTabs['home'] = array(
            'title' => new XenForo_Phrase('home'),
            'href' => $this->_options->homePageUrl,
            'position' => 'home',
            //'linksTemplate' => 'navigation_home_tab'
        );
 
        $this->_options->homePageUrl = '';
        XenForo_Application::set('options', $this->_options);
    }
}
Ok first we check to see if the homePageUrl option is set. If not we don't even want a home tab so we simply return. Next we add the extra tab. Everything is the same as a normal home tag except it will now be selected and if you uncoment the link with the linksTemplate you can create a template you specify, in this case navigation_home_tab, and show sub links. After the extra tab is added we need to make it so the default XenForo home tab isn't shown so simply remove it and set it in XenForo's factory. Now navigate to ACP -> Developement -> Code Event Listeners and add a new listener as follows:
Listen to Event: navigation_tabs
Execute Callback: MercSite_Listener_NavigationTabs :: listen
Add-on: MercSite

That's it. You now have a functioning home page and system in place to build your own site/sections. There is one more (more optional) section to go.
 
Seperating Forum and Site Designs
This section is simply to add a little bit of change to separate your main site look from your forum look. For example how xenforo.com looks different to xenforo.com/community. All we will do here is use a different base Controller to select an alternative Page Container in a way I used in the past that seems good enough to me. The same thing can be achieved with a different style however I haven't digged to find the best way to hardcode one yet, the ways I know seem hacky.

The way we are going to do this is our Abstract Controller will test to see if the template has a prefix of 'site' and if it does then it will change the Page Container to something else. This means when returning a response view you would do things like site:home to show the home template with an alternative Page Container.

Create a new file in ControllerPublic called Abstract.php. Give it the following contents...
Code:
<?php

class MercSite_ControllerPublic_Abstract extends XenForo_ControllerPublic_Abstract
{
    public function responseView($viewName, $templateName = 'DEFAULT', array $params = array(), array $containerParams = array())
    {
        if (!empty($templateName) && $templateName[0] == 's' && empty($containerParams['containerTemplate']))
        {
            if (substr($templateName, 0, 5) == 'site:')
            {
                $templateName = substr($templateName, 5);
                $containerParams['containerTemplate'] = 'SITE_CONTAINER';
            }
        }

        return parent::responseView($viewName, $templateName, $params, $containerParams);
    }
}
The SITE_CONTAINER is the page template that will be used so you will have to create it. The last thing to do is go into ControllerPublic and edit Home.php to extend MercSite_ControllerPublic_Abstract instead of XenForo_ControllerPublic_Abstract and set the template that is returned to be prefixed with site: if you want to use the different Page Container.
 
This is a great tutorial, but I don't see the XenPlus library attached. Can you please upload it?
 
Top Bottom