Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!


Try Vanilla Forums Cloud product

Ready to contribute?

Amazing! Sign our contributors' agreement and then join us on GitHub.

Update for critical security issue in PHPMailer included in release Vanilla 2.3.1

R_J · Cheerleader & Troubleshooter · Moderator


Last Active
Moderator, Developer, Community Developers
  • Re: Bugs & deficiencies with moderation queue

    A part of this (the post count) seems to be fixed recently (https://github.com/vanilla/vanilla/pull/5102)
    The LastCommentID/LastDiscussionID problem is still open, but it is already an issue at GitHub: https://github.com/vanilla/vanilla/issues/4115

    Also notifications for moderation items is on the list:

    Everything but the sparse information of moderation items is already reported.

  • Re: Roadmap discussion for Vanilla 2017

    Thanks for the insights! I really appreciate that you keep us informed of what is going on at Vanilla Inc.
    By the way: I find the "our reading list" discussion very cool =)

    Not much of a constructive feedback from my side, but more of an opinion concerning release cycles: in my opinion there should be a lot of smaller releases instead of one major release every X month where "X months" feels to me like once a year.

    Sometimes bugs and glitches are noticed by several new users and having to answer "this is a known bug" more than one time makes me feel uneasy. So if the community would do pull requests against the current stable OS release, would you consider releasing patched minor versions?

  • Re: Adding a "skip to answer" button to a question

    Don't change core files

    At first: changing any files is bad. The "Vanilla way" is to make changes by adding code, not by removing lines.

    The reason is quite simple: as soon as you change the Q&A plugin itself, you have to ensure that your changes do not get lost once you do an update.

    Create your own plugin

    That's why you should create your own plugin. Since it requires the Q&A plugin, make sure you have such a line in the PluginInfo array of your own plugin
    'RequiredPlugins' => array('QnA' => '1.2'),

    Prevent answers from being removed

    As soon as your plugin is activated it should set the config setting "QnA.AcceptedAnswers.Filter" to false in order to prevent the answers from being removed from the normal comments. That's really easy. Put that in your new plugin (remember: don't change files from others)

    public function setup() {
        saveToConfig('QnA.AcceptedAnswers.Filter', false);

    There are some method names with special meaning: setup() will automatically be called when the plugin gets enabled. The above saves a value to the config (you already guessed that from the functions name, I suppose)

    Add some CSS

    But you still have the answers at the top, and I'm afraid to say that there is no obviously elegant way to hide the answers (but fear not, there is a nifty trick). The standard way to "solve" that would be to hide that part with CSS. You could create a /plugins/yourpluginname/design/yourpluginname.css file and use the style below to hide that answers.

    div.DataBox-AcceptedAnswers {
        display: none;

    Afterwards you would need to include that file whenever a discussion is displayed

    public function discussionController_render_before($sender) {
        $sender->addCssFile('yourpluginname.css', 'plugins/yourpluginname');

    Please bear with me, the next few words are kind of fuzzy...
    ..._render_before() is another "special" method which is called as the first method for every page that is called, even before it is rendered. That is the method you use to add css and js files. It could be
    discussionsController_render_before() for every "www.yourforum.com/discussions/[recent|mine|participated|...]" page,
    discussionController_render_before() for every "www.yourforum.com/discussion/DiscussionID/DiscussionTitle" page,

    I think you understand the idea. base_render_before() is special special. It will be called before every page.

    But really, the above sentences where not exact, they could be an introduction, but as soon as you have understood how MVC is done in Vanilla you would be able to get a better understanding.

    A better solution

    By hiding what you do not like to see with CSS you a) do not hide it before search engines and b) you send superfluous data from the server to the client. It would be far more elegant not to send anything at all.
    You've changed the plugin and deleted the following lines:

    public function DiscussionController_AfterDiscussion_Handler($Sender, $Args) {
      if ($Sender->Data('Answers'))
         include $Sender->FetchViewLocation('Answers', '', 'plugins/QnA');

    The problem

    There is no config setting in order to change something here. Next thing to think about would be if you could change what fetchViewLocation does. If it would return an empty string we would have reached the goal. Although you might be able to do something like that with a custom theme (I honestly don't know if that would be possible, but it would be one thing to think about), something like that would be overkill.
    Okay, so no config setting, no way to change the "target" of the include file - next thing would be to check the view. But the answers.php file is quite simple and also has no ways to change the output.

    The solution!

    But the reason why the view is loaded (i.e. the answers are written to the top) is that the condition of the if clause is true if ($Sender->Data('Answers')). So what if there would be no Answers? Nothing would be written - that's the key! We either have to prevent that this info is stored or we have to delete it. Here is the part of the plugin where the information is set:

       public function DiscussionController_BeforeDiscussionRender_Handler($Sender, $Args) {
          if ($Sender->Data('Discussion.QnA'))
             $Sender->CssClass .= ' Question';
          if (strcasecmp($Sender->Data('Discussion.QnA'), 'Accepted') != 0)
          // Find the accepted answer(s) to the question.
          $CommentModel = new CommentModel();
          $Answers = $CommentModel->GetWhere(array('DiscussionID' => $Sender->Data('Discussion.DiscussionID'), 'Qna' => 'Accepted'))->Result();
          if (class_exists('ReplyModel')) {
             $ReplyModel = new ReplyModel();
             $Discussion = NULL;
             $ReplyModel->JoinReplies($Discussion, $Answers);
          $Sender->SetData('Answers', $Answers);

    Prevent the data from being fetched from the db?

    Generally spoken we can only change anything if we see a fireEvent('...');. In the getWhere()method of the CommentModel would be such a fireEvent(), I guess, but this is more complex that it should be. We wouldn't be able to tell if we take influence on exactly that call or on any other plugin/class calling this method, so no need to look at the CommentModel. We simply let the assignment happen.

    "Unset" $Sender->Data('Answers')

    The code snippet above is executed when the event "BeforeDiscussionRender" is fired. The answers are written when the event "AfterDiscussion" if fired. All we have to do is to find an event that is fired after BeforeDiscussionRender and before AfterDiscussion. The easiest way is to search for where the AfterDiscussion event is fired, scroll upwards and there you'll find $this->fireEvent('AfterPageTitle');.

    Try the following snippet:

    public function discussionController_AfterPageTitle_handler($sender) {
        $sender->setData('Answers', null);


    If you wrap the setup() and the snippet right above in a custom plugin, you have achieved to get the answers from off the topic to ther original position without touching any file "which doesn't belong to you". That's great! As I've said before: preventing text from being transferred to the client is into my opion the best way but using CSS or even JS is sometimes the fastest solution and in some edge cases it might be the only solution. But not this time B)

    This does not answer your question concerning that button, but whenever I read "I've changed the file XY and everything is great" I feel like I have to take action in order to prevent people from hacking Vanilla instead of extending Vanilla.

    But let me take a look at your button question and I'll try to answer that, too.

  • Re: A good text editor?

    I have not invested much time and love, but it is working: https://vanillaforums.org/addon/aeconfig-plugin

  • Re: Synchronization of usernames

    @dribbons Sorry, but whenever I see someone speaking about changing the core I have to stress that /!\ it is a bad idea /!\.

    Please look at the following code for a small plugin and see if it can do what your code changes does. I do not understand why and what your code does since I'm not using jsConnect, but I would expect that the plugin prevents a change of the username

    $PluginInfo['PreventUserNameChange'] = array(
        'Name' => 'Prevent UserName change',
        'Description' => 'Only useful when you want to prevent syncing to overwrite usernames',
        'Version' => '0.1',
        'Author' => 'R_J',
        'License' => 'MIT'
    class PreventUserNameChangePlugin extends Gdn_Plugin {
        public function userModel_beforeSaveValidation_handler($sender, &$args) {
            $userID = $args['FormPostValues']['UserID'];
            $user = Gdn::userModel()->getID($UserID, DATASET_TYPE_ARRAY);
            if (!empty($user["Name"])) {
                $args['FormPostValues']["Name"] = $user["Name"];

    If this plugin is helpful for you, please consider extending the description and uploading it to the addon section.