Please upgrade here. These earlier versions are no longer being updated and have security issues.
HackerOne users: Testing against this community violates our program's Terms of Service and will result in your bounty being denied.

Help with new 'DiscussionWordMention' custom rule

AaronWebsteyAaronWebstey ✭✭✭
edited December 2014 in Vanilla 2.0 - 2.8

I'm developing a simple custom YAGA rule - partially to entertain my users, and mostly to help learn the ins and outs of YAGA and Vanilla in general. I can successfully assign this rule to a badge (see ridiculous attachment), but when I can't seem to get the badge to auto-award when someone has the specified word in a discussion/comment. Can anyone see anything obvious I'm doing wrong (probably @hgtonight‌ :S )?

<?php if(!defined('APPLICATION')) exit();

/**
 * This rule awards badges if a discussion body contains a given string
 *
 * @author Aaron Webstey
 * @since 1.0
 * @package Yaga
 */
class DiscussionWordMention implements YagaRule{

  public function Award($Sender, $User, $Criteria) {
    $Discussion = $Sender->EventArguments['Discussion'];
    $Comment = $Sender->EventArguments['Comment'];

    $Pos1 = strpos($Discussion->Body, $Criteria->WordToMatch);
    $Pos2 = strpos($Comment->Body, $Criteria->WordToMatch);

    if( ($Pos1 !== false) || ($Pos2 !== false) ) {
      return $Discussion->InsertUserID;
    }
    else {
      return FALSE;
    }
  }

  public function Form($Form) {
    $String = $Form->Label('Yaga.Rules.DiscussionWordMention.Criteria.Head', 'DiscussionWordMention');
    $String .= $Form->Textbox('WordToMatch', array('class' => 'WideInput'));
    return $String;
  }

  public function Validate($Criteria, $Form) {
    $Validation = new Gdn_Validation();
    $Validation->ApplyRules(array(
        array(
          'Name' => 'WordToMatch', 'Validation' => array('Required')
        )
    ));
    $Validation->Validate($Criteria);
    $Form->SetValidationResults($Validation->Results());
  }

  public function Hooks() {
    return array('CommentModel_BeforeNotification');
  }

  public function Description() {
    $Description = sprintf(T('Yaga.Rules.DiscussionWordMention.Desc'), C('Vanilla.Comment.MaxLength'));
    return Wrap($Description, 'div', array('class' => 'InfoMessage'));
  }

  public function Name() {
    return T('Yaga.Rules.DiscussionWordMention');
  }

  public function Interacts() {
    return FALSE;
  }
}
«1

