Script to convert .mov to .gif

I generated a shell script to take care of batch converting .mov files to .gif

ffmpeg -i 01-redirects-and-public-pages.mov -s 600x400 
-pix_fmt rgb24 -r 10 -f gif - | gifsicle --optimize=3 
--delay=10 > 01-redirects-and-public-pages.gif

Initially I was running the above on an individual basis. It wasn’t hugely time consuming, in comparison to all the other video work I was doing. Worth turning into a script though, just to stop the copy paste, so I changed it to the following:

for i in *.mov; 
do 
ffmpeg -i "$i" -s 600x400 
-pix_fmt rgb24 -r 10 -f gif - | gifsicle --optimize=3 
--delay=10 > "${i%.mov}.gif" ; 
done

Making the script has taken repetition away, a key component of working efficiently.

jQuery search filter for ACF custom fields in WooCommerce

I needed a solution to filter by custom fields that I’d added to the back end of a WooCommerce site and decided a solution to “roll-my-own” would be the most appropriate.

Here’s the php I used to make the checkbox fields:

// if we're on the shop archive or category pages
if(is_shop() || is_product_category()) {
  // get all the fields choices possible

  $fa = array( //filter array
    'product_type',
    'region',
    'accreditation_linked_to',
    'curriculum',
  );

  foreach($fa as $filter){
    echo '<div class="' . $filter . '" ><h5>' . $filter . '</h5>' ;

    $filter_choice = get_field_object($filter);
    if( $filter_choice['choices'] ) {
        foreach( $filter_choice['choices'] as $value => $label ) {
                echo '<input class="' . $filter . ' ' . str_replace(' ', '-', strtolower($label)) . ' filterbox" type="checkbox" name="' . str_replace(' ', '-', strtolower($value)) . '" id="' . str_replace(' ', '-', strtolower($label)) . '"> ' . $label . '<br>';
        }
    }
    echo '</div>' ;
  }
}

I targeted the list item at the beginning of each product in the list page template. Here’s the solution I used to show the data attributes in each <li> on the content-product.php WooCommerce template:

wc_product_class( '', $product );

$fa = array( //filter array
	'product_type',
	'region',
	'accreditation_linked_to',
	'curriculum',
);

// get the product id
global $product;
$fields = get_fields($product->get_id());

foreach ($fa as $key => $filter_array_item) {

	// reset attr_concat to empty string at beginning of each loop
	$attr_concat = '';

	if(is_array($fields[$filter_array_item])) {
		foreach($fields[$filter_array_item] as $attr) {

			$attr_concat .= str_replace(' ', '-', $attr) . ' ' ;
		}
		echo ' data-' . $filter_array_item . '="' . strtolower($attr_concat) . '" ';
	}
	else {
			// $attr needs to be lowercased and hyphenated where spaces occur.
			echo ' data-' . $filter_array_item . '="' . strtolower($fields[$filter_array_item]) . '" ';
	}

}

Here’s the initial Javascript filtering solution I put together, which worked on my fist set of checkbox fields but not on others:

if( product_type.length  > 0 ) {

  // if any values, hide all + show appropriate ones later
  $('.products .product').hide();

  $.each(product_type, function() {
        
    // get name to filter by
    var filterName = $(this).attr('name');
    $('[data-product_type~="'+filterName+'"]')
    .fadeIn('fast');

    // in the .products div, find .product 
    $('.products').find('.product')
    .filter(function () {
        
    return $(this).attr('data-product_type') == filterName;     
    }).fadeIn('fast');

  });
}

Ultimately this solution only worked for the product_type checkboxes – my first set of filters only ever had a single element to filter by and the chosen approach, using .filter wasn’t effective at showing all the others that had more than one data-attribute choice.

The key was to drop the filter function and get more basic with the solution. This method relies on the markup targeting and uses the tilde before the equals sign ‘~=’ to target data attributes that contain the subsequent text, even if they don’t exactly match it in entirety. Here’s the eventual code that worked for the full set of data-attributes to show:

