Skip to content

Instantly share code, notes, and snippets.

@FilipNest
Last active August 31, 2016 12:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save FilipNest/c439926076ee6e7008ceba239d17c2f2 to your computer and use it in GitHub Desktop.
Save FilipNest/c439926076ee6e7008ceba239d17c2f2 to your computer and use it in GitHub Desktop.
irisjs cms tutorial

This tutorial will show you how to create a simple blog site with a live updating comment feed and email notifications when a comment is received.

You'll need node.js 6.0 or greater installed.

Creating a blog site with a live updating comment feed and email notifications

Getting started

  • Create a new folder and initialise a new node.js npm project by using npm init and following the instructions.

  • Install Iris.


npm install --save https://github.com/FilipNest/Iris

  • Create a launch file with your basic site configuration. Let's call it mysite.js
  • Inside mysite.js put the following:
var config = {
  "sitePath": "/mysite",
  "port": 4000, 
  "dbEngine": "nedb",
  "siteEmail": "you@yoursite.org",
  "max_file_size": 10
}

require("irisjs")(config);

(Change the sitePath, port and site email address as needed.)

  • Run the site using node mysite.js

Visit localhost:4000 in your web browser and you should be greeted with a screen asking you to fill out an administrator email address and password.

Select the standard installation type, type in your preferred admin account access details and press install.

Creating a home page and a blog home page.

Before we get started creating a blog content type, head over to the content section in the admin menu and create a new entity of type page. There is nothing special about this type, it just comes packaged into the standard installation so you might as well use it.

Give it a path of /. This will make it your site's home page.

Don't worry about content too much, we'll add more to this page later.

Add another page for your blog homepage and give it the path of /blog

You can visit these pages at their relevant paths but it won't look great until we get onto theming a bit later. Go to /admin to go back to the administration system.

Creating some content types

Head over to the structure -> entities tab in the menu.

You should already have a page and user type but we're going to create two new ones. First of all, a blog post type.

  • Select Create new entity type
  • Name the entity type blog and optionally give it a description.

You'll now need to add some fields to your entity type.