Comments

  • Oh, and obviously I've got the locale stuff set up properly (I think).

  • I would use return array('CommentModel_AfterSaveComment', 'DiscussionModel_AfterSaveDiscussion'); for function Hooks so that you access the comment/discussion body as "early" as possible.
    You've been using an event that (I'd guess) is only fired when someone is mentioned in a comment. So your award would only be granted if you put "Eggselent work, @AaronWebstey!" in a comment.

    function Award never returns the $Comment->InsertUserID. I'd suggest

    if (strpos(strtolower($Comment->Body), strtolower($Criteria->WordToMatch)) !== FALSE) {
      return $Comment->InsertUserID;
    }
    if (strpos(strtolower($Discussion->Body), strtolower($Criteria->WordToMatch)) !== FALSE) {
      return $Discussion->InsertUserID;
    }
    

    But I'm not sure if this will work. Since this will be fired for each discussion and comment save. Maybe you have to check for the controller, too. @hgtonight will know that... I'd try something with

    if (strtolower($Sender->ControllerName) == 'discussioncontroller' && strpos...) {
      return discussion author
    }
    same for comment...
    
  • peregrineperegrine MVP
    edited December 2014

    your problem is the Name underneath the photo needs to be "WordToMatch"

    based on your code.

    and also what r_j suggested.

    you need to make sure test for Discussion and Comment

    I may not provide the completed solution you might desire, but I do try to provide honest suggestions to help you solve your issue.

  • this needs to be WordToMatch instead of Eggselent.

    I may not provide the completed solution you might desire, but I do try to provide honest suggestions to help you solve your issue.

  • Thanks for the help, @R_J‌ ! I have implemented your suggestions, and no dice :( I get the hooks and the strtolower (duh, I should have thought of that), but I'm not sure why the ID never would have been returned by my code (it returns if either strpos was not false, I thought). Anyway, that's not important and I appreciate the help!!

    I haven't tried the ControllerName bit, because I figured that can only exclude possible cases where it would be awarded. If I get it working and need to add in that bit (or if I'm wrong about this), I'll give it a shot.

    Thanks again very much! Still picking through the other rules looking for ideas but if anyone has a suggestion I'll be very appreciative.

  • Maybe you haven't seen peregrines comments? ;)

    If I understand the inner guts of such a custom rule right, your Award function will be called on every hook (I've recommended AfterSave):
    a) when a discussion is created
    b) when a comment is created

    So you would have to make sure that when a comment is created, there is no second reward for the discussion.

    Also the hook is called
    c) when a discussion is edited
    d) when a comment is edited

    In that cases, you wouldn't like to award someone a second time. I do not know a way to achieve that...

  • peregrineperegrine MVP
    edited December 2014

    @r_j said: Maybe you haven't seen peregrines comments?

    I guess he didn't believe me... :)

    So you would have to make sure that when a comment is created, there is no second reward for the discussion

    You can only get awarded a badge once I believe, same with PeregrineBadges :)

    see comment - http://vanillaforums.org/discussion/comment/221756/#Comment_221756

    • for changing your dashboard settings.

    and here is the code...

    class DiscussionWordMention implements YagaRule{
    public function Award($Sender, $User, $Criteria) {
    
    $PostInfo = $Sender->EventArguments['FormPostValues'];
    
    $PBodyText = val('Body',$PostInfo);
    $PUserID =  val('InsertUserID',$PostInfo);
    
        if (strpos(strtolower($PBodyText), strtolower($Criteria->WordToMatch)) !== FALSE) {
         return $PUserID;  
        }
     return FALSE;
    }
    public function Form($Form) {
    $String = $Form->Label('Yaga.Rules.DiscussionWordMention.Criteria.Head', 'DiscussionWordMention');
    $String .= $Form->Textbox('WordToMatch', array('class' => 'WideInput'));
    return $String;
    }
    public function Validate($Criteria, $Form) {
    $Validation = new Gdn_Validation();
    $Validation->ApplyRules(array(
    array(
    'Name' => 'WordToMatch', 'Validation' => array('Required')
    )
    ));
    $Validation->Validate($Criteria);
    $Form->SetValidationResults($Validation->Results());
    }
    public function Hooks() {
    return array('CommentModel_AfterSaveComment', 'DiscussionModel_AfterSaveDiscussion');
    }
    public function Description() {
    $Description = sprintf(T('Yaga.Rules.DiscussionWordMention.Desc'), C('Vanilla.Comment.MaxLength'));
    return Wrap($Description, 'div', array('class' => 'InfoMessage'));
    }
    public function Name() {
    return T('Yaga.Rules.DiscussionWordMention');
    }
    public function Interacts() {
    return FALSE;
    }
    
    }
    

    I may not provide the completed solution you might desire, but I do try to provide honest suggestions to help you solve your issue.

  • Yes my apologies @R_J‌ and @peregrine‌ ; I had not seen his comment before I posted my reply. THen I was writing another reply, and you replied again already! Thanks, I'll try that code :)

  • the odd thing is many people don't refresh their browser or click on discussions after they post.

    same thing with conversations - they just wait for a notification to pop up instead of refreshing.

    saves alot of time and keeps you up to date.

    I may not provide the completed solution you might desire, but I do try to provide honest suggestions to help you solve your issue.

  • AaronWebsteyAaronWebstey ✭✭✭
    edited December 2014

    I wish I could double-awesome @peregrine‌ 's post. Curse you for being better than me/bless you for helping!! Well OK, just the latter. I just can't wait until I've had enough time to know how to do this stuff myself without running for help.

    Thanks very, very much to @peregrine‌ and @R_J‌ !!! Oh and @hgtonight‌ FYI, peregrine has made this into a working rule (as minimally tested by me).

  • peregrineperegrine MVP
    edited December 2014

    edit. rescinded so as not confuse issue then.

    I may not provide the completed solution you might desire, but I do try to provide honest suggestions to help you solve your issue.

  • Actually, I named the badge 'Eggsellent', and left your code as you posted it:

  • peregrineperegrine MVP
    edited December 2014

    Eggsellent.

    I may not provide the completed solution you might desire, but I do try to provide honest suggestions to help you solve your issue.

  • Yeah, I think $Criteria->WordToMatch gives the value of the WordToMatch input on the badge config page (which is labeled 'Mentioned a word?' in my original screenshot).

  • Sweet rule man!

    I am working on Yaga 1.1 atm which will make distribution of Custom Rules with a plugin much easier so it is great to see some new rules :).

    Search first

    Check out the Documentation! We are always looking for new content and pull requests.

    Click on insightful, awesome, and funny reactions to thank community volunteers for their valuable posts.

  • @peregrine said:
    You can only get awarded a badge once

    I was mislead by the Award Value in the screenshot :(
    Very nice idea to simply use FormPostValues!

    @AaronWebstey welcome at the joyful world of Vanilla coding :)

  • Thanks again to everyone! You are all Eggsellent.

  • @hgtonight‌ I just tested the new hook, works great!

    For people who are interested, you can include a new rule from a plugin like so:

    public function Yaga_AfterGetRules_Handler($Sender) {
        $Classname = 'YourRule';
    
        include(dirname(__FILE__).'/rules/class.'.strtolower($Classname).'.php');
        $Rule = new $Classname();
        $Sender->EventArguments['Rules'][$Classname] = $Rule->Name();
    }
    

    (will only work with the Yaga master branch as of now)

  • @Bleistivt said:
    hgtonight‌ I just tested the new hook, works great!

    For people who are interested, you can include a new rule from a plugin like so:

    public function Yaga_AfterGetRules_Handler($Sender) {
    $Classname = 'YourRule';

      include(dirname(__FILE__).'/rules/class.'.strtolower($Classname).'.php');
      $Rule = new $Classname();
      $Sender->EventArguments['Rules'][$Classname] = $Rule->Name();
    

    }

    (will only work with the Yaga master branch as of now)

    Great to get external verification. :)

    I just want to note that you shouldn't have to include the file directly. Assuming you know the class name, and it's file matches the auto loader requirements, you can just instantiate it in that class since the plugin folder is automatically added to the search path.

    Great sample of things to come!

    Search first

    Check out the Documentation! We are always looking for new content and pull requests.

    Click on insightful, awesome, and funny reactions to thank community volunteers for their valuable posts.

Sign In or Register to comment.