In conclusion, we updated the data model for our module using the drag and drop form editor and then updated the view to use our new data field. 

Hopefully you are beginning to see the power and flexibility of SonicJs as a content management system.

Next, we'll take a look at SonicJs' ultimately power tool; Hooks!

[MENU-TITLE id="335"]

Overview

Before diving into the developer guide, please make sure you have gone thru the Overview and User Guide sections. 

Assuming you have done that, the developer guide will focus on the various methodologies for extending the SonicJs core including theming, module development, overriding core functions, advanced configuration, integration, etc.
 

Hooks

Hooks are what gives SonicJs it's flexibility.  It can be a bit confusing at first if you haven't worked with the event-emitter design pattern. However, once you get used to it, you'll see how well it enables us to follow the SOLID principals

Hooks allow us to alter core functionality and even the functionality defined in other modules without touching the code within the target function.

Let's look at a simple example:

// Get an array of objects of a certain content type, // and optionally pass in a filter to fine tune the results function getDataForMyModule(contentType, filter){ let data = getDataFromDatabase(contentType, filter); return; } // Example call to the above function // We want all records of type "blog" let data = getDataForMyModule("blog", {});

Simple enough right? We want a list of content types of a certain kind ("blog" in our example). 

But what if we wanted to implement a site-wide rule dictating that we never wanted to display a blog post with a future publish date. This will allow us to load up our website with future blog post that will automatically publish once their publish date has been reached.

// Here, we had added our new business rule directly in the function. function getDataForMyModule(contentType, filter){ filter.publishDate < new Date(); // <--here is the new filter we're adding let data = getDataFromDatabase(contentType, filter); return; }

But there is a MUCH better way of adding this new business rule. Take a look at a different approach:

eventBusService.on("postModuleGetData", async function(options) { if (options.shortcode.name === "HELLO-WORLD") { var d = new Date(); var weekday = new Array(7); weekday[0] = "Sunday"; weekday[1] = "Monday"; weekday[2] = "Tuesday"; weekday[3] = "Wednesday"; weekday[4] = "Thursday"; weekday[5] = "Friday"; weekday[6] = "Saturday"; let dayOfTheWeek = weekday[d.getDay()]; options.viewModel.data.greeting = `Happy ${dayOfTheWeek}`; } });
// Get an array of objects of a certain content type, // and optionally pass in a filter to fine tune the results function getDataForMyModule(contentType, filter){ eventBusService.emit("preDataFetch", filter); let data = getDataFromDatabase(contentType, filter); return; } // Before calling the above function, we'll setup our business rule eventBusService.on('preDataFetch', function (filter) { filter.publishDate < new Date(); }); // Example call to the above function // We want all records of type "blog" let data = getDataForMyModule("blog", {});

So we're doing the same thing as before, but we've  broken away from the need to ever again have modify our core getDataForMyModule() function.

We can now very easily implement the "S" and "O" in SOLID - Each time we want to implement a business rule in our getDataForMyModule(), we can simply "subscribe" to the "emitter event" and inject our new code.

Hook List

  • High Level
    • startup
    • requestBegin
    • afterFormSubmit
  • Page Generation
    • postProcessPage
    • getRenderedPagePostDataFetch
    • preRender
    • getRenderedPagePostDataFetch
    • preProcessSections
    • preProcessRows
    • preProcessColumns
    • preProcessModuleShortCode
    • beginProcessModuleShortCode
    • beginProcessModuleShortCodeDelayed
  • Modules
    • modulesLoaded
    • postModuleGetData
    • postProcessModuleShortCodeProccessedHtml

Modules

Modules allow you to extend and modify SonicJs' capabilities. Module file are isolated in their own folder yet can alter both core functionality and even functionality of other modules. 

SonicJs modules are extremely  powerful and flexible. Unlike most CMS, where modules offer only "bolt-on" style extension with perhaps some APIs to plug into, SonicJs allows the developer to completely alter the functions and/or data used throughout the system.

The easiest way to explore the awesomeness of SonicJs modules is to build and example. Let's do that!

Hello World Part 1

We'll create a "Hello World" module in part 1 and then extend it in the subsequent parts. Let's get started!

  1. From the Admin, Select "Modules" from the left side navigation
  2. Click "New Module"
  3. Module title -> "Hello World" , System Id -> "hello-world"
  4. Leave "Can Be Added To Column" set to true.
  5. Click "Create Module"

That's it! We've created our first module and can now add it to a page.

Optional: Before we do that, open your project in your favorite IDE (ie VS Code) and check out the new files generated for you in this folder: /server/modules/hello-world 

  1. Next, if you haven't already, add a page to your site. Call it "Sandbox" or whatever you want.
  2. Next, create a section on the page
  3. If you're not sure how to create a page, please see the "Create Page" section in the User Guide
  4. Click on "empty column" to select the column
  5. Click on the Module dropdown and select "Hello World"
  6. Enter your first name or type "Bozo" if you feel like a clown
  7. Click "Submit"

You should now see your module added to the page and it should be displaying the data that you entered in the First Name field.

Hello World Part 2

Now, let's dig in a bit to our module and make some changes. Currently the output of our module looks like this:

Now when we click on our module, then click the "edit" icon, we'll see our edit screen:

As a simple tweak, let's now add a "Last Name" field to our form:

  1. From the admin UI, Modules -> Hello World -> Edit
  2. Add a new "Last Name" text field:

So, now we can back back to our module settings, and fill in a last name:

But when we click on submit, the view hasn't changed. So we now need to update our view to use the new data field.

In your favorite IDE (ie VS Code) open up the view file for your module located here:

/server/modules/hello-world/views/hello-world-main.handlebars

Now we'll add {{ data.lastName }} , our view now looks like this:

After you save your update, the web server will automatically restart. All we need to do is go back to our webpage and refresh:

Hello World Part 3

Now, let's add a hook and add some simple business logic to our module.

We'll replace the "Hello to you" in our template with some dynamic text depending on the day of the week. For example: "Happy Monday..."

To do this we need to use a hook and modify the view model being used for our view template.

Open up the hello-world-main-service.js file in your module and add a new event subscription inside of your startup function:

eventBusService.on("postModuleGetData", async function(options) { if (options.shortcode.name === "HELLO-WORLD") { var d = new Date(); var weekday = new Array(7); weekday[0] = "Sunday"; weekday[1] = "Monday"; weekday[2] = "Tuesday"; weekday[3] = "Wednesday"; weekday[4] = "Thursday"; weekday[5] = "Friday"; weekday[6] = "Saturday"; let dayOfTheWeek = weekday[d.getDay()]; options.viewModel.data.greeting = `Happy ${dayOfTheWeek}`; } });

Now we just need to make a small tweak to our template in order to use the new greetings property:

<div>{{data.greeting}} {{ data.firstName }} {{ data.lastName }}, from the Hello World module!</div>

Which gives us the final result:

Conclusion

At this point, hopefully you have at least a high level idea of how modules work within SonicJs.

As you may be starting to see, hooks allow you to inject business logic into various stages of the module's processing in order to achieve your specific use case. Hooks can also alter SonicJs' core functionality to meet your needs.

It's also a good time to mention that you can easily add your own hooks and use them in various custom modules.