https://github.com/akhoury/nodebb-plugin-import-ubb
This section is up here because it's very important for you to read it, so let's make few things clear before we go on.
- 'NodeBB' == 'NBB' == 'nbb' == 'Nbb'
- when you see the term OLD it refers to your source forum or bulletin-board
- when you see the term NEW it refers to NodeBB
- ALL of the OLD variables, must start with an underscore character:
_ _cid--> old category id, some forum softwares use different terms for categories, such as forums_uid--> old user id_tid--> old topic id_pid--> old post idcid--> new category iduid--> new user idtid--> new topic idpid--> new post id
You need node module that has the following interface.
config: a JS object that will be passed tosetup()and it contains the following:
{
dbhost: '127.0.0.1', // a string, db host entered by the user on the UI
dbuser: 'admin', // a string, db username entered by the user on the UI
dbpass: '123456', // a string, db password entered by the user on the UI
dbport: 3306, // a number, db port entered by the user on the UI
dbname: 'my_schema', // db schema, or name, entered by the user on the UI
tablePrefix: 'bb_', // db table prefix, entered by the user on the UI, ignore it if not applicable
// these values are not defaults, these are just examples
}callback(err, config): a function that send 2 arguments
- err: if truthy the export process will throw the error and stop
- config: just return the configs that were setup on the exporter, in case they were modified
Query the users, filter them at will, then call the callback(err, map) wih the following argurments
- err: if truthy the export process will throw the error and stop
- map: a hashmap of all the users ready to import
In the map, the keys are the users _uid (or the old userId).
Each record should look like this:
{
// notice how all the old variables start with an _
// if any of the required variables fails, the user will be skipped
"_uid": 45, // REQUIRED
"_email": "u45@example.com", // REQUIRED
"_username": "user45", // REQUIRED
"_joindate": 1386475817370, // OPTIONAL, [UNIT: MILLISECONDS], defaults to current, but what's the point of migrating if you don't preserve dates
"_alternativeUsername": "u45alt", // OPTIONAL, defaults to '', some forums provide UserDisplayName, we could leverage that if the _username validation fails
"_password": '', // OPTIONAL, if you have them, or you want to generate them on your own, great, if not, all passwords will be blank
// if you would like to generate random passwords, you will need to set the config.passwordGen.enabled = true, note that this will impact performance pretty hard
// the new passwords with the usernames, emails and some more stuff will be spit out in the logs
// look for the [user-csv] OR [user-json] tags to grep for a list of them
// save dem logs
"_signature": "u45 signature", // OPTIONAL, defaults to '', over 150 chars will be truncated with an '...' at the end
"_picture": "http://images.com/derp.png", // OPTIONAL, defaults to ''. Note that, if there is an '_piçture' on the 'normalized' object, the 'imported' objected will be augmented with a key imported.keptPicture = true, so you can iterate later and check if the images 200 or 404s
"_website": "u45.com", // OPTIONAL, defaults to ''
"_banned": 0, // OPTIONAL, defaults to 0
"_location": "u45 city", // OPTIONAL, defaults to ''
"_reputation": 1, // OPTIONAL, defaults to 0, (there is a config for multiplying these with a number for moAr karma)
"_profileviews": 1, // OPTIONAL, defaults to 0
"_birthday": "01/01/1977", // OPTIONAL, [FORMAT: mm/dd/yyyy], defaults to ''
"_showemail": 0, // OPTIONAL, defaults to 0
"_level": "administrator" // OPTIONAL, [OPTIONS: 'administrator' or 'moderator'], defaults to '', also note that a moderator will become a NodeBB Moderator on ALL categories at the moment.
}Note: Categories are sometimes known as forums in some forums software
Query the categories, filter them at will, then call callback(err, map) wih the following argurments
- err: if truthy the export process will throw the error and stop
- map: a hashmap of all the categories ready to import
In the map, the keys are the categories _cid (or the old categorieId).
Each record should look like this:
{
// notice how all the old variables start with an _
// if any of the required variables fails, the category and all of its topics/posts will be skipped
"_cid": 1, // REQUIRED
"_name": "Category 1", // REQUIRED
"_description": "it's about category 1", // OPTIONAL
"_order": 1 // OPTIONAL, defauls to its index + 1
"_skip": 0, // OPTIONAL, if you want to intetionally skip that record
}Note: Topics are sometimes known as threads in some forums software
Query the topics, filter them at will, then call callback(err, map) wih the following argurments
- err: if truthy the export process will throw the error and stop
- map: a hashmap of all the topics ready to import
In the map, the keys are the topics _tid (or the old topicId).
Each record should look like this:
{
// notice how all the old variables start with an _
// if any of the required variables fails, the topic and all of its posts will be skipped
"_tid": 1, // REQUIRED, THE OLD TOPIC ID
"_uid": 1, // OPTIONAL, THE OLD USER ID, Nodebb will create the topics for user 'guest'
"_cid": 1, // REQUIRED, THE OLD CATEGORY ID
"_title": "this is topic 1 Title", // OPTIONAL, defaults to "Untitled :id"
"_content": "This is the first content in this topic 1", // REQUIRED
"_thumb": "http://foo.bar/picture.png", // OPTIONAL, a thumbnail for the topic if you have one, note that the importer will NOT validate the URL
"_timestamp": 1386475817370, // OPTIONAL, [UNIT: Milliseconds], defaults to current, but what's the point of migrating if you dont preserve dates
"_viewcount": 10, // OPTIONAL, defaults to 0
"_locked": 0, // OPTIONAL, defaults to 0, during migration, ALL topics will be unlocked then locked back up at the end
"_deleted": 0, // OPTIONAL, defaults to 0
"_pinned": 1 // OPTIONAL, defaults to 0
}Query the posts, filter them at will, then call callback(err, map) wih the following argurments
- err: if truthy the export process will throw the error and stop
- map: a hashmap of all the posts ready to import
In the map, the keys are the posts _pid (or the old postId).
Each record should look like this:
{
// notice how all the old variables start with an _
// if any of the required variables fails, the post will be skipped
"_pid": 65487, // REQUIRED, OLD POST ID
"_tid": 1234, // REQUIRED, OLD TOPIC ID
"_uid": 202, // REQUIRED, OLD USER ID
"_content": "Post content ba dum tss", // REQUIRED
"_reputation": 0, // OPTIONAL, defaults to 0
"_votes": 0, // OPTIONAL, defaults to 0, can be negative
"_timestamp": 1386475829970 // OPTIONAL, [UNIT: Milliseconds], defaults to current, but what's the point of migrating if you dont preserve dates.
}If you need to do something before the export is done, like closing a connection or something, then call the callback
YourModule.log([args])YourModule.warn([args])YourModule.error([args])
In these 3 functions, you can basically do whatever you want, such as printing something on the console based on its level, or logging to a file. However, the arguments that each call passes in will be picked up, and emitted in corresponding events, and shown to the user. The event emitted are:
exporter.logexporter.warnexporter.error
You do not have to do anything extra to emit the events, just implement these functions at will and use them appropriately. see this for example.
YouModule.testrun(config, callback)
just a function for you to be able to test your module independently from nodebb-plugin-import
// for example
YourModule.testrun = function(config, callback) {
async.series([
function(next) {
YourModule.setup(config, next);
},
function(next) {
YourModule.getUsers(next);
},
function(next) {
YourModule.getCategories(next);
},
function(next) {
YourModule.getTopics(next);
},
function(next) {
YourModule.getPosts(next);
},
function(next) {
YourModule.teardown(next);
}
], function(err, results) {
if(err) throw err;
// or whatever else
fs.writeFile('./tmp.json', JSON.stringify(results, undefined, 2), callback);
});
};- Most forums, when creating a topic, a post will be created immediately along with it, this last post will be the main-post or parent-post or topic_content_post or whatever other term it's known with, and it's usually saved in the same table with the other posts, known as the "reply-posts". Usually this parent-post have some sort of flag to differentiate it, such as
is_parent = 1orparent = 0or something close. - Most likely, you may have to do some tables
joining to get each Topic's record along with its parent-post's content, then save it the_contenton eachtopicsMap.[_tid]object. - You should discard all of the other data on that parent-post as in NodeBB, it will be the Topic's content.
- Remember to filter these parent-posts from your reply-posts query so they don't get imported twice.
In order for your exporter to be automatically by the nodebb-plugin-import plugin as a compatible exporter,
its name needs to start with nodebb-plugin-import-, i.e. nodebb-plugin-import-ubb
You don't have to do that for it to work, you can type it in manually and it works fine.
Because it would only works with the nodebb-plugin-import plugin, and I wanted to namespace it somehow. I don't care anymore, call it whatever you want.