Easily host your own web sites | part 5: Web design (geek/hacker)

Contents


Rambling introduction. Sorry.

beardy guysIn the last post in this series, I looked at some options for creating a website with relative ease. Perhaps the word “easily” should not be present in the title of this, the last episode.

If you’re a budding geek/hacker, sooner or later running WordPress blogs is going to get old. You could spend a lifetime learning how to bend WordPress to your will (which would be like dancing on shifting sand – it’s constantly changing underneath you and you’ll die from exhaustion) or you could set your mind to doing the thing from scratch. Or near enough from scratch.

I first got into programming (at the age of 11 or so) when the “hacker” community demographic genuinely consisted of bearded recluses. Social skills were an inconvenience. If you couldn’t say it with ones and zeroes, it wasn’t worth saying. Later on, when I took Computer Science at university (along with Law), little had changed. Less than three percent of the people in my year for CompSci were women. Less than three percent had discovered personal hygiene products. It wasn’t the same three percent, by the way.

I say this by way of apology. My path to web development was long and winding. I had experience with many different programming languages before my eyes were opened to the wonders of web-centric coding. This gave me quite an advantage. Diving into this material can be intimidating, if you’ve little or no experience of code. Let me just reassure you: all you need to do is assimilate a little bit of knowledge at a time. Eventually it will all start to hang together. With the tools I’ll show you, you’ll be hammering out awesome code before you can say Xyzzy.

Disclaimer: proceeding down this path may not turn you into a geek. Or cause you to grow a beard. Please try to contain your disappointment, if so.

The building blocks

Many (but by no means all) web sites are built using a combination of:

  • HTML: the language which describes the components of a web page. It has much more to do with layout/structure than with how things actually look. HTML is probably the easiest bit to learn. In your browser, you can right-click on most web pages and select “view source”. What you’ll be looking at is largely HTML code. Start learning HTML at W3Schools. Whenever I need a quick reference for HTML, I go here.
  • CSS: Cascading Style Sheets describe how a web page should look. Colour, positioning, borders, etc. Reference here. Tutorials here. Learn how to do fancy things with CSS at CSS Play and goggle at others’ CSS prowess in the CSS Zen Garden.
  • JavaScript: There seems to be an increasing trend to use “CamelCase” when writing “JavaScript”, these days. When I started learning, it was simply “javascript” – not to be confused with Java. JavaScript is a true programming language, which runs in your browser. It is loaded along with the HTML and CSS and actions are performed on the basis of the JavaScript code instructions. Reference here. Tutorials here.
  • PHP: A hugely popular server-side (so-called because it runs at the web server end, not in the browser) programming language. PHP is by no means the only – nor necessarily the best – web programming language out there. Ruby, Python, Java and ASP.NET all have a strong presence in modern web sites. PHP is the one with which I and many other far superior web developers are familiar. It’s idiosyncratic and inconsistent at times, but I have found it to be easy to learn and for the most part well documented. The official PHP site is an excellent reference tool. If you learn PHP, you’ll be using this site’s function search box regularly. The W3Schools tutorials don’t cut the grade for me here. Start learning PHP at the home of PHP.
  • MySQL: one of the most popular database management systems on the planet. If you need to store a lot of data (or even a little), you’ll benefit from a database engine powering your web site. Learn about using MySQL within PHP here. The official reference is here.

Gasp. That’s a lot to get hold of, isn’t it? Don’t worry – you’ll see that a lot of this learning dovetails together. For example, once you’ve learnt some JavaScript, picking up PHP will be that bit easier, because they have much in common.

Frameworks

You can get by, just knowing the building blocks. But if you want to create efficient, reusable code, without continually reinventing the wheel, you’d do well to make use of some frameworks. Each language has many frameworks and you could spend a lifetime trying them all out. I will steer you in the direction of a small few, but by all means try out as many as you have time for. Each is different from the other, which can be bewildering, but it also means you’re likely to find frameworks which fit or even enhance your style of working.

  • Twitter’s Bootstrap: built by the mighty Twitter, Bootstrap gives you consistent CSS styles, which (amongst other things) help to ensure your web sites look very similar in different browsers. Sooner or later you’ll be stung by cross-browser differences. Making use of a CSS framework such as Bootstrap will reduce the impact of this pain. Bootstrap is more than just a CSS framework, it also has JavaScript plugins. There’s much to explore here and unless you’re already a CSS/design guru, it will make your web sites look better.
  • jQuery: a phenomenal JavaScript library – it enables you to do phenomenal things on the browser side, with very little coding, which would otherwise take you hours of work. jQuery is not the only such library, but it is one of the best. It is further enhanced visually by the excellent jQuery UI.
  • CodeIgniter: I have built many web sites with this PHP development platform. It was my first introduction to the MVC (Model, View, Controller) paradigm, where you split out your database work (Models) from your presentation/pages (Views) and your application/business logic (Controllers). CodeIgniter has some flaws, including the suggestion (a view shared by many) that it would benefit from a ground-up rewrite. But it is well-built, fast, solid and will help you to concentrate on the unique features of your web site, without having to reinvent the wheel all the time. Your resulting code will be easier to understand and maintain.

Special mention

There’s a rapidly up-and-coming PHP framework that deserves some attention at this point, Laravel. I have not personally had chance to experiment with it yet, but I intend to use it for my next web application. I understand it addresses many of the shortcomings of other PHP frameworks and apparently CodeIgniter developers are flocking to it in droves.

Graphics

As I’m sure you can tell, graphic design is not my strong suit. I am amongst other things a web developer, not a web designer. It’s rare to find someone who’s good at both. Nevertheless, even if you’re not graphically inclined, there are still a lot of things you can do to give yourself a “leg up”. Here are my top graphics tips:

  1. Don’t be afraid to copy. There are very few genuine innovators out there. Most fashion is a rehash of other fashion. It’s fine not to be original. See what works, figure out why it works, and put your own spin on it.
  2. Get a good graphics package. Don’t be put off by its name; download GIMP. It’s like Adobe Photoshop at the great price of free. If you’re not short of cash, I can also recommend Adobe Fireworks, which is an excellent but expensive web-focussed design package.
  3. Follow the GIMP tutorials. Really. Do it.
  4. Use an online colour scheme generator.
  5. Soak up some design inspiration from Designpiration or Web Creme.

That’s me done. Now go forth and conquer the interwebs.

Photo of bearded gents copyright © Igal Koshevoy, licensed under Creative Commons. Used with permission.

Easily host your own web sites | part 4: web design (DIY)

Contents


web designThere are many different ways of approaching web design and development, ranging from packages that do all the hard work for you, to coding the thing from scratch. If you were of the former persuasion, then I suspect you wouldn’t be reading this series, so we’re going to look at two different DIY approaches: moderate DIY (requiring little or no programming) and geek/hacker (getting your hands dirty with code). We’ll concentrate on the former, today.

