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

Anyone know of an addon to ban users from a discussion?

12346

Comments

  • MikeOlsonMikeOlson
    edited July 2017

    grrrr. I should have caught that. Although still allows saving comment..... Looking....

  • R_JR_J Ex-Fanboy Munich Admin

    I guess I have seen the mistake. Check the CommentModel as to what is passed as EventArguments.

  • MikeOlsonMikeOlson
    edited July 2017

    edited my post. Stupid question

  • When I look at class.commentmodel.php and do a search for BeforeSaveComment I find this:

            // Prep and fire event
            $this->EventArguments['FormPostValues'] = &$FormPostValues;
            $this->EventArguments['CommentID'] = $CommentID;
            $this->fireEvent('BeforeSaveComment');
    

    Strange because it needs to insert the discussionid when it updates the table, so it must get the discussionid from somewhere so I looked for a mentionof "discussionid" after the fire event above and found that it gets it from the form fields like it is doing in the check for approval section?

                        // Check for approval
                        $ApprovalRequired = CheckRestriction('Vanilla.Approval.Require');
                        if ($ApprovalRequired && !val('Verified', Gdn::session()->User)) {
                            $DiscussionModel = new DiscussionModel();
                            $Discussion = $DiscussionModel->getID(val('DiscussionID', $Fields));
                            $Fields['CategoryID'] = val('CategoryID', $Discussion);
                            LogModel::insert('Pending', 'Comment', $Fields);
                            return UNAPPROVED;
                        }
    
    
    

    so would I do something like this?

    public function CommentModel_BeforeSaveComment_Handler($sender, $args) {   
            $DiscussionID = $args['Discussion']->DiscussionID;
            $DiscussionModel = new DiscussionModel();
            $Discussion = $DiscussionModel->getID(val('DiscussionID', $Fields));
    
            if(!isset($Discussion->Attributes['DiscussionBan'])) {
                return;
            } 
    
            if(in_array(Gdn::session()->UserID, $Discussion->Attributes['DiscussionBan'])){
                $sender->Validation->addValidationResult('Body', 'You have been banned from participating in this discussion.');
            } 
    }
    

    I just don't know if $Fields is being defined further into the event than what we have access to. In that case perhaps I need to get it out of $FormPostValues?

  • R_JR_J Ex-Fanboy Munich Admin

    You're almost there! You just need some more understanding of the scope of variables. Inside of your function you only have two variables: $sender and $args.
    $sender is an instance of the CommentModel. It is always worth checking if a class provides a property that could be helpful. In this case, there is no helpful property.
    $args is an array containing everything which has been passed to the EventArguments array, but only everything that has been defined in the same function that the method fireEvent() is called.

    So the code snippet above shows that there are only two elements available in the $args array. $args['CommentID'] is obviously wrong. So your only chance is $args['FormPostValues']

    I like how you do reverse engineering through Vanilla. Take one step more and try to find the place where you can see what $FormPostValues in this context are!

    You are looking at the method save() of the CommentModel. Class methods are called by connecting class name and method name with the "->" operator. So do a full text search for "commentmodel->save(". There are not many hits for that and the most promising is the PostController.

    The method is called inside of the comment() method of the post controller (no surprise). Search through this method for "discussionid". There are a lot of results this time, but there is one special line which is quite verbously commented:
    $this->Form->setFormValue('DiscussionID', $DiscussionID); // Put this in the form values so it is used when saving comments.

    The discussion id is set to the form values (as "DiscussionID"). Well, you already guessed that you could take from there, but that was the proof that you were right!

  • Ok so I think this should work but still not stopping banned user from submitting.

    public function CommentModel_BeforeSaveComment_Handler($sender, $args) {   
            //$DiscussionID = $args['Discussion']->DiscussionID;
            $DiscussionID = $args['FormPostValues']->DiscussionID;
    
            $DiscussionModel = new DiscussionModel();
            $Discussion = $DiscussionModel->getID($DiscussionID);
            /   
            if(!isset($Discussion->Attributes['DiscussionBan'])) {
                return;
            } 
    
            if(in_array(Gdn::session()->UserID, $Discussion->Attributes['DiscussionBan'])){
                $sender->Validation->addValidationResult('Body', 'You have been banned from participating in this discussion.');
            } 
    }
    
  • Never mind I got it! Your example earlier gave me the hint that I needed for getting at the value.

    public function CommentModel_BeforeSaveComment_Handler($sender, $args) {   
        $DiscussionID = val('DiscussionID', $args['FormPostValues']);
    
            $DiscussionModel = new DiscussionModel();
            $Discussion = $DiscussionModel->getID($DiscussionID);
    
            if(!isset($Discussion->Attributes['DiscussionBan'])) {
                return;
            } 
    
            if(in_array(Gdn::session()->UserID, $Discussion->Attributes['DiscussionBan'])){
                $sender->Validation->addValidationResult('Body', 'You have been banned from participating in this discussion.');
            } 
    }
    
  • MikeOlsonMikeOlson
    edited July 2017

    The only thing that I need to fix is that the menu option isn't working from the list of dicsussions in category view, and not from the original post inside the discussion.

    If I use the menu option from recent discussions it works fine.

    The menu option from recent discussion list calls: https://vikefans.com/vfforums/discussion/discussionban/400
    But resolves to: https://vikefans.com/discussion/#/discussion/discussionban/400

    From the discussion listing the menu option calls: https://vikefans.com/vfforums/categories/discussion/discussionban/400
    And the pop up displays page not found

    From inside the discussion on the original post the menu option calls: https://vikefans.com/vfforums/discussion/400/discussion/discussionban/400
    And the pop up displays the discussion inside the popup.

  • MikeOlsonMikeOlson
    edited July 2017

    If I hardcode the path everything works fine but that isn't going to be of much help to anyone else.

    This works everywhere, but is a hardcoded url:

        public function base_discussionOptions_handler($sender, $args) {
            // If user hasn't moderator permissions, we do not want to edit anything.
            if (!Gdn::session()->checkPermission('Garden.Moderation.Manage')) {
                return;
            }
            if (isset($args['DiscussionOptions'])) {
                $args['DiscussionOptions']['discussionBan'] = array(
                    'Label' => t('Ban User from Discussion'),
                    //'Url' => 'discussion/discussionBan/'.$args['Discussion']->DiscussionID,
                    'Url' => 'https://vikefans.com/vfforums/discussion/discussionban/'.$args['Discussion']->DiscussionID,
                    'Class' => 'Popup'
                );
            }
        }
    
  • R_JR_J Ex-Fanboy Munich Admin

    Congratulations! Another step to the final plugin =)

    Sometimes what is passed to the EventArguments is an object and you have to access it with $args['Discussion']->DiscussionID and sometimes it is an array and you would have to use it like that $args['Discussion']['DiscussionID'].

    The val() function is a helper function in the framework Vanilla uses and its advantage is that you don't have to care if a) you need an object property or an array value and b) if that is existing or not. val() can provide a default value if what you are looking for cannot be found.


    Try if 'Url' =>url( '/discussion/discussionBan/'.$args['Discussion']->DiscussionID), solves your problem

  • That works ^^

  • MikeOlsonMikeOlson
    edited July 2017

    So I wonder if I can add the option for mods on the individual comments to ban the person that made the comment?

    That way as mods are scrolling through the discussion when they see something that breaks the rules they can remove the person from the conversation without having to scroll all the way back up or back to the original post.

    To do that (and I am guessing for sake of discussion) could I do something like:

        public function base_discussionOptions_handler($sender, $args) {
            // If user hasn't moderator permissions, we do not want to edit anything.
            if (!Gdn::session()->checkPermission('Garden.Moderation.Manage')) {
                return;
            }
            if (isset($args['DiscussionOptions'])) {
                $args['DiscussionOptions']['discussionBan'] = array(
                    'Label' => t('Ban User from Discussion'),
                    'Url' => url('/discussion/discussionBan/'.$args['Discussion']->DiscussionID),
                    //'Url' => 'https://vikefans.com/vfforums/discussion/discussionban/'.$args['Discussion']->DiscussionID,
                    'Class' => 'Popup'
                );
            }
    
            if (isset($args['CommentOptions'])) {
                $args['CommentOptions']['discussionBan'] = array(
                    'Label' => t('Ban User from Discussion'),
                    'Url' => url('/discussion/discussionBan/'.$args['Discussion']->DiscussionID),
                    //'Url' => 'https://vikefans.com/vfforums/discussion/discussionban/'.$args['Discussion']->DiscussionID,
                    'Class' => 'Popup'
                );
            }
    
        }
    
  • Ha oops no of course i can't because that is in the discussionoptions handler

  • This however is getting me there but the URL is incorrect. If I add the following to my class:

        public function base_commentOptions_handler($sender, $args) {
            // If user hasn't moderator permissions, we do not want to edit anything.
            if (!Gdn::session()->checkPermission('Garden.Moderation.Manage')) {
                return;
            }
            if (isset($args['CommentOptions'])) {
                $args['CommentOptions']['discussionBan'] = array(
                    'Label' => t('Ban User from Discussion'),
                    'Url' => url('/discussion/discussionBan/'.$args['Discussion']->DiscussionID),
                    //'Url' => 'https://vikefans.com/vfforums/discussion/discussionban/'.$args['Discussion']->DiscussionID,
                    'Class' => 'Popup'
                );
            }
    
        }
    
  • And this works however I don't exactly know why removing the URL() from the URL arg makes it work...

        public function base_commentOptions_handler($sender, $args) {
            // If user hasn't moderator permissions, we do not want to edit anything.
            if (!Gdn::session()->checkPermission('Garden.Moderation.Manage')) {
                return;
            }
            if (isset($args['CommentOptions'])) {
                $args['CommentOptions']['discussionBan'] = array(
                    'Label' => t('Ban User from Discussion'),
                    'Url' => '/discussion/discussionBan/'.$args['Discussion']->DiscussionID,
                    'Class' => 'Popup'
                );
            }
    
        }
    
  • R_JR_J Ex-Fanboy Munich Admin

    @MikeOlson said:
    And this works however I don't exactly know why removing the URL() from the URL arg makes it work...

        public function base_commentOptions_handler($sender, $args) {
            // If user hasn't moderator permissions, we do not want to edit anything.
            if (!Gdn::session()->checkPermission('Garden.Moderation.Manage')) {
                return;
            }
            if (isset($args['CommentOptions'])) {
                $args['CommentOptions']['discussionBan'] = array(
                    'Label' => t('Ban User from Discussion'),
                  'Url' => '/discussion/discussionBan/'.$args['Discussion']->DiscussionID,
                    'Class' => 'Popup'
                );
            }
          
        }
    

    I haven't tested it, but I guess that the url function returns an error while the string concatenation simply gives a wrong result.
    Have you checked the link that is generated? Is there really a discussion ID in the link? Check where the CommentOptions event is fired and look up how you could be able to get the DiscussionID

  • Yep there is a discussionid in the link. The generated link is https://vikefans.com/vfforums/discussion/discussionBan/400

    It works correctly so I guess we could move on to whatever is next.

  • The only other thing that I could think of is maybe giving the user a notification that he has been banned from a thread.

  • R_JR_J Ex-Fanboy Munich Admin

    Well, indeed strange, but as long as it works...

    Sending a notification to a user is quite complicated. What about addding a css class to a discussion where you are banned? And/or output some message to the user at the only place where it is important for him: at the comment box.

    Look at those two lines, they are very helpful:
    github.com/vanilla/vanilla/blob/master/applications/vanilla/views/post/comment.php#L7-L8

    There is an event you can use which a) allows you to add a css class to the comment form ("Hidden" would be a good choice) and b) output some infomation. Try echoing <div class="Warning">test</div>

  • That would work.

    So I need to create a handler function for BeforeCommentForm right?

Sign In or Register to comment.