LightGallery Plugin for NodeBB

I just started using NodeBB about a week ago. A project came up that required the use of a forum and being that I have such an affinity towards Node.js... the results of my search for open source forum software quickly narrowed down to NodeBB. Previously I've used both Discourse and PhpBB from both administrator and user standpoints, and I've been fairly pleased with both. While getting to know NodeBB more intimately, there has certainly been a few things that I disliked and that stood out almost immediately.

  1. The documentation could be more up to date.
  2. The Docker container was a bit wonky when trying to get it to work (another post), which pointed back to #1.
  3. Viewing post images left a lot to be desired as there is no built in gallery support and there werent any plugins for such. The images in posts were simply inlined.
  4. Uploading was limited to direct uploads to the server (future post about the Cloud Storage plugin I wrote)

Write a NodeBB Plugin (nodebb-plugin-lightgallery)

The solution to problem #3 above came in the form of writing a NodeBB plugin:

This post will explain the general process of doing that...

So after a quick search to find an actual javascript gallery, I ended up choosing lightgallery which is also on GitHub and is a beautiful piece of work in it's own right. The documentation is pretty great and made it easy to integrate. I also believe that I've used forums where lightgallery was being used as the gallery plugin because the interface seems very familiar, although I haven't confirmed that suspicion.

LightGallery itself is modular and as such is made up of many different modules in addition to it's core:

$ npm install lightgallery lg-thumbnail lg-autoplay lg-video lg-fullscreen lg-pager lg-zoom lg-hash lg-share

As such I included all of those libraries in my plugin.json file which is the configuration file of a NodeBB plugin. Further, NodeBB operates with a hook architecture and the hook I was interested in is filter:middleware.render which gets called before each page render and allows you to alter the page beforehand. Per the documentation which reads:

For example, a filter may be used to alter posts so that any occurrences of "apple" gets changed to "orange". Likewise, filters may be used to beautify content (i.e. code filters), or remove offensive words (profanity filters).

Our goal is to change every post such that markup is altered to match that required by LightGallery, and further we must instantiate the LightGallery plugin:

If you're familiar with Express middleware then this is a pretty easy concept to grasp. As I was still new to NodeBB and writing plugins for it, I chose the name "myfiltermethod" for the method that would be called on each filter:middleware.render hook. The scripts section of plugin.json defines the javascript files that will be compiled and minified to deliver to the client side, and in our case this is used for our own plugin specific code. Note that the scripts property is used to define our javascript while the modules property is different in that it's used to define third party javascript. Both will be bundled and sent to the client. Finally, the "staticDirs" and "css" are used to define the font, image, and css assets that lightgallery needs. Because NodeBB maps the files defined by the staticDirs property as:

/path/to/your/plugin/public/images to /plugins/my-plugin/images

We need a way to let the LightGallery plugin know about this new mapping, as it will want to look for assets from a more basic root path by default. The method I chose to solve this was to simply change the path in the LightGallery scss files and then re-compile the scss assets during build time which is all handled via Travis-CI build process. The highlighted lines show where the scss variables are changed before running Grunt for each of the LightGallery dependancy modules.

Once all of the dependancies are taken care of we need to actually write some code. In this case the code is pretty simple in that there is only a single module which is called by the filter:middleware.render hook and inside of that module a single function updatePost(post) which handles the post manipulation using the Cheerio library. Since the hook is a server side piece of code (there are client side hooks with NodeBB), DOM selection using jQuery or even native Javascript was not available. While I think Cheerio is idealy supposed to be a drop in replacement for jQuery, I found it to be a bit different in it's operation and it took a bit to get used to it. I prefer either of the other two methods to be honest.

The updatePost function takes in the post data which is a JSON object with the markup held in the content key. We generate a lightgalleryWrapper which maps to the required wrapper tag that LightGallery needs to identify which markup should be included in the image gallery. Note that we also key the wrapper's id with the post.pid such that multiple galleries can exist per page, in fact one per post.

<div id="lightgallery' + post.pid + '"></div>

Then we iterate through the content's markup finding all of the images and translate them to the markup required by LightGallery, assigning the final updated markup back to the content value of the post object.

post.content = html

Finally executing the callback at the end passing in null first (where the error object were to go if there were errors) and the data object.

return callback(null, data)

Result

Instead of images opening inline as shown in the first gif below, using the nodebb-plugin-lightgallery plugin displays images in a beautiful image gallery, making not only viewing easier but also navigating, downloading, and otherwise interacting with the images in the post.

NodeBB WITHOUT the nodebb-plugin-lightgallery Plugin

NodeBB WITH the nodebb-plugin-lightgallery Plugin

Conclusion

So writing my first NodeBB plugin was fairly straight forward and while it didn't take too much effort, I feel that it delivers a HUGE improvement over a "stock" NodeBB install. The user experience is very much improved when using a gallery over having images open inline, particularly when it comes to forums. Next time I will write about the 2nd NodeBB plugin I decided to write nodebb-plugin-cloudstorage which provides a way to offload images to external cloud providers (Cloudinary, ImageKit, and AWS S3) vs storing them locally on the NodeBB server.