Moderate DIY: Content Management Systems

I’ve been through the loop a few times with several different content management systems. These are software packages that typically provide a high level of functionality for your website, without involving you in lots of programming. There are some big names in this field, in both the commercial and open source worlds. In the latter case, the “big players” include:

There are hundreds of CMSes out there. By all means, experiment and try a few out. They have many things in common, so you’ll learn useful skills whatever you do. If you want to hit the ground running though, I would recommend checking out WordPress. Each iteration of WordPress is becoming more friendly to the web developer as well as the end user (content editor). The installation is a breeze. If your target website is a blog or a simple corporate website, WordPress is a great choice. There are some caveats, however.

WordPress may well be the most popular and most widely deployed CMS on the planet. This has pros and cons. On the one hand, you will find a large community of helpful and knowledgeable users, lots of documentation and a decent rate of product improvement. On the other hand, WordPress’s success makes it a large target. WordPress has suffered from some fairly high-profile security flaws. Because it drives so many web sites, WordPress is a tempting target for spammers and identity thieves alike. In short, if you plan to use WordPress (and I think you should), you’ll need to be committed to keeping up to date with security patches. Fortunately, applying WordPress patches is simplicity itself. You shouldn’t find it too onerous to keep it up to date – unless you are managing a hundred WP websites; but even then, there are tools for that

For the remainder of this section, I’ll proceed along the basis that you’ve selected WordPress as your CMS of choice. I’ll also assume that you are setting this up on the server created by following the earlier parts of this series (that is, you are using Virtualmin as your server/website management solution).

Installation

The installation of WordPress is well documented on the WordPress website, so I’ll not rehash that here. Let me suggest a couple of things to assist your installation though.

First, for each WordPress site, it is easiest to start from scratch, setting up a new site/domain within Virtualmin. If this is the very first site you’ve set up with Virtualmin, you will also be creating your first “user”. Virtualmin can handle multiple users on a single server, where each user is a kind of “mini administrator” of the website(s) within his or her control.

Virtualmin will by default create a username that corresponds to the first part of the domain you specify. So if your domain is “fredbloggs.changeip.com”, your user name will be “fredbloggs”. Virtualmin will also create (by default) a new MySQL database of the same name (“fredbloggs”). This MySQL user will have full control over the database.

If however, this is the not the first website you’ve created with Virtualmin, you have the option of adding the website to an existing user’s profile (in which case, you need to ensure you’re creating a sub- server), or creating a new user/domain/MySQL database. If you go down the sub-server route, you’ll be using the original username for your credentials, but your MySQL database name will be different (again, corresponding to the first part of the domain, by default).

To flesh this out a bit, you might do the following:

Site 1
Type: Virtual Server
Domain: fredbloggs.changeip.com
Username: fredbloggs
MySQL database: fredbloggs
MySQL username: fredbloggs
Web site root directory: /home/fredbloggs/public_html

Site 2
Type: Virtual Sub-server
Domain: funkyshop.changeip.com
Username: fredbloggs
MySQL database: funkyshop
MySQL username: fredbloggs
Web site root directory: /home/fredbloggs/domains/funkyshop.changeip.com/public_html

Note the different root directories (where the web site’s files are stored). I’d recommend the following procedure for obtaining and extracting the WordPress files:

  • Go to the WordPress download page
  • Make a note of the URL for the “tar.gz” download. This is a Linux-friendly compressed archive. At the moment the address is http://wordpress.org/latest.tar.gz, but that may of course change.
  • Fire up an SSH connection to your server.
  • Navigate to the web site root directory.
  • If you logged on as root, use “su” to change to the website owner: su fredbloggs
  • Use the command “wget” to download the WordPress archive file: wget http://wordpress.org/latest.tar.gz
  • Extract the files: tar zxf latest.tar.gz
  • Move the extracted files out of the newly-created “wordpress” directory into the current directory: mv wordpress/* .
  • Tidy up the archive file and the now empty directory: rmdir wordpress; rm latest.tar.gz
  • Proceed with the installation per the instructions on the WordPress site.

Design

Restyling a CMS is never a trivial task. If you are creating a commercial site, it may well be best to engage the services of an experienced WordPress designer. Until then, you have some other options. For starters, there are a lot of WordPress themes freely available which will completely change the look and feel of your site. Most of these themes can be customised in various ways, from adding your own logo, to changing the colour scheme. If you’re handy with CSS, you can tailor them even further, through the built-in CSS editor.

The problem is of course that your website will end up looking like a lot of other websites out there. For a personal blog, that’s probably not a big deal. If that doesn’t statisfy you though, you have a couple of ways of creating something a bit more personalised.

Several theme frameworks are available for WordPress. They are essentially skeleton themes, which come with instructions for customising to your own design. This tends to come with a steep learning curve. You’ll do well to avoid this until you’re reasonably fluent in HTML, CSS and PHP.

I recently came across a software package called Artisteer, which provides a WYSIWYG environment for designing themes for WordPress and many other CMSes. I have had a brief look at it, following a recommendation elsewhere. It too comes with a learning curve, though not as steep as beginning with a framework theme. Also, it is not free. But if you think you’d like to make your name designing cool WordPress sites, Artisteer (or something like it) would most likely speed up the design process.

Plug-ins

One of the great advantages of using a CMS, is that other people create generic plug-ins, which extend and enhance the functionality of the site. WordPress is no exception, with thousands of plug-ins available on its website. There are far too many plug-ins to list here, but here are a few that I would install straight away, on most WordPress sites I create:

  • Cookie Law Info: “A simple way to show how your website complies with the EU Cookie Law. Implied Consent. Style it to match your own website.”
  • Google XML Sitemaps: “[Generate an] XML-Sitemap compliant sitemap of your WordPress blog. This format is supported by Ask.com, Google, YAHOO and MSN Search.”
  • JetPack: an official WordPress plugin, which supercharges the commenting system, adds mobile-friendly themes, adds great stats and much more.
  • Ultimate Maintenance Mode: a handy plugin if you ever need to take your site offline

This is all you need to know, to get a massively featured website up and running in relatively no time, using pre-built tools. In the next and final article in this series, I’ll look at doing the whole thing more or less from scratch.

Easily host your own web sites | part 3: networking

Contents


network cablesSo far in this series, we’ve set up our server hardware and installed and configured the operating system and web hosting application. In today’s article, we’ll look at how you enable people to browse content on your server (wherever they and it may be).

Port forwarding

Most SOHO routers are configured to prevent unsolicited inbound traffic. If you want people to be able to view web sites on your server therefore, you need to create a conduit through which web traffic can pass. This is usually called “port forwarding”. Broadly speaking, ports segregate different types of network traffic. Web traffic comes in on port 80 (TCP), SSH traffic on port 22 (TCP), encrypted web traffic (HTTPS) on port 443 (TCP) and so on. So you need to configure your router to forward port 80 traffic to the static IP address you configured for your server (see part 2). You may also wish to forward port 10000 (Webmin) and port 22 (SSH) so you can administer your server remotely, but be warned that as soon as you do, so-called “script kiddies” will start trying to break into your server. So proceed with caution, and read up on the risks. You’re pretty safe forwarding port 80, provided your web sites are well designed.

To find out how to set up port forwarding on your router, read the manual, or check out the references at portforward.com. Once you’ve done that, check that the port is forwarded by browsing to your network’s external IP address. How do you find that out? From within the network, browse to icanhazip.com.

If you’ve forwarded the port correctly, and if you’re not caught by the hairpin NAT gotcha (see below), you should see the default web page from your new web server. On my new server, the page looks like this:

It works!

This is the default web page for this server.

The web server software is running but no content has been added, yet.

Dynamic vs. Static IP addresses

If you are going to be hosting any kind of internet service (like a web server), it is easiest to have a static IP address. DNS (the Domain Name System) converts “human-friendly” web addresses such as “pomeroy.me” into “computer-friendly” IP addresses such as “178.248.39.218”. This is great if your network’s external IP address never changes, but for many customers on cable, broadband or dial-up connections, their IP address is “dynamic”, meaning that it might be different each time the modem or router reboots. There is a workaround for dynamic IP addresses (see the section on Dynamic DNS, below), but in the long run, it might be easiest simply to ask your ISP for a static IP address. In many cases that costs little or no more than a dynamic address.

Dynamic DNS

With Dynamic DNS (DDNS), your router or server contacts a DDNS service provider on the internet and reports its current external IP address. The DDNS server then transmits this information through the internet via the DNS system. Changes can take a while to take effect, but it’s better than nothing. My current DDNS provider of choice is changeip.com. ChangeIP.com has a free service, where you use a sub-domain from one of several domains they have available (e.g. fredbloggs.changeip.org).

Many routers can speak DDNS, meaning that as soon as the router reboots, it can check in with the DDNS provider and let it know if the IP address has changed. See your router’s manual for more information. If your router doesn’t directly support this, you can install software on your server that will periodically update the DDNS service. Read ChangeIP.com’s page on DDNS clients. You’ll need to install one of the scripts on your server, and set up a cron job to run the script as often as you like (providing you don’t breach the DDNS provider’s terms of service). You can of course use Webmin to set up your cron job.

Regular DNS

If you have your own domain registered, like me, and you’re using a static IP address, all you need to do is point your domain to your external IP address. Log into your domain host and add “A records” as needed.

Note: Did you know that the customary “www.” prefix on many websites is an unnecessary hangover from earlier in the internet’s history? You can use www.mydomain.com if you like, or simply mydomain.com. Note however that when you set up a “mydomain.com” web site using Virtualmin, Virtualmin automatically makes a “www.mydomain.com” alias, so that either will resolve to the same web site code. If you want DNS to work with both, you’ll need to add two A records: one for “@” and one for “www”.

Hairpin NAT

For most people, that’s it. You should now be able to create web sites on your server, use DNS or DDNS to broadcast those web sites to the outside world, and receive visitors from all around the world. But there’s a very significant “gotcha” with some routers, when you’re attempting to browse your web site from your LAN. If you’re on the same physical network as your web server, when you enter the web address in your browser, it should attempt to browse to your network’s external IP address. In other words, your web query goes out and attempts to come back in again. This out-and-back-again behaviour is known as “hairpin NAT” and not all routers can handle it.

If you’re one of the unlucky ones and you don’t want to swap your router for a better one, you have two main options. Technically, the best option is to run your own private DNS server (which you can do on the same server), but that is complicated and a bit of a hassle. If you’re only browsing from a single PC, the quick-and-dirty solution is to edit your hosts file. This file is located somewhere like “C:\Windows\System32\drivers\etc\hosts”. There are a lot of resources on the internet that will explain to you more about this file, but let me help you over one little hurdle: if you’re using Windows Vista or later, you need to edit this file as an Administrator. To do that, right-click Notepad and choose “Run as Administrator”. Then open the hosts file from within Notepad. You’ll need to change the files filter to “*.* (All files)”.

The default hosts file contains details about the format of entries. Suffice it to say that you’ll put here the DNS name of your web site and the internal LAN IP address of your web server. Your web browser, when looking up an IP address, should check the hosts file first, so in this scenario, you won’t be going out and back again. If you then take that computer somewhere else (e.g. it’s a laptop and you’ve taken it to work), you’ll need to remove the relevant hosts file entries, in order to browse to the external IP address of your network.


Networking is a complicated area and I have only scratched the surface here, in an effort to get most people up and running quickly. By all means if you’re stuck at a particular point, post a comment and, time permitting, I’ll try to point you in the right direction. In the meantime, stay tuned for part 4 of this series in which I will provide some initial pointers on building your own web site.

Cables image copyright © Pascal Charest, licensed under Creative Commons. Used with permission.

Easily host your own web sites | part 2: server

Contents


If you followed part 1 of this series, you’ll now have a physical machine ready for installation of your new server operating system and applications. In this post, part 2 of the series, I’ll be focusing on the operating system and one application – the application which will provision your websites.

Operating system

ubuntu logoAlthough you’ll most likely be very familiar with Microsoft Windows or Apple’s OS X, we will be using Linux to power this server. Linux is an operating system well suited to web servers, but most compellingly, you can freely download it and don’t need to worry about costs or client/device access licences.

Note: some people prefer you to refer to the operating system as “GNU/Linux”. I am not promoting any particular viewpoint here; I simply feel this article would become even more cumbersome to read if I wrote “GNU/Linux” all the time. I shall stick to “Linux”, which is also the term with which most people are familiar, for better or worse.

There are many, many flavours (“distributions”) of Linux available. I’ve been through this loop quite a few times, so let me save you some time and point you in the direction of a distribution that I have found most suitable for this “home web server” scenario: Ubuntu. There are several editions of Ubuntu, but the most appropriate is Ubuntu Server, which you can download here. Make sure you download the 32-bit version, if you’re not running a 64-bit machine.

Why Ubuntu?

There are many reasons for choosing Ubuntu for this project, but here are my top five:

  1. The apache2-suexec-custom package: This is delving into detail very early, so for now just take my word for it that this Ubuntu package is going to make your life a lot easier when we set up Virtualmin (see below). Other Linux distributions lack this package and you end up having to recompile the Apache server to enable virtual hosting. For most readers, that’s an unnecessary complication.
  2. Community support: at the time of writing, the official Ubuntu forums had heading towards two million members. That translates into a huge pool of knowledgeable people willing to help with problems.
  3. Heritage: Ubuntu is based on Debian, one of the granddaddy Linux distributions. Ubuntu’s mission was to bring Linux into the mainstream, making it more user-friendly, without sacrificing the features that make Debian such a robust and stable system.
  4. Long-term support: each iteration of Ubuntu has an “LTS” version, confirming it will remain in support for five years from the date of first release. Most significantly, this means that one can expect security patches to be available for a predictable period of time.
  5. Simplicity: installing Ubuntu is usually very easy. It detects an extremely wide range of hardware and makes many sensible choices for the end user, which results in a straightforward installation process. Once installed, many packages are readily available to enhance and extend the features of the server.

Installation

So, you’ve downloaded Ubuntu Server and burnt your installation DVDs. There are many places on the internet that take you through the installation of your Ubuntu server. Here, I will simply concentrate on the critical settings and options that will ensure your server is ready to host websites from day 1.

Network configuration

The first “gotcha” relates to networking. If you connect your server to a network on which a DHCP server is running, Ubuntu will automatically use DHCP. You could later change this, but it’s much easier to get it right during the installation. With 12.04, very early on in the installation wizard, and just after DHCP configuration has taken place, you’ll see a dialogue box headed “Configure the network”. This has a “Hostname” field and a “Go Back” button. After you’ve entered your desired host name, I recommend choosing the “Go Back” button. This in fact takes you to a screen you’ve not seen previously in the installation, which contains an option to configure the network manually. Choose that option and then give your server a static IP address on your LAN.

Software selection

At The basic software selection wizard screen, make sure you choose at least the following:

  • OpenSSH server
  • LAMP server
  • Mail server

You may also wish to install the PostgreSQL database, if you’re supporting that in addition to, or instead of MySQL.

Postfix (email server)

At the Postfix Configuration screen, you probably want to choose “Internet with smarthost”. We’ll deal with the detailed configuration of Postfix later. When asked for the SMTP relay host, enter the address of the mail server that will accept outward-bound email from this system (your ISP’s mail server, or an Exchange server already on your LAN, for example).

Post-installation

You now have a functioning server. At this point you should be able to disconnect the mouse, keyboard and monitor you borrowed previously and connect via SSH. When I need a command line/shell, I use PuTTY and PuTTY Connection Manager to connect to most of the Linux and Unix servers I administer, but any SSH-capable terminal emulator will do.

root

At the moment, you can’t log on as the all-powerful root user. You’re supposed to use sudo as a non-privileged user, to avoid inadvertent damage to your server. But the way we’ll be using this server, you’ll quickly find that a pain. So log in as the user you created during installation and then issue the following command:
sudo passwd root
You can set a password for root and log on as root from that point onwards.

Note: I don’t recommend doing this on a production server or in any environment where security is a particular concern.

Package manager

Ubuntu comes with a lot (I mean a phenomenal quantity) of “packages” (programs) that you can freely install to enhance the functionality of your server. We use a package manager to add, remove and upgrade these packages.

This server does not have a graphical user interface installed, but that doesn’t mean we have to use a command line to install every package we want, or to process updates. Start by issuing this command:
apt-get upgrade -y; apt-get install aptitude -y
You may need to reboot your server after this, particularly if the Linux kernel (the core of your operating system) has been upgraded during this process.

You now have a console based semi-graphical package manager at your disposal called aptitude. At the command line, issue the following command:
aptitude
Once aptitude has loaded, take some time to explore the help facility to learn how to use it (press ?).

Packages to install

I recommend installing at least the following packages (several of which will be needed by Webmin – see below):

  • apache2-suexec-custom
  • any php5 modules you think you’ll need (search for “php5”)
  • a console-based text editor; I prefer vim, which should already be installed, but “joe” has a much gentler learning curve
  • libnet-ssleay-perl
  • libauthen-pam-perl
  • libio-pty-perl
  • apt-show-versions
  • libapt-pkg-perl
  • bind9
  • webalizer

How to find stuff

Most Linux distributions are configured with “locate”. This command helps you to track down files by name. Typically, the locate database updates overnight. You can force an update by running the command “updatedb”. You could then use that (for example) to find the file we’re about to edit in the next step.

Configure the apache2-suexec-custom package

On my test system, the suexec configuration file is at /etc/apache2/suexec/www-data. The first line probably contains the text “/var/www”. We need to be able to serve up web pages under the /home directory, so change that line to “/home” and save the file.

Enable additional Apache modules

Virtualmin depends on some otherwise optional modules for Apache (the web server program). Enable these modules and force Apache to load them with the following commands:
a2enmod suexec
a2enmod actions
service apache2 restart

Virtual hosting application: Virtualmin

Virtualmin logoThere are a few virtual hosting applications around – software that helps you to host multiple websites on a single server, without having to edit Apache’s config files by hand. My favourite application is Virtualmin, which has community and professional variants. The free community variant is more than sufficient for our purpose.

Virtualmin is actually a module for the server management software, Webmin, so we’ll be installing both. Again, Webmin is one of many tools available, which allow you to administer your server remotely. Once Webmin is installed, you’ll very rarely need to use the command line.

Install Webmin

The instructions for downloading and installing Webmin are here. If the reference to Debian confuses you, remember that Ubuntu is based on Debian. You can download the software anywhere you like on your server, but I’ve taken to creating a folder at /root/installed-packages, so I can easily track what I’ve installed outside the package management system. On my test system, I’m issuing the following commands:
mkdir /root/installed-packages
cd /root/installed-packages
wget http://prdownloads.sourceforge.net/webadmin/webmin_1.610_all.deb
dpkg --install webmin_1.610_all.deb

Note: you can use the Webmin APT repository to install Webmin and instructions are on the same page, but it’s a bit more fiddly.

Install Virtualmin

The Webmin site includes some instructions for installing Virtualmin here, but I think there’s a better way. Copy the link from that page for the “Virtualmin module in Webmin format” – ignore the Debian module. Then, browse to the Webmin interface (https://your.server’s.ip.address:10000) and go to Webmin–>Webmin Configuration–>Webmin Modules. Select the radio button next to “From ftp or http URL” and paste the link you copied into the field. Then click “Install Module”. Do the same for the link for the “Virtualmin theme in Webmin format”.

After the install, go to Webmin–>Webmin Configuration–>Webmin Themes. From the drop-down box, choose “Virtualmin framed theme” and click “Change”. Ignore the “Post-Installation Wizard” for now, and hit F5 to refresh your browser and use the Virtualmin theme for Webmin.

Post-installation Wizard

You may need to go through this wizard several times, fixing issues as you go along. Here are the things I needed to fix. After fixing each item, go back to the wizard. Eventually you’ll be using the “Check Configuration” wizard; a kind of post-post-install routine…

MySQL

You need to enter your MySQL username and password.

DNS zones

We won’t be running a DNS server initially, so in the wizard, click “Skip check for resolvability”. Within Virtualmin’s “Features and Plugins” page, uncheck “BIND DNS domain”.

Postfix

You’ll probably see an error message when you run the “Check configuration” wizard that says “A problem was found with your Postfix virtual maps : No map sources were found in the Postfix configuration”. To fix this, follow the “Postfix configuration” link, then click the “Virtual Domains” icon. Set “Domain mapping lookup tables” to “hash:/etc/postfix/virtual”.

Apache

I’ve found that Virtualmin can complain that certain modules (which we enabled earlier) are not available. Fix this by going to: Webmin–>Servers–>Apache Webserver–>Configure Apache Modules. You shouldn’t need to change anything – just click the “Enable Selected Modules” button.

Deal with this error message: “Apache configuration file /etc/apache2/mods-enabled/php5.conf contains SetHandler lines that prevent PHP from running with domain owner permissions. These lines must be removed.” as follows by editing the file in question (using your command-line text editor installed earlier). You’ll probably see some text along the following lines:

<FilesMatch "\.ph(p3?|tml)$">
SetHandler application/x-httpd-php
</FilesMatch>
<FilesMatch "\.phps$">
SetHandler application/x-httpd-php-source
</FilesMatch>

Delete that in its entirety and save the file. Then issue the following command:
service apache2 reload

Final check/refresh configuration

Your final “Check configuration” will run successfully, but may issue two warnings: one about the IP address of the web server and another about FTP access. You can safely ignore both of these.

Next steps

Great! Now your server is ready to host multiple websites. Explore the “Create Virtual Server” link within Virtualmin to see the options now available to you.

Of course you want these websites to be accessible from the internet, so in part 3, I’ll deal with the hurdles you’ll need to jump before you can inflict your new web sites on the unsuspecting public. Until then, happy tinkering!

Ubuntu logo copyright © Canonical Ltd. Virtualmin logo copyright © Virtualmin, Inc. All rights acknowledged.

Easily host your own web sites | part 1: hardware

Contents


Web serversThere is no shortage of cheap web hosting companies, offering packages from pennies per month. If you’re looking for more control and flexibility and you’re not planning to host a mission-critical eCommerce website though, self-hosting is quite rewarding and not as difficult as it sounds.

This is the first in a series of posts where I outline some options for becoming a small-time web host. In this article, I discuss the hardware you’re going to need.


You’re best off coming to terms with the idea that you’ll be leaving your new web server switched on 24 hours a day. Whether we admit it or not, ultimately we’re hoping that whatever websites we host will become popular and reach a worldwide audience. That means your server must theoretically be available and reachable round the clock. In my view, as a bare minimum you should have:

  • A computer. Doesn’t need to be fancy or modern. Preferably less than five years old, but it won’t be a deal breaker, if not. We’ll call this box “the server”. It won’t need a monitor, keyboard and mouse attached full-time. You’ll only need to borrow these for the initial setup. We’ll be connecting to the server remotely as soon as possible and from that point onwards, we can run this server “headless” (i.e. with nothing attached other than the UPS).
  • An uninterruptible power supply (UPS). Your UPS protects your server from the vagaries of your household power supply. It doesn’t do a server much good to lose power suddenly. A UPS is a battery backup which can enable your server to shut down gracefully in the event of a power cut. The better UPSes also clean the incoming power, protecting the server from “bucks” and “boosts”.
  • A router capable of port forwarding. Shouldn’t be too much of a problem – most routers can do this. I’m assuming here that you’re on some kind of “always on” internet connection like broadband or cable.

The server

Unless you’re hosting a lot of websites, getting a lot of traffic, or building very complex websites, this machine won’t need much power. And it doesn’t need to break the bank. You could consider using a Raspberry Pi for example (although that will be harder to set up than an ordinary PC). I’ve used second-hand computers, old laptops and all sorts to host web sites. Currently I have an old IBM xSeries tower server in my loft, but you probably don’t need that kind of power or resilience when you start out with web hosting. You could even just use a virtual machine on your home computer, if you’re happy to leave that switched on all the time. I’m going to assume that you’re using a dedicated machine though. I’m also going to assume that we’ll be using typical PC hardware; you can self-host with Mac hardware quite easily, but that’s not where the majority of my experience lies.

The UPS

We’re going to be running Linux on this server, so the primary requirement is that your UPS is supported by Linux. APC UPSes used to be supported under Linux by the apcupsd program (a so-called ‘daemon’, which runs continuously on the server). In recent years however, APC short-sightedly changed their UPS range so they could no longer communicate with apcupsd (to the considerable anguish of the Linux community). Your best bet is either to source an old APC UPS – you can still easily find the replacement batteries – or buy any Eaton UPS. Eaton UPSes are supported by the Network UPS Tools daemon and I know that Eaton is commercially committed to the Linux platform for the foreseeable future.

The router

As long as your router supports port forwarding, you’ll be okay. A lot of routers are capable of being upgraded with aftermarket “firmwares” to provide previously unavailable capabilities. This is a good way of obtaining a near enterprise-class router on the cheap. Probably the most famous of these firmwares is DD-WRT. Have a look on that website for a list of supported routers if you want to go down this route (ahem).


So that’s it for this post. Short and sweet. While you’re off sourcing your hardware, I’ll be hard at work thinking about part 2, in which we’ll start to set up your new server’s operating system and supporting applications. See you soon!

Servers image copyright © Widjaya Ivan, licensed under Creative Commons. Used with permission.

Rackspace API for CodeIgniter

Logo_lockup_version-2 SPOTRackspace is a great email hosting company, providing, amongst other things, a handy API for creating bespoke email solutions. The exercise of integrating that API into your application is of course left to the end user. I’ve spent some time working on a Rackspace API library for the PHP programming framework, CodeIgniter. This is not functionally complete – I have only implemented the interfaces that I needed – but it should provide a useful springboard for your own projects.

Configuration

In /system/application/config/RackspaceAPI.php:

<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');

$config['user_key']   = 'your user key';
$config['secret_key']     = 'your secret key';
$config['user_agent']     = 'name of your app';
$config['api_version']    = 'v0'; // amend if necessary
$config['rackspace_host'] = 'api.emailsrvr.com'; // amend if necessary

/* End of file RackspaceAPI.php */
/* Location: ./system/application/config/RackspaceAPI.php */

Library

<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
 * Uses curl and pecl_http
 */
class Rackspace_API {
    
  /**
   * Store recent http_message
   * @var object
   */
  protected $_http_message;
  
  /**
    * CI object
    * @var object
    */
  protected $_ci;

  /**
   * Rackspace config items
   */
  protected $_user_key;
  protected $_secret_key;
  protected $_user_agent;
  protected $_api_version;
  protected $_rackspace_host;
  
  function __construct() {
    $this->_ci =& get_instance();
    $this->_ci->config->load('RackspaceAPI', TRUE);
    $this->_user_key = $this->_ci->config->item('user_key', 'RackspaceAPI');
    $this->_secret_key = $this->_ci->config->item('secret_key', 'RackspaceAPI');
    $this->_user_agent = $this->_ci->config->item('user_agent', 'RackspaceAPI');
    $this->_api_version = $this->_ci->config->item('api_version', 'RackspaceAPI');
    $this->_rackspace_host = $this->_ci->config->item('rackspace_host', 'RackspaceAPI');
  }


  /**
   * Get info about a domain
   * @param string $domain
   * @return stdClass Object ( 'error'  => bool,
   *                           'result' => string (error message) | stdClass Object
   */
  public function getDomainInfo($domain) {
    return $this->genericGet('/customers/me/domains/'.$domain);
  }

  
  /**
   * Get all domain names
   * @return stdClass Object ( 'error'  => bool,
   *                           'result' => string (error message) | array (domains)
   */
  public function getDomains() {
    $obj = $this->genericGet('/customers/me/domains');
    if(!$obj->error){
      // Reformat into an array of domains
      foreach($obj->result->domains as $domain) {
        $domains[]=$domain->name;
      }
      $obj->result = $domains;
    }
    return $obj;
  }


