Yeoman Reveal.js Generator: Quick Reveal Presentations

A while back I gave a Reveal.js presentation on how to create a Reveal.js presentation using the scaffolding tool Yeoman. It’s been pretty helpful for building quick presentations and distributing them through my github account for anyone to see and I keep going back to that presentation when I start my next one, so I thought it should be in a blog post.

If you want to view the actual presentation, give it a whirl.

Let’s start with the basics. Node.js comes with npm so let’s make sure we have the most up-to-date node installed on our system.

node

sudo npm cache clean -f
sudo npm install -g n
sudo n stable
npm -v

Update npm

sudo npm install npm -g

yeoman

Update yeoman

npm install -g yo

Update the Reveal.js Generator

npm install -g generator-reveal

yo yeveal | Build a new presentation

Navigate to a new directory

yo reveal

Follow the instructions. Be sure to include your github account name in order to deploy your presentation to a github page.

yo reveal:slide "Slide title" --markdown

Boom, this builds you a new slide in your slides directory.

No just run grunt serve to see your presentation (with live reload) in your browser:

grunt serve
  • Grunt is a task runner
  • You can build your own tasks for things like compiling CSS.
  • Or you can rely on others’ grunt files. For Reveal’s generator, this already includes:
    • Node server
    • Autoreload
    • Lint
    • Deploy (to github in this case)

Super handy.

Bonus: Scaling Images on your slides

I ran into this issue a few times when I would insert an image and it just wouldn’t scale to the slide right. I think this is very important if you’re giving a talk. To fix, just use some quick CSS magic to help you.

<div class="big-image-container">
    <img class="big-image" src="myimage.jpg"/>
</div>

For the container class:

.reveal div .big-image-container {
    max-height:80%;
    max-width:70%;
    margin:0 auto;
}

And the image class:

.reveal img.big-image {
    height:auto;
    margin:0 auto;
}

Twitter REST API with geocode lookup

Recently I was given the task of building a search feature that will find all geolocations of Tweets with a keyword and/or hashtag.

The Twitter REST API has the following limitations:

  • User must enable Location for their Tweet — This removes a lot of the sample size
  • A Tweet REST API query can only target specific geo coordinates, along with a radius
  • The Search API’s recent index only spans for the last 6-9 days

Using my Drupal 8 Twitter module as a template, I was easily able to make this work for finding locations within the intercontinental United States.

$params = array(
 "q" => $this->keyword,
 "count" => $this->num_results,
 "result_type" => "mixed",
 "lang" => "en",
 "geocode" => "39.8,-95.583068847656,2500km",
 );
 $tweets = $twitter->get("search/tweets", $params);

Basically, I’m polling for all tweets with a radius of our country starting in roughly the middle. Thanks to ThoughtFacet for the tip.

So this is a nice workaround if you are looking for Tweets that have geolocations.

Managing dependencies with Composer Manager

Prerequisites:

  • Drupal 8
  • Composer

Use Case:

  • You want to add dependencies to your custom module using composer update at the root of your site directory.
  • Updating Drupal core removes any custom dependencies added to the root composer.json, potentially breaking any modules that require them.

Quick run-down:

First, download composer manager to your site’s modules directory and run the init.php command to register the ‘drupal-update’ command. This command is what will look through your module’s composer.json files and update the main composer.json file at your site’s docroot.

cd mysiteroot
drush dl composer_manager
php modules/composer_manager/scripts/init.php
composer drupal-update
In your sites directory, add a composer.json file and make sure you have both the name and requirements:
{
  "name": "drupal/mymodule",
  "require": {
    "mailchimp/mailchimp": "2.0.6"
  }
}

Now, run composer drupal-update again and you should see your dependencies in the vendor directory.

See also:
https://bojanz.wordpress.com/2015/09/18/d8-composer-definitive-intro/
https://www.drupal.org/node/2405811

Htaccess: Backreferences and multiple conditions and rules

Two rules I want to highlight today:

  1. Only the variables from the last-matched RewriteCond are available as back-references. 1)https://www.webmasterworld.com/forum92/5888.htm
  2. Conditions only apply to the very next rule. You’ll need to repeat the condition in order for it to apply to another rule. 2)http://serverfault.com/questions/264076/rewrite-conditions-backreferences-passed-to-all-rules

