ghost

A 8-post collection

Sidebar Mechanics of a Custom Bootstrap Site

Written by Michael Earls
 bootstrap  ghost  jquery  html

Ghost development makes it easy to define a custom sidebar by using partials. However, the partial runs in the context of the post.hbs or page.hbs files. This limits the functionality that you can provide.

The sidebar partial hierarchy

For my custom Bootstrap theme, I created two separate partial files for the sidebar; one for the author page, and one for the page, post, and tag templates. I did this so that I could display information about myself on those pages. This, of course, assumes that you are running a single author blog. It would be a bit more complicated for multiple authors (if you even wanted to do such a thing at all).

Here is the code for the post page page which shows the location of the sidebar as a bootstrap <div /> with the id of "sidebar-container" and a css class of col-md-4.

<div class="row">
    <div id="sidebar-container" class="col-md-4">
        {{> sidebar author}}
    </div> <!-- .col-md-4 -->
    <div class="col-md-8">
        <!-- content... -->
    </div> <!-- .col-md-8 -->
</div>

Notice that I sent in the author context so that the sidebar would have access to the author data.

Here is the author.hbs partial that is used in the above code:

<!-- sidebar -->
{{!include the various components}}
{{> "search-form"}}
<div id="sidebar-component-container"></div>
{{> "bio-panel"}}
{{> "sidebar-theme-picker"}}
<!-- /sidebar -->

This sidebar adds a sidebar component container (which I'll discuss below) as well as calls to bio-panel and sidebar-theme-picker partials.

Here's the bio-panel.hbs which takes advantage of the fact that we passed in the author context from the page:

<a name="author" class="sr-only"></a>
<div class="panel panel-primary">
    <div class="panel-heading">
        About {{name}}
    </div>
    <div class="panel-body">
        {{#if image}}
        <img class="img-thumbnail center-block" src="{{image}}" alt="{{name}}'s Picture" />
        {{/if}}
        <h1 class="author-title center-block">{{name}}</h1>
        {{#if bio}}
            <div class="bio-container">
                <h2 class="author-bio text-justify">{{bio}}</h2>
            </div>
        {{/if}}
        <div class="author-meta center-block sidebar-meta">
            {{#if location}}<p><i class="fa fa-map-marker"></i> <small>{{location}} <a href="https://www.google.com/maps/place/{{ encode location }}" aria-label="{{location}} map link" target="locationMap">(map)</a></small></p>{{/if}}
            {{#if website}}<p><i class="fa fa-link"></i> <small><a href="{{website}}">{{website}}</a></small></p>{{/if}}
        </div>
    </div>
</div>

The <a /> with the sr-only class is simply a bookmark for screen readers. It is not displayed to the user.

All of the data that is displayed is coming from the author context that we passed into the original partial.

This works well when we only need to use the information from a single context like page or post where we have author information, but what happens when we want to display the author information from a context that has multiple possible authors? In this case, we pass in the author context like so (this is from the tag.hbs page):

<div id="sidebar-container" class="col-md-4">
    {{#posts.[0].author}}
    {{> "sidebar"}}
    {{/posts.[0].author}}
</div> <!-- .col-md-4 -->

In this case, I am simply pulling the author context off of the first post. Obviously, if the author you want to display in the sidebar is not the author of the first post, you'll need to pick a different post.

Note: there are (as of this writing) experimental API calls you can make to retrieve some of this information, but it outputs some code in the html header that I am not comfortable with. I just get a weird feeling doing it that way.

Sleight-of-hand tricks for sidebar content out of context

If you are reading this post and the Bootstrap theme is enabled, you should see a panel with Social Media share buttons in it. As you can see from the code above, those links are not included in any of the sidebar code.

For the post social media links to work, I have included a hidden <div /> in the post.hbs template that contains another <div /> decorated with the css class of sidebar-component.

<div class="panel panel-primary share sidebar-component">
    <div class="panel-heading">
        <p class="panel-title">Share this post</p>
    </div> <!-- .panel-heading (share) -->
    <div class="panel-body">
        <a class="text-primary share-link" href="https://twitter.com/intent/tweet?text={{encode title}}&amp;url={{url absolute="true"}}"
           onclick="window.open(this.href, 'twitter-share', 'width=550,height=235');return false;"
           title="Share on Twitter" aria-label="Share on Twitter"><i class="fa fa-3x fa-twitter-square"></i><span class="sr-only">Twitter</span></a>
        <a class="text-primary share-link" href="https://www.facebook.com/sharer/sharer.php?u={{url absolute="true"}}"
           onclick="window.open(this.href, 'facebook-share','width=580,height=296');return false;"
           title="Share on Facebook" aria-label="Share on Facebook"><i class="fa fa-3x fa-facebook-square"></i><span class="sr-only">Facebook</span></a>
        <a class="text-primary share-link" href="https://plus.google.com/share?url={{url absolute="true"}}"
           onclick="window.open(this.href, 'google-plus-share', 'width=490,height=530');return false;"
           title="Share on Google+" aria-label="Share on Google+"><i class="fa fa-3x fa-google-plus-square"></i><span class="sr-only">Google+</span></a>
    </div> <!-- .panel-body (share) -->
</div> <!-- .panel (share) -->

Then, there is code in the page initialization that moves any of those divs to the <div /> in the sidebar with the id of sidebar-component-container.

Here is that code:

function moveSidebarItems() {
    // move items to the sidebar
    $('.sidebar-component').each(function () {
        $(this).detach().appendTo($('#sidebar-component-container'));
    });
}

$(moveSidebarItems);

This is pretty self-explanatory. It uses jQuery to move the intended sidebar content to the sidebar after the page has loaded. That means that the server has already processed it and used the context that the div is defined under. In simple terms, it means that you can hide a div in any part of a page or in any partial under any context and display data about that context, then simply make sure that the div has the class sidebar-component attached to it and it will get moved into place automatically. It should be noted that there is no defined order as that is out of the scope of this discussion. It could be done with custom html attributes (data-sidebar-order or similar attribute and ordered during the move at page ready).

Standardizing Ghost Client Development

Written by Michael Earls
 blog  programming  ghost  GTCA

While Ghost is a great blogging platform, developers of custom themes may find themselves reinventing some common blog functionality (Google analytics, blog comments, etc.) Each developer has most likely come up with their own way to implement each of these features in their blog. But what happens when the blog owner changes themes? Right now, they'd have to redo all of their code injection to match the requirements of the new theme. If there was a standard way to define these properties, then switching themes would be easy and would not require any changes on the user's part.

Jack Preston has developed a great standard for setting common configuration values by using his GTCA object. It meets the needs of a good standard for configuration variables.

It is a great idea and I'd like to expand on it a little.

Functional Standardization

While configuration settings are the current purpose behind GTCA, I would like to put forth an additional use case: functional standardization.

There are things that I would like to add to all of my themes that require the user of the theme to call a function on the window object. It would be nice if I could roll those calls into a functional GTCA standard.

However, this will require a bit more effort with regard to GTCA development and deployment. In short, it would require that GTCA go from being a simple "include this code somewhere in your page before {{ghost_head}}" to being a full-fledged CDN-hosted .js file that theme designers could include (or download).

Example

Here is what I would add first if GTCA were to add functions in addition to variables. This code adds the ability to bind icons to any element by using a specific CSS class and providing a Font Awesome icon name as well as an optional size.

Then, all the GTCA-compliant theme author would have to do is add a link to the gtca.min.js file in their default.hbs file.

Then, they would instruct their users to put the following code in their settings on Ghost:

window.__themeCfg.iconMap.defaultIconSize = 'fa-lg';  
window.__themeCfg.addLinkIcon('nav-home', 'fa-home');

Of course, this opens the doors for the possible nightmare of dependencies (my code makes use of jQuery). I'm sure I could rewrite my code to be plain old JavaScript, so that would limit some of the liability.

So, in this case, I would include a link to the gtca.min.js file as well as the gtca-icon-map.min.js file in my default.hbs file.

My idea is not fully fleshed out, but I'd like to put it out there for discussion. I think GTCA is a great idea and a good start to add client functionality to Ghost.

I am afraid that this line of thinking could lead to over engineering and we'd end up with a big giant mess, but I do feel that some basic functionality in addition to configuration data would be acceptable.

Thoughts?

Am I abusing Ghost?

Written by Michael Earls
 blog  programming  ghost

I have been working steadily on creating my new Ghost blog to be exactly what I want. However, I have found myself dipping into JavaScript to sew up the loose ends that I perceive with Ghost's built-in functionality.

Too much WordPress?

Am I trying to create a blog that has more WordPress in it? Have I spent so much time using a CMS that provides things like plugins, widgets, and server-side code that I'm defiling the elegance of the creators' original intent?

Perhaps blogs aren't meant to have dynamic icons, user-changeable themes or context-aware sidebars.

Client-side scripting OK?

Or, maybe they are. Maybe I've been a server-side developer for so long that I'm missing the point of client-side scripting - to do the things that servers just aren't good at. Perhaps I need to utilize client script to do the "heavy lifting" of modifying the page without having to reload the page every time the user clicks on something.

I really don't know the answer. I'm a little bit uncomfortable as I add more and more code to my theme. It's also a bit strange to see my page "sputter" as it loads the scripts and applies the styles.

Conclusion

I am pleased with the final result and my home page loads in under 1.5 seconds. I guess that's the ultimate goal I've had - better performance. Many people in the Ghost community refer back to the focus of Ghost - content, but as a developer, I am concerned with performance and SEO for the users of my theme. I still believe that "content is king", but not for me. I have a dual-purpose blog. Sure, I have content (you're reading it), but I am writing about the very software that I use to write with.

Integrating Bootstrap Pagination with Ghost

Written by Michael Earls
 blog  bootstrap  programming  ghost  development  casper  pagination

In my previous post, I discussed how to integrate Bootstrap into the default Ghost theme. This post will cover the way that I modified pagination to use Bootstrap's pagination control.

To start, you will need to create a new pagination.hbs file in the partials folder in the root of your theme.

Because of the way that Bootstrap pagination works, I had to implement pagination using client-side JavaScript. The implementation uses a combination of server-side handlebars markup and client-side script.

The variables that we'll use in the pagination are defined in the site-init.js file in /assets/js.

var prev;  
var pages;  
var page;  
var next;  
var pageUrl;  
var pageUrlPrev;  
var pageUrlNext;  
var numbersSurroundingEllipses = 3;  
var useSimplePagination = false;

Here is the pagination.hbs for the customized pagination that I am using on my site:

<script type="text/javascript">  
    // set the values that we'll use in the bootstrap-pagination.js file
    {{!if there is no value for the variable, display a 0}}
    prev = {{#if prev}}{{prev}}{{else}}0{{/if}};
    pages = {{#if pages}}{{pages}}{{else}}0{{/if}};
    page = {{#if page}}{{page}}{{else}}0{{/if}};
    next = {{#if next}}{{next}}{{else}}0{{/if}};
    pageUrl = '{{page_url}}';
    pageUrlPrev = '{{pageUrl prev}}';
    pageUrlNext = '{{pageUrl next}}';
    pageUrlFirst = '{{pageUrl 1}}';
    pageUrlLast = '{{pageUrl pages}}';
</script>
<nav>
  <ul class="pagination bootstrap-pagination">
  </ul>
</nav>

This particular bit of code is a little confusing at first glance because it combines client- and server-side script. Let's break it down.

Since this code will appear each time the pagination control is displayed, we don't want to define the variables here. That's why they were defined in the init script file. So, each time the code runs on the page, it will set the value of the variables needed to create the pagination control.

Notice the if statements. Since these values may be empty, we can't just use them "raw" to define the values of our JavaScript variables. This will lead to a JavaScript error because essentially, an empty value would render the following code:

prev = ;

This is obviously incorrect. The {{#if prev}}{{prev}}{{else}}0{{/if}}; code will check to see if we have the prev variable defined. If so, we'll simply print it out. If not, we'll provide the number 0. It then repeats this process for the remaining initialization variables.

After the initialization code, we just create a nav control with the ul defined. It is empty when it is defined. In order to fill it with page navigation controls, we will use jQuery code when the document is ready.

The main default.hbs file includes a link to the pagination script. Here is the code that runs when the page starts up:

$(document).ready(function() {  
    // see if there are any previous pages
    // if so, append them to the pagination ul
    if (prev > 0) {
        $('ul.bootstrap-pagination').append('<li class="prev"><a href="' + pageUrlFirst + '" title="Go to first page" aria-label="First"><span aria-hidden="true"><i class="fa fa-angle-double-left"></i></i></span></a></li>');
        $('ul.bootstrap-pagination').append('<li class="prev"><a href="' + pageUrlPrev + '" title="Go to previous page" aria-label="Previous"><span aria-hidden="true"><i class="fa fa-angle-left"></i></span></a></li>');
    }

    if (useSimplePagination) {
        doSimplePagination();
    }
    else {
        doComplexPagination();
    }

    // if we have pages after this one, display the 'next' buttons
    if (next > 0) {
        $('.bootstrap-pagination').append('<li class="nxt"><a href="' + pageUrlNext + '" title="Go to next page" aria-label="Next"><span aria-hidden="true"><i class="fa fa-angle-right"></i></span></a></li>');
        $('ul.bootstrap-pagination').append('<li class="nxt"><a href="' + pageUrlLast + '" title="Go to last page" aria-label="Last"><span aria-hidden="true"><i class="fa fa-angle-double-right"></i></i></span></a></li>');
    }
});

As you can see, this code will add the navigate to first and navigate to previous buttons if we are not on the first page. It does this by using jQuery selectors to find any ul's on the page that use the .bootstrap-pagination class and appends the li controls that have the proper links.

Then, it checks to see if it should perform simple or complex pagination. Simple pagination merely shows "Page X of Y". Complex pagination shows the page numbers with an ellipses in the middle.

Simple pagination is performed by the following function:

function doSimplePagination() {  
    $('.bootstrap-pagination').append('<li><a href="' + pageUrl + 'page/"' + page + '" aria-label="Page ' + page + ' of ' + pages + '">Page ' + page + ' of ' + pages + '</a></li>');
}

This shows the user what page they're on.

Complex pagination shows a set number of page numbers on either side of an ellipses. The number of page numbers to show is determined by the variable numbersSurroundingEllipses. This value is set in the init script to a default value of 3.

It is possible to override this number on the Settings->Code Injection screen on your site's dashboard. If the value is set to -1, then the pagination control will display all page numbers.

The code to determine which pages to display on the control is a bit complicated. In short, it will show the number of pages as defined by the numbersSurroundingEllipses variables and the page directly before and after the current page so that you can navigate between pages. When you get within a certain number of pages from the end (calculated based on the pages - (numbersSurroundingEllipses * 2) calculation), the ellipses is not shown and a link to the page in the "middle" is displayed.

Refer to the doComplexPagination() function in the bootstrap-pagination.js file.

The very last thing we do when creating the pagination control is set the current page as active:

$('li.page' + page).addClass('active');

This provides you with Bootstrap pagination on your Ghost blog.

Here is a screenshot of the pagination control:

Simple Pagination:
Simple Pagination

Complex Pagination:
Complex Pagination

Note: This implementation requires a link to FontAwesome fonts (used in the left and right buttons).

To make this work, make sure you remove (or alter) any CSS in your theme's screen.css file that might define properties on the nav element or the pagination class. I had a terrible time getting this working at first because the default theme heavily customizes these elements and has class names that clash with Bootstrap.

Integrating Bootstrap with the Casper Ghost Theme

Written by Michael Earls
 blog  bootstrap  css  programming  ghost  theme  casper

I have been working on my blog lately to customize the theme and provide some features that I wanted. I started with the default Ghost theme (Casper) and modified it to suit my needs.

I have shared my theme (I named it "Ghost Cerkit Theme") on GitHub in a new repository:

Ghost Cerkit Theme

I added a few features, like Google custom Search and Disqus comments. I also added a sidebar that shows Author information for the author of the first post on the site. This is for blogs that have a single author that want to show their information on all pages (except the Author page, where it is suppressed).

To use the theme, there are a few customization options that you'll have to change. The hardest one is probably the Google custom search. Instructions for how to create a custom search engine can be found on the Ghost website. In order for your custom search to work, you will need to put your search engine id in the hidden cx form field on the search form.

I had to make significant changes to the screen.css file in order for Bootstrap to work properly. There are a lot of settings in the default theme that interfere with Bootstrap. I believe the best way to see what changed would be to view the diffs for the css file (and some of the core .hbs templates).

In addition to the custom search form, I also added the ability for the user to change the current site theme. Since the custom theme utilizes Bootswatch themes, I was able to add a selection box containing all of the Bootswatch themes. When the user picks a new theme, the site changes to that theme and stores its name in a cookie on the user's browser. Whenever the user visits a page on the site in the future, the chosen theme is used instead of the default theme. There is also a reset button to return the site to the original theme as defined by the site owner. It is possible to hide the theme selector. Just add the following code to the header:

showThemeSelector = false;

I also replaced the Casper pagination control with a Bootstrap pagination control that shows more detail. However, in order for this to work, I had to utilize JavaScript. This might affect SEO a bit, but with the theme retaining the meta tags for navigation, I don't think there will be a major impact.

I will be writing up additional blog entries about how I implemented the Bootstrap pagination for Ghost as well as an entry about the theme selector.

The theme still needs some polish to remove all of the CSS that interferes with Bootstrap. I worked as hard as I could to remove any customizations that are specific to my personal site, but there are still some things that need to be done in order for it to work completely.

I retained the MIT license on the original Casper theme so that there are no constraints on usage. I thought about licensing it under GPL 2, but adding a more restrictive sublicense seemed inappropriate in this case. If you use the code and feel inclined to give me attribution, that would be great. Just link to my website (https://cerkit.com).

Adding Syntax Highlighting (Code Formatting) to Ghost

Written by Michael Earls
 Code  ghost  syntax highlighting  prettify

Update: I have switched to using Prism for my syntax highlighting. I wrote a blog post about how to switch.

I have found a way to format my source code with Ghost. By default, you use four spaces to indicate to Ghost that you are writing code.

To get highlighting to work, I had to use Google's Code Prettify. Unfortunately, it requires that you use a <pre/> tag to contain your code. I say unfortunately because I was hoping to keep a "pure" markdown blog. I guess it's worth it for me to have code formatting. Since code prettify uses JavaScript, this will not work in most RSS readers, so having the <pre/> tag is the way to go.

To use it in Ghost, simply access your settings and click on <> Code Injection from the menu on the left. Then, in the footer, paste the following code:

<script src="//cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js?skin=sons-of-obsidian" type="text/javascript">

Once you've added that script to your footer, you simply need to use the following code to insert source code:

<pre class="prettyprint linenums"><code>function someCode(){  
    // this is code
}
</pre></code>

A note about adding <script/> tags using this process: you will have to HTML encode the first < on the line, or Ghost will think you are trying to embed a script.

So, in the example above, this is what the first line looks like:

<pre class="prettyprint linenums lang-htm"><code>&lt;script src=...</script></code></pre>

It's a little kludgy, but it works. I'm guessing this is inherent in the behavior of the web browser, as this is the same type of issue I was running into using the browser-based WYSIWYG editor on WordPress.

Once again, I had to HTML Encode the < on the <pre/> tags so the system wouldn't get confused.

Update: I just learned that it is necessary to completely encode any HTML you use when putting it withinin a <pre/> tag. I have also learned that it is necessary to put the code in a <code/> tag within the <pre/> tag so that the Ghost engine doesn't incorrectly interpret == (that's reserved markdown for highlighting).

Here is what the above code looks like when "prettyfied":

function someCode(){  
    // this is code
}

Also, when using the ` character to show <pre/> tag, you will need to make sure that you use an empty tag by displaying it as I have (with the / character at the end). This will ensure that it does not get confused and actually render a <pre/> tag. I have had mixed results using the ` character for inline code when showing HTML, so your results may vary.

And, to display the ` character in your posts, you will need to use

&#96;

post image credit: Stanly Ng

New Blog

Written by Michael Earls
 blog  wordpress  ghost

Goodbye, WordPress!

After much internal mental debate, I have decided to restart my blog from scratch. The blog has been powered by WordPress for a very long time (since I converted it from DasBlog somewhere around 2004).

After a recommendation from Shawn Wildermuth, I decided to go with Ghost. It was very easy to get setup (I had it up and running in about 10 minutes, including all Internet DNS changes and nameserver reassignments).

Also, it works perfectly with SSL, so I am able to provide SSL to my readers, which is a goal I had for the new year.

Why Change?

WordPress was starting to irritate me. It injected an enormous amount of unnecessary HTML and it rendered very large documents for simple pages. The biggest issue I had with it is that it encoded all of my source code before storing it in the database, causing it to render incorrectly and cause me grief.

With Ghost, all I have to do is wrap it in code markdown: my code here (using ` character on either side) for inline code and four spaces for multi-line code, like so:

function doIt(message){  
    var abc = message + ', World';
    alert(abc);
}

I haven't found a code formatting "plugin", but the fact that I no longer have to fight with WordPress to insert code makes me happy.

UPDATE: I found a code formatter. I am using Google Code Prettify.

The HTML rendered by Ghost is clean and small, just like I wanted it. I got some errors from Google analytics, so I haven't put in my analytics tracker, yet. As soon as I do, that will probably be the ugliest element in my output.

I am going to make an attempt to create a blog that is low-impact and allows readers on mobile devices to read my blog without eating up too much of their data.

WordPress is heading in the right direction with their recent updates, but there's really no way they'll ever extract themselves from their worst attribute: everything is written in PHP.

What now?

For the future of my blog, I am going to look at the traffic for each of the posts in my old blog. I will migrate the most popular posts to this new blog so that they will remain on the Internet. Hopefully, I can find a way to make the permalinks work.

Update: Ghost has a great guide on how to import a WordPress blog into Ghost. I have used this guide to successfully move my blog over. It even retained my permalinks. The only thing it can't do is retain the links to the years and months (ex. https://cerkit.com/2009/12/ is now broken.

If not, I guess I can count on Google to re-index my site, especially now that it will not be as "wordy" on the HTML side.

Also, I believe that I read somewhere that they give higher rankings to sites that support SSL (https).

Thanks for bearing with me. I also need to look into what to do about my RSS subscribers. This may be the end of an era as far as that goes.

EDIT Jan 19, 2016: Ghost provides an RSS feed at https://cerkit.com/feed