HackerOne users: Testing against this community violates our program's Terms of Service and will result in your bounty being denied.

Working with vf 3.1?

KasparKaspar Moderator
edited August 2019 in Vanilla 3.x Help

Have anyone used the Countdowntimer plugin with VF 3.1?

I can enable it

No error is shown.

Settings just shows an empty white bar.

Posting a [COUNTDOWN] just result in the text [COUNTDOWN)

The NumReplacementsPerPost line is not added to config

Adding it manually does nothing (not that I expected that, it just enables having several countdowns)


  • Options
    KasparKaspar Moderator

    Tested in vf 2.6.4 aswell, still not working.

    Also manually added the 3 config lines for Tag, Time and Timezone - no dice

  • Options
    R_JR_J Ex-Fanboy Munich Admin

    Please try if changing

    class CountdownTimer extends Gdn_Plugin {


    class CountdownTimerPlugin extends Gdn_Plugin {

    already solves the problem

  • Options
    R_JR_J Ex-Fanboy Munich Admin

    Delete /cache/addon.json afterwards

  • Options
    KasparKaspar Moderator

    Oh my holy electron ;-) It works!

    Was that all it needed? - gotta find some time to get acquainted with some code standards.

    As I have seen commented - it does not have permission control

    It also seems it is not restricted to the format set in the settings "Time" fields (I thought it would be restricted to the given format)

    If a typo happens an error is thrown but the post is actually accepted/allowed to be posted - which result in theme being broken for the thread it is posted in.

    Here I have put an space after "2019" - and it gives this error.

    DateTime::__construct(): Failed to parse time string (2019 -08-17 08:00:00) at position 12 (0): Double time specification


    I don't have time during the next week - but after that I am hoping you would lend some support to help me improve on the above.

    The permission part I have seen in several plugins and mentioned here on forum - enough to get me going but the other 2 I have no idea where begin with.

  • Options
    R_JR_J Ex-Fanboy Munich Admin

    Naming Conventions

    In the past Vanilla was very forgiving when speaking about plugins class names and file names. There are a few combinations that worked in the past and currently still work. These two are the most secure:

    Standard until recently:

    Folder: FooBar
    File: class.foobar.plugin.php
    Class: FooBarPlugin

    Current standard:

    Folder: foo-bar
    File: FooBarPlugin.php
    Class: FooBarPlugin

    A lot of older plugins simply miss the "Plugin" in the class name.

    If you really want/need another class name (i.e. "FooBar" instead of "FooBarPlugin") in the plugin than the folder suggests, you can specify the class name in the addon.json like that: "className": "FooBar"

    "Creating" Permissions

    If you like to write a plugin with custom permissions, you need to register those permissions. The process of registering permissions couldn't be easier: you just have to specify an extra key into the addon.json and when the plugin is enabled, those permissions are registered automagically.

    To find examples, search in the addon.json files for "registerPermissions".

    Let's take two examples, the easy one first:

    "registerPermissions": [

    Before the Flagging plugin is enabled, the permission does not exist. When it gets enabled, the permission is created. You should stick to some rules:

    1. Begin with "Plugins"
    2. Make the second part the name of your plugin
    3. Choose if possible one of the already existing permissions (view, add, manage etc) as third part

    The "Plugins" rule will group all plugin settings. Although you are able to createa group on your own, don't do it. You can think about creating your own permission group if you are creating a masterpiece like YAGA, but otherwise simply stick with "Plugins"

    The reason for the second rule should be obvious: if your plugin is called "FooBar" and there is a permission called "Plugins.Fancy.Manage" nobody would know what that is good for.

    The last one is to avoid clutter.

    Now let's take a look at a more complex example:

       "registerPermissions": {
           "Plugins.Pockets.Manage": "Garden.Settings.Manage",
           "0": "Garden.NoAds.Allow"

    What you can see here is how to register more than one permission - and way more interesting - how to set default values!

    "Plugins.Pockets.Manage" will be created by the plugin and it should be created just like "Garden.Settings.Manage" is. So the permissions for Garden.Settings.Manage are copied to the new permission.

    The second row shows a general "set for all" or "deny for all" approach by specifying either "0" or "1"

    After you have registered your own permissions (extend the PluginInfo, delete the /cache/addon.json, disable, reenable the plugin), you can use them...

    Checking for Permissions

    One of the simplest way to check for a permission is in the countdown plugin: $Sender->Permission('Garden.Settings.Manage');

    If the user doesn't have that permission, he will be routed to a "permission error" page. But you can check for permissions wih the function "checkPermission" or even better with Gdn::session()->checkPermission(). Take a look at the method in the session class to find out more about it or browse through the code to find examples. It returns a true or false, so it is really easy to work with it.

  • Options
    R_JR_J Ex-Fanboy Munich Admin

    You want to be sure that no wrong time format is entered? The settings page accepts anything even strings that aren't even close to being a time. The plugin uses the ConfigurationModule which helps creating a settings page easily, but doesn't provide easy input validation. That must be done with normal methods of the form (and maybe even the validation) class.

    When a form has been filled out and the user submits it, it normally is a http POST request. search Vanilla for "->authenticatedpostback" to find numerous examples.

    Take a look at the ProfileExtenders settingsController_profileFieldAddEdit_create() method for a quite complex example.

    The process would be the following:

    1. check if this is an authenticated post request
    2. check if the entered string can be converted into a time
    3. if not, set an error
    4. if no error is set, save the values

    You would have to add the code right before the $Cf->Initialize() line.

    See the line "$sender->Form->addError('Label is required.', 'Label');" in the ProfileExtender. Use it exactly like that if you have checked for a valid time and that check failed. See the class Gdn_Form method addError to find out more about the possible parameters.

    As a rule of thumb: text printed by the form class do not need to be enclosed in the T() function for translation because that is the default and will happen in the form class.

    In the end, you will end up with a snippet like that:

           if ($sender->Form->authenticatedPostBack()) {
               $formPostValues = $sender->Form->formValues();
               // Do validation here
               if ($formPostValues['FooBar.Time'] == '') {
                       'Please enter a time',

    You have said "the other 2"? Just drop a note if there is something left

Sign In or Register to comment.