This came in handy when I was testing out the following rules for a Drupal htaccess. Basically, I wanted all the admin pages to be routed to the .com English version of the site. If the editor tried logging into example.co.uk/uk/user, they would route to example.com/user. In the first example, I don’t have access to the HTTP_HOST backreference. It doesn’t matter in this case because i’m defining it in the rule. In the second and third rules, I’m repeating the condition so I can access the same back reference for two different rules.


    #keep admin pages on .com instance (uk only for now) (PROD)
    RewriteCond %{HTTP_HOST} ^(.*\.)?example.com$ [NC]
    RewriteCond %{REQUEST_URI} ^/(uk)/(user|admin)/?(.*)? [NC]
    RewriteRule ^(.*)$ https://www.example.com/%2/%3 [R=301,L,NC]

    #keep admin pages on .com instance (uk only for now) (DEV / STAGE)
    RewriteCond %{HTTP_HOST} ^(dev|stg).example.(co.uk|de|fr|nl)$ [NC]
    RewriteRule ^uk/user/?(.*)? https://%1example.com/user/$1 [R=301,L,NC]
    RewriteCond %{HTTP_HOST} ^(dev|stg).example.(co.uk|de|fr|nl)$ [NC]
    RewriteRule ^uk/admin/?(.*)? https://%1example.com/admin/$1 [R=301,L,NC]

References   [ + ]

A quick glance at Backbone.js: Todo MVC

I had been on and off with Backbone for some time but had finally come around to getting somewhat of an understanding of the basics of how Backbone is organized and how it can help with your JavaScript-robust applications. I’ve read numerous tutorials and hadn’t quite gotten the picture. They were either outdated, too bloated, or not enough. I’ll attempt at helping to clarify the basics in this tutorial and perhaps write more advanced topics later.

Some immediate things-to-consider from my approach sofar:

  • I’m not using require.js, which I know a lot of front-enders depend on
  • I’m relying on Yeoman’s Backbone Generator which saves an enormous amount of leg work for getting the structure of your application ready. If you haven’t tried Yeoman yet, give it a go. It’s super great for getting projects off the ground.
  • Basically, what got me to this point was a combination of reading Addy Osmani’s Backbone tutorials and this tutorial which uses Yeoman to speed up the process.

Let’s dive in. First, we’ll look at our main.js file.

The init method immediately calls upon a view from our Todos colllection. In views/todos-view.js, our view defines most of the logic for interacting with our app, but we will frequently call on various methods defined in the Collection that it renders.

(function(){
    
    'use strict';
    
    window.backboneApp = {
        Models: {},
        Collections: {},
        Views: {},
        Routers: {},
        init: function () {
            console.log('Hello from Backbone!');
            new this.Views.TodosView({
                collection: new this.Collections.TodosCollection()
            });
        }
    };

    $(document).ready(function () {
        backboneApp.init();
    });
})();

For example, after completing the bare necessities of a Todo app, I wanted to add a Clean button that will clear all checked off items on the Todo Collection.

We’ll start by modifying our todos.ejs template and adding a ‘Clean’ button.


<form class="input-append">
<input id="new-todo" type="text" placeholder="What do you need to do today?" /> 
<input class="btn" type="submit" value="Submit" />
</form>

<form class="filter"><input id="clear-completed" class="btn" type="button" value="Clean" /></form>
<ul><!-- Where our To Do items will go --></ul>

We’ll now jump to our parent view, todos-view.js and add an event listener for our Clean button:


events: {
    'click #clear-completed': 'clearCompleted'
},

// Clear all completed todo items, destroying their models
clearCompleted: function() {
    _.invoke(this.collection.completed(), 'destroy');
    return false;
}

The clearCompleted method uses Underscore’s invoke method to get a list of the completed items and then use Backbone’s native destroy method to not only remove them from the view, but delete their models as well. The list of completed items is obtained from the collection’s completed method:


backboneApp.Collections.TodosCollection = Backbone.Collection.extend({
    
    completed: function() {
      return this.filter(function(todo){
        return todo.get('completed');
      });
    }

});

We’re using Backbone’s other native method, get, to obtain a list of models that have the completed attribute. Opening our todo-model.js file, we see that our model defaults to ‘false’. The ‘toggle’ method toggles this attribute. Backbone keeps track of these ‘states’ for each model.


(function(){
    
    'use strict';

    backboneApp.Models.TodoModel = Backbone.Model.extend({

        defaults: {
            title: '',
            completed: false
        },
        
        toggle: function() {
            this.save({
                completed: !this.get('completed')
            });
        }
    });

})();

A quick staging environment setup with LAMP stack, virtual hosts, and installation tips on Ubuntu 14

I’ve joined the bandwagon on Digital Ocean’s cheap SSD VPS hosting options ($5/mo say what!) and have been very delighted to jump into a fresh Ubuntu shell with no issues. If you’re like me and haven’t been installing servers for years then you might find some of these tips helpful. I may add more tips later on so let me know if I can help with some general tips (not too fancy!?) for the future.

For starters, let’s start with everyone’s favorite: users and permissions. I always find it somewhat annoying when I add a user or settle on root and I have to continually type ‘sudo’ for simple things like writing to a file or adding a directory.

adduser myuser

This will create a /home/myuser directory. Now to grant all the sudo privileges to that user, it’s as easy as

usermod -aG sudo myuser

The -a makes sure that the sudo group is added and does not replace other groups assigned to the user.

Password Protect A Public Directory

Recently, I needed to create a staging site for a client but I wanted to keep the directory password protected and prevent it from being crawled by search engines.

You can password protect a public directory using the htpasswd command and .htaccess file.

You need to make sure a few things are in place.

First, make sure that your .htaccess is read by Apache by navigating to your site’s httpd.conf file (now referred to as site.conf in recent versions of Linux).

Navigate to /etc/apache2/sites-available.

cp 000-default.conf seojeek.conf // copy default settings
vi seojeek.conf
ServerName seojeek.com
ServerAlias www.seojeek.com
 
ServerAdmin me@seojeek.com
DocumentRoot /var/www/seojeek

<Directory "/var/www/seojeek">
Options Indexes FollowSymLinks MultiViews
AllowOverride ALL
Order allow,deny
allow from all
</Directory>

The AllowOverride ALL was added so that Apache would read and obey nested .htaccess files. Once I added .htaccess to the staging directory, I wanted to ensure that this directory was not visible to search engines (robots.txt) and password protected (.htpasswd file).

Robots.txt

We’ll start with the preventative measure of making sure search engines aren’t crawling my staging site.

At the site root, touch a robots.txt file and add the following:

disallow: /client_site_directory/*

Somewhere on the server, we’ll create a .htpasswd file that will store all of our username and encrypted passwords. We generate this with the htpasswd command. You may need to install apache utils first.

htpasswd .htpasswd username

Now, to password protect the directory, you’ll modify .htaccess:

AuthType Basic
AuthName "restricted area"
AuthUserFile /var/www/seojeek/client_site_directory/.htpasswd
require valid-user

Be sure to restart apache.

apachectl restart

Now, when you navigate to yoursite.com/client_site_directory, you’ll receive a login prompt!

Loading WordPress in a Static HTML page for performance comparison

Recently, I was trying to figure out what’s the best way to load a Recent Posts feed onto a static HTML site and I came across a couple of resources that suggested loading wp-blog-header.php onto the static page and using the WordPress API to query your latest posts.

require('wp-blog-header.php')

I was expecting a larger page load, but only saw a 1kb increase in page size and 150ms increase in load time (average). So I proceeded to build a query for the Recent Posts query, which I found on WorldOWeb.

<?php 
 $args = array( 'numberposts' => 6, 'post_status'=>"publish",'post_type'=>"post",'orderby'=>"post_date");
 $postslist = get_posts( $args );

 foreach ($postslist as $post) : setup_postdata($post); ?>
 <div class="events">
 <p><strong><?php the_date(); ?></strong></p>
 <p><a href="<?php the_permalink(); ?>" title="<?php the_title(); ?>"><?php the_title(); ?></a></p> 
 </div>
<?php endforeach; ?>

I also tried using PHP’s DOMDocument class. So, on average, including WordPress in your static HTML will only increase the load time by ~150ms.

Is that a lot? Share your thoughts.