Building a simple site with Pico

09/11/15

For the inaugural post on my new blog here, I thought I'd go through how I built the blog itself using the Pico CMS. Pico is "a stupidly simple and blazing fast, flat file CMS". Unlike the big three CMS solutions (Wordpress, Joomla, Drupal), Pico does not use a database. Instead, all content is stored in the file system in a specific structure and Pico will automatically create navigation links to that content. This makes it much simpler to set up and also makes it really fast. Editing content is as easy as editing text files and then pushing them to the server which can be all automated using Git and a task runner like Gulp or Grunt. It's dynamic enough that maintenance is easier than a static site, but much simpler and lighter weight than a full blown, database backed CMS like Wordpress. Of course there are multiple flat file CMS solutions out there, but I picked Pico because it's only dependency is that PHP be installed. Since PHP is pretty much ubiquitous, this makes it ideal for running on just about any platform including budget hosting services.

Creating the Site

First things first, I downloaded and installed Pico on my machine following the instructions on their site.

Next, I created a custom theme using the Pico default theme as a guide. Themes really only require a directory under the 'themes' directory and an index.html file to get started. I also created an 'assets' directory to hold all of my custom CSS, javascript, images, etc. The directory structure for my theme looks like this:

-themes
    -my_theme
        -assets
            -css
                style.css
        index.html

I used one of the free Bootstrap themes available on bootswatch.com as a base. My theme index.html file looks like this:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>{% if meta.title %}{{ meta.title }} | {% endif %}{{ site_title }}</title>
{% if meta.description %}
    <meta name="description" content="{{ meta.description }}"> 
{% endif %}{% if meta.robots %}
    <meta name="robots" content="{{ meta.robots }}">
{% endif %}

    <link href="{{ theme_url }}/assets/css/style.css" rel="stylesheet">
    <link href="https://maxcdn.bootstrapcdn.com/bootswatch/3.3.5/journal/bootstrap.min.css" rel="stylesheet">
    <link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css" rel="stylesheet">

    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
      <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->

</head>
    <body>
            <nav class="navbar navbar-default navbar-fixed-top">
              <div class="container">
                <div class="navbar-header">
                  <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                  </button>
                  <a class="navbar-brand" href="#">tommooney.net</a>
                </div>
                <div id="navbar" class="collapse navbar-collapse">
                  <ul class="nav navbar-nav">
                        {% for page in pages %}
                            {% if not page.date %}
                                <li><a href="{{ page.url }}">{{ page.title }}</a></li>
                            {% endif %}
                        {% endfor %}
                  </ul>
                  <ul class="nav navbar-nav pull-right">
                        <li><a href="https://github.com/steelheaddigital"><i class="fa fa-github fa-lg"></i></a></li>
                        <li><a href="https://www.linkedin.com/pub/tom-mooney/28/931/49"><i class="fa fa-linkedin fa-lg"></i></a></li>
                        <li><a type="application/atom+xml" href="feed"><i class="fa fa-feed fa-lg"></i></a></li>
                  </ul>
                </div><!--/.nav-collapse -->
              </div>
            </nav>

            <div class="container">
                <div>
                    {% if is_front_page %}
                        <div id="posts">
                        {% for page in pages %}
                            {% if page.date %}
                            <div class="post">
                                <h3><a href="{{ page.url }}">{{ page.title }}</a></h3>
                                <p class="meta">{{ page.date_formatted }}</p>
                                <p class="excerpt">{{ page.excerpt }}</p>
                            </div>
                            {% endif %}
                        {% endfor %}
                        </div>
                    {% else %}
                        <div class="post">
                            {% if meta.date %}
                                {% if meta.title %}<h2>{{ meta.title }}</h2>{% endif %}
                                    <p class="meta">{{ meta.date_formatted }}</p>
                                {% endif %}
                            {{ content }}
                        </div>
                    {% endif %}
                </div>
            </div><!-- /.container -->
        <footer id="footer" class="navbar-fixed-bottom">
            <div class="container">
                <p class="text-center">© 2015 Tom Mooney</p>
            </div>
        </footer>

        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
    </body>
</html>

Then, I just set the theme in the config.php file to my_theme and I was up and running with my customized theme.

$config['theme'] = 'my_theme'; 

I also changed the content directory in the config file so it is named just 'content' instead of 'content-sample'

$config['content_dir'] = 'content/';

Finally, I created a directory under 'content' called 'posts', which is where all of my blog posts will go.

Deployment

Finally, I partially automated deployment of new blog posts with Git and a simple Gulp task.

First, I just initialized a git repo and committed it.

git init
git add -A
git commit -m "initial commit"

I'm also using the git-ftp plugin since my cheap shared host doesn't have git installed and I had no luck getting it installed on the server. For the initial push, I ran:

git ftp init --user ftp_user_name --passwd ftp_password ftp://my.domain.com/public_html'

For this first push, I also had to manually FTP over the config.php file and 'vendor' directory as those are in the .gitignore file included with Pico, for good reason as you might have different config settings on the server than on your local machine.

Finally, I installed Gulp in the project and added a really basic Gulp task to do future deployments.

npm install gulp
npm install gulp-run

And my gulpfile.js added to the root of the project looks like this.

var gulp = require('gulp');
var run = require('gulp-run');

gulp.task('deploy', function() {
  run('git ftp push --user ftp_user_name --passwd ftp_password ftp://my.domain.com/public_html'').exec()
    .pipe(gulp.dest('output'));
});

Now whenever I add new content I can simply do a git commit and push the changes via the Gulp task.

git add -A
git commit -m "new post"
gulp deploy

That's it! So far, I'm very impressed with Pico. It is so much simpler for a basic site like this than something like Wordpress.