Adding extra 'opts' data to the "DiscourseEvent.on(:post_created)" event from javascript?


(Tom) #1

I’ve been looking through the github repository and on the forums as well, but I’ve not been able to find what I’m looking for yet. Apologies if this has already been answered before.

I’m trying to add some extra data (just 1 custom value is all i need) to a post right before/when it’s created. I need to do this from a javascript asset, and I need to be able to access it in the plugin.rb file using:

DiscourseEvent.on(:post_created) do |post, opts, user|

If this isn’t possible or not easy to do from a javascript asset then being able to append text just before post creation to the :raw post would also work for what I need.

I’m only just starting to create my first plugins so I’m still trying to figure out how to use events/existing functions.

Thanks in advance if anyone could point me in the right direction.


(Angus McLeod) #3

I’m not entirely sure what you’re trying to accomplish, but what I think you need is:

In an initializer (i.e. assets/javascripts/discourse/initializers/initializer_example.js.es6):

import Composer from 'discourse/models/composer';

export default {
  name: 'initializer_name'
  initialize() {
    // this adds your property to the attributes serialized to the server by the createPost method in the composer model
    Composer.serializeOnCreate('your_property');

    withPluginApi('0.8.12', api => {
       api.modifyClass('model:composer', {
          // set your_property here, or modify the composer controller and set it on the model there instead
       })
    })
  }
}

In plugin.rb

after_initialize do
   add_permitted_post_create_param('your_property')

   DiscourseEvent.on(:post_created) do |post, opts, user|
     # opts[:your_property] is available.
   end
end

For an example, see my events, ratings or locations plugins.


(Tom) #4

I’ll give that a try, thanks!


(Tom) #5

I’ve tried this, but it was not completely successful. I’ll try to explain what I need this for exactly:

I’m making a plugin that will hide the original poster as soon as a post is created, but only if that option is enabled for that specific post. I have added a new button to the editor and that works fine. Toggling that button shows the variable named hide being set to true/false correctly, and the code you provided does work and “get” the value correctly.

However, it after pressing the button the variable changes, but the variable in your code does not get updated because it only updates once the editor is re-opened or the post is being edited. So I need a way to check the hide boolean variable value when the post is actually “saved” / “created”, allowing me to send that to the plugin.rb. And then in the plugin.rb it will check for that value and if that value is true, it will “hide” the original poster.
(that hiding the poster part all works if I add a check like this:

next unless opts[:raw].to_s.include? "<DontHidePost>"

but it doesn’t work when i try to do it like this:

next unless opts[:hide_post] == true

I hope this makes sense now.

If it helps I can pm you my plugin.rb file and my initializer file?

Edit: TL;DR:

  • I need the value of the variable/parameter to be “grabbed” and passed on at the very last moment when the post is being saved/created, instead of when the editor is being opened.
  • I need to be able to get the value of that variable in the plugin.rb file, which currently does not seem to work. The variable is getting passed on, and it’s available in .on(:post_created). It’s just the “old” value from when the editor was openend, not when it was changed after.

I appreciate all the help.


(Angus McLeod) #6

When the property is set depends on how you’re setting it. Post your code for that here, i.e. whatever you’ve put inside api.modifyClass('model:composer', {}).

Keep in mind that when you’re modifying a class on the client, you’re using Ember. If you haven’t already, I suggest a quick peruse of the Ember Guide to see the various hooks and methods available to you in a model, or the controller.


(Tom) #7

Thanks for the reply. I currently have this in my initializer:

var hide = true; // this gets toggled true/false whenever the button is pressed.

function initializeHideToggle(api) {
  api.includePostAttributes('hide_devs');

  api.modifyClass('model:composer', {
    hide_devs: hide,

    @on('init')
    @observes('post')
    @observes('model.composeState')
    setHide() {
      this.set('hide_devs', hide);
    },
  });
}

export default {
  name: "toggle-hidedevs",
  initialize(container) {
    Composer.serializeOnCreate('hide_devs', 'hide_devs');
    const siteSettings = container.lookup('site-settings:main');
    if (siteSettings.hide_devs_enabled) {
      withPluginApi('0.1', api => initializeHideToggle(api), {
        noApi: () => priorToApi(container)
      });
    }
  }
};

Like mentioned in the previous post, I get the hide_devs variable in the plugin.rb file correctly, however it’s the value from when the editor was last openend, instead of when the post was created. Not sure what to listen for to make it update according the value on “post”. I’ve tried looking through the discourse source files to find an observes example that would trigger on “post” but I’ve not yet found one that seems to do something like that.

I’ll have a look, thanks for the suggestion!


(Angus McLeod) #8

You mentioned your property is being toggled by a button? Please post whatever you’ve done to add that button too. Post all your client code. Use the Hide Details feature so it’s not a huge post.


(Tom) #9
All client code from toggle-hidedevs.js.es6
import {
  withPluginApi,
  decorateCooked
} from 'discourse/lib/plugin-api';
import ComposerController from 'discourse/controllers/composer';
import {
  onToolbarCreate
} from 'discourse/components/d-editor';
import Composer from 'discourse/models/composer';
import {
  default as computed,
  on,
  observes
} from 'ember-addons/ember-computed-decorators';



var stop = false;
var hide = true;

function initializeHideToggle(api) {
  if (Discourse != undefined) {
    if (Discourse.User != undefined) {
      if (Discourse.User.current() != undefined) {
        var usr = Discourse.User.findByUsername(Discourse.User.current().username);
        (function waitForUser() {
          if (usr != undefined) {
            if (usr._result != undefined) {
              var groupHide = usr._result.groups.find((g) => g.name == "hide_devs");
              if (groupHide != undefined) {
                console.log("Enabling hide plugin because user is allowed.");
                api.onToolbarCreate(toolbar => {
                  toolbar.addButton({
                    id: "toggle_hide_devs_btn",
                    group: "extras",
                    icon: "user-secret",
                    perform: function () {
                      var btn2 = $(".toggle_hide_devs_btn");
                      if (btn2 != undefined && btn2 != null) {
                        btn2.toggleClass("dis", hide);
                        hide = !hide;
                        console.log('New state after button press: ' + hide);
                      }
                    }
                  });
                });
              }
              stop = true;
            }
          }
          if (!stop) {
            setTimeout(waitForUser, 300); // check every 300ms. Because depending on network speed, it may take longer to load user info.
          }
        })();
      }
    }
  }

  api.includePostAttributes('hide_devs');

  api.modifyClass('model:composer', {
    hide_devs: hide,

    @on('init')
    @observes('post')
    @observes('model.composeState')
    setHide() {
      this.set('hide_devs', hide);
    },
  });
}

export default {
  name: "toggle-hidedevs",
  initialize(container) {
    Composer.serializeOnCreate('hide_devs', 'hide_devs');
    const siteSettings = container.lookup('site-settings:main');
    if (siteSettings.hide_devs_enabled) {
      withPluginApi('0.1', api => initializeHideToggle(api), {
        noApi: () => priorToApi(container)
      });
    }
  }
};

It’s quite a mess, tried to clean it up as much as possible after all testing. Hope it makes sense without any comments.

Thanks again for helping me out with this.


(Angus McLeod) #10

Replace all of your code with this and it will work. I just tested it. Let me know if you have questions about specific parts.

Client

import Composer from 'discourse/models/composer';
import { withPluginApi } from 'discourse/lib/plugin-api';

export default {
  name: 'test-initializer',
  initialize() {
    Composer.serializeOnCreate('hide_devs', 'hideDevs');

    withPluginApi('0.8.12', api => {
      const user = api.getCurrentUser();

      user.findDetails().then(function() {
        const groups = user.get('groups');
        const inHideGroup = groups.find((g) => g.name == "hide_devs");

        if (inHideGroup) {
          api.modifyClass('model:composer', {
            hideDevs: false
          })

          api.onToolbarCreate(toolbar => {
            toolbar.addButton({
              id: "toggle-hide-devs-btn",
              group: "extras",
              icon: "user-secret",
              action: "toggleHideDevs"
            });
          });

          api.modifyClass('component:d-editor', {
            actions: {
              toggleHideDevs() {
                this.toggleProperty('outletArgs.composer.hideDevs');
              }
            }
          });
        }
      })
    })
  }
}

Server

# name: discourse-test
# about: test
# version: 1.0
# authors: Angus McLeod
# url: test.com

after_initialize do
   add_permitted_post_create_param('hide_devs')

   DiscourseEvent.on(:post_created) do |post, opts, user|
     puts "Hide devs: #{opts[:hide_devs]}"
   end
end

(Tom) #11

Will try this now and let you know :smiley: Thank you very much for all the help!


(Tom) #12

Works like a charm. Thanks a lot! :tada:


(Tom) #13

Had to change one thing in it though: add a null/undefined check for the “user” variable, as non-signed in users were unable to load the site (just a white screen) due to JavaScript undefined errors. Didn’t happen all the time, but some weird reason sometimes caused it to be null for non-logged in users (not sure how the composer got even activated in the first place if not logged in)

Other than that, still works great after some time :smiley: