Developing Discourse Plugins - Part 3 - Add custom site settings

Previous tutorial: Developing Discourse Plugins - Part 2 - Connect to a plugin outlet


Site Settings

If you visit /admin/site_settings on a Discourse you have administrator capabilities on, you’ll see a list of configuration settings. Out of the box, we provide what we think are the best settings for a Discourse install, but we also understand that people want to tweak their installations to make their forum just the way they want it.

Chances are, unless your plugin is very simple, you’ll want to add settings that the users of your plugin can change and use to configure functionality. Fortunately, this is quite easy to do!

config/settings.yml

The first thing you’ll need to do is create config/settings.yml within your plugin folder. This file will outline all the settings your plugin will need. Here’s an example file:

plugins:
  awesomeness_enabled:
    default: true
    client: true
  awesomeness_max_volume:
    default: 10
    client: true

The file needs to be in YAML format. YAML can be quite picky so if Discourse is having trouble loading your settings I suggest you try validating your YAML with a tool like YAMLint.

I’ll explain the example file in detail. The top level is plugins and that tells Discourse that we want these settings to appear under “Plugins” in the site settings.

After that, there are two settings declared, awesomeness_enabled and awesomeness_max_volume. Discourse reasons the type of the settings from the default, so awesomeness_enabled is a boolean and awesomeness_max_volume is a number.

The client: true is important to understand. Discourse is made up of two major applications, the server side API written in Ruby on Rails, and the client side application written in Ember.js. By default, we don’t expose settings to the Ember.js client app unless you add client: true. We do this because some settings are private like API keys and should not be sent to end users. Also, if we sent every setting to the client that could be a lot for end users to download!

In our example case, we want both of those settings to be accessible in the JavaScript world as well as the server side world.

An important second step

Before you can use your newly added site settings, you need to add translations for them. Since Discourse supports many languages, any text you add will have to support being translated into other languages.

Let’s create the translations for our settings in English:

config/locales/server.en.yml

en:
  site_settings:
    awesomeness_enabled: "Is this plugin awesome?"
    awesomeness_max_volume: "What is the maximum volume possible?"

The labels we added in that file will be displayed in the admin section. It’s a good idea to be clear as possible as to what the setting accomplishes.

Declaring the setting as the ‘enabled setting’

Now that we have our site setting, we should tell Discourse that it’s the one that turns our features on and off.

Open your plugin.rb file and add the following line below the metadata comments:

   enabled_site_setting :awesomeness_enabled

Make sure to start all of your other settings with “awesomeness_” in order for the settings button at /admin/plugins to work correctly.

Accessing your new Settings

First, you’ll need to restart your development server to have the settings take effect. Once you do that, the settings should be available to your server and client side code.

We automatically inject the site settings into most JavaScript objects, so if you are declaring a Component, Controller, Route, View or Model you should be able to access the site setting by simply using this.siteSettings.awesomeness_enabled. In most handlebars templates you should also be able to say {{siteSettings.awesomeness_enabled}} and the setting value will be displayed.

We haven’t covered much Ruby stuff in this series yet, but if you want to access the site settings in the Ruby application you can do so via: SiteSetting.awesomeness_enabled

Now go forth and add custom settings to your plugins!


More in the series

Part 1: Plugin Basics
Part 2: Plugin Outlets
Part 3: This topic
Part 4: git setup
Part 5: Admin interfaces
Part 6: Acceptance tests
Part 7: Publish your plugin

32 Likes

For those more familiar with YAML, this was probably obvious, and thanks to YAMLint that you referenced I was able to figure this out pretty quick, but I thought it was worth mentioning that the format for the config/locales/server.en.yml needs to specifically be:

en:
  site_settings:
    awesomeness_enabled: "Is this plugin awesome?"
    max_volume: "What is the maximum volume possible?"

Correct?

Another quick question, it’s great that I can get to the settings by clicking on the Change Settings button, but how do I get the plugin specific Settings button directly to the right of the plugin like the poll plugin?

https://s3.amazonaws.com/f.cl.ly/items/162n220f0M0P3e0j2U3M/Image%202015-07-29%20at%207.29.28%20PM.png

Or is that a little too much for a Beginner’s Guide?

4 Likes

I updated the guide to fix both of those :smile:

4 Likes

How do you get the settings button to pre-populate the search field like Discourse Tagging does? I feel like I’m missing something obvious.

Edit: Nevermind, I figured it out. You had to use *_enabled to get it to filter. I originally didn’t have an enabled setting because the URL was simply the factor for determining if it is enabled.

1 Like

For some reason, {{siteSettings}} in the poster-name-right outlet returns undefined. I tested with other outlets and the return the expected value. I’m not sure why :sadpanda:

Had to work around it by using {{Discourse.SiteSettings}}

1 Like

This isn’t working because the Post view code path is some of the oldest code we have and it doesn’t do all the automatic stuff newer code can do :frowning:

I’ve come up with a fix that I think will solve your problem:

https://github.com/discourse/discourse/commit/11d1619e2c5239dd5f7fb1446c313e13a60fc26d

In the future I’ll be able to remove this hack and it’ll just work.

3 Likes

Hey @eviltrout how can I add a user setting? I mean a setting for a plugin that every user can change on his profile? Can I set a default too?

I guess is the same as an Custom User Field, so can I create a new User Field from a plugin?

Unfortunately there’s no easy way to do this right now. There is probably a good argument to be made for such an API for plugin authors.

Until then the way to do it is to add the fields via plugin outlets to the user preferences, tap into serializers and saving logic to store the setting the PluginStore.

1 Like

I ran into this a while back too. We have to expose the field in the site settings too. I’ll see if I can add a PR to make this easier.

https://github.com/tgxworld/discourse-cakeday/blob/14c902199a236026c3c67a3f99027fe063226363/plugin.rb#L11-L31

1 Like

For anyone following this tutorial and trying to get SiteSettings.awesomeness_enabled to return anything in a rails console, be aware that the tutorial is wrong. It should be SiteSetting.awesomeness_enabled (Setting without the s at the end!)

1 Like

Hmmm, this works for me in the hbs files

{{#if siteSettings.plugin_outlet_locations_enabled}}

Different syntax depending on where it’s being called from?

EDIT
Ah, the singular - plural Rails “magic” thing.

Nope, your first guess was correct >.>

JS has Discourse.SiteSettings but Rails has SiteSetting.

3 Likes

Oops! Thanks for letting me know. I’ve updated the tutorial.

2 Likes

Well, I believe some infrastructure are not complete yet. But I am refurnishing old login plugins while it’s worthwhile to asking the future change to plugin infra. Shall I move settings from login to plugin prefix?

I would recommend to do that, yes.

1 Like

Just to clarify, this is only the case if you say client: true, right?

Yes, that is correct. Javascript gets access to the “client side” site settings.

2 Likes

I do not completely understand this statement. I set it to client: false but it was still visible in admin settings. What exactly happens if you set client to false.

Thank you.

We ship up a big JSON object of all the site settings that have client: true to all users, so those are considered public and viewable.

If they don’t have client: true then they are meant to be available from the server side. The admin section is an exception - we need to return all site settings to admin users so they can be changed! It uses a different API to get them all.

9 Likes

Work for me! 2018-4-28

Code

config/settings.yml

config/locales/server.en.yml

plugin.rb

Result

http://localhost:3000/admin/site_settings/category/plugins


(oh, I saw the translation missing hint)

Thanks!

3 Likes