Creating an Addon.

I totally forgot to thank you for this thread. This is the thread that taught me how to build stuff for XenForo initially.

Kind of funny looking back now... The first hour of mucking with it, I remember I was like, "Deerrrrr what?" I think I've come a little ways since then. :)

Thanks Shawn, really appreciated. :)

When I seen that you "liked" it this morning I reviewed the tutorial and seen how far I have come too, and it may be time to re-write parts of it. I can't take all the credit though, Darren Gordon gave me feedback in this thread that was truly helpful, :)
 
How to customize this error:
You do not have permission to view this page or perform this action.

I think this is generated on this file: \library\LimitSignature\ControllerPublic\LimitSignature.php
PHP:
if (!$sigcheck)
                {
                    return $this->responseNoPermission();
                
                }

@xfrocks @Chris Deeming or any other coder avialble, need your help.
Consider me as zero knowledge on Xenforo/Php :)

Thanks !
 
Just an update for Xenforo 1.2.x
I think file: \library\LimitSignature\ControllerPublic\LimitSignature.php
Needs to be updated this way.
Correct me if I'm wrong :)

PHP:
<?php

class LimitSignature_ControllerPublic_LimitSignature extends XFCP_LimitSignature_ControllerPublic_LimitSignature
{

    public function actionSignature()
    {

        if (!XenForo_Visitor::getInstance()->canEditSignature())
        {
            return $this->responseNoPermission();
        }

        else
        {
            $visitor = XenForo_Visitor::getInstance();

            $options = XenForo_Application::get('options');

            if ($visitor['message_count'] < $options->sigmessageCount AND
                (!$visitor['is_moderator'] AND !$visitor['is_admin']))
            {

                $applytogroups = $options->siglimitGroups;

                if (empty($applytogroups))
                {
                    $applytogroups = array(2);  // default it to Registered group
                }

                $belongstogroups = $visitor['user_group_id'];

                if (!empty($visitor['secondary_group_ids']))
                {
                    $belongstogroups .= ','.$visitor['secondary_group_ids'];
                }

                $groupcheck = explode(',',$belongstogroups);

                unset($belongstogroups);

                $sigcheck = true;

                foreach ($groupcheck AS $groupId)
                {

                    if (in_array($groupId, $applytogroups))
                    {
                        $sigcheck = false;
                        break;
                    }

                }


                if (!$sigcheck)
                {
                    return $this->responseNoPermission();

                }

            }

        }

        $sigPerms = array();
        $visitor = XenForo_Visitor::getInstance();

        $sigPerms['basic'] = $visitor->hasPermission('signature', 'basicText');
        $sigPerms['extended'] = $visitor->hasPermission('signature', 'extendedText');
        $sigPerms['align'] = $visitor->hasPermission('signature', 'align');
        $sigPerms['indent'] = $visitor->hasPermission('signature', 'align');
        $sigPerms['smilies'] =  $visitor->hasPermission('signature', 'maxSmilies') != 0;
        $sigPerms['link'] = $visitor->hasPermission('signature', 'link') && $visitor->hasPermission('signature', 'maxLinks');
        $sigPerms['image'] = $visitor->hasPermission('signature', 'image') && $visitor->hasPermission('signature', 'maxImages');
        $sigPerms['media'] = $visitor->hasPermission('signature', 'media');
        $sigPerms['block'] = $visitor->hasPermission('signature', 'block');
        $sigPerms['list'] = $visitor->hasPermission('signature', 'list');
       
        return $this->_getWrapper(
            'account', 'signature',
            $this->responseView(
                'XenForo_ViewPublic_Account_Signature',
                'account_signature',
                array('sigPerms' => $sigPerms)
            )
        );


    }

}
 
How to customize this error:


I think this is generated on this file: \library\LimitSignature\ControllerPublic\LimitSignature.php
PHP:
if (!$sigcheck)
                {
                    return $this->responseNoPermission();
               
                }

@xfrocks @Chris Deeming or any other coder avialble, need your help.
Consider me as zero knowledge on Xenforo/Php :)

Thanks !
That error is the default XenForo no permission message.

If you want to customise the error message, replace:

PHP:
return $this->responseNoPermission();

With:

PHP:
throw $this->getErrorOrNoPermissionResponseException('phrase_name_here');

'phrase_name_here' could either be the title of a phrase or just some text (phrasing is better, of course ;))
 
  • Like
Reactions: rdn
That error is the default XenForo no permission message.

If you want to customise the error message, replace:

PHP:
return $this->responseNoPermission();

With:

PHP:
throw $this->getErrorOrNoPermissionResponseException('phrase_name_here');

'phrase_name_here' could either be the title of a phrase or just some text (phrasing is better, of course ;))
Thanks, as expected it works :)
Now new problem arise :)

How to detect the minimum post count value set on Admin Limit Sig Option ? :)
 
To access options:

PHP:
$theOption = XenForo_Application::get('options')->get('optionId');

Or, if you need to get the options more than once:

PHP:
$options = XenForo_Application::get('options');

$theOption = $options->get('optionId');
$anotherOption = $options->get('anotherOptionId');
 
Thanks for an awesome tut @Lawrence I am going to get stuck into this later on today.

Is it still current for XF 1.2? Or are there any changes of gotchas we should be aware of?
 
Thanks for an awesome tut @Lawrence I am going to get stuck into this later on today.

Is it still current for XF 1.2? Or are there any changes of gotchas we should be aware of?

Although not tested on XF 1.2, I see no reason for it not to be current for 1.2. Even though people still find this tutorial helpful, it should be updated, as I learned a lot more about XF than I did when I wrote it.
 
Last edited:
Although not tested on XF 1.2, I see no reason for it not to be cureent for 1.2. Even though people still find this tutorial helpful, it should be updated, as I learned a lot more about XF than I did when I wrote it.

Hi Lawrence, thanks for replying.

Do you think you might be updating it soon? I can hold of if need be. Or if not, could you perhaps give us a quick 'update' on what you might have done differently? That would be a great help.
 
Great tutorial, and it's helped me get to grips a little with the XenForo / Zend / MVC structure.
I do have one question (and although this may be more generic it's still applicable so may help others).

Basically this plugin replaces the method / function actionSignature().

Now when going through the tutorial I did wonder why the extra stuff from the original function() wasn't present in the addon. This was addressed in #44, seems the function has changed in 1.2
That explains it.

But that begs another (noob) question. Isn't there a way to interrupt a function (like you would with a template hook), so that you could inject the new LimitSig code, without worrying about updated pieces of original xenForo code before and after the injection point (Well, unless they affect your plugin of course, but I think you get what I'm waffling about :))
 
Unfortunetely, there's not really a way to do that. With template hooks, it injects like you pointed out. With php and overwriting the functions, you are limited to replacing the function. The closest you can come to injecting is to call the parent function, in this situation you'd call parent::actionSignature(); and that executes the original funciton (along with any other addon). If you don't do this, you'll break compatability with any other add on that extends that function also.
 
Thanks Daniel. My next question then, would be why is this not possible? Is it down to the architecture and structure of PHP itself?
 
Yes, it's just how php executes.

With the template hooks, by time the hook is "called", it's already executed the script before it. After it executes your 'chunk' it'll continue to the rest. That's perfect injection. However, with the php part. If you have a class with a function, and then you have another class that extends the first one, and that second class has the same function. It's overwriting the first class' function. You can still call the first function by calling parent::function(); but it has to execute the entire function before continuing on. So in comparison, it's actually the opposite. You can't inject your function into the parent function but you need to inject the parent function into yours.

In some cases it is inconvenient. For example, I made an add on that modifies how thread prefixes are handled. To avoid overwritting the entire default function, I called the parent function at the start of my function, stored the response as a variable ($parent = parent::actionWhatever(); ) and then accessed the parameters of the paren'ts response ($var = $parent->params['var']) and ran an additional call to the datawriter to save the prefix the way I wanted. The downside is an extra query that shouldn't be needed but the upside is not overwriting the parent function or breaking compatability with other add ons.
 
Thank you Lawrence. Your tutorial is definitely a jump-start for me with XF! Appreciate your effort in sharing this knowledge!
 
@Lawrence
An exception occurred: Undefined index: sigPerms in /var/www/xf/library/XenForo/ViewPublic/Account/Signature.php on line 10

  1. XenForo_Application::handlePhpError() in XenForo/ViewPublic/Account/Signature.php at line 10
  2. XenForo_ViewPublic_Account_Signature->renderHtml() in XenForo/ViewRenderer/Abstract.php at line 227
  3. XenForo_ViewRenderer_Abstract->renderViewObject() in XenForo/ViewRenderer/HtmlPublic.php at line 67
  4. XenForo_ViewRenderer_HtmlPublic->renderView() in XenForo/ViewRenderer/Abstract.php at line 249
  5. XenForo_ViewRenderer_Abstract->renderSubView() in XenForo/ViewRenderer/HtmlPublic.php at line 64
  6. XenForo_ViewRenderer_HtmlPublic->renderView() in XenForo/FrontController.php at line 572
  7. XenForo_FrontController->renderView() in XenForo/FrontController.php at line 158
  8. XenForo_FrontController->run() in /var/www/xf/index.php at line 13
I got this error when I load the edit signature page withe the admina acount.

then I downloaded this zip file and installed. same thing happend :(

Any help? am i doing this wrong or this guide line is outdated?
 
Last edited:
Can someone tell me how the actionSignature() method is called? I'm reviewing several plugins with controllers and none of them specifically call their controller member methods. Thanks!
 
You create a variable to hold the SQL query for you, inside the Installer.php file example:

PHP:
protected static $table = array(
        'createQuery' => 'CREATE TABLE IF NOT EXISTS `xf_my_table` (
                    `mytable_id` int(10) NOT NULL AUTO_INCREMENT,
                    `other_field` VARCHAR( 50 ) NOT NULL ,
                    `other_2_field` VARCHAR( 50 ) NOT NULL ,
                    `other_3_field` VARCHAR( 50 ) NOT NULL ,                 
                    PRIMARY KEY (`mytable_id`)
        ) ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;',
        'dropQuery' => 'DROP TABLE IF EXISTS `xf_my_table`'
);

Then, you create two functions, install() and uninstall():

PHP:
public static function install()
    {
        //We get here the instance of the XenForo db.
        $db = XenForo_Application::get('db');
     
        //Tell the db to query our 'createQuery'.
        $db->query(self::$table['createQuery']);
    }


PHP:
public static function uninstall()
    {
        //We get here the instance of the XenForo db.
        $db = XenForo_Application::get('db');
     
        //Tell the db to query our 'dropQuery', because we are uinstalling.
        $db->query(self::$table['dropQuery']);
    }


Then after, just set the two callbacks via the add-on manager and it's done!

This was fantastic. I'm using this today in my addons. Got a question though - why is the variable and methods required to be static?

Also - please tell me if I have this correct: I use the controller to insert something into the database, and the model to retrieve it or send it to the view, yes? I understand the basic MVC architecture, but XF's interpretation of it I'm still learning. Thank you! EDIT: Your tutorials at GitHub answered this question. Fuhrman you tutorials over at GitHub are awesome!! Do you have plans to create more?
 
Last edited:
@CaptainMorgan: you have to make the variable static because XenForo uses call_user_func to call your addon callback to install your addon, and it does not instantiate your class, it will just pass the name of the class as string, example:

PHP:
call_user_func(
    array($addOnData['install_callback_class'], $addOnData['install_callback_method']),
    $existingAddOn,
    $addOnData,
    $xml
);

Where install_callback_class is the name of the class which is called to install your addon. This way, you do not have access to "$this" in your class, since it was called statically.

And to the your other question: you can use your Controller to manage the requests (redirect, etc), the Model to retrieve something from database and the DataWriter to write to the database. This is what I do.

And about my tutorials: thanks! :) I plan to create a few more, just don't know when.
 
Top Bottom