if( product_type.length || region.length || accreditation_linked_to.length || curriculum.length ) {

  // if any values, hide all + show appropriate ones later
  $('.products .product').hide();

  if( product_type.length > 0 ) {
    $.each(product_type, function(){

        var filterName = $(this).attr('name');

        $('[data-product_type~="'+filterName+'"]')
        .fadeIn('fast');

    });
  }

  if( region.length > 0 ) {
    $.each(region, function(){

        var filterName = $(this).attr('name');

        $('[data-region~="'+filterName+'"]').fadeIn('fast');

    });
  }

  if( accreditation_linked_to.length > 0 ) {
    $.each(accreditation_linked_to, function(){

        var filterName = $(this).attr('name');

        $('[data-accreditation_linked_to~="'+filterName+'"]')
        .fadeIn('fast');

    });
  }

  if( curriculum.length > 0 ) {
    $.each(curriculum, function(){

        var filterName = $(this).attr('name');

        $('[data-curriculum~="'+filterName+'"]').fadeIn('fast');

    });
  }
}

I then managed to refactor the code into the following, so more can be added dynamically from here on out.

$('input.filterbox').on('click', function() {

    // save checked items to array
    var product_type = $('.product_type .filterbox:checked');

    var region = $('.region .filterbox:checked');

    var accreditation_linked_to = $('.accreditation_linked_to .filterbox:checked');

    var curriculum = $('.curriculum .filterbox:checked');

    // save checked items to array of arrays
    var filterArray = [product_type, region, accreditation_linked_to, curriculum]

    filterArray.forEach(toggleFilterArray);

    // function show/hide filter array
    function toggleFilterArray(item, i, arr) {
      if( item.length > 0 ) {

        // remove the content
        $('.products .product').hide();

        $.each(item, function(){

            // find item to be shown
            var filterName = $(this).attr('name');

            // find target data-attribute
            var filterPlace = $(this).attr('class').replace(' '+filterName, '').replace(' filterbox', '');

            // fade in target element
            $('[data-'+filterPlace+'~="'+filterName+'"]').fadeIn('fast');
        });
      }
    }

  });

Countdown to Christmas 2019

Here goes, my first experience with React, the front end language, developed by facebook. I followed some instruction from @dtkatz and managed to solve a puzzle or two of my own over the course of building this app.

first the App is loaded on the index.js page, there’s a public folder where index.html loads <div id="root"></div> after loading index.js

// import React class & ReactDOM classes to extend
import React from 'react';
import ReactDOM from 'react-dom';

// import App class from the local folder
import App from './App';

// render the App
ReactDOM.render(
  <App />,
  document.getElementById('root')
);

let’s take a look in the App class it shows the Clock, which is a self contained element but also allows for the input of a new date by the user.

Give it a go and change date for yourself! Remember, it has to be in the future, as it’s a live countdown and you can’t count down to yesterday, alas ( – sigh )

// Start with the react component, to extend
import React, { Component } from 'react';
import Clock from './Clock';

// make sure it can be styled
import './app.css';

// the App class, will extend the Component class
class App extends Component {

   // constructor method constructs initial object
   constructor(props) {

     // component passes own props
     super(props);

     // setting state
     this.state = {
       deadline: 'December 25, 2019',
       newDeadline: ''
     }
   }
 changeDeadline() {

     // set the new deadline
     this.setState({deadline: this.state.newDeadline})
   }
 render() { 
     return (
         <div className="App">
           <div className='App-title'>Countdown to {this.state.deadline}</div>
           <Clock
         deadline={this.state.deadline}
           />
           <div>
             <input
             placeholder='new date'
             onChange={event =>(this.setState({newDeadline: event.target.value}))} 
             />
             <button onClick={() => this.changeDeadline()}>Submit</button>
           </div>
         </div>
     )
   }
 }

 // exports the App class
 export default App;

Then we’re on to the Clock class. It’s responsible for updating and changing the time to the deadline, which is set to default to Christmas but can be reset to any date you put in the input. Follow the same format to countdown to your inputted date.

 // Start with the react component, to extend
 import React, { Component } from 'react';

 // make sure it can be styled
 import './app.css';

 // the Clock class, will extend the Component class
 class Clock extends Component {
   constructor(props) {
     super(props);
     this.state = {
       days: 0,
       hours: 0,
       minutes: 0,
       seconds: 0
     }
   }

 // beware: research componentWillMount and it's alternatives,
 // add the UNSAFE_ prefix to make this work for 2019!
 // once Component class is mounted, will also mount getTimeUntil
   UNSAFE_componentWillMount() {

     // pass deadline to the getTimeUntil function
     this.getTimeUntil(this.props.deadline);
   }

 // once the Component class has done with the mounting task, set interval to getTimeUntil the deadline repeated every second
   componentDidMount() {
     setInterval(() => this.getTimeUntil(this.props.deadline), 1000);
   }

 // getTimeUntil function is passed the deadline and calculates time until the deadline, it then changes the display to reflect that
   getTimeUntil(deadline) {
     const time = Date.parse(deadline) - Date.parse(new Date());
     const seconds = Math.floor((time/1000) % 60);
     const minutes = Math.floor((time/1000/60) % 60);
     const hours = Math.floor(time/(10006060) % 24);
     const days = Math.floor(time/(10006060*24));

 // changes the display
     this.setState({days, hours, minutes, seconds,});
   }

 render() {

 // always need to return something
     return (
       <div>
         <div className='Clock-days'>{this.state.days} days</div>
         <div className='Clock-hours'>{this.state.hours} hours</div>
         <div className='Clock-minutes'>{this.state.minutes} minutes</div>
         <div className='Clock-seconds'>{this.state.seconds} seconds</div>
       </div>
     )
   }
 }

 // export Clock class
 export default Clock;