  /**
   * Get info about a mailbox ($domain@$id)
   * @param string $domain
   * @param string $id
   * @return stdClass Object ( 'error'  => bool,
   *                           'result' => string (error message) | stdClass Object
   */
  public function getMailboxInfo($domain, $id) {
    return $this->genericGet('/customers/me/domains/'.$domain.'/rs/mailboxes/'.$id);
  }
   
 
  /**
   * Used by Get functions above - generalised use case
   * @param string $url - see the API; constructed by the calling function
   * @return stdClass Object ( 'error'  => bool,
   *                           'result' => string (error message) | stdClass Object
   */
  private function genericGet($url) {
    $this->get(
        $url,
        'application/json');
    if($this->_http_message->getResponseCode() == 200) {
      // Call worked.  JSON is missing enclosing brackets, apparently needed by json_decode
      $json = '['.$this->_http_message->getBody().']';
      if(is_string($json)) {
        $obj = json_decode($json);
        $result->error = false;
        $result->result = $obj[0];
      } else {
        // JSON failure
        $result->error = true;
        $result->result = 'Failed to parse JSON';
      }
    } else {
      // API call failed
      $result->error = true;
      $result->result = $this->_http_message->getHeader("x-error-message");
    }
    return $result;
  }
  

  /**
   * Create a mailbox ($domain@$id)
   * @param string $domain
   * @param string $id
   * @param string $first: First name
   * @param string $last: Last name
   * @param string $name: Display as
   * @param string $office: Name of office/profit centre
   * @param string $locno: Office/profit centre number
   * @param string $password
   * @param string $fwd: comma-separated forwarding address(es) - max 4 off domain
   * @param string $save: save forwarded email - 'true' or 'false'
   * saveForwardedEmail
   * @return stdClass Object ( 'error'  => bool,
   *                           'result' => string (error message) | stdClass Object
   */
  public function addMailbox($domain, $id, $first, $last, $name, $office,
          $locno, $password, $fwd, $save='true') {
    $fields = array(
        'password' => $password, 
        'emailForwardingAddresses' => $fwd,
        'firstName' => $first,
        'lastName' => $last,
        'displayName' => $name,
        'saveForwardedEmail' => $save,
        'organization' => $office,
        'organizationUnit' => $locno);
    return $this->genericPost( '/customers/me/domains/'.$domain.'/rs/mailboxes/'.$id, $fields);
  }


  /**
   * Used by Post functions above - generalised use case
   * Note: Rackspace API suggests use POST to add, PUT to edit
   * @param string $url - see the API; constructed by the calling function
   * @param array $fields - data to be POSTed
   * @return stdClass Object ( 'error'  => bool,
   *                           'result' => string (error message) | stdClass Object
   */
  private function genericPost($url, $fields) {
    $this->post(
        $url,
        $fields,
        'application/json');
    if($this->_http_message->getResponseCode() == 200) {
      $result->error = false;
      $result->result = $this->_http_message->getBody();
    } else {
      // API call failed
      $result->error = true;
      $result->result = $this->_http_message->getHeader("x-error-message");
    }
    return $result;
  }


  /**
   * Edit user's forwarding
   * @param string $domain
   * @param string $id
   * @param string $fwd: comma-separated forwarding address(es) - max 4 off domain
   * @return stdClass Object ( 'error'  => bool,
   *                           'result' => string (error message) | stdClass Object
   */
  public function changeForwarding($domain, $id, $fwd) {
    $fields = array(
        'emailForwardingAddresses' => $fwd
        );
    return $this->genericPut( '/customers/me/domains/'.$domain.'/rs/mailboxes/'.$id, $fields);
  }
  
    
  /**
   * Edit user's location
   * @param string $domain
   * @param string $id
   * @param string $office: Name of office/profit centre
   * @param string $locno: Office/profit centre number
   * @return stdClass Object ( 'error'  => bool,
   *                           'result' => string (error message) | stdClass Object
   */
  public function changeLocation($domain, $id, $office, $locno) {
    $fields = array(
        'organization' => $office,
        'organizationUnit' => $locno);
    return $this->genericPut( '/customers/me/domains/'.$domain.'/rs/mailboxes/'.$id, $fields);
  }
  
    
  /**
   * Edit user's name
   * @param string $domain
   * @param string $id
   * @param string $first: First name
   * @param string $last: Last name
   * @param string $name: Display as
   * @return stdClass Object ( 'error'  => bool,
   *                           'result' => string (error message) | stdClass Object
   */
  public function changeName($domain, $id, $first, $last, $name) {
    $fields = array(
        'firstName' => $first,
        'lastName' => $last,
        'displayName' => $name);
    return $this->genericPut( '/customers/me/domains/'.$domain.'/rs/mailboxes/'.$id, $fields);
  }
  
    
  /**
   * Edit user's password
   * @param string $domain
   * @param string $id
   * @param string $password
   * @return stdClass Object ( 'error'  => bool,
   *                           'result' => string (error message) | stdClass Object
   */
  public function changePassword($domain, $id, $password) {
    $fields = array(
        'password' => $password);
    return $this->genericPut( '/customers/me/domains/'.$domain.'/rs/mailboxes/'.$id, $fields);
  }
  
    
  /**
   * Used by Put functions above - generalised use case
   * Note: Rackspace API suggests use PUT to edit, POST to add
   * @param string $url - see the API; constructed by the calling function
   * @param array $fields - data to be PUT
   * @return stdClass Object ( 'error'  => bool,
   *                           'result' => string (error message) | stdClass Object
   */
  private function genericPut($url, $fields) {
    $this->put(
        $url,
        $fields);
    if($this->_http_message->getResponseCode() == 200) {
      $result->error = false;
      $result->result = $this->_http_message->getBody();
    } else {
      // API call failed
      $result->error = true;
      $result->result = $this->_http_message->getHeader("x-error-message");
    }
    return $result;
  }


  /**
   * Delete a mailbox
   * @param string $domain
   * @param string $id
   * @return stdClass Object ( 'error'  => bool,
   *                           'result' => string (error message) | stdClass Object
   */
  public function deleteMailbox($domain, $id) {
    return $this->genericDelete("/customers/me/domains/$domain/rs/mailboxes/$id");
  }
  
  
  /**
   * Used by Get functions above - generalised use case
   * @param string $url - see the API; constructed by the calling function
   * @return stdClass Object ( 'error'  => bool,
   *                           ['result' => string (error message)]
   */
  private function genericDelete($url) {
    $this->delete($url);
    if($this->_http_message->getResponseCode() == 200) {
      // Call worked.
      $result->error = false;
    } else {
      if($this->_http_message->getResponseCode() == 500) {
        // Internal server error
        $result->error = true;
        $result->result = 'An internal server error occurred deleting  object.  Url: '.$url;
      } else {
        // API call failed
        $result->error = true;
        $result->result = $this->_http_message->getHeader("x-error-message");
        
      }
    }
    return $result;
  }
  

  
  // The remainder of this file is mostly lifted from Rackspace's examples: http://api-wiki.apps.rackspace.com/api-wiki/index.php/PHP_Examples_(Rest_API)
  private function get($url_string, $format) {
      $headers = array("Accept: $format");
      $curl_session = self::construct_session($url_string, $headers);
      $this->_http_message = self::send_request($curl_session);
  }

  private function post($url_string, $fields, $format) {
      $headers = array("Accept: $format");
      $curl_session = self::construct_session($url_string, $headers);
      curl_setopt($curl_session, CURLOPT_POST, true);
      curl_setopt($curl_session, CURLOPT_POSTFIELDS, $fields);
      $this->_http_message = self::send_request($curl_session);
  }

  private function put($url_string, $fields) {
      $curl_session = self::construct_session($url_string, array());
      curl_setopt($curl_session, CURLOPT_CUSTOMREQUEST, 'PUT');
      curl_setopt($curl_session, CURLOPT_POSTFIELDS, $fields);
      $this->_http_message = self::send_request($curl_session);
  }
  
  private function delete($url_string) {
      $curl_session = self::construct_session($url_string, array());
      curl_setopt($curl_session, CURLOPT_CUSTOMREQUEST, 'DELETE');
      $this->_http_message = self::send_request($curl_session);
  }

  private function send_request($curl_session) {
      $response = curl_exec($curl_session);
      curl_close($curl_session);
      /* Reponse string may contain two HTTP sessions, if there was an initial
         "HTTP/1.1 100 Continue" response.  So strip that first response out.  Eg:
                  HTTP/1.1 100 Continue
                  Via: 1.1 [proxy]

                  HTTP/1.1 400 Bad Request
                  Via: 1.1 [proxy]
                  Connection: Keep-Alive
                  Proxy-Connection: Keep-Alive      
                  ...     
       * 
       */
      $response = preg_replace('|HTTP/1.1 100.*HTTP/1.1|isU', 'HTTP/1.1', $response);
      return new HttpMessage($response);
  }

  private function construct_session($url_string, $existing_headers) {
      $headers = array_merge(
              self::authorization_headers(), $existing_headers);
      $url = self::construct_uri($url_string);
      $curl_session = curl_init($url);
      curl_setopt($curl_session, CURLOPT_HEADER, true);
      curl_setopt($curl_session, CURLOPT_HTTPHEADER, $headers);
      curl_setopt($curl_session, CURLOPT_RETURNTRANSFER, true);
      return $curl_session;
  }

  private function authorization_headers() {
      $time_stamp = date('YmdHis');
      $data_to_sign = $this->_user_key . $this->_user_agent .
          $time_stamp. $this->_secret_key;
      $signature = base64_encode(sha1($data_to_sign, true));
      $headers = array();
      $headers[] = "User-Agent: " . $this->_user_agent;
      $headers[] = 'X-Api-Signature: ' .
          $this->_user_key . ":$time_stamp:$signature";
      return $headers;
  }

  private function construct_uri($url_string) {
      $url = 'http://' .  $this->_rackspace_host . '/' . $this->_api_version . $url_string;
      return $url;
  }
}

?>

Example

Example usage:

function testRackspace() {
    $this->load->library('Rackspace_API');
    $client = new Rackspace_API();
    $obj = $client->getMailboxInfo('somedomain.com', 'test.user');
    if($obj->error) {
      echo 'Error: '.$obj->result;
    } else {
      var_dump($obj);
    }
  }

Image copyright © Rackspace Ltd. All rights acknowledged.

Connecting to Windows/MSSQL 2008 from Linux/CodeIgniter/PHP

Update: I’ve written a new article, covering CodeIgniter 3 and Ubuntu 14/16. Read it here.

Microsoft SQL Connecting to Microsoft SQL Express 2008 from Linux/PHP is a lot trickier than I expected. These notes are really for my own benefit so I can reproduce the setup, but maybe they’ll help you too. One of the problems is that many existing PHP drivers for MS SQL have difficulty talking to SQL 2008. Here’s a workaround using FreeTDS and ODBC.

My web application is built using CodeIgniter, the PHP application framework. It resides on an Ubuntu Server box, running Apache. Prerequisites on that Ubuntu Server (which I installed using Aptitude, but your favourite package manager will do):

  • unixODBC
  • FreeTDS
  • FreeTDS development package/(header files and libraries)

To my freetds.conf file (in /etc/freetds on my server) I added a section that looks something like this:

[my-server]
host = my.server.local
port = 1433
tds version = 9.0

Note: TDS version 9.0 should work with SQL 2008.

In /etc/odbcinst.ini, add the following ODBC driver (32-bit):

[TDS]
Driver = /usr/lib/odbc/libtdsodbc.so
Description = FreeTDS driver
Setup = /usr/lib/odbc/libtdsS.so

or 64-bit:

[TDS]
Driver = /usr/lib64/libtdsodbc.so
Description = FreeTDS driver
Setup = /usr/lib64/libtdsS.so

(You may need to check the precise location of the driver and setup files.)

And to /etc/odbc.ini, I inserted a DSN similar to the following:

[my-server]
Driver = TDS
Description = My Server
ServerName = my-server
Database = my-db

Generally within CodeIgniter, I am connecting to MySQL databases and that’s how my default connection is configured. I therefore added a second configuration to my database.php configuration file, like this:

$db['my_server']['hostname'] = "dsn=my-server;uid=myusername;pwd=mypassword";
$db['my_server']['username'] = '';
$db['my_server']['password'] = '';
$db['my_server']['database'] = '';
$db['my_server']['dbdriver'] = 'odbc';
$db['my_server']['dbprefix'] = '';
$db['my_server']['pconnect'] = TRUE;
$db['my_server']['db_debug'] = TRUE;
$db['my_server']['cache_on'] = FALSE;
$db['my_server']['cachedir'] = '';
$db['my_server']['char_set'] = 'utf8';
$db['my_server']['dbcollat'] = 'utf8_general_ci';

Now the ODBC driver within CodeIgniter can produce queries that MS SQL doesn’t like. We can fix this with a hack. You really REALLY shouldn’t do it this way (!) but to get things working and as described >here<, I edited the CodeIgniter core file system/database/drivers/odbc_driver.php. The function _from_tables() has a line reading:

return '('.implode(', ', $tables).')';

I changed it to this:

return implode(', ', $tables);

(In other words, we’re removing the spurious parentheses.)

I created a database method m_my_server.php like this:

/**
 * NOTE: We're using a feature-incomplete driver here.  Don't attempt to use
 * CodeIgniter's ActiveRecord Class or num_rows().  Use bare queries instead.
 */
class M_my_server extends Model {

  var $my_server;

  function M_my_server() {
      parent::Model();
      $this->my_server = $this->load->database('my_server', TRUE);
  }

  function get() {
    $query = $this->my_server->query('SELECT TOP(100) * FROM dbo.tblUserSummary');
    $result = $query->result_array();  // note ->num_rows() doesn't work with this driver
    if(count($result) > 0) {
      return $result;
    } else {
      return false;
    }

  }
}

/* End of file m_my_server.php */
/* Location: ./application/models/m_my_server.php */

At the SQL Server end, I set up a new standard SQL user (myusername/mypassword) rather than attempting to get Windows authentication to work (I bet it wouldn’t).

My SQL instance wasn’t listening to TCP/IP connections by default. I fired up SQL Server Configuration Manager and browsed to SQL Server Network Configuration –> Protocols for [my db instance]. Then you have to right-click TCP/IP and click Enable.

With all that in place, the following controller produces successful results:

  function SQLtest() {
    $this->load->model('m_my_server');
    $result = $this->m_my_server->get();
    if($result) {
      print_r($result);
    } else {
      echo 'nada';
    }
    exit;
  }

It’s not ideal; for one thing, bare SQL queries involve learning Microsoft’s particular dialect of SQL (whereas I’m used to MySQL). The tables I’m querying are generated by Microsoft Forefront TMG though, so I’m basically stuck with MSSQL. At least now I can query those tables from my favourite PHP application framework.

Image copyright © Microsoft. Used with permission from Microsoft.

Job Done

Steve Jobs by acaben: http://www.flickr.com/photos/acaben/541420967/sizes/l/in/photostream/Business legend Steve Jobs died late last night. I heard about it through that most modern of news outlets: Facebook. I read the story on my company-supplied iPhone (which, by the way, I didn’t want; I’d rather use the Android that I keep in my drawer – long story).

The death of Steve Jobs follows the release of the much-anticipated iPhone 4S, the successor to the iPhone 4. Perhaps that ‘S’ should stand for ‘swan song’?

This morning, I mentioned Jobs’ death to a friend, who said, “Who’s Steve Jobs?”

Who’s Steve Jobs? I guess if you’re indifferent to technology and design, you could be forgiven for not knowing his name. But whether you’ve heard of him or not, his influence has almost certainly had an impact on you. The number of modern innovations associated to his name is impressive:

  • First successful personal computer with a graphical user interface (the Macintosh)
  • First WWW (World Wide Web) server
  • Pioneering in rich content email
  • Through Pixar, the first entirely CGI film, Toy Story
  • iTunes: simple access to large online catalogue of music, incorporating digital rights management
  • iPod: user-friendly range of media players
  • iPhone: user-friendly smart phone. When asked to recommend a phone to colleagues, I suggest Android for the technophiles and iPhones for the technophobes. The success of this approach tells me everything!
  • iPad: a device that “experts” claimed was superfluous but which has shipped in phenomenal numbers, battering all manufacturers’ competition in the process
  • Obsessively well-designed low voltage power supplies (built in cable management, magnetic quick-release plugs)
  • Multi-touch mouse

Given the number of patents that bear his name, I have inevitably missed some…

I am not an Apple fanboy. In fact one of my favourite sports is baiting Apple fanboys. But I simply can’t help admiring this charismatic man who for so long helmed one of the most successful companies of all time simultaneously bringing design genius and technical excellence to the masses. Steve Jobs, one time living legend, your legacy will live on.

Meanwhile, someone somewhere just patented the iHarp.

Steve Jobs image copyright © Ben Stanfield, licensed under Creative Commons. Used with permission.

Kindle Fire: Pretender to the iPad’s Crown?

Kindle Fire showing InsensateWhen last week I read about the new $199 Kindle Fire released in the States, my first thought was, “This is a loss leader for a content delivery system.” Amazon has recognised it can make so much money out of selling ebooks, it doesn’t need to make a profit on the reading units.

In the Fire, Amazon has produced a unit that can in all important respects compete with most of the other tablets/pads out there, apparently (but not actually) for a fraction of the cost to the consumer. Obviously, Amazon wants to be positioned ready for when that 50:50 ebooks-to-print-books ratio tips to 75%+. And of course it’s not just ebooks, because Amazon can deliver audio, video and apps to the Fire, positioning it squarely in Apple/iTunes territory.

How is Apple going to challenge a $199 contender to the $499 iPad’s crown? I think we can be certain that Apple will respond forcefully – it can’t afford to take a drubbing in its most strategic consumer sector. Nevertheless, this looks to me like a very smart move on Amazon’s part and I’m not sure which (if any) large company is in a position to challenge this move.

It looks like PC Pro agrees with me. What do you think?