Apache, FlightPHP, Javascript, PHP

Setting up FlightPHP to work with Backbonejs

FlightPHP and Backbonejs work well together inherently by design. Even though the two frameworks are written in two different languages and are designed for different tiers in your enterprise application development design, its hard to ignore the ease of integrating the two.. Most Backbone examples you see posted on the Internet involves node.js on the server; which makes sense considering you program both in javascript. However, the most common (and cheapest) hosting on the Internet is shared host PHP hosting. As long as your load is relatively light you can get really great results out of a shared PHP host as the new PHP runs virtually as fast as C++ on the server (takes about 2x the RAM though).


How Backbone works with FlightPHP

Backbone is a REST-centric library. Basically what that means is that you have to set up four endpoints in your FlightPHP index file for every collection of models in your application. With Backbone, you will not be setting up AJAX calls – the library handles all of the traffic cop stuff for you automatically. Using a Backbone model: a save uses a POST if the model does not have an id set on it from a previous save, and uses a PUT to update the record if it does have an id; a get without and id in the path returns the array of JSON objects (used by Backbone.Collection), including an id in the path returns that record; and a model.destroy uses a DELETE verb to indicate a record delete.

Your CRUD functions will map to the GET, POST, PUT, and DELETE HTTP verbs on your server.

/* basic PHP pseudocode structure for handling backbone CRUD functionality */
// Create
Flight::route('POST /rest/somecollectionname', function($id){
    // posting a new record (saving the model)
        // return the object saved with new id created by db
// Read
Flight::route('GET /rest/somecollectionname(/@id)', function($id){
    // getting a collection or a single record if id was provided
        // return an array of javascript objects or a single
        // object if an id was provided
// Update
Flight::route('PUT /rest/somecollectionname/@id', function($id){
    // updating a record (saving a model with an id)
        // return updated object
// Delete
Flight::route('DELETE /rest/somecollectionname/@id', function($id){
    // deleting a record (destroying a model)

Flight also allows you to map these endpoints to a separate class which is probably the best way to go if you are working on an API that’s larger than a couple of endpoints.

Apache issues – something Node users never have to deal with

On some shared hosts PUT and DELETE requests are blocked by default. You’ll get a 403 Denied error on the first PUT or DELETE attempt if these HTTP verbs are blocked. Its, unfortunately, a fact of life that has to be dealt with. Apache allows PUT and DELETE by default, so its probably the way they have their virtual hosts configuration set (usually because of security issues). You can sometimes (probably) get away with a .htaccess directive to overcome this shortfall. Other than that I recommend you contact whatever host you have for a solution.

# try this in .htaccess
# https://gist.github.com/umidjons/9107445
order deny,allow
allow from all

Even if your shared host vehemently denies you access to these verbs, a workaround is relatively easy to script. For instance: add an updateid to your model to indicate an update and handle it as a new record on the client, etc. It shouldn’t come down to that though (a paying customer is a paying customer).

If you are running your own server you should open “/etc/httpd/conf/httpd.conf” as root and look for something that looks like:

<Directory /home/*>
AllowOverride All
Options -MultiViews -Indexes FollowSymlinks IncludesNoExec +Includes
Order allow,deny
Allow from all
Order deny,allow
Deny from all

Anywhere you see “GET POST” just add put and delete : “GET POST PUT DELETE”. Then restart httpd server with “/sbin/service httpd restart” and they should work. Also, here is a link to a good how-to for Apache in case you get confused: https://www3.ntu.edu.sg/home/ehchua/programming/howto/Apache_HowToConfigure.html
Apache Docs

In many cases you might not even need those two verbs. Many people use Backbonejs to display and sort data on the client without ever needing to use PUT or DELETE.

FlightPHP, Geolocation, PHP

A WhatsMyIP GeoIP Map Site with FlightPHP

IP With A Here Map

FlightPHP is a micro-framework I’ve been tinkering around with lately. Written by Mike Cao, Flight is a small framework of eleven files that offers an elegant solution to routing and JSON API development in PHP. Installation is simple and Flights’ only requirement is having PHP 5.3 or higher on your host. FlightPHP is released under the MIT license so it’s legal to use in business endeavors without having to worry about major repercussions.
I feel it’s fair to mention that I also use two bits of functionality other than the framework from external sources in this example. Firstly, I use geoplugin.com and their PHP class for IP geolocation data. Yes, I could have either purchased or obtained a geolocation database from somewhere and pulled all the geographic information from that – I didn’t. The geoplugin site is free and easy to use. Secondly, I use a MIME class written by Robert Widdick for returning the proper headers when handling static file requests. Originally, I started with a limited associate array of file types and their associated header strings which I then used to create the response header for static file responses. I wanted to avoid the added headache of having to add header strings to an array every time I wanted to host a new filetype in the static folder. Robert Widdicks’ package works for nearly every different file type you would want to host and is released under the GNU GPL license.

IP With A Google Map

Why I chose to use the FlightPHP micro-framework

In any programming language the laconic solution is the most desirable. Thus, using any framework in your project becomes a balancing act of sorts. The bigger the framework, the larger the overhead. The more file reads a framework makes on the server, the slower the response. Too much server overhead lowers usage capacity. Using a framework that gives you desired functionality without excess becomes more and more important than in the past as the Internet grows to host single page web apps that demand more and more from the server.
The term “micro-framework” was alien to me until a few weeks ago. “How much functionality can a really small framework have and still be called a framework?” In my opinion, a framework in programming is a package of code that forms the underlying structure or interface for your application development that abstracts commonly needed functionality away to a simpler or more convenient interface. Or put another way; a bunch of code that makes programming an application a bit easier. Looking at the work “micro” prefixed to the definition made my expectations grow spartan.
I found that micro-frameworks are more task oriented and less one-size-fits-all from what would be considered a normal framework, so finding the right one for your particular project takes a bit of research. Faced with the myriad of micro-frameworks available on the Internet a programmer should choose one based on the common aspects needed in the particular project being planned. Most of the time you’ll find one that gives you almost all of the basic boilerplate functionality you desire. Depending on the feature needed, you could either fill in the gaps your own programming skills or find an external package. Either way, you’ll probably wind up with a smaller server footprint that loads faster and takes up less memory.
I came across Flight while looking for a smaller framework that I could use for an addon idea I came up with. If any, the only framework I would use for a new project is CodeIgniter. CodeIgniter is great and I really like using it, don’t misunderstand me; but its overhead seemed unnecessary for the task at hand. The addon idea I had would be creating mainly JSON traffic; entering data and retrieving it as needed. If it wasn’t for the data models, most of CodeIgniter would just be overhead. I desired a smaller framework for the backend layer that loads faster and has less overhead. I chose Flight because I like the way it ‘felt’ while coding with it. I eventually abandoned my idea, but I will most likely continue to use FlightPHP in future projects and experiments as its design naturally lends itself to single-page web application development.

IP With Wikimapia Map

Using FlightPHP

Using FlightPHP is as simple as using a global static class to call functionality. You extend Flight by mapping your global variables, functions and classes to that class. I really like the concept of the framework and its functionality here in this Flight object and plain PHP for the rest. Scripting an application using Flight just seems more intuitive and clean to me as a result.
In Flight you have no Controllers, Flight just handles the routing and response. All the routes and route handlers in your application are defined in the index.php file. Defining routing paths in your application is as simple as calling the static ‘route’ method of the global Flight object with the path string and a callback. Path strings can contain wildcards, regular expressions, named parameters, optional parameters, and HTTP verbs. Route paths defined like this in Flight are, in structure, a bit similar to the way you would code a node router in javascript. However, Flight is very flexible with regard to routing, you can map route responses to static functions and class methods.
There is also another routing feature that reminded me of node: if you return true from any route handler, execution is passed to the next matching route handler. That makes any route handler that returns true “middleware” of sorts, except that it is more explicit in functionality as it only executes for the route defined. Handling common request evaluation functionality, user agent parsing for instance, in a wildcard route defined before any of the other routes just seems more elegant and intuitive to me than calling a separate function at the beginning of every route handler.
Although FlightPHP uses the concept of views, there is no template engine included with the framework. By default, Flight uses straight PHP to render template output. However, you could use any template engine you desire by registering a class and overriding the ‘render’ function if the need arises. To render any PHP template you call the static ‘render’ function on the global Flight object passing it a path string and an associate array of data that will resolve as variables in the template during the rendering process. If you want to use partial views and a layout template that’s easy too. Just call ‘render’ on the partial views before the layout template render call with a third string representing the variable name that will hold the results to echo out during the layout render call. Really simple and elegant.
Request information is handled by Flight as well. If you need to access information about the request: request cookies, GET/POST variables etc.., they are contained in an object retrieved through the Flight::request method. Aggregate data properties of the returned request object are conveniently accessible using either object or array syntax.
Error handling, caching, redirects, JSON and JSONP responses are all handled through the FlightPHP object also. For more information on using FlightPHP its well documented here.
All in all, FlightPHP gives you a pleasant beginning to any project you may have in mind without the burden of too much overhead.

The Project Layout

The No Script Version

A Few Notes About My Code

Going over each step of the code step by step seems redundant to me. There are, however, a few notes I felt should be mentioned before gazing upon the code. Most programmers can figure out exactly what is going on in the code just by looking at the index.php file. As I stated previously, everything goes through the Flight object. There is no having to remember a bunch of different classes to extend in Flight.

About how the external resources are used

I created an includes folder to keep all of the external resources separate from the framework. As I mentioned previously, I used an external class for handling MIME types on static file requests. I didn’t register that class in the global object as not every request is a static file request. I did, however map the ‘getStaticFile’ method in the global object where I include it as necessary lazily in an effort to conserve resources. I also didn’t register the geoplugin class globally in the FlightPHP object as it is only used once per ip. All subsequent requests after the initial geoplugin service request for IP geolocation data are retrieved from the SQLite database – not the geoplugin web service.

About SQLite

I used SQLite and PDO instead of MySQL and PDO in this example for convenience. SQLite is included with PHP, so there is no need to configure a database if you want to try the code out on XAMPP. Another benefit of using SQLite is that SELECT queries are executed faster than MySQL queries; so its performant. The SQL used in the table creation in this example technically doesn’t have to be as verbose as it is. I could have just used: “CREATE TABLE(and a bunch of column name separated by commas)” and the table would have been created, SQLite would have figured the rest out. It’s fairly easy to translate the table creation SQL to MySQL the way it is now if you have to. Also, using PDO isn’t the same as using the SQLite engine; don’t count on any of the SQLite engine functions documented in the PHP documentation in other words. When using PDO you can only use PDO functions. Although not required in this instance, I find that relying totally on PHP for the grunt work that SQLite functions provide makes my code more portable. The down side of using SQLite is that it does not scale up at all. For an example on initializing MySQL with PDO and FlightPHP check out the FlightPHP documentation.

About Serving Static Files

Setting up Flight to serve up static file was simple. I first set a global variable in Flight for the static folder path using the same format as Flight uses for the views folder for convenience. I then mapped a global function in Flight to handle returning the file. I could have, technically, set up static file retrieval through the htaccess file on the server. I’ve had my share of problems, however, with htaccess files hosted on shared hosting in the past; they seem to differ in small ways from host to host. I actually like having all requests run through the index.php file better as it lends to greater flexibility in my coding.
The route used for serving static files is matched to the root folder level with a wildcard. Based on the premise that the routes are tested in order, from top to bottom, other dynamic routes can be included if needed. When adding routes with this setup you should consider at least one folder level route prefix to make route matching easier. Or, you could change the code to serve all of the static resources from a different folder route instead of root; just remember create a sub-folder structure in the static folder to match the route as the getStaticFile function operates off of the request URL path.

About The Maps

The three maps used in this example are displayed in an iframe. The google map is the only one of the three that require an API key for embedding. The way its coded, if you don’t include an embed key from google the google map will not be displayed. The maps lacks a bubble dialog on the exact IP location as a result. Considering that IP geolocation is an approximation of the users location, it really doesn’t matter. If you want to display a bubble dialog on the maps you’ll have to get a javascript API key for each of the three and do the javascript yourself.
Initially, I wanted to leave javascript out of this example, keeping the focus on using the PHP framework for HTML site development in this blog post. Since the maps displayed in the iframe require javascript to be enabled, and I had nothing but problems sizing the map iframe properly with CSS, I used javascript to accomplish the task. The noscript version of the page has no javascript and the center pane is sized and placed with CSS as its supposed to be.

About the CSS

I hacked out my own CSS for this project. Because I used the box-sizing feature of CSS3, this example only supports modern browsers. Furthermore, I didn’t use any media queries in the CSS. Horizontal scrolling starts at 700 pixels; so the outputted page is less than ideal for smaller screens. In reflection, perhaps I should have used Bootstrap and made it look like everything else on the Internet.

About setting this up and actually using this

I tried to make it easy to set this up for hosting by mapping global variable towards the top of the index.php file. Most of the variables are self explanatory. The title prefix variable is what is displayed in the upper left corner of the page (branding) and in the title. That links to whatever overrides the base URL variable. As I stated previously, if you don’t include a google embed key the google map will not be shown; its coded to show the wikimapia map instead if the default map type is still set to google (which is based on a google map). If you use google analytics, uncomment and set the UA key and the script will inject the boilerplate code at the bottom of the HTML. Every hit is recorded in the database in case you want to roll your own later.

A Birdhouse or a Townhome

Technically, this is a WhatsMyIPSite too.

<?php echo “<h1>Your IP is: “ . $_SERVER[REMOTE_ADDR] . “</h1>”; ?>


// http://flightphp.com/
require 'flight/Flight.php';
// http://www.geoplugin.com/webservices/php
// require 'includes/geoplugin.class.php';
// http://www.phpclasses.org/package/5082-PHP-Determine-the-MIME-type-of-a-file.html
// require 'includes/phpmimetypeclass-2010-10-19/class.mime.php';

// http://firephp.org/
//require 'includes/FirePHP.class.php';
//Flight::set('isdebug', false);
//$isdebug = Flight::get('isdebug');
//if($isdebug){$firephp = FirePHP::getInstance(true);}

// Set framework variables
// Override the base url of the request. (default: null)
Flight::set('flight.base_url', null);
// Allow Flight to handle all errors internally. (default: true)
Flight::set('flight.handle_errors', true);
// Log errors to the web server's error log file. (default: false)
Flight::set('flight.log_errors', true);
// Directory containing view template files (default: ./views)
Flight::set('flight.views.path', 'views');
// set your own variables now
Flight::set('flight.static.path', 'static');
// use js? (include maps and scripts in other words)
Flight::set('js', false);
Flight::set('title_prefix', 'WhatsMyIPSite');
Flight::set('meta_description', 'WhatsMyIPSite tells visitors what IP address they are making requests from.');
// set default maptype here (google, here, or wikimapia)
Flight::set('maptype', 'google');
// Google Maps Embed API key
// https://developers.google.com/maps/documentation/embed/guide#api_key
// Flight::set('google_embed_key', 'your_google_map_embed_key_here');
// Google Analytics - replace UA-XXXXX-X with your site id
//Flight::set('google_analytics_site_id', 'UA-XXXXX-X');

// Now do classes
// Register db class with constructor parameters
Flight::register('db', 'PDO', array('sqlite:data/flightgeoip.sqlite3'), function($db){
    // after construct callback (with new object passed in)
    // now create tables if needed
    $db->exec('CREATE TABLE IF NOT EXISTS geo (
                    id INTEGER PRIMARY KEY,
                    ip TEXT UNIQUE,
                    geoplugin_city TEXT,
                    geoplugin_region TEXT,
                    geoplugin_areaCode TEXT,
                    geoplugin_dmaCode TEXT,
                    geoplugin_countryCode TEXT,
                    geoplugin_countryName TEXT,
                    geoplugin_continentCode TEXT,
                    geoplugin_latitude TEXT,
                    geoplugin_longitude TEXT,
                    geoplugin_regionCode TEXT,
                    geoplugin_regionName TEXT)');
    $db->exec('CREATE TABLE IF NOT EXISTS requests (
                    id INTEGER PRIMARY KEY,
                    path TEXT,
                    ip TEXT,
                    uastring TEXT,
                    time INTEGER)');
//$db = Flight::db();

// Now extend Flight with your methods
 * getStaticFile
 * Returns a static file in the static directory or 404.
 * @param [string] $filepath - path to the static file
Flight::map('getStaticFile', function($req_path){
    $filepath = Flight::get('flight.static.path') . $req_path;
	// sometimes relative to root not folder
		$server_path = realpath($_SERVER['DOCUMENT_ROOT']);
		$this_path = realpath(dirname(__FILE__));
		if (strlen($this_path) > strlen($server_path)) {
			$tmp = substr($this_path, strlen($server_path));
			$filepath = Flight::get('flight.static.path') . substr($req_path, strlen($tmp));
    if (file_exists($filepath)) {
		// we don't need MIME id until we need it
		require_once 'includes/phpmimetypeclass-2010-10-19/class.mime.php';
		$mime = new MIMETypes('includes/phpmimetypeclass-2010-10-19/class.types.php');
        $mimetype = $mime->getMimeType($filepath);
        header("Content-Type: $mimetype", true);
    } else {
        // 404 it
 * checkIP
 * Checks the ip of the request for geolocation
 * information, if none is found the information is requested.
 * @param [string] $ip - request IP
Flight::map('checkIP', function($ip){
    // query db for ip first
    $db = Flight::db();
	// If localhost set a real IP
	if($ip = '::1'){
		$ip = '';
    $dbquery ="SELECT * FROM geo WHERE ip IS '" . $ip . "'";
    $result = $db->query($dbquery);
	// does it exist in db?
    if($result->fetch(PDO::FETCH_NUM) > 0){
		$result = $db->query($dbquery);
		foreach ($result as $row) {
			// set geo information into globals
			if(strlen($row['ip']) > 0){Flight::set('ip', $row['ip']);}
			if(strlen($row['geoplugin_city']) > 0){Flight::set('city', $row['geoplugin_city']);}
			if(strlen($row['geoplugin_region']) > 0){Flight::set('region', $row['geoplugin_region']);}
			if(strlen($row['geoplugin_areaCode']) > 0){Flight::set('areaCode', $row['geoplugin_areaCode']);}
			if(strlen($row['geoplugin_dmaCode']) > 0){Flight::set('dmaCode', $row['geoplugin_dmaCode']);}
			if(strlen($row['geoplugin_countryCode']) > 0){Flight::set('countryCode', $row['geoplugin_countryCode']);}
			if(strlen($row['geoplugin_countryName']) > 0){Flight::set('countryName', $row['geoplugin_countryName']);}
			if(strlen($row['geoplugin_continentCode']) > 0){Flight::set('continentCode', $row['geoplugin_continentCode']);}
			if(strlen($row['geoplugin_latitude']) > 0){Flight::set('latitude', $row['geoplugin_latitude']);}
			if(strlen($row['geoplugin_longitude']) > 0){Flight::set('longitude', $row['geoplugin_longitude']);}
			if(strlen($row['geoplugin_regionCode']) > 0){Flight::set('regionCode', $row['geoplugin_regionCode']);}
			if(strlen($row['geoplugin_regionName']) > 0){Flight::set('regionName', $row['geoplugin_regionName']);}
		Flight::set('geoinfo', true);
		require 'includes/geoplugin.class.php';
        $geoplugin = new geoPlugin();
		// geoplugin.com API up?
        if( strlen($geoplugin->ip ) > 0){
			$ipquery = "INSERT INTO geo (ip, geoplugin_city, geoplugin_region, geoplugin_areaCode, geoplugin_dmaCode, geoplugin_countryCode, geoplugin_countryName, geoplugin_continentCode, geoplugin_latitude, geoplugin_longitude, geoplugin_regionCode, geoplugin_regionName) VALUES(:ip, :geoplugin_city, :geoplugin_region, :geoplugin_areaCode, :geoplugin_dmaCode, :geoplugin_countryCode, :geoplugin_countryName, :geoplugin_continentCode, :geoplugin_latitude, :geoplugin_longitude, :geoplugin_regionCode, :geoplugin_regionName)";
			$stmt = $db->prepare($ipquery);
			$stmt->bindParam(':ip', $geoplugin->ip);
			$stmt->bindParam(':geoplugin_city', $geoplugin->city);
			$stmt->bindParam(':geoplugin_region', $geoplugin->region);
			$stmt->bindParam(':geoplugin_areaCode', $geoplugin->areaCode);
			$stmt->bindParam(':geoplugin_dmaCode', $geoplugin->dmaCode);
			$stmt->bindParam(':geoplugin_countryCode', $geoplugin->countryCode);
			$stmt->bindParam(':geoplugin_countryName', $geoplugin->countryName);
			$stmt->bindParam(':geoplugin_continentCode', $geoplugin->continentCode);
			$stmt->bindParam(':geoplugin_latitude', $geoplugin->latitude);
			$stmt->bindParam(':geoplugin_longitude', $geoplugin->longitude);
			$stmt->bindParam(':geoplugin_regionCode', $geoplugin->regionCode);
			$stmt->bindParam(':geoplugin_regionName', $geoplugin->regionName);
			// globals
			if(strlen($geoplugin->ip) > 0){Flight::set('ip', $geoplugin->ip);}
			if(strlen($geoplugin->city) > 0){Flight::set('city', $geoplugin->city);}
			if(strlen($geoplugin->region) > 0){Flight::set('region', $geoplugin->region);}
			if(strlen($geoplugin->areaCode) > 0){Flight::set('areaCode', $geoplugin->areaCode);}
			if(strlen($geoplugin->dmaCode) > 0){Flight::set('dmaCode', $geoplugin->dmaCode);}
			if(strlen($geoplugin->countryCode) > 0){Flight::set('countryCode', $geoplugin->countryCode);}
			if(strlen($geoplugin->countryName) > 0){Flight::set('countryName', $geoplugin->countryName);}
			if(strlen($geoplugin->continentCode) > 0){Flight::set('continentCode', $geoplugin->continentCode);}
			if(strlen($geoplugin->latitude) > 0){Flight::set('latitude', $geoplugin->latitude);}
			if(strlen($geoplugin->longitude) > 0){Flight::set('longitude', $geoplugin->longitude);}
			if(strlen($geoplugin->regionCode) > 0){Flight::set('regionCode', $geoplugin->regionCode);}
			if(strlen($geoplugin->regionName) > 0){Flight::set('regionName', $geoplugin->regionName);}
			Flight::set('geoinfo', true);
			// geoplugin.com not responding
			Flight::set('geoinfo', false);

 * This acts as middleware of sorts
 * If your route function returns true
 * execution is passed on to the next
 * matching route function.
Flight::route('*', function(){
    $request = Flight::request();
    $db = Flight::db();
    $insert = "INSERT INTO requests (path, ip, uastring, time)
                VALUES (:path, :ip, :uastring, :time)";
    $stmt = $db->prepare($insert);
    $stmt->bindParam(':path', $request->url);
    $stmt->bindParam(':ip', $request->ip);
    $stmt->bindParam(':uastring', $request->user_agent);
    $stmt->bindParam(':time', time());
    // stuff hit into db
    // return true to pass execution
    return true;
// Now define your routes and handlers

//front page
Flight::route('GET /', function(){
	$templatedata = array();
	$request = Flight::request();
	// redundant as Flight::get is available in template too 
	$templatedata['ip'] = $request->ip;
	$templatedata['geoinfo'] = Flight::get('geoinfo');
	$templatedata['meta_description'] = Flight::get('meta_description');
	// evaluate the query string (if any)
		$templatedata['js'] = $request->query->js;
		$templatedata['js'] = Flight::get('js');
		$templatedata['maptype'] = $request->query->maptype;
		$templatedata['maptype'] = Flight::get('maptype');
	Flight::render('partials/iframe_insert.php', $templatedata,
    Flight::render('default.php', $templatedata);

// place any other dynamic routes here

// everything else (static folder)
Flight::route('GET /*', function(){
    $fpath = Flight::request()->url;
// Now Set It Off!


<!DOCTYPE html>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title><?php echo Flight::get('title_prefix'); ?> - Your IP is: <?php echo Flight::get('ip'); ?></title>
    <meta name="description" content="<?php echo $meta_description; ?>">
    <meta name="viewport" content="width=device-width, initial-scale=1">
	<link rel="icon" type="image/x-icon" href="favicon.ico" />
	<?php if($geoinfo && $js){	?>
    <script type="text/javascript">
		window.onload = function() {resizemap();}
		window.onresize = function() {resizemap();}
		function resizemap(){
			var map = document.getElementById("map");
			var container = document.getElementById("container");
			map.height = container.clientHeight - 75;
			map.width = container.clientWidth;
	<?php } ?>
	<link rel="stylesheet" href="css/main.css">
<noscript>Either enable JavaScript or click <a href="?js=0">here</a> </noscript>
<div id="container">
            <div id="toppane">
                <div class="box30">
					<div id="lefttop"><div id="brand"><a href="<?php echo Flight::get('base_url'); ?>"><?php echo Flight::get( "title_prefix"); ?></a></div></div>
                <div class="box40">
					<div id="centertop"><span id="iplbl">Your IP is: </span><br/><?php echo Flight::get('ip'); ?></div>
                <div class="box30">
					<div id="righttop">
						<?php if($geoinfo && $js){	?>
						<div class="tmbutt"><a href="?maptype=here">Here</a></div>
						<div class="tmbutt"><a href="?maptype=wikimapia">Wikimapia</a></div>
						<?php if(Flight::has('google_embed_key')){ ?>
						<div class="tmbutt"><a href="?maptype=google">Google</a></div>
						<?php } ?>
						<?php }else{ ?>
						<div class="tmbutt">
						<?php echo date('M d Y'); ?>
						<?php } ?>
				<!--<div style="clear:both;"></div>-->
			<?php echo $iframe_insert; ?>
	<div id="bottompane">
		IP <?php echo Flight::get('ip'); ?> =
		<?php echo Flight::get('latitude'); ?> ,
		<?php echo Flight::get('longitude'); ?> - 
		<?php echo Flight::get('city'); ?>, 
		<?php echo Flight::get('region'); ?>, 
		<?php echo Flight::get('countryName'); ?> - 
		Telephone Area Code: <?php echo Flight::get('areaCode'); ?>
		<a class="right" target="_blank" href="http://geoplugin.com/">geoplugin.com</a>
<?php if($js && Flight::has('google_analytics_site_id')){	?>
        function(){(b[l].q=b[l].q||[]).push(arguments)});b[l].l=+new Date;
    ga('create','<?php echo Flight::get('google_analytics_site_id'); ?>');ga('send','pageview');

<?php } ?>


<?php if($geoinfo && $js){
		switch ($maptype)
		case 'here':
			$mapurl = "https://www.here.com/?map=" . Flight::get('latitude') . "," . Flight::get('longitude') . ",12,satellite";
		case 'google':
				$mapurl = "https://www.google.com/maps/embed/v1/view?key=" . Flight::get('google_embed_key') . "&center=" . Flight::get('latitude') . "," . Flight::get('longitude') . "&zoom=12&maptype=satellite";
			} else{
				$mapurl = "http://wikimapia.org/#lang=en&lat=" . Flight::get('latitude') . "&lon=" . Flight::get('longitude') . "&z=12&m=b";
		case 'wikimapia':
			$mapurl = "http://wikimapia.org/#lang=en&lat=" . Flight::get('latitude') . "&lon=" . Flight::get('longitude') . "&z=12&m=b";
			$mapurl = "https://www.google.com/maps/embed/v1/view?key=" . Flight::get('google_embed_key') . "&center=" . Flight::get('latitude') . "," . Flight::get('longitude') . "&zoom=12&maptype=satellite";

			<div id="mappane">
			<a href="?phpecho$mapurl;?">?phpecho$mapurl;?</a>	
<?php }elseif($geoinfo){  ?>
	$heremapurl = "https://www.here.com/?map=" . Flight::get('latitude') . "," . Flight::get('longitude') . ",12,satellite";
	$googlemapurl = "https://www.google.com/maps/@" . Flight::get('latitude') . "," . Flight::get('longitude') . ",12z";
	$wikimapiamapurl = "http://wikimapia.org/#lang=en&lat=" . Flight::get('latitude') . "&lon=" . Flight::get('longitude') . "&z=12&m=b";
	$openstreetmapmapurl = "http://www.openstreetmap.org/#map=12/" . Flight::get('latitude') . "/" . Flight::get('longitude');
	$weathermapurl = "http://www.wunderground.com/wundermap/?lat=" . Flight::get('latitude') . "&lon=" . Flight::get('longitude') . "&zoom=8&pin=" . Flight::get('city') . "%2c%20" . Flight::get('region');
	<div id="mappane">
		<div class="box50">
			<h3><?php echo Flight::get('ip'); ?></h3>
			<ul class="nsl">
				<li class="nsli"><span class="nsinfo">City: <strong><?php echo Flight::get('city'); ?></strong></span> </li>
				<li class="nsli"><span class="nsinfo">Region: <strong><?php echo Flight::get('region'); ?></strong></span> </li>
				<li class="nsli"><span class="nsinfo">Area Code: <strong><?php echo Flight::get('areaCode'); ?></strong></span> </li>
				<li class="nsli"><span class="nsinfo">DMA Code: <strong><?php echo Flight::get('dmaCode'); ?></strong></span> </li>
				<li class="nsli"><span class="nsinfo">Country Code: <strong><?php echo Flight::get('countryCode'); ?></strong></span> </li>
				<li class="nsli"><span class="nsinfo">Country Name: <strong><?php echo Flight::get('countryName'); ?></strong></span> </li>
				<li class="nsli"><span class="nsinfo">Continent Code: <strong><?php echo Flight::get('continentCode'); ?></strong></span> </li>
				<li class="nsli"><span class="nsinfo">Latitude: <strong><?php echo Flight::get('latitude'); ?></strong></span> </li>
				<li class="nsli"><span class="nsinfo">Longitude: <strong><?php echo Flight::get('longitude'); ?></strong></span> </li>
		<div class="box50">
			<h4>View location on one of these map sites:</h4>
			<ul class="nsl">
				<li class="nsli"><a class="nsinfo" href="<?php echo $googlemapurl; ?>" target="_blank">Google Map</a> </li>
				<li class="nsli"><a class="nsinfo" href="<?php echo $heremapurl; ?>" target="_blank">Here Map</a> </li>
				<li class="nsli"><a class="nsinfo" href="<?php echo $wikimapiamapurl; ?>" target="_blank">Wikimapia Map</a> </li>
				<li class="nsli"><a class="nsinfo" href="<?php echo $openstreetmapmapurl; ?>" target="_blank">Open Street Map</a> </li>
			<h4>Weather Map:</h4>
			<ul class="nsl">
				<li class="nsli"><a class="nsinfo" href="<?php echo $weathermapurl; ?>" target="_blank">
					Weather Underground Wundermap
					</a> </li>
		<!--<div style="clear:both;"></div>-->
<?php }else{  ?>
			<div id="mappane">
				<h2>Unable to retrieve Geographical information at this time. Please try again later.</h2>
				<h3>Our Geographical Information Provider May Not Be Responding</h3>
				<p>If you are connected to the Internet you can try one of the links below to view your IP address location.
				 These Internet map application providers provide a map based on your IP address by default.</p>
					<li><a href="https://www.google.com/maps/" >google.com</a></li>
					<li><a href="https://www.here.com/" >here.com</a></li>
					<li><a href="https://maps.yahoo.com/" >maps.yahoo.com</a></li>
<?php } ?>	

My hacked out main.css

body  {
	margin: 0px;
	padding: 0px;
	background-color: #444444;
	color: white;
	min-width: 700px;
#container {
div#toppane {
	box-sizing: border-box;
	height: 50px;
	background-color: #030303;
	color: #cccccc;
	box-sizing: border-box;
	padding-bottom: 25px;
	margin-bottom: -25px;
	height: 100vh;
	box-sizing: border-box;
	background-color: #030303;
	color: #cccccc;
	height: 25px;
	position: fixed;
	width: 100%;
	font-size: 200%;
#brand a {
	font-size: 70%;
	line-height: 100%;
	box-sizing: content-box;
	width: 30%;
	box-sizing: content-box;
	width: 40%;
	box-sizing: content-box;
	width: 50%;
	box-sizing: border-box;
	height: 100%;
	padding:15px 10px 10px 10px;
.tmbutt a {
	color: #cccccc;
	text-decoration: none;
	background-color: #444444;
	color: #eeeeee;
	font-size: 125%;
	list-style: none;
	padding: 10px;

External Resources Used:
Debugging PHP:
Articles relating to micro-frameworks:
A great listing of various PHP resources:

Source Code Package

Available on GitHub

Codeigniter, Links, PHP

TinyImageManager for CodeIgniter

If you use the TinyMCE Image Manager and CodeIgniter together, this class can save you a bit of work.

This package can handle TinyMCE Image Manager image uploads using CodeIgniter.
It is a port of the TinyMCE image manager plugin to integrate it with CodeIgniter framework.
This class was originally written by Andrey Antonov.
-Pablo Matias Perrone”


Internet Explorer, PHP

A PHP Helper for Pinned Sites on Windows 7 and Internet Explorer 9

This PHP helper will provide a site with a digital footprint on the Windows 7 taskbar by enabling the pinned sites feature with static and javascript jumplists when a page is pinned. The advanced and dynamic features of pinned sites cannot be provided with a simple helper such as this one. All this helper does is set up the shortcut, window, and jumplists features of a pinned site.

Microsoft Pinned Site Features

Microsoft Pinned Site Features Overview

Windows 7 treats an Internet domain as a first class citizen by giving web developers access to modify the way the browser appears when opening a pinned site, as well as modifying the context menu with links. When you integrate with Internet Explorer 9 using javascript you have the ability to show an icon overlay on the pinned shortcut, flash the taskbar shortcut entirely to show the user that your site needs some attention, add more jump list items in a new category, and provide live notifications to the context menu by altering the javascript jumplist. You even have the ability to change the thumbnail button bar icons and handle click events for them. If you want to provide the best user experience for your Internet Explorer 9 users you must learn about pinned sites.

Pinning a site to the taskbar can be done a couple of ways. Firstly, a user can drag a tab or the favicon next to the address bar from Internet Explorer 9 to the taskbar and it will automatically pin itself. You could include a graphic on the page and make it draggable by setting the img, and then dragging that to the taskbar on the bottom of the screen. You cannot, unfortunately, pin a site to the taskbar programmatically – it must be associated with a user interaction.

You could add the site to the Start->All Programs menu with a javascript function “window.external.msAddSiteMode();”. But that site shortcut is not in a pinned state – its added. You don’t automatically get a nice sub menu without explicitly right clicking on the newly created menu item and choosing to pin it to the start menu. That’s when you’ll get a nice menu item with the sub menu that you defined when you click the start menu and hover over the shortcut. I added a function to the helper that will add an a tag with the onclick attribute set to that function anyway, every little bit helps

Tasks Menu

Microsoft Pinned Site Tasks Menu

Most of the more advanced features of pinned sites are implemented in javascript. However, the static features of pinned sites are implemented using special meta tags defined by Microsoft. You use static meta tags inserted into the html of the page to populate the Tasks jumplist and set up all of the window settings. The window settings of pinned sites are: shortcut name, tooltip, starturl, navigation button color, and window size.The shortcut name is used as the name of the shortcut when used anywhere else, when used on a pinned site it is shown in the tooltip when hovering over the shortcut. The tooltip is used as the tooltip for the shortcut when not used on the taskbar. The starturl is the page that is opened up by default when clicking on the pinned shortcut. Using the starturl setting in this way enables you to keep a consistent interface when using the feature on multiple pages, just keep in mind that the window settings should be the same on every page. The navigation button color can be any named or hex color. A 24 x 24 version of the favicon is placed to the left of these buttons to provide a branded experience to the user. The window size it basically the initial width and height of the window that opens up. When a user resizes the window, those settings are remembered by Windows and used instead.

I’m not certain, but I believe Microsoft designed pinned sites using meta tags as a fallback for all of those IE users that do not trust javascript running in their browser. In any case, the design makes it simple to add the static pinned site features to any webpage by inserting special meta tags in the page heading. Granted, you can’t put your sitemap in the context menu using static features. But you can put the five most useful links inside the context menu that open up in in a domain branded window using only meta tags.

The icon of the pinned site’s shortcut is the favicon of the page that’s pinned. On Windows 7 the favicon is king for domains. Its the graphic that represents your domain to the user and it should be looking as best as it can. If your site is using a graphic format other than .ico for your shortcut icon change it. All the other browsers can handle .ico files and Windows won’t replace it with the infamous blue e globe shortcut icon on the system. When pinning a site, the system is going to be looking for 32 x 32 .ico file and if it can’t find it the system will frame the 16 x 16 .ico (yuck). You best bet would be to go to one of those free icon generator sites that output layered .ico files (http://www.rw-designer.com/image-to-icon – custom sizes = 16,32,64,128) and upload a 128 x 128 gif (for the transparancy) of your logo and let it generate a layered .ico file for you. Doing this you’ll get all four sizes used by the different Windows systems for shortcuts packed into the one .ico file and its out of the way – proper graphics everywhere. However, if you’re uncomfortable with the increased file size of your icon you should at least include a 16 x 16 and a 32 x 32 layer in your outputted icon (http://www.rw-designer.com/image-to-icon – custom sizes = 16,32). Microsoft stated :Recommended: 16×16, 32×32, 48×48 and Optimal: 16×16, 24×24, 32×32, 64×64 as layered icon sizes for pinned sites in the documentation (http://msdn.microsoft.com/en-us/library/gg131029%28v=vs.85%29.aspx). The choice is up to you in the end. I do know Windows will do its best to resize the existing graphics where it can but its probably better to go with one of the Microsoft recommendations in the end.

Pinned Site Window

Pinned Site Window Customization

As I mentioned previously, when you pin a site to the taskbar or Start->All Programs menu a special branded “version” of Internet Explorer 9 will automatically open up and close the previous unpinned IE window. Internet Explorer 9 needs to do that to set up the shortcut menu. The meta tags and javascript on that page is evaluated and the context menu is created behind the scenes. The context menu will persist after you close the window, so don’t link to anything that relies on the application state.
The menu items that are shown when you right click on a pinned site shortcut are implemented in two different ways. There are five slots in the Tasks heading that are implemented with meta tags. You are also allowed to create a second jump list and category in javascript that appears above the Tasks jump list with ten additional jump list items by default. Internet Explorer only allows one jump list category other than the Tasks jump list. Each menu item is represented with three pieces of data: the label text of the menu item, the action uri that is opened when the menu item is clicked, and the icon uri that points to a 16 x 16 icon .ico file hosted somewhere on your server. So, to sum it up, you get a total of fifteen menu items; five reserved for static tasks, and another ten under the heading of your choice – not bad at all.

Huffington Post Pinned Menu

Huffington Post Pinned Menu

Notes on items in the jumplists: addresses can point anywhere, not just to addresses inside your domain. When adding items to the javascript jumplist they must be coded last first in javascript for proper placement (my helper fixes that). Microsoft states: “The Jump List is designed primarily for dynamic tasks that change as the user interacts with your application” in reference to the javascript jumplist. All my helper does is set up the shortcut and adds links to it – there is no dynamic features implemented. If you want more than a list of links, such as icon overlays or a dynamic jumplist, you will have to write that in yourself with javascript. In actuality the javascript jumplist can contain twenty items, only ten are visible by default. It could be considered unreasonable to ask a visitor to change any operating system setting, that’s why I only mentioned ten items previously.

To see the other ten invisible items on the javascript jumplist you’ll have to change the settings for the taskbar. Right-click the Start menu and click Properties. On the Start Menu tab, click the Customize button to open the Customize Start Menu dialog box. Change the Number of recent items to display in Jump Lists option to a number that fits your needs. Click OK to close the dialog box.

Obviously, being able to point to fifteen different places on your site when someone right clicks a shortcut in the operating system is a wonderful thing. It can retain users on your site and make them happier at the same time. However, there’s more to pinned sites than what this helper provides and implementing those features could improve site usage and user retention even further. Microsoft provides some reasonable statistics on Pinned Sites usage here: http://windowsteamblog.com/ie/b/ie/archive/2011/02/10/internet-explorer-9-hits-rc-milestone.aspx that may give you a bit more insight.

Amazon Pinned Site Menu

Amazon Pinned Site Menu

This helper is pretty simple to use. You create a helper object by passing in the six window settings. You then populate the Tasks jumplist by calling the add_meta_task method for every task you add, passing in the item text, item url, and item icon url for each. If you want more than the five links that the Tasks jumplist provides you have to create a javascript jumplist by calling the create_js_jumplist method and passing it the category label text. Then add the items to it by calling the add_js_jumplist_item method for each link, again passing in the item text, item url and item icon url for each. You can add up to twenty items, but only ten will show by default. Then you echo the object out wherever you want the meta tags and script block to appear.

Whether you need a jump start on implementing the pinned sites feature on your site or if you just want to have a bunch of links available to your visitors when they pin your site, this helper can help make you life a little easier by providing a simple way of getting your pinned site up and running quickly.









Ie9_helper usage example

require_once 'Ie9_helper.php';
<!DOCTYPE html>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        $ie = new Ie9_helper("IE 9 Test", "IE 9 Test", "http://somedomain/ie9test.php", "Red", 530, 600);
        $ie->add_meta_task("Home Page", "http://somedomain/index.php", "http://somedomain/images/favicon.ico");
        $ie->add_meta_task("Some Other Page", "http://somedomain/ie9test.php", "http://somedomain/images/favicon.ico");
        // up to 5 meta tasks like above
		// now try the js list
        $ie->create_js_jumplist("New JS Jump List");
        $ie->add_js_jumplist_item("item1", "http://somedomain/ie9test.php", "http://somedomain/images/favicon.ico");
        $ie->add_js_jumplist_item("item2", "http://somedomain/ie9test.php", "http://somedomain/images/favicon.ico");
        $ie->add_js_jumplist_item("item3", "http://somedomain/ie9test.php", "http://somedomain/images/favicon.ico");
        $ie->add_js_jumplist_item("item4", "http://somedomain/ie9test.php", "http://somedomain/images/favicon.ico");
        $ie->add_js_jumplist_item("item5", "http://somedomain/ie9test.php", "http://somedomain/images/favicon.ico");
        $ie->add_js_jumplist_item("item6", "http://somedomain/ie9test.php", "http://somedomain/images/favicon.ico");
        $ie->add_js_jumplist_item("item7", "http://somedomain/ie9test.php", "http://somedomain/images/favicon.ico");
        $ie->add_js_jumplist_item("item8", "http://somedomain/ie9test.php", "http://somedomain/images/favicon.ico");
        $ie->add_js_jumplist_item("item9", "http://somedomain/ie9test.php", "http://somedomain/images/favicon.ico");
        $ie->add_js_jumplist_item("item10", "http://somedomain/ie9test.php", "http://somedomain/images/favicon.ico");
        // MS says 10 are shown by default but 
		// you can add 20 in case the user changes settings
        $ie->add_js_jumplist_item("item11", "http://somedomain/ie9test.php", "http://somedomain/images/favicon.ico");
        $ie->add_js_jumplist_item("item12", "http://somedomain/ie9test.php", "http://somedomain/images/favicon.ico");
        $ie->add_js_jumplist_item("item13", "http://somedomain/ie9test.php", "http://somedomain/images/favicon.ico");
        $ie->add_js_jumplist_item("item14", "http://somedomain/ie9test.php", "http://somedomain/images/favicon.ico");
        $ie->add_js_jumplist_item("item15", "http://somedomain/ie9test.php", "http://somedomain/images/favicon.ico");
        $ie->add_js_jumplist_item("item16", "http://somedomain/ie9test.php", "http://somedomain/images/favicon.ico");
        $ie->add_js_jumplist_item("item17", "http://somedomain/ie9test.php", "http://somedomain/images/favicon.ico");
        $ie->add_js_jumplist_item("item18", "http://somedomain/ie9test.php", "http://somedomain/images/favicon.ico");
        $ie->add_js_jumplist_item("item19", "http://somedomain/ie9test.php", "http://somedomain/images/favicon.ico");
        $ie->add_js_jumplist_item("item20", "http://somedomain/ie9test.php", "http://somedomain/images/favicon.ico");
        echo $ie;
        <title>IE 9 Test Page</title>
        <link rel="shortcut icon" href="http://somedomain/images/favicon.ico" >
        echo $ie->add_to_all_programs_menu();

Ie9_helper.php source


 * Ie9_helper
 * A class that automates some of the Internet Explorer 9 integration features for
 * pinned sites. Basically creates all of the static meta tags required to make a site pinnable.
 * Has the method to create the javascript jumplist and method to add items to it.
 * Should test for IE9 before creating this object.
 * @author Timothy Tocci    timtocci at ymail dot com
 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
 * @version 0.2
 * @link https://timothytocci.wordpress.com/2011/12/23/a-php-helper-for-pinned-sites-on-windows-7-and-internet-explorer-9/
class Ie9_helper{
    private $jsjl_created;  // is the list category created?
    private $tasks = array();   // used for the static task array (jump list)
    private $listcat;   // only one defined list category
    private $jsitemarr = array();   // used for the js array (dynamic jump list)
    private $mtags = array();   // used for the initial meta tag array (props of the pinned site)    
    // Code below allows for dynamic property assignment - good for experimenting    
    // - allows you to assign values to properties that doesn't exist (makes php more js like)
    protected $dynprops = array();  // array that holds dynamically assigned properties
    // magic method that gets a previously assigned dynamic property - better have a value
    public function __get($property) {
        return $this->dynprops[$property];
    // magic method that defines a new dynamic property
    public function __set($property, $value) {
        $this->dynprops[$property] = $value;
        return true;
    // -------------If not experimenting just chop this code block out------------------------------------//
     * __construct
     * this constructor sets up the initial pinned site object
     * @param string $shortcut_name - name of the shortcut (like you're ever gonna see it)
     * @param string $tooltip - tooltip that shows when you hover over shortcut
     * @param string $starturl - startup url for shortcut - this opens up when you click on it
     * @param string $color - the color of the navigation buttons (named color or hex css2.1 / 3)
     * @param integer $width - INITIAL width of window - user resize overrides
     * @param integer $height  - INITIAL height of window - user resize overrides 
    public function __construct($shortcut_name, $tooltip, $starturl, $color, $width, $height) {
        $this->jsjl_created = false;
        $this->set_window_size($width, $height);        
     * __toString
     * magic method used to turn the object into an insertable string. Lists out all
     * of the meta tags held in the $mtags and $tasks arrays - then adds the items
     * in the $jsitemarr inside script tags
     * @return string to insert into doc
    public function __toString() {
       $temp="\n" . "<!-- ie pinned site -->" . "\n";
        foreach ($this->mtags as $value) {
            $temp .= $value . "\n";
        if(count($this->tasks) > 0){
            foreach ($this->tasks as $value) {
                $temp .= $value . "\n";
        if(count($this->jsitemarr) > 0){
            $temp .= "<script type='text/javascript'>" . "\n";
            $temp .= $this->listcat . "\n"; 
            $this->jsitemarr = array_reverse($this->jsitemarr); // fixes the last first problem
            foreach ($this->jsitemarr as $value) {
                $temp .= $value . "\n"; // js item
            $temp .= "</script>" . "\n";
        $temp .= "<!-- end ie pinned site -->" . "\n";
        return $temp;
     * create_js_jumplist
     * creates the javascript jumplist - add items with  add_js_jumplist_item
     * @param string $Jump_list_title
     * @return boolean success
    public function create_js_jumplist($Jump_list_title) {
        $this->listcat = "window.external.msSiteModeCreateJumplist('$Jump_list_title');";
        $this->jsjl_created = true;
        return true;
     * add_js_jumplist_item
     * adds a javascript ite to the javascript jumplist Note: fixes the last first
     * problem with javascript jumplist (if altering the js jumplist with js remember
     * that items are added to the menu last first)
     * @param string $item_text -  menu label text
     * @param string $item_uri - action url
     * @param string $item_icon_uri - address of .ico file shown in menu
     * @return boolean success
    public function add_js_jumplist_item($item_text, $item_uri, $item_icon_uri){
        if($this->jsjl_created == false){
            return false;
        $this->jsitemarr[] = "window.external.msSiteModeAddJumpListItem('$item_text', '$item_uri', '$item_icon_uri');";
        return true;
     *  get_task_metatag
     * returns a meta tag for the page that defines a task link inside a pinned
     * site menu. Only five of these static jump links are allowed on a pinned site.
     * @param string $task_name - name on the pinned site menu
     * @param string $task_action_uri - web page that opens up
     * @param string $task_icon_uri - 16 x 16 icon on the pinned site menu (.ico)
     * @return string  - contains the meta tag
    private function get_task_metatag($task_name, $task_action_uri, $task_icon_uri) {
        $metatag = "<meta name='msapplication-task' content='name=$task_name;action-uri=$task_action_uri;icon-uri=$task_icon_uri'/>";
        return $metatag;

     * add_meta_task
     * used to add static jump list tasks to the object that appear in the pinned site
     * right click context menu each with a different name, url, and icon.
     * Only five of these static meta tags are allowed on a 
     * page. Any item in the jump list should have identical meta tags in the page heading for
     * consistency.
     * @param string $task_name - name on the pinned site menu
     * @param string $task_action_uri - web page that opens up
     * @param string $task_icon_uri - 16 x 16 icon on the pinned site menu
     * @return boolean success
    public function add_meta_task($task_name, $task_action_uri, $task_icon_uri) {
        $this->tasks[] = $this->get_task_metatag($task_name, $task_action_uri, $task_icon_uri);
        return true;

     * add_to_all_programs_menu
     * method creates the link needed to add the site to the start menu. Could call
     * the msAddSiteMode() method from within javascript as an alternative.
     * @param string $shortcut_text
     * @return string containing the link html 
    public function add_to_all_programs_menu($shortcut_text  = 'Add To Start Menu - All Programs' ){
        $atag = "<a onclick='window.external.msAddSiteMode();' href='#'>$shortcut_text</a>";
        return $atag;
     * set_shortcut_name
     * creates the meta tag that holds the shortcut name for the pinned site
     * and inserts it into the private $mtags array
     * @param string $shortcut_name
     * @return boolean success
    private function set_shortcut_name($shortcut_name){
        $meta = "<meta name='application-name' content='$shortcut_name'/>";
        $this->mtags[] = $meta;
        return true;
     * set_shortcut_tooltip
     * creates the meta tag that holds the tooltip for the pinned site shortcut
     * and inserts it into the private $mtags array
     * @param string $tooltip
     * @return boolean success
    private function set_shortcut_tooltip($tooltip){
        $meta = "<meta name='msapplication-tooltip' content='$tooltip'/>";
        $this->mtags[] = $meta;
        return true;
     * set_start_url
     * creates the meta tag that holds the pinned site startup url
     * and inserts it into the private $mtags array
     * @param string $starturl
     * @return boolean success
    private function set_start_url($starturl){
        $meta = "<meta name='msapplication-starturl' content='$starturl'/>";
        $this->mtags[] = $meta;
        return true;
     * set_navbutton_color
     * creates the meta tag that sets the navigation button color
     * and inserts it into the private $mtags array
     * @param string $color
     * @return boolean success
    private function set_navbutton_color($color){
        $meta = "<meta name='msapplication-navbutton-color' content='$color'/>";
        $this->mtags[] = $meta;
        return true;
     * set_window_size
     * creates the meta tag that sets the initial width and height of
     * pinned site window and inserts it into the private $mtags array
     * Windows will remember the user's resize so keep in mind that this is 
     * only for the initial setting.
     * @param integer $width
     * @param integer $height
     * @return boolean success
    private function set_window_size($width, $height){
         $meta = "<meta name='msapplication-window' content='width=$width;height=$height'/>";
        $this->mtags[] = $meta;
        return true;

Codeigniter, PHP

A Setting That Many CodeIgniter Developers Miss While Taking Their Code To Production

These two settings inside the index.php file should be changed to random strings. The folder names should also be changed to match as well.

* This variable must contain the name of your "system" folder.
* Include the path if the folder is not in the same  directory
* as this file.
$system_path = 'system';

* If you want this front controller to use a different "application"
* folder then the default one you can set its name here. The folder
* can also be renamed or relocated anywhere on your server.  If
* you do, use a full server path. For more info please see the user guide:
* <a href="http://codeigniter.com/user_guide/general/managing_apps.html">http://codeigniter.com/user_guide/general/managing_apps.html</a>
$application_folder = 'application';

You may be wondering to yourself: “why does it matter?”. It matters when you DO NOT want your application fingerprinted as a codeigniter application by a hacker. One of the first things some of these guys (or gals) do is run a directory fuzzer on your server. That fuzzer makes requests to your server through a list of common directory names. Believe me, the two terms ‘application’ and ‘system’ is early on in that list, I tried it.

Give a hacker as little information about the internals of your program as possible and make it a bit more secure.

Codeigniter, PHP

Path Rerouting in CodeIgniter

Rerouting in CodeIgniter is really simple if you already know how paths work in the framework. You have a controller that is used as the route redirect target. In other words, it picks up the redirected path as its own. Everything other than that is simple CodeIgniter coding. The path can be as many variables large as you may require.

Fairly complex routing systems can be set up this way without the bit of complexity you would have using straight php without a framework.  Below is a short and sweet, step by step simple example of how to accomplish a two variable reroute.

Step By Step

First Step is download CodeIgniter from their website.

Second Step is to unzip it into your server root or a subfolder.

Third Step is to create a .htaccess file that tells apache how to handle requests. This step isn’t necessary if you don’t care about index.php being in your paths. My code is installed in a subfolder named ciredir on my Xampp server in Windows: