Published:

Using two different techs can often be difficult, however for a front-end developer both Pug and CouchCMS can save countless hours of work, the only issue is they don't play well out of the box. Check out how to get around some these issues solved in part with Gulp.

What are Pug and CouchCMS?

If you haven't actually used either of these techs then a little introduction will be useful. Although neither was designed with the other in mind, I have found that there is no reason why you can't leverage the power of both. Here is a little background info on both of these and the challenges with using them together:

PUG

Pug is what used to be known as Jade, a popular HTML templating library that allows you to do many cool things such as breaking your site into handy modules and using variables and repeating blocks of code. It also forgoes the traditional HTML format of using tags to specify blocks of code, instead depending on tabbing within the document to determine hierarchy. Whilst this makes it super quick to markup code, it can also make using the tag and attribute system found in CouchCMS a bit of a headache.

COUCHCMS

CouchCMS is an open source CMS that can take care of a lot of your backend just with the inclusion of a few tags. Written in PHP, this type of CMS is sometimes known as a light CMS as it sits on top of your HTML and requires no knowledge of PHP to implement. Although the concepts are fairly easy to pick up it actually has the capacity to perform in some incredible ways due to the huge amount of backend behaviours it opens up with just one or two attributes or tags. Whole user login and management systems can be created, reusable templates make things like article creation a breeze and things like Paypal integration come baked in. 

An example of Pug markup language
An example of Pug markup language

Using them in combination

As a front-end developer, I personally love being able to leverage the backend features that CouchCMS offers, however straight off the bat there are a couple of issues when using these two together as CouchCMS requires PHP files whereas Pug outputs HTML pages. Attributes that CouchCMS uses can also be a pain as Pug attempts to decipher blocks of HTML included as an attribute. However, with the help of Gulp I have found that it is possible to use these two awesome techs together, let's run through some of these challenges and how they can be solved.

First steps - creating the flat site

Initially, I create the entire site in Pug so that it looks and feels as expected. This first run through can be considered as the GUI Prototype that is often passed on to back-end devs in full stack teams. Once I have this base and it's signed off then it's time to start integrating CouchCMS into the project, this process will remove all of the dummy content so make sure to save a separate copy at this point.

PHP vs HTML

CouchCMS needs all of its files in PHP format so that they can be read in and used to create the pages within the CMS, however, Pug outputs HTML instead. This can be taken care of care with a Gulp task that changes the filenames to .php instead of .html, this is how I implement that:

 

// PHP ext

// ============

// Changes HTML ext to PHP


gulp.task('phpext', () => {

    return gulp.src(paths.prod + '/*.html')

        .pipe(plugins.vinylPaths(plugins.del))

        .pipe(plugins.rename(function (path) {

            path.extname = ".php"

        }))

        .pipe(gulp.dest(paths.prod));

});

 

This basically switches out the file extension and is best performed at the end of the build process. CouchCMS also requires some opening and closing PHP tags on every page:

php require_once( 'couch/cms.php' ); ?>

These can be included within Pug and won't affect compilation or even rendering of the HTML pages before they are turned into PHP files.

Templates, groups and editable regions

Template tags are used to specify new pages within the CMS and groups are used to specify how different fields within the admin tool are grouped on each page. I usually add these at the top of each Pug module or page when and where they are required. They won't interfere with compilation or the rendering of the HTML page, here is an example:

 

//- CMS config

<cms:template title="News"></cms:template>

<cms:editable name="group_intro" label="Intro" desc="Intro to page" type="group"></cms:editable>

 

In terms of editable regions, these can be wrapped around blocks of content fairly easily as long as they are on the same level as the content they are wrapping. This is an example of that in action:

 

<cms:editable name='personal_social' label='Personal social' desc='The links to personal social accounts' group="group_personal_social" type='textarea'>

a(href="#" title="Link to Facebook profile")

    img.svg(src="img/icon-social-fb.svg" aria-hidden="true" role="presentation")

a(href="#" title="Link to Twitter profile")

    img.svg(src="img/icon-social-tw.svg" aria-hidden="true" role="presentation")

a(href="#" title="Link to LinkedIn profile")

    img.svg(src="img/icon-social-li.svg" aria-hidden="true" role="presentation")

 

Linking to other PHP pages and pretty URLS

So far we have changed the file extensions to match the PHP format used by Pug, however, we also need to be able to link to those pages. To take care of this I use another Gulp task which searches through my project and looks for marked strings to replace and then switch it over to .php ext with the full site URL in front. Using the Gulp Replace package can help take of the hard work, this is what the task looks like with a few example pages:

 

// Couch assets

// ============

// Couch CMS absolute path and text replacement


gulp.task('couchIncludes', function () {

    return gulp.src(paths.prod + '/*.php')

        .pipe(plugins.replace('index.html', 'index.php'))

        .pipe(plugins.replace('about.html', 'about.php'))

        .pipe(plugins.replace('news.html', 'news.php'))

        .pipe(gulp.dest(paths.prod));

});

 

Although it's a pain setting an entry for each page, templates which produce lots of cloned pages usually make up the bulk of a big site and the links for these are usually calculated by CouchCMS on a channel page. 

Pretty URLS

CouchCMS can automatically create SEO friendly addresses for each of your pages, to get this to work you generate some .htaccess rules to rewrite page directions. However, this doesn't help much when it comes to on page links to things like images and CSS files. To get around this we can use a similar technique as the above example, adding in absolute URLs for each of the assets needed. Adding to the above example, we now have a task that looks like this:

 

gulp.task('couchIncludes', function () {

    return gulp.src(paths.prod + '/*.php')

        .pipe(plugins.replace('href="style', 'href="style'))

        .pipe(plugins.replace('src="script', 'src="script'))

        .pipe(plugins.replace('href="img', 'href="img'))

        .pipe(plugins.replace('src="img', 'src="img'))

        .pipe(plugins.replace('a href="', 'a href="'))

        .pipe(plugins.replace('index.html', 'index.php'))

        .pipe(plugins.replace('about.html', 'about.php'))

        .pipe(plugins.replace('news.html', 'news.php'))

        .pipe(plugins.replace('article.html', 'article.php'))
        .pipe(gulp.dest(paths.prod));

});

 

Tidying up other bits of code

Generally, I find other issues cropping up as I am going through and adding in CouchCMS tags and attributes such as 'show' which can be used within image tags to write in paths and alt text. Regularly checking the compiled PHP pages can give an insight into how these are being handled by Pug and any HTML parsers you are using which can also incorrectly manage what is perceived to be mistakes in your code. When errors do crop up I use a second version of the text replacement task above which does a second cycle through and adjusts any mistakes. I also use a couple of markers to specify where text replacements should occur if it's not possible to manage these indirectly.

For example, a standalone "show" tag can lead to some strange issues as it is self-closing. To get around this I wrap the closing part of the tag in double quotes like so:

<cms:show k_success "/>"

This preserves the self-closing part of the tag which can then be targeted by text replacement. This is an example of what kinds of text replacement go into the Gulp task and although it's a bit of trial and error these can be reused from project to project:

 

gulp.task('couchExcludes', function () {

    return gulp.src(paths.prod + '/*.php')

        .pipe(plugins.replace('<a href="<cms:show k_site_link /><cms:show k_page_link />">', '<a href="<cms:show k_page_link />">'))

        .pipe(plugins.replace('=""',''))

        .pipe(plugins.replace('&lt;','<'))

        .pipe(plugins.replace('&gt;','>'))

        .pipe(plugins.replace('">"</cms:show>','/>'))

        .pipe(plugins.replace('">"</cms:date>','/>'))

        .pipe(plugins.replace('&quot;/>&quot;','/>'))

        .pipe(plugins.replace('image_2>">','image_2 />">'))

        .pipe(plugins.replace('image_3>">','image_3 />">'))

        .pipe(plugins.replace('">" </cms:show>','/>'))

        .pipe(plugins.replace('">" </cms:paginator>','/>'))

        .pipe(plugins.replace('">"','/>'))

        .pipe(plugins.replace('</cms:show>',''))

        .pipe(plugins.replace('</cms:search_form>',''))

        .pipe(plugins.replace('<cms:show k_site_link /><cms:show k_page_link />','<cms:show k_page_link />'))

        .pipe(plugins.replace('"/>"\'','/>\''))

        .pipe(plugins.replace('"/>"/img','/>/img'))

        .pipe(plugins.replace('<cms:show k_site_link /><cms:show k_site_link />','<cms:show k_site_link />'))

        .pipe(plugins.replace('<cms:show folder_link_custom>','<a href="<cms:add_querystring "<cms:link masterpage=\'news.php\' />" "cat=<cms:show k_folder_name/>" />"#k_search_form><cms:show k_folder_title /></a> <br>'))

        .pipe(gulp.dest(paths.prod));

});

 

CouchCMS will let you know if there is something it doesn't like along with a line number (often times it is still hard to trace the exact position) which can be used in reference with the compiled PHP to trace the offending bit of code and adjust it.

Final thoughts

Hopefully this will have given you a little bit of insight into how to overcome the worst aspects of integrating Pug and CouchCMS into your workflow. CouchCMS can also be hosted locally so you are able to check out what the templates look like without having to upload the files each time, this can be a big time saver after you have stripped the mocked up content from your prototypes.

Let me know your thoughts on the above and how the process can be improved down in the comments below.



Comments