Then add some styling with app.css to make it a bit more useable. Since it’s counting down to Christmas, forgive me the Christmas Cheese please?

body {
   background: url('images/Christmas-bg.jpg');
   background-size: cover;
   background-color: green;
 }
 .App {
   text-align: center;
   font-size: 35px;
   margin-top: 20%;
   background-color: red;
   padding: 1rem;
 }
 .App-title {
   font-size: 50px;
 }
 .Clock-days,
 .Clock-hours,
 .Clock-minutes,
 .Clock-seconds {
   display: inline;
   margin: 10px;
 }

I found the image at Pexels but you could choose any typical Christmas scene to add, or why not make the initial deadline for Mardi Gras and change the background to suit?!

Uses for VMs

The plan to develop on VMs for a while should be beneficial for a few things:

  • There’s Database work to be done and a VM is a good place to do it
    Can simulate some stress testing scenarios with VMs, namely:
    • Make a small server space and put a large website/DB in it. – then try to optimise the site a little, maybe caching the MySQL output or other??
    • Corrupt some data by shutting down the machine half way through a MySQL dump – then try to retrieve as much of the data as possible.
    • Make an attempt at clustering – run several DB or duplicates of DBs on the server and attempt to make them balance.
  • There’s also a need to learn about Drupal, so it’s an excellent environment to run that on.

What I need is a simple package on which I can run Ubuntu and install in the same way as the rest of our services are run. Then, if I can access that through a browser and on command line, I should be able to achieve all of the above and make some progress.

I’ll document my findings and any pain points or ideas along the way.

Gutenberg Rabbithole

ACF recently released their much anticipated update which allows for Gutenberg blocks to be easily created with included, related meta-boxes. This has vastly reduced the complexity and time it takes to make custom Gutenberg Blocks.

It’s arguable that developers should try and avoid plugin dependency as much as possible, so should the blocks be created independently of ACF, even though much more time would need to be invested up front?

Whilst it’s clear that ACF makes building the blocks MUCH easier and arguments against can be made, the article below describes the challenges associated with this goal.

Custom development of blocks and markup.

There’s some useful tutorials out there, showing how to build your own Gutenberg Blocks. Here’s some of the resources I uncovered to find out about Gutenberg Block Building:

The authors have done a good job of explaining how to get up and running with your own blocks, what the different parts are and why they’re necessary. I have listed the resources in my opinion of how directly they address building custom blocks for Gutenberg. The LearnWebCode video was concise, accurate & I found it easy to understand. What’s more, there’s a link below the video to the LearnWebCode github account which has a repo of this very lesson.

In particular, I found the process of creating React based Javascript with https://babeljs.io to be simple and enlightening. I had never made any React code before this tutorial and the Babeljs website made the process easy and fun.

Then ACF came along and cleared all before it. The speed and simplicity of setup when using the plugin was a significant difference from the other techniques outlined above. It’s not just that it’s fast at setting up the blocks but the content and the meta boxes that have to populate the blocks is a breeze to create with ACF as well.

What is ACF?

ACF (Advanced Custom Fields) is a WordPress plugin which allows you to add extra content fields to your WordPress edit screens. These extra content fields are more commonly referred to as Custom Fields and can allow you to build website’s faster

https://www.advancedcustomfields.com/resources/

Not everyone wants to support a plugin dependency – especially a paid one – but in this case, it saves so much time in creating the blocks we’re going to need, that, at least for the MVP, it’s worthwhile doing.
~ Full disclosure, I have to confess, I do love this plugin. It’s been very useful in building all the meta content boxes’ custom fields for pretty much every WordPress site I’ve ever built. I don’t like using plugins as a rule but I’ve known ACF for years and always found it a worthwhile inclusion.

Now that a solution for the block creation has been found, I can get on with the other parts of my challenge. The blocks included by default are many. If you’re installing WordPress for yourself and playing around with themes and curating content for yourself, there could be a use for many of these blocks but since we’re not in the business of letting clients make design choices, we actually want to restrict what could be put into any given page, to make sure it fits with our markup and the layout of the site. That means turning off, or ‘blacklisting’ all the blocks we don’t want to appear as choices on a given theme. Since we also want our own custom markup on every block, so that we’re catering to a single css styles system, we actually want to remove almost all of the default, out-of-the-box blocks.

This is where the age of Gutenberg becomes an issue. Usually, the shear size of the WordPress community means that there’s lots of articles written to help with almost any subject you’d care to research but Gutenberg is so new and is being applied in all sorts of exciting new ways. It’s very flexible and it has changed a lot about the way WordPress is going to be used in the future. All this, in turn, means that there isn’t the usual number of articles describing the thing you want to find out about, so corroborating the best technique to do something can be a little more time consuming than normal.
~ So hopefully, I can save you some time:

If you’re using WordPress 5.2 or higher, you can toggle the blocks available within the page options. That’s the 3 vertically aligned dots at the top right of your edit page. Toggling blocks on and off on a per article basis doesn’t sound very developer-y though, there’s surely some programming that can be used to achieve the same thing…

This method described by Jason Bahl might be of interest:
https://jasonbahl.com/2018/05/29/whitelisting-blacklisting-blocks/
~ by his own admission, it’s quite possibly a fragile method – this article’s over a year old now but worth a read in case you come across this method in your own search. However, there may be a better way of tackling this challenge.

https://rudrastyh.com/gutenberg/remove-default-blocks.html

Misha Rudrastyh has written a more recent article about a PHP solution for custom block selection. He’s included a very handy list of core blocks you may wish to use (or not). It’s this method that I’ve chosen to be able to add/remove blocks from Custom Post Types and general site content areas.

// remove unwanted default gutenberg blocks

add_filter( 'allowed_block_types', 'llwp_gutenberg_allowed_block_types' );

function llwp_gutenberg_allowed_block_types( $allowed_blocks ) {

    $allowed_blocks = array(

        'core/preformatted',

        'core/html',

        'core/image',

        'core/paragraph',

        'core/heading',

        'core/list',

        'acf/testimonial'

    );

    return $allowed_blocks;

}

The first six items of the array are the core out-of-the-box blocks that we want to keep. The last is the first test block, made with ACF. Naming convention is important to show the blocks that you want. If it’s a core block, start the definition with core/, start ACF built blocks with acf/.

Full Width Gutenberg Blocks

Selecting blocks that break out of the css content wrapper are one of the exciting aspects of this technology. There’s more on preparing to add the full width aspect to your theme found here: http://jschof.com/gutenberg-blocks/gutenberg-block-alignment/

To support full width with ACF built blocks, add 'align' => array( 'wide', 'full' ) to the attributes list . Here’s what the code looks like in full for the definition of my first test block:

// ACF gutenberg blocks testing
 add_action('acf/init', 'my_acf_init');
 function my_acf_init() {
 // check function exists
 if( function_exists('acf_register_block') ) {
     // register a testimonial block
     acf_register_block(array(
         'name'              => 'testimonial',
         'title'             => __('Testimonial'),             
         'description'       => __('ACF testimonial block.'),
         'render_callback'   => 'testimonial_block_render_callback',
         'category'          => 'formatting',
         'icon'              => 'admin-comments',
         'keywords'          => array( 'testimonial', 'quote' ),
         'align'             => array( 'wide', 'full' )
     ));
 }
 }

One page to rule them all? (maybe this goes in a later article about the wider challenge of building the sites we’re after?)

Nerdy note: If TYPO3 happens to be your weapon of choice for the majority of your sites. It has a built-in configuration language (typoscript) which means that configuration meta-data about clients, such as ‘strap-line’, address & contact details, google analytics and API codes/keys all have a natural place in the setup.ts document in the root of every site. It would surely be a good idea to have something similar in WordPress. So the plan is to make a theme options page to support the basic meta-data, that’s useful site-wide and doesn’t have one specific page that’s an obvious home for it. Typically containing everything you’d normally find in the root typoscript docs…

Inspire day at Hangleton Primary

I’ve been asked, as a dad of one of the students, to talk about what I do to the pupils at Hangleton Primary School in Hove. Here are some thoughts on the message I’d like to get across.

Find a job you love and you’ll never have to work a day in your life.

Confucius (allegedly)

Whoever really said it first, the idea is sound. Another way I’ve seen this said is in the life-guidance writing, Desiderata:

Keep interested in your own career, however humble; it is a real possession in the changing fortunes of time.

Max Ehrmann ~ 1927

We’re lucky enough to live in a society that will look after those who can’t look after themselves. If you can’t work, you will be supported by the state. But a career path is much, much more than just putting food on the table. There are, of course times when we work to sustain ourselves but those lucky enough to work in the fields they enjoy will have a valuable (if abstract) possession in their skills that no misfortune can remove.

Upskill!

Like riding a bike, you never unlearn a skill that you’ve learned. It may be years from one use of the skill to the next but you’ll never forget it and may well be glad to have as many skills as possible.

Follow your interests

When I was at school and they asked the question, “what do you want to be when you grow up?” many of the girls in the class answered that they wanted to be a hairdresser, many of the boys answered that they wanted to be a stuntman. I think the reality is, no one really knew what they wanted to do for a living at that early stage.

I still don’t know what I want to be when I grow up , perhaps I never will!

There will be some subjects that you enjoy more than others. It may be because you’re better at them but more likely that you’re better at them because they stimulate you. Our school and career systems channel us into specific areas of learning – they do this for good reason but the other skills that might fall along the way may be useful to you later in life. ~ We no longer live in a world where you can get a job at 16 and still be sure you’ll be working in the same industry when you’re ready to retire. Being prepared for more than one career path is going to be beneficial, adding more than an extra ‘string to your bow’ will reap dividends in the end.

My Journey

My favourite subjects at school were mostly the practical arts: CDT, Art, Pottery, Music… and I loved my sports and extra school clubs like canoeing and the CCF. They’re still my favourite subjects and they’ve plotted the course of my career for the most part but not everyone can make a living by painting or crafts.

It doesn’t matter what degree you get, but that you get a degree.

Mr Harwood, (chemistry teacher, careers adviser Reigate GS)

I didn’t rate Mr Harwood’s chemistry lessons – maybe because I never had much of an affinity for learning Chemistry – but this piece of advice has always stayed with me. I think, what Mr Harwood meant was that, provided you embrace the challenge, higher education is less about being taught a subject and more about learning to teach yourself – that’s why people say, “what are you reading?” when asking about others’ time at university. Being awarded a degree, shows the world that you are capable of teaching yourself to a level acceptable to a university. If you can learn one subject, you can learn another… and another – the only constraint is your time.

Study at university if you can – it will teach you far more than the subject you choose to study

I had to take the opportunity of going to university whilst in the right age window and the choices I made at school mapped out the pathway I would take in my late teens and early twenties. I took Geography at GCSE and enjoyed the ‘physical’ side of that enough to take an A-level in Geology. Geology class was taught by an inspirational teacher called Roger Birch at Collyer’s 6th form college in Horsham. Out of 13 class members in my yeargroup, 10 of them went on to do Geology degrees (was that all because of the field trips?).

I studied Geology at The University of Birmingham and loved it – even the early lectures and the late lab sessions but I knew I wasn’t interested in a job in the typical fields a geology graduate goes into. For one thing, I was really into my music at this stage, so after I graduated (no one can take my degree away now I’ve got it) I tried to make a go of things with my band. Spent a couple of years doing local jobs, like driving and a position with a local animal charity but these jobs were purely as a means to earn money to survive. I was living with my parents and needed very little in the way of money and my interests laid firmly in the musical realm.

The band never worked out but as chance would have it ( – and chance favours the prepared) I was successful with an open audition for a percussion show called STOMP.

What a life!

STOMP was a wonderful job for a young and single person, well paid for theatre and immensely enjoyable. After several years though, my body was well worn and the life of touring around the world from one hotel to another was wearing too. I longed for my own toaster, or to have a bowl of cereal when I became hungry at night, instead of having to go out and buy all my food from restaurants and takeaways.

My work in London had included a few different bands. Each of them required its own promotion which invariably meant its own website. Since I didn’t start work until 6pm every day, I took the opportunity to teach myself some web design, during the day, while everyone else was at work. Enter Macromedia Dreamweaver 2004.

Building in code – work with your hands

By the late 00’s I was married and had stopped touring with the show, opting instead to ‘sit down’ in the London production. I had my long, lost cereal and a toaster all of my own but I had climbed to the limit of my career path in the industry, by being rehearsal director and lead in the show. I was tired, ageing and frankly, getting bored. I knew it was time for a change.

Changing career path

I’m an extraordinarily lucky person. By marriage, I was entitled to emigrate to the USA and try my hand at a new career. We arrived in California in June 2008 and I was ‘forced’ to open a company with my wife as the registered owner, so I could work and start getting paid.

After some time freelancing with various web technologies, I was awarded an informal position at a local marketing agency. I gained experience in various projects until my wife got pregnant and wanted to be closer to her family in Louisiana.

Medical insurance then meant I had to get an employed position, instead of a self-employed one, and so began my second career as a web designer/developer.

I’ve worked for marketing agencies and advertising agencies and even returned to work for the company my wife had to start back in California all those years ago.

After 10 years in the USA, we returned home to the UK and I needed local employment here but the learning hasn’t stopped. I’ve gone from what I would describe as a web developer, to a back-end web developer. Meaning I get to do the things I enjoy most about building in code.

I now work for a local web development agency, building the functionality of websites and servers, without having to worry too much about making them look attractive – that’s the job of the front-end developers…

I see this job as having plenty of scope for growth. You’ll never learn all there is to learn, of course but you will need to keep growing your knowledge and experience in order to stay interested in your career. Back end development and sys-admin are subjects I can happily learn about for years to come and be paid to use my experience and detective skills to solve web problems. I’ll easily remain interested and employable for another decade or more, by which time, provided I make it that far, I’ll be thinking about the next life transition, the final career of retirement.

All the way along, I’ve been lucky enough to do things because I chose to.
– Of course there are days where you don’t want to go to work, and jobs at work that you don’t want to do, because you don’t enjoy them for whatever reason. And of course, those jobs still need to be done. Get through them and try to find something enjoyable about them. Even your dream career will have its weaknesses. If you follow your interests enthusiastically enough though, people will notice and likely, before too long, you too will be able to say you’re not ‘working’ for a living, because you’re doing something you love to do.

Maybe by then, I’ll know what I want to be!

Notes:

In my talk to the school kids, I was able to suggest the scratch educational programming interface to the children and they seemed familiar with it already. Here’s a link to a similar technology by google called blockly – https://developers.google.com/blockly/ – this platform is particularly interesting because it shows the code behind the behaviours which are built in the graphical builder.

I’ve been asked if I would go back and do a workshop with the kids on coding. One day, with a little preparation, I think it could be a great idea.

Domain Practise

The difficulty with learning about domains and apache configuration is that ideally, it has to be done in the real world. It’s not possible to properly emulate the world wide web on a n internal machine – but we can make a hybrid!

In times past, we’ve had great ideas which we’ve acted on quickly to secure the right domain – it’s often the first port of call for many a branding ideation session. Of course, not all of those ideas come to fruition and this can leave many domains, sitting, parked and wasting their potential, somewhat.

Hosting is a necessary expense for a website accessible to the web but need not be an expense when it comes to training. An internal VM can be used instead. As long as the VM has a known IP and you don’t need to see your site outside of its network, you’ll be able to use it.

Setting up the VM

My colleague Mike setup the VMs here at LiquidLight. If you want to follow along with how he did it, he made an excellent write up of it here. After this setup we had a bare VM with the minimal software.

Provisioning the VM to act as a server can be approached in myriad ways. Server setup is beyond the scope of this article and can vary considerably dependant on your requirements and distribution but if you liked Mike’s write up of creating the VM on Xen, you could find details on setting up a server in Mike’s next article, so I’ll link to that here for a concise write up.

How does DNS work?

DNS (Domain Name System) can be thought of like a phone book of all the world’s websites. Rather than remembering the IP address of each and every site you want to visit, you remember the name and the name is stored in a database on the DNS with its corresponding IP address.

To look up the IP address from the name, your browser checks on 4 caches to find the relevant IP:

  • The browser cache itself – all the sites you’ve visited previously are stored within the cache for a defined period.
  • If unsuccessful, the browser checks the OS cache/hosts file – the OS has its own cached list of domains.
  • If not successful with the above, the browser would communicate with the router that maintains its’ own cache of DNS records.
  • Finally, If all these local lookups fail, the browser would move on to the ISP. ISPs retain their own cache of DNS but if they don’t have the relevant domain, they call on a domain server further and further up the line until they find the correct domain, (at which point, all other devices in the chain update their cache with the correct IP for that domain).

About IP addresses

Usually, a website will have a domain name that looks something like this; e.g. google.com’s IP is 216.58.210.238 – and if I put this IP into my browser, I get the google home/search page. This should be true, whether I’m on my home network, in an internet cafe, or on my network at work.
Your home network may be configured for an IP address like 192.168.1.1 or 192.168.0.1.
10.0.0.1 is more commonly seen in business computer networks than in home networks – Like 192.168.0.1, it’s an internal IP and can only be reached from inside your network.

If you were creating a site you wanted to be reachable on the web, you would point a domain at the hosting server’s IP, the IP lookup would go through the above process of checking caches and return the external, public IP of the web host.

In this exercise, there was no need for the site to be seen outside of the ‘home network’ and so, no need to rent any hosting server space from a web host.

A VM was created and assigned an IP of 10.0.0.233 and a spare domain had an ‘A’ name added and pointed to 10.0.0.233.

(The “Hello World” message on this site won’t be viewable by anyone outside this internal network and the IP may not even exist to the computer this article is being read on but its reachable from this office and hence can be used to practice setting up domains on this local network, without incurring charges from a web host. – thrifty!)


What were the pain points?

Symptom: can’t to ssh into the server

I had dome something wrong in setting it up and now had to trouble shoot ssh connection before I could diagnose any issue at all.
The VM I was trying to reach was called ‘jeremyvm’ and it was installed on our internal server called ‘Phantom’. I could reach the VM from Phantom with the following command: sudo xl console jeremyvm

Once inside the VM, I could use sudo su - to become root and start troubleshooting what was and wasn’t working.

It was helpful at this point to know that using the command ssh -v increased the verbosity of the error reporting and ssh -vv increased another step. I could have put another v on the end to get a third level of error report verbosity but I’d found the cause by then.

Was it listening on port 22? ~ Yes but I still couldn’t connect to the VM via SSH.
Was SSH being blocked by a firewall? ~ Yes! Here’s the commands that singled me out:

## SSH ##
for i in 82.68.37.54; do
  iptables -A INPUT -p tcp --dport 22 --src $i -j ACCEPT
done
iptables -A INPUT -p tcp --dport 22 -j DROP

The above is part of the iptables settings to block SSH requests from computers outside of our IP address (the office). The line for i in 82.68.37.54; do means that for the stated IP address, do the following.

You can see, on the next line, the word ACCEPT, so if I’m coming from the right IP address, I should be accepted onto the server as an SSH connection.

The problem was, the machines inside the network see the local IP addresses instead of the external IP addresses, hence it looked to the internal iptables like I was trying to connect to the network from an IP that wasn’t 82.68.37.54 but 10.0.0.10.

Solution: Unblock the relevant IPs

IP tables commands are added to every server we configure to block unwanted traffic from doing sneaky, hacky things but we’re already protected by a firewall here so, the above problem was unnecessary as well as unusual. I commented out the lines that were blocking my IP with a # and left it at that.


Symptom: the browser refused connection;
Solution: It turned out Apache wasn’t runningstart apache.

Although I’d set apache to run the previous day, I hadn’t put a provision on the machine to make sure Apache was started at bootup. Our VMs backup, update and reboot overnight, so the next day, when I came back to configure the rest of the setup, Apache wasn’t running. This question, answered on StackOverflow gives more detail as to how to set Apache to run at boot. Although there are differences based on distribution, essentially, every solution involves making a symbolic link from /etc/init.d/ to the appropriate run-level folder in /etc/.


Symptom: URL showed http://www.domain.com/domain.com/domain.com/domain.com...etc ~ A redirect loop.

It seems like by this stage, apache was running and when the request hit the right place, it has tried to fulfil its objective and forwarded to the right place. However, there’s a line in the Apache config file that takes care of redirecting and I’d missed something here.

My simple Apache config file looked a bit like this:

### Primary domain name ###
ServerName www.yourdomainname.com
ServerAlias yourdomainname.com

### Document root ###
VirtualDocumentRoot /var/www/yourdomainname.com/html

    ### Logs ###
    CustomLog /var/log/apache2/yourdomainname.com/access.log combined
    ErrorLog /var/log/apache2/yourdomainname.com/error.log
    LogLevel error

    ### Admninistrator ###
    ServerAdmin admin@yourdomainname.com

    <Directory /var/www/yourdomainname.com/html>

            ### Rewrite rules ###
            RewriteEngine On
            RewriteBase /

            ### Restrict domain access ###
            RewriteCond %{HTTP_HOST} !^www\.yourdomainname.com$ [NC]
            RewriteRule ^(.*)$ www.yourdomainname.com/$1 [L,R=301]

    </Directory>

The part I had misconfigured was here:
RewriteRule ^(.*)$ www.yourdomainname.com/$1 [L,R=301]


Solution: Correct the RewriteRule to read:
RewriteRule ^(.*)$ http://www.yourdomainname.com/$1 [L,R=301]

The addition of http:// on the beginning of the domain stopped the infinite redirect because it was a clear demarcation of the domain, vs the directory structure within the site. You can have many /directories within one another but if you’ve got two slashes, there’s a lot less ambiguity about where the domain ends and the directory structure begins.


On a previous exercise, I had failed to enable the site I was trying to setup (i.e. failed to setup a symlink between a file in /etc/apache2/sites-enabled/yourdomain.com.conf and /etc/apache2/sites-available/yourdomain.com.conf, so I made sure I had this site enabled with the command a2ensite /etc/apache2/sites-available/yourdomain.com.conf.

Once this command was complete, I had a domain name, with an ‘A record’ pointed at my internal IP; 10.0.0.233,
I had an apache config file enabled on the VM at that address /etc/apache2/sites-available/yourdomain.com.conf
and I had an index.html file with “Hello World” content at the place I was sending the request:
### Document root ###
VirtualDocumentRoot /var/www/yourdomainname.com/html

Now, when I go to the domain name yourdomain.com in my browser, I see “Hello World” in all its basic glory.

Conclusions

“Practise makes better” – You may be familiar with the phrase ‘practise makes perfect’ but you’ll get better at whatever you practise, whether it’s good or bad technique. Practise the wrong thing and you’ll get better at implementing things that way. So it’s important to synthesise an environment as close as possible to real production when training or practising, especially when it comes to domains and apache config.

Rather than investing in new domain names and hosting packages to practise on, you can use any domain, perhaps a spare, unused, speculatory one and an internal VM set up to be a server on your local network.

Next steps, configure some subdomains with more apache config files for this VM, to test your prowess with a digital curve ball…

Making use of all those domains!

Mistakes are important.

If you’re doing something you know you can do, you’re not doing anything

Learning anything requires making mistakes. Make no mistakes and you’re not pushing yourself into new territories. Risk and reward are all part of the job BUT you can’t be reckless with a client’s assets or hard won google rankings.

Some sys-admin tasks need to be learned in a live environment, on a live server. Live domains and servers don’t come for free, so I propose recycling a little, to make use of the domains I’ve bought up, over the years, hoping to do something useful with.

All that’s needed to complete the toolkit is some hosting. Hosting (of the variety I need) doesn’t need to cost the earth and can be used again and again for practice, without breaking the bank. I could buy some and do some site setup/apache config practice on it, or I could use a local VM for free, either way, I’ll come back on here and write up what I learn from the experience.

Skillshare and Linux courses

I’m lucky enough to be working for a company that uses a video tutorial site that we’re encouraged to access. I’ve used video tutorial sites before and have often found them to be found wanting a little bit. Skillshare has quite a large number of courses and quite a variety of topics and teachers too. This makes for a smorgasboard of educational flavour and should be something to suit everybody.

I was after some hard core linux bootcamp training and found as an additional benefit, that the tutor’s voice was perfectly suited to pre-bedtime lectures. It’s not that he put me to sleep, more that he approached the topic with a calmness that let me absorb information without stress.

If you’re reading this and thinking, “blue light ruins your sleep patterns, dummy”. Well, you’d be right of course but since linux is all about the command line, I lost nothing by simply listening to the tutorials, instead of having to watch the screen as well.

The hour or so before going to sleep and after waking up in the morning are often dead time, to me. If the kids are awake and need help with their mornings, I’ll do that but I’m often left reaching for the headlines (which I’ve already read) or, sometimes, I’ll watch a video on YouTube coz I like to try to keep learning.

Skillshare has meant that I get to do something truly constructive with my downtime.

Comes with an app, all self contained and linked via your user account, so you can start watching on your phone and stop half way through a video and then pick up again on your browser at work, seamlessly. The app also chromecasts, which I thought was a very handy touch – especially if I want to put the tutorial on TV and work along with it on my laptop.

We’re sharing Skillshare at my place of work and I can see from the interest list, that we’re taking in subjects as diverse as video, programming, strategy and illustration.

I’ve used other video sites in the past, like Lynda and TeamTreehouse and of course, the peerless YouTube – do you have a favorite that I haven’t mentioned? Why is it better than others you’ve come across?