Resource icon

How to extend user criteria (for use in trophies, upgrades and notices/notifications) 1.0

No permission to download
This add-on is not affiliated with XenForo Ltd.

Introduction

I recently was asked the question, how would you go about extending the criteria for awarding trophies?

I remember looking a few months back and for some reason I thought it wasn't possible. I was wrong.

It very much is possible, and actually quite straight forward. Included for download is a very small add-on that adds a very simple new criteria.

We currently have a user criteria called "User has no avatar":
1.webp


This could be useful to use in Notices or Notifications to remind someone that they could, or how to add an avatar:

2.webp


Let's add the opposite: "User has an avatar". We could then, perhaps, use this criteria to reward someone, via a trophy, some points for taking the time to add an avatar.


Creating an add-on

First, we need to create an add-on. To do that, we must first ensure our development board has Debug mode switched on.
  • Open library/config.php
  • Add the following line:
PHP:
$config['debug'] = true;
  • This enables the "Development" tab in the Admin CP. Go to this tab in the Admin CP and click "Create an Add-on"
  • Give your add-on at least a unique ID, and a title and click Save.
  • For the purposes of this guide, the add-on ID will be UserCriteriaExample
Create some PHP
The great thing is, this add-on is so simple, we only need to write a small bit of PHP. This is the Listener.php file which we will tell XenForo to listen to when certain events happen.
  • First, create a new folder in your library directory that matches your add-on ID. In my example, I am creating a directory called "UserCriteriaExample".
  • In that directory, create a new file called Listener.php. Its contents to start with will need to look like this:

    PHP:
    <?php
    
    class UserCriteriaExample_Listener
    {
    
    }
  • The class name is made up of the filename, relative to the library folder. So this file is in the folder UserCriteriaExample with a name of Listener.php (UserCriteriaExample/Listener.php) so its classname is UserCriteriaExample_Listener (slashes replaced with under scores and the .php extension removed).
  • Within this class we only need to define two functions. There's only two very small parts to adding user criteria. We need to listen to a) the template_hook event to add the new user criteria options to the criteria template and b) the user_criteria event which extends the userMatchesCriteria function in XenForo_Helper_Criteria class.
  • To define the first function, we need to create the function with the appropriate callback signature. This will now leave our Listener.php file looking like this:

    PHP:
    <?php
    
    class UserCriteriaExample_Listener
    {
        public static function templateHook($hookName, &$contents, array $hookParams, XenForo_Template_Abstract $template)
        {
    
        }
    }
  • The template which generates the criteria for trophies, user promotions and notices/notifications is called helper_critera_user. There are a number of hooks in this template, which we can use. We want to add an avatar related so let's search that template for "avatar". When you do this, you will see the template code for the avatar critera. If we now scroll up and look for the nearest hook, we will find:

    Code:
    <xen:hook name="user_criteria_profile">
  • So, now we can now restrict our templateHook function to only do stuff within the user_criteria_profile hook. We can do that with a simple IF statement. The end result is:

    PHP:
    <?php
    
    class UserCriteriaExample_Listener
    {
        public static function templateHook($hookName, &$contents, array $hookParams, XenForo_Template_Abstract $template)
        {
            if ($hookName == 'user_criteria_profile')
            {
    
            }
        }
    }
  • Ok, I'm going to cheat a little. You would usually create your own templates and inject the code in, I'm going to do something slightly quicker which just takes 3 lines of code. First, this line gets the currently selected user criteria in the template.
    PHP:
    $userCriteria = $template->getParam('userCriteria');
  • Next, this line sees if the criteria already has our option already selected. Our option is going to go by the name of "has_avatar". If the "has_avatar" value is already set, the $checked value will be 'checked' else, it will be blank. This will make sense in a moment...
    PHP:
    $checked = $userCriteria['has_avatar'] ? 'checked' : '';
  • Lastly, the next line adds some HTML to our helper_user_criteria template. Notice how the $checked parameter is concatenated in the middle of the HTML. This basically ensures the option is selected or not depending on the user criteria set in the database.
    PHP:
    $contents .= '<li><label><input type="checkbox" name="user_criteria[has_avatar][rule]" value="has_avatar"' . $checked .' /> User has an avatar</label></li>';
  • Save the file :)

Create a Code Event Listener

  • Now we can go to Admin CP and create the Code Event Listeners. For ease, just simply fill it out like this:
    3.webp

  • Save the listener, let's go check out the criteria!

Progress so far...

4.webp


Success! We have a new option. You can set this option now, but right now it doesn't do anything.


More PHP...

  • When we save criteria it is saved as an array of rules (and then serialized) and stored in the database. If you check out our previous HTML, our rule name was defined as "has_avatar".
  • We need to define that second function which we'll call criteriaUser. That will leave our listener like this:
    PHP:
    <?php
    
    class UserCriteriaExample_Listener
    {
        public static function templateHook($hookName, &$contents, array $hookParams, XenForo_Template_Abstract $template)
        {
            if ($hookName == 'user_criteria_profile')
            {
                $userCriteria = $template->getParam('userCriteria');
                $checked = $userCriteria['has_avatar'] ? 'checked' : '';
                $contents .= '<li><label><input type="checkbox" name="user_criteria[has_avatar][rule]" value="has_avatar"' . $checked .' /> User has an avatar</label></li>';
            }
        }
        
        public static function criteriaUser($rule, array $data, array $user, &$returnValue)
        {
    
        }
    }
  • User criteria is literally as simple as looking at value and then determining whether the criteria is met (true) or not (false) against a value in the $visitor array (defined here as $user). So how can we check if the user has an avatar? Well, if they have an avatar they will have $user['avatar_date'] or $user['gravatar'] set in the xf_user tables. With that in mind, our remaining code will look like this:

    PHP:
    <?php
    
    class UserCriteriaExample_Listener
    {
        public static function templateHook($hookName, &$contents, array $hookParams, XenForo_Template_Abstract $template)
        {
            if ($hookName == 'user_criteria_profile')
            {
                $userCriteria = $template->getParam('userCriteria');
                $checked = $userCriteria['has_avatar'] ? 'checked' : '';
                $contents .= '<li><label><input type="checkbox" name="user_criteria[has_avatar][rule]" value="has_avatar"' . $checked .' /> User has an avatar</label></li>';
            }
        }
        
        public static function criteriaUser($rule, array $data, array $user, &$returnValue)
        {
            switch ($rule)
            {
                case 'has_avatar':
                    if (!empty($user['avatar_date']) || !empty($user['gravatar']))
                    {
                        $returnValue = true;
                    }
                break;
            }
        }
    }
  • Notice how all we're saying is if $rule is "has_avatar" we check if the avatar_date or gravatar fields are NOT empty for the user. If they are NOT empty, the $returnValue is true (the criteria value is true if they do have an avatar). So save it! One more Code Event Listener in the Admin CP:

    5.webp


    Let's create a trophy and test!

    Let's create a trophy for the new criteria. The only criteria needs to be "User has an avatar"...

    Success!

    6.webp


    Check out the attached resource, and adjust it accordingly. Feel free to ask if you have any questions :)
Author
Chris D
Downloads
132
Views
2,906
First release
Last update

Ratings

5.00 star(s) 8 ratings

More resources from Chris D

Latest reviews

Great Tutorial

Very clean and useful. Everything needed is fully explained

Thanks Chris for sharing your knowledge
EXCELLENT! This is the FIRST XF tutorial I've read that actually covers the BASICS -- the Development tab, Library folders, what Listener.php does, and how the hook mechanism works. I can't say I totally understood it, but I now know a lot more than I did. THANK YOU!!
Excellent guide! The screenshots and redacting are great! I have been struggling trying to find good development guides, so it was great when I found this. A clear and useful tutorial for us that want to start modifying XF. Thanks!
Sweet TY :) very helpful for my current clients custom job :) most of us appreciate the time it takes to write and submit guides like this.. I will contribute soon as i catch up.. Jumped on XF a lil too late :(
Great, thanks to the guide to extend trophies
Thanks for taking the time to write such a thorough and excellent guide, Chris!
Great work!
Chris D
Chris D
Thanks Fuhrmann :)
D
Awesome tutorial resource from Chris! Very helpful, very useful! :)
Top Bottom