nodebb-plugin:{name}:options structures
-
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.
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.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 ..
-
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) { Settings.prepare(); });
Once
prepare()
'd, theSettings
module will look for all form inputs that have adata-field
attribute, and save those values under theconfig
hash. Use namespaces liberally.Settings.prepare()
also adds an event handler to a button with the idsave
.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. </label> </div> <div class="form-group"> <label for="langPrefix"> Prefix for <code>code</code> blocks </label> <input class="form-control" placeholder="lang-" type="text" data-field="nodebb-plugin-markdown:options:langPrefix" id="langPrefix" /> </div>
Save button:
<button class="btn btn-lg btn-primary" id="save">Save</button>
-
-
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 cannest
objects in the actual#config
hash? I know redis is string-based but maybe, just maybe, we can abstract that by safelyJSON.stringify
andJSON.parse
based on VALUEtypeof
,Array.isArray
andnull|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]; } })() } }); };
-
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
- PreNodeBB-PullRequest preview: - Affected files: public/src/utils.js, src/meta.js - Purpose: Allowing the 'config' hash to safely save Stringified Objects and Arrays, but leave everything else alone.
- PreNodeBB-PullRequest preview: - Affected files: public/src/utils.js, src/meta.js - Purpose: Allowing the 'config' hash to safely save Stringified Objects and Arrays, but leave everything else alone. - prePRpreview.js
Gist (gist.github.com)
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:
-
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
ornodebb-plugin-<plugin_id>:myset
.We are aware that the mentions and markdown plugins are not following this as can be seen from https://github.com/designcreateplay/NodeBB/wiki/Database-Structure. 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?