For now let's add a title field and a body field.

  • Press add new field and add a textfield with the label Title
  • On the next screen you can give it a field description if needed to help on the edit page but most importantly, set the field view permissions to allow anoymous and authenticated users to view the field.
  • Optionally mark the field as unique (you won't want two blogs to have the same title)
  • Hit save and then add a body field of type longtext. Similar settings apply to this field.
  • Hit save on the body field.
  • The longtext field has an additional option of a widget. This is a setting that allows you to add a WYSIWYG editor to the field. You might want to write in Markdown or plain text/html and leave this blank, but if you want to use CKEditor (as many will) select edit on the body field and head over to the widget tab. Hit save on the CKEditor widget.

Entity type access permissions

You want other people to be able to view your blog entries so head over to the permissions screen in the users section and select can view any blog for anonymous and authenticated in the entity section. Do the same for the page entity type while there.

Create a URL structure for your blog

You might want to type in unique urls for each blog, but it helps to have a default.

The autopath module is there to help with this.

Go to structure -> autopath and in the blog post section you should be able to create a pattern for making URLS.

Type in /blog/[title] and it'll automatically use a url friendly version of the title in the URL.

Creating a blog entry

Head over to the content section of the administration interface and you'll see your blog entity type. Select it and use the interface to create a new blog. Call it hello world for example.

Leave the path field blank and it will automatically get filled in by the autopath settings as mentioned above. You can override it if you want.

When done, head over to the URL for the blog. Something like - http://localhost:4000/blog/hello-world . You can go directly to the blog path by clicking view content and then the blog title in the list.

As you'll notice, the blog's template is just a list of fields. We'll now make a theme to style it.

Creating a theme

  • Create a folder in your site directory called themes. (Your site directory was specified with the sitePath configuration parameter. mysite in this case). It should already have folders like configuration in it.

  • In this new themes directory, create a subdirectory with your theme name. mytheme for example.

  • In the mytheme directory, add a file called mytheme.iris.theme (change the mytheme part to whatever you named your theme).

  • Add two directories to your mytheme folder templates and static. We're going to be using these a bit later.

  • Add the following JSON configuration to your theme's .iris.theme file:

{
  "name":"My theme", 
  "regions":["header","sidebar"]
}
  • Visit the themes tab in the Iris administration menu and select and enable your theme.

Creating some template files

Every template in Iris is an HTML file that is parsed through the Handlebars-powered template engine. When Iris searches for templates it looks for them in module and theme template folders passing in a few search arguments. These arguments are split by a __ in the actual file name. Here's an example to help explain what that means:

If looking for a template to display a page entity, Iris looks for templates named page.html . It also passes in the entity ID so you could easily override a template for a page with page__1.html. General entity pages (if no other template is found, can be stored at entity.html). The default entity.html is what you're currently seeing when you visit your page. It simply lists all the fields on an entity and their labels.

Error pages take the name of their status code, so use 404.html for a 404 page.

Any system templates provided by modules can also be overriden in your theme or custom modules (you could even replace the whole admin theme if you wanted to).

The HTML wrapper template

A special template file called html.html is used for the wrapper of the page (everything that goes around it). This can also be overriden for more specific templates, html__page__1.html for example.

For the wrapper template to work it needs to include a special tag of [[[MAINCONTENT]]] . Put this wherever you want the inner template markup to display.

It is also a good idea to include the following markup for any messages that dispaly to a user:

{{{iris embed="messages"}}}

And the following in the <head></head> to include any clientside scripts or stylesheets loaded by an Iris module. We'll use some of these later.

{{{iris embed="tags" name="headTags"}}}

So let's start with creating an HTML wrapper template.

  • Create an html.html file and place it in the templates folder of your theme.
  • In that file, put something like the following:
<html>

  <head>
    
    <title>{{current.title}}</title>
  
    {{{iris embed="tags" name="headTags"}}}
  
  </head>
  

  <body>
  
    {{{iris embed="messages"}}}
    
    [[[MAINCONTENT]]]
  
  </body>

</html>

Notice the current Handlebars variable. You'll use this to access the entity currently being viewed and its fields.

The blog entity type template

For now, let's keep the blog page template very short. Use the following in a blog.html file (named after your entity type).

<article>

  <h1>{{current.title}}</h1>

  {{{current.body}}}

</article>

The {{{current.body}}} Handlebars tag has triple brackets so that it allows the HTML through. Double curly brackets would leave it as plain text.

Create another template for page.html in a similar way. entity.html could be used for both but we're eventually going to put different content in them so it makes sense to separate the two.

Serving static files (including a stylesheet)

You'll probably want to include a stylesheet in your template. Simply add it to the static folder in your theme directory and include it in your html.html file like this:


<link rel="stylesheet" href="/themes/mytheme/style.css" />

Static files can also be saved in the whole site root under a /static folder. They will be available via the root url rather than /themes/themename/.... This is useful for non theme specific static assets.

Refresh your page once your stylesheet file is in and it should be used.

Creating a menu and adding it to a region with the menu block module

Although this site will be small, you might want to put in a simple menu to navigate between the home page and the blog page. And if you keep this site you'll probably want to expand it in the future by adding further menu pages.

  • Head over to the structure -> menu section of the admin section (you'll need the menu UI module enabled but this comes with the standard installation).
  • Select create new menu and give it a title of main.
  • Add some links using the form filling in the paths / & /blog for our two pages and giving them menu titles. They won't need any child items for now.
  • We could embed this directly in our theme by adding the following tag to our html.html template:
{{{iris embed="menu" menu="main"}}}
  • However, we can also use the menu block module and the Iris region system to add this menu to a region in the theme. Remember the regions we added in the .iris.theme file? Let's add them to our template.
  • Edit html.html and put in the following somewhere near the top. Before the [[[MAINCONTENT]]] tag:

{{{iris embed="region" region="header"}}}

  • Go to the modules admin page and enable the menu block module if it is not enabled already.
  • Now head back to the structure section of the admin system and create a menu block. Head to the blocks section and add a new block of type menu. Call it main menu and select the main menu.
  • Now in the structure -> region section add the main menu block to the header region.
  • Hit save and the menu should appear in your template allowing you to move between the two pages. Use CSS to style it or, if you want to overwrite the whole handlebars template (found in /node_modules/irisjs/modules/core/menu/templates/menu.html) create a menu.html or a menu__main.html file in your template.

Embedding a sidebar with a list of blogs to sit on the home page.

Once the blog site has more blogs you're going to want to have an easy to access list of the latest ones. As this will be placed both on the main blog page and each individual blog page we can place it in a single sidebar template that will be visible on both templates. This can be done directly with templates but we're going to use the lists module to show how it can be done through the user interface. We're going to use the direct in template version for comments later so you will learn both in this tutorial.

  • Go to the modules page in the administration interface and enable the lists module.
  • Go to structure/blocks and create a new block of type list of blog.
  • Name it blogs and skip the query section (we want to show all blogs so you don't need to filter them).
  • Put in a limit and a sort if you want.
  • The important part is the template. As the field description says you can use the list variable which is an array of the fetched entities. Put in a template such as:
<ul>
  {{#each list}}

  <li> <a href="{{this.path}}">{{this.title}}</a></li>

  {{/each}}
</ul>
  • Put this block in your sidebar in the regions page and then put the sidebar in your theme with:

{{{iris embed="region" region="sidebar"}}}

Tick the live update box if you want the list to update without a page refresh (more on this later).

You should now have a list of blogs on your site that you can style however you want.

Create a comment entity type and allow it to be attached to blog posts

We're now going to allow comments on blogs and list them along with a comment form.

  • Create a new entity type called comment.
  • Add a body longtext field and an author textfield (eventually you can allow comments from only logged in users and reference their user names but we're going to go for a simple text field for now).
  • Add an entity reference field called parent and in the settings select entity type blog and field to search on as title.
  • Make sure all these fields are available for anonymous users to view and edit.
  • Also make sure the comment entity type is available for anonymous users to view via the users -> permissions page.
  • While you're on the permissions page allow the can create comment permission for anonymous users. We'll need this later.

Making comments appear underneath a blog post

We're now going to use the entity embed system to show any comments for a blog post underneath it.

  • In your blog.html template, put in the following snippet. We'll then move onto what each part means.

 <section>
  
    {{#iris embed="entity" entities='["comment"]' queries='[{"field":"parent.eid", "operator":"IS", "value":$current.eid}]' sort='{"eid":1}' as |comments|}}
    
    <ul>
    
      {{#each comments}}

        <li>
          {{{this.body}}}
          <b>{{this.author}}</b>
        </li>

      {{/each}}
      
    </ul>

    {{/iris}}
  </section>

Unlike the previous Iris embeds we've used, the entity embed is a block embed. It starts with a {{#iris and ends with a {{/iris}} tag. Entity queries take the following parameters:

  • Entities - An array of strings stating which entities you want to query for.
  • Queries - An array of JSON objects with the following keys:
    • Field - the field to check. Here we're checking the parent.eid (a subfield on the parent field that says which entity ID we're fetching. The entity reference field also has an entityType subfield)
    • Operator - is, includes, contains
    • Value - The field to check the value against. Here we're using the current entity ID and preceding it with a dollar sign so that it gets loaded in as a variable from the current context.
  • Sort - An object with a key of the field you want to sort by and either 1 for ascending or -1 for descending. We're sorting by ascending eids as we want the latest comment to appear at the bottom.
  • Limit - We can limit the results if required.
  • Skip - We can skip a few results if needed. Not necessary here.

Making the comments live update when a new one is posted

Add liveupdate=true to the entity embed and in a new browser tab add a new comment through the administration interface (type the name of the blog you want to add it to in the autocomplete bit of the parent field). It should automatically update on the list on the relevant blog page without you having to refresh the page. This will be handled by JavaScript automatically loaded into every visitor's page if you put the headTags tags in your header in the html.html template.

Creating a comment form so users can post comments

Creating a module

To create a comment form you're going to need to create a custom Iris module. It's not much work and will be a good introduction to how to forms, hooks and module system works.

  • Create a folder in your site folder called modules like you did with themes.
  • In it put a new folder called comments.
  • Create a file in this folder called comments.iris.module and put in the following information:

{
  "name": "comments",
  "dependencies": {
    "entityReference": "1.0.0"
  },
  "description": "Adds a comment form for blog entities",
  "weight": 2
}


The weight is given so that it loads after the entity reference module which it depends on.

  • Create a file called comments.js in the same directory. This will be where your module's code will go.
  • Go to the administration interface and enable the comments module on the modules page.
  • It won't do anything yet so let's go to the comments.js file and start creating the form.

(You can bundle in the comment entity type into this module so that it's automatically made available when the comments module is enabled. This isn't necessary here but if you want to you can copy the comment.json from your configurations/entity folder in your site folder into a /schema subfolder in your module.)

Restarting the server after making JavaScript code changes

Theme changes can be made without having to restart the system. Things like hooks and modules need to be initialised by the system so everytime you make a change to a module's code you'll need to restart the Iris system. This can be done through the restart form in the administration system. Your session should persist between a restart.

Registering a new form

You'll need some JavaScript knowledge for this bit. You should be able to get through without too much by copying and pasting but it helps to understand how functions and objects work.

The comments module we've created will automatically have been registered under the iris.modules.comments object. We can now use this object to register a hook into the form system that responds when we request a comment form. Let's try this with the following code:

iris.modules.comments.registerHook("hook_form_render__comments", 0, function (thisHook, data) {

  data.schema.author = {
    "title": "author",
    type: "text"
  };

  data.schema.body = {
    "title": "comment",
    "type": "textarea"
  };

  data.schema.eid = {
    "type": "hidden",
    default: thisHook.context.params.eid
  };

  thisHook.pass(data);

});

The parent hook is called hook_form_render and in this case is being called for the specific form comments. This hook will be called any time someone tries to view a form with the formID comments.

The number that comes after the hookname is the weight of the hook. This is fine to set at 0 but you can set it higher to take place after another hook has run and overwrite the form (there aren't any other hooks for this comment form but there could be in the future). You can overwrite system forms in this way. All Iris hooks work like this allowing you to hook into any core funtionality and extend it.

Then we have a function which takes two parameters.

  • thisHook - information about the hook that has been called such as who has called it (details of the client) and if it has any special parameters included in it (under thisHook.context). It also carries two special methods pass and fail which need to be run at the end of the hook to tell the system that the hook has finished. It is very important to always pass or fail a hook.
  • data - the form object being generated which this hook will extend.

The form system Iris uses is based on a library called JSONForm. It is best to read the documentation directly at JSONForm at https://github.com/joshfire/jsonform/wiki for information.

You can see we have extended the form with three fields, first an author text field and a textarea field for the comment body then a hidden field which contains the entity ID of the entity you're adding a comment to. This will be passed in as a parameter in the embed (note this being used in the default value).

We've then finished the hook off by passing it and sending through the data object we have added to.

Restart Iris using the administration system to get the form to initialise.

Adding the form to the page

To embed the form, go to your blog.html template and add in the snippet:


{{{iris embed="form" formID="comments" eid=current.eid}}}

The eid=current.eid parameter will be passed into the form render hook to be used to reference the current blog entity.

If you refresh the page you should see your new form.

Feel free to submit it but it won't do anything. We need to build a submit handler.

Adding a submit handler to the form.

Here's the code for the submit handler:

iris.modules.comments.registerHook("hook_form_submit__comments", 0, function (thisHook, data) {

  var entity = {

    author: thisHook.context.params.author,
    body: thisHook.context.params.body,
    entityType: "comment",
    parent: {
      "entityType": "comment",
      eid: thisHook.context.params.eid
    }

  };

  iris.invokeHook("hook_entity_create", thisHook.authPass, entity).then(function (entity) {

    thisHook.pass(data);

  });

});

It starts similarly to the render handler, this time calling the form's submit hook. Its thisHook object gets the form data in the thisHook.context.params object. First we create an entity with it, filling out all the fields we created on the comment type. The entity reference field is a two part field so we need to fill in the entityType part as well as the entity ID we have been passed through.

Once our entity is created we invoke another hook inside the submit handler, hook_entity_create.

The first parameter to the invokeHook function is the name of the hook, the second is the authPass of who the hook will be called as (the person submitting the form in this case) and the final parameter we're using here is the entity object.

We want to wait until the entity has been saved into the database to finish the submit handler so we're finishing the hook by chaining onto the result of hook_entity_create. To understand more about how this works, look up JavaScript Promises, this is an example of one.

We're done! Restart the server again to make those changes go live and try to submit the form. The comments should show up. If you've enabled liveupdating on the comment entity embed as in this tutorial new comment should show up for all users without them needing to refresh the page.

Using the Triggers module to send an email every time a comment is recieved

Now to send an email when a comment is received.

  • Enable the triggers module.
  • Create a new trigger at config -> triggers
  • Call the action email comment
  • Set the event type to comment created
  • Leave the conditions blank as we want to recieve emails for all comments, you can change this to something like only receiving emails for certain blog post comments or by certain authors later.
  • Under actions select email
  • Fill in the information you want to send in the email.
  • You can use tokens from the comment itself such as [author] and [body]. These are listed in the conditions section of the form. New comment by [author] could be your subject for example.
  • We're done. Add a new comment and the email should send.

Filtering out unwanted HTML from comments

The textfilters module allows you to strip out HTML and tags you don't want to appear in a textarea if they are submitted by a user. Filters can be useful in the case of comments by allowing bold and italic tags for instance but nothing else.

  • Go to config > content authoring > text filters and make a new text filter. Call it safe comments and add the tags you want to support in the form. b,i,strong,em for example. Ignore the attributes section as you'll probably want to strip out all attributes from HTML tags.

  • Save the filter and go to structure -> entities -> comment and the manage fields section of the comment entity type.

  • Edit the comment body field and select the text filter you have just created.

  • All comments should now be filtered before they are viewed. Edit the filter and it will update automatically.

Exporting config to a server

Iris has been built with version control in mind so it's easy to export your configuration and import it onto a live server. To do so, head over to the config tab of the administration menu and export config. The config page will show you all the configuration files that differ between your live and staging configuration directories. If you hit export your configuration will be moved to the staging folder in your site's folder. That can then be moved to your live server and imported where necessary.

A few extra bits about templating

Putting in templates directly with the template embed

If you don't want to use regions or simply want to split your template files into smaller, reusable chunks you can embed any template using the template iris embed.

Such as:

{{{iris embed="template" template="footer__$current.eid"}}}

The same template lookup system works as with the normal file naming. Use __ to seperate words in a template that will be looked for. This embed will start looking for footer__1.html for example followed by footer.html if it doesn't find it.

Using the req global variable

The whole node.js req request object is passed through to the template. This gives you access to everything including the page's query string, req.query, the url itself req.url, the authPass of the user visiting the site (userid etc) req.authPass and more. Look at the node.js and Express documentation for more details.

More complex Handlebars helpers

Iris ships with the whole very powerful Assemble Handlebars helpers library. Visit https://github.com/assemble/handlebars-helpers for more information. You can also easily extend Iris Handlebars helpers and more with hooks such as hook_frontend_handlebars_extend.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment