Beginner's Guide to Creating Discourse Plugins - Part 1


(cpradio) #64

You should use branches for dev related work and master for prod. Then you can push the commits you consider ready for prod when you are comfortable with them.

On top of that using a repo per plugin would grant you the ability to easily control the plugins and their state in each environment.


I’m having some trouble getting this symlink to work. It doesn’t seem to catch it inside Vagrant. When I go to /vagrant/plugins/, where I did the symbolic link, ls shows my plugin as red. Do I somehow have to add my ~/code/my-plugin as a shared folder in Vagrant or something like that?


Somewhere I read that simple bundle exec rake assets:clean would work. So this isn’t true?

(Jithin Krishnan) #67

What is the best way to modify Discourse Database from plugin?
I am developing a Discourse plugin that need to create a new table and store some data.
Also it needs to fetch content from my server. Could you suggest the best way to do this?

(Mittineague) #68

Hi Jithin_K_M welcome to the forum

I don’t know what exactly you have in mind to do.
But IMHO it would most likely be best for you to use the existing “custom fields” tables. i.e.


(Jithin Krishnan) #69

Thanks Mittineague for your quick reply :slight_smile:

Could you suggest some docs where I can find some snippets for using these custom fields within my plugin?

(Mittineague) #70

Unfortunately I don’t think it there is specific documentation yet.

What I usually do is study other plugins that work and try to pick out what I want to do from them. I keep in mind that by this “monkey see - monkey do” pragmatic approach I may be learning less than best practice, but I trust that the Discourse team knows better than I do.

I know these 3 plugins use custom fields, are “official”, and should be OK to use as a learning source.

Staff Notes
GitHub - discourse/discourse-staff-notes: Plugin for Staff users to create notes on users

Solved (aka Accepted Answer)
GitHub - discourse/discourse-solved: Allow accepted answers on topics

discourse/plugins/poll at master · discourse/discourse · GitHub

(Jithin Krishnan) #71

Thanks for your suggestions. It would be great if there is an official documentation on plugin development (assessing admin settings, database etc.)

(Mittineague) #72

If you work your way through the tutorials you will gain some valuable “hands on” experience.

And this is good

(Jithin Krishnan) #73

Thanks a lot Mittineague :slight_smile:

I previously started following the Beginner’s Guide, but my plugin was not loaded (might be caching issue).
Tried again now and got it working!
Let me go through the complete guide.

(Jithin Krishnan) #77

What is the best way to render ajax response inside a widget?
I was able to console.log the ajax response, but it is not rendering inside the widget.

Tried to call this.scheduleRerender() after getting ajax response but both results in an infinite loop.

(Jithin Krishnan) #78

I am getting following error when trying to load a helper module inside my widget

Could you please help me to resolve this error?

(Robin Ward) #79

You should make sure your widget can contain state. After the ajax request, set it on the state object and trigger a this.scheduleRerender and it should appear. For an example look at how the post menu shows who liked something.

Looks like you have the wrong path to your helper. One way to see all the paths that Discourse has resolved is by typing require._eak_seen in your console. Look for the correct path name.

(Jithin Krishnan) #80

Thanks Robin Ward for your help. I was able to load my module after investigating with require._eak_seen.

Below is the code for my widget. The problem is that this.scheduleRerender() is causing an infinite loop. The div always shows loading animation (even without this.scheduleRerender) which seems this.state.loading is not being set.

import { createWidget } from 'discourse/widgets/widget';
import { getTopic } from 'discourse/plugins/my-plugin/discourse/helpers/topics';
import { ajax } from 'discourse/lib/ajax';
import { h } from 'virtual-dom';

export default createWidget('topic-widget', {
  tagName: '',
  defaultState() {
    return { loading: false};

  refreshTopic() {
    if (this.state.loading) { return; }
    this.state.loading = true;
    this.state.topic = 'empty';
    getTopic(this).then((result) => {
      this.state.topic = result;
      this.state.loading = false;

  html(attrs, state) {
    if (!state.topic) {
    const result = [];
    if (state.loading) {
      result.push(h('div.spinner-container', h('div.spinner')));
    } else if (state.topic !== 'empty') {
    } else {
      result.push(h('', 'No topic.'))

    return result;

Could you help to resolve this error?

(Robin Ward) #81

Your code looks mostly fine, however, all widgets that deal with state require a key attribute. You should have seen a warning about this, although maybe the warning only appears when running tests?

Try adding a buildKey function like buildKey: () => 'topic-widget'.

That should probably fix it. Also, a much less serious issue is you might want to add topic: null to your state object. Javascript is much faster when it knows the shape of the object in advance.

(Jithin Krishnan) #82

Thanks Robin Ward.
Adding buildKey function worked fine for me.

(Alan Tan) #83

@eviltrout I got bitten by this in development and didn’t get a warning about setting a key attribute.

(Robin Ward) #84

Ah, it only warns in Ember.testing:

We might want to just raise an error if that happens now? It is pretty dangerous for Widgets to do that.

(Alan Tan) #85

:thumbsup: for raising an error instead. Maybe we can extend that to development too? :thought_balloon:

(Robin Ward) #86

Hopefully this will prevent others from making this mistake: