nodebb-plugin:{name}:options structures

  • Plugin & Theme Dev

    Sorry long post with snapshots.

    I find my self saving JSON strings in the DB for few of the nodebb-plugins I wrote. I feel like this is a little fragile.
    Anyone have a better approach?

    See in this example, the options are valid JSON strings, which get parsed on plugin init.
    Screen Shot 2014-01-13 at 2.21.47 PM.png

    See in this example, the External navigation items are also stored as stringified array of objects from a hidden field, then, on admin page load, I parse then populate the fields, in the visible fields, and this is another custom-save button which doesnt submit immediately, but rather populates the new data in a new object, stringifies it, sets it on the hidden field, then $('#save'\).trigger('click'\) (edit: ugh emoji y u do dis?) which is the real saveBtn that Settings listens to its click event.

    Screen Shot 2014-01-12 at 5.38.08 PM.png

    Either way, it's either not user friendly, or not developer friendly... can we make the Settings a little smarter, which with will allow Objects/Sets to be saved in the db, with, of course, some meta data that will dictate how these new Structures will be renders in the admin/plugin/:name pages, like grouping multiple inputs under one object,

    .. i dont know ..

  • NodeBB Admin

    When I had the same problem in the rss plugin I just made my own save route so I could save hashes and sets.

  • GNU/Linux Admin

    While not exactly the most graceful, this is how the settings page (and by extension, most of the plugins I write) handle settings to the db:

    	require(['forum/admin/settings'], function(Settings) {

    Once prepare()'d, the Settings module will look for all form inputs that have a data-field attribute, and save those values under the config hash. Use namespaces liberally.

    Settings.prepare() also adds an event handler to a button with the id save.

    Example input:

    <div class="form-group">
    	<label for="smartypants">
    		<input type="checkbox" data-field="nodebb-plugin-markdown:options:smartypants" id="smartypants" />
    		Use "smart" typograhic punctuation for things like quotes and dashes.
    <div class="form-group">
    	<label for="langPrefix">
    		Prefix for <code>code</code> blocks
    	<input class="form-control" placeholder="lang-" type="text" data-field="nodebb-plugin-markdown:options:langPrefix" id="langPrefix" />

    Save button:

    <button class="btn btn-lg btn-primary" id="save">Save</button>
  • Plugin & Theme Dev

    @julian yea, I figured that out, that's pretty straight forward and simple, but doesn't solve complex options structures.
    @baris sweet! that's what I wanted, thanks! basically, not using Setting.prepare().

    Thanks guys.

    gtg clean up teh code.

  • Plugin & Theme Dev

    ok, the custom api route works great, the one thing I don't like is that I feel like my plugin is now polluting the "global" redis (or mongo)-namespace

    Following the @baris way this is saving the plugin configs as a global object..

      db.setObject('nodebb-plugin-rss:feed:' + feed.url, feed);
      db.setAdd('nodebb-plugin-rss:feeds', feed.url);

    so now, some plugins have their config options in the

    #config //object

    and others in the global namespace.
    and in one of the plugins I am writing, it has values in both !

    is it just me or that doesn't look right?
    anyway we can nest objects in the actual #config hash? I know redis is string-based but maybe, just maybe, we can abstract that by safely JSON.stringify and JSON.parse based on VALUE typeof, Array.isArray and null|undefined checks.

    ADD: and since there is some performance hit with large numbers of fields, we can restrict that to the 'config' fields/values only. That's the redis example, not too sure about Mongo

            module.setConfigField = function(key, data, callback) {
                  // make sure it's either an Array or Object, but also 'truthy' since typeof null == 'object'
                  if (typeof value == 'object' && !!value) 
                      value = JSON.stringify(value); 
                  redisClient.hset('config', key, value, function(err, res) {
                            if(callback) {
                                    callback(err, res);
            module.getConfigFields = function(fields, callback) {
                    redisClient.hmget('config', fields, function(err, data){
                        if (err) return callback(err, null);
                        var config = {};
                        for (var i =0; i< fields.length; i++) { 
                            // or maybe use eval
                           // IMPORTANT this is will return a NUMBER for the '0' and '1' configs stored in the DB, which means some code will need to be changed across to check
                            // i.e if ( config.allowGuestPosting === '1'  ) ==TO==> if (config.allowGuestPosting === 1) 
                            // or we backward compatibility we can check if it's a number and convert back to a string if(!isNaN(parseFloat(value)) && isFinite(value))
                            config[fields[i]] = (function(){try { return JSON.parse(data[i]); catch { return data[i]; } })()           
  • Plugin & Theme Dev

    no opinion yet? I know you're busy, so I didn't want to pollute with another Pull Request, I made this sample Gist of what I'm thinking of. Basically just a way to allow configs to be safely saved a Objects and Arrays

    let me know.

    I'll integrate it in NodeBB src and PullRequest it, if you think that's a potential solution, otherwise, I can keep it out, in each Plugin code :sad:

  • NodeBB Admin

    Just had a quick chat with the others and we don't like storing json strings. I think the best way is to store them as global hashes/sets but namespace them with the plugin_id. So store all your plugin config under hashes/sets named like nodebb-plugin-<plugin_id>:myhash or nodebb-plugin-<plugin_id>:myset.

    We are aware that the mentions and markdown plugins are not following this as can be seen from We will fix that after 0.3.0 and store only core settings in the global config hash. Each plugin will have its own set of namespaced structures. Does that sound good?

  • Plugin & Theme Dev

    @baris sounds good. Thank you!

Suggested Topics

| | | |