@quahfamili said:
I solved it, its okay already 😛
How did you solve this problem? I have this problem too.
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 ..
When I had the same problem in the rss plugin I just made my own save route https://github.com/barisusakli/nodebb-plugin-rss/blob/master/index.js#L232 so I could save hashes and sets.
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, 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.
</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 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]; } })()
}
});
};
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:
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 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?
@baris sounds good. Thank you!