Making CodeIgniter 2.0′s RewriteRule and PATH_INFO work on Site5

by koesbong on March 30, 2011

I wrote a web application in CodeIgniter and needed to use the combination of query strings and URI segments for the URL and this Stack Overflow post came in very handy. One thing that I changed was that I removed “parent::_sanitize_globals();” from MY_Input.php because that will break CI 2.0′s CSRF protection.

Anyways, things were going well on my local development environment until I deployed it to Site5. It didn’t matter which link I clicked on, it always redirected me back to the home view. For the longest time, I thought there was something wrong with the .htaccess file, but none of the solutions Josh Cody or I came up with worked.

I then spent some time googling and somehow stumbled upon this on CodeIgniter’s wiki. It basically said that the RewriteRule in .htaccess will only work if you use “REQUEST_URI” as the value for $config['uri_protocol'], which I thought was very weird. I contacted Site5 about this and they have no idea why that is either.

So now, it is almost not an option to remove the URI segment and query strings support from this application because of how far along this project is, so I asked Nathan Ostgard to see if there is anything Josh and I missed or how I can get around Site5′s restriction. Lo and behold, he saved the day (and he has saved many of my days and nights).

Here is the workaround that you can use to make your RewriteRule work even though you don’t use REQUEST_URI for $config['uri_protocol'] in Site5.

/*
|----------------------------------------------------------------------
| URI PROTOCOL
|----------------------------------------------------------------------
|
| This item determines which server global should 
| be used to retrieve the URI string.  The default 
| setting of 'AUTO' works for most servers.
| If your links do not seem to work, try one of 
| the other delicious flavors:
|
| 'AUTO'			  Default - auto detects
| 'PATH_INFO'		  Uses the PATH_INFO
| 'QUERY_STRING'	  Uses the QUERY_STRING
| 'REQUEST_URI'	  Uses the REQUEST_URI
| 'ORIG_PATH_INFO'	  Uses the ORIG_PATH_INFO
|
*/
if (empty($_SERVER['PATH_INFO'])) {
    $pathInfo = $_SERVER['REQUEST_URI'];
    $index = strpos($pathInfo, '?');
    if ($index !== false) {
        $pathInfo = substr($pathInfo, 0, $index);
    }
    $_SERVER['PATH_INFO'] = $pathInfo;
}

$config['uri_protocol']	= 'PATH_INFO';

And one last thing, you should only add the chunk of code from line 19-26 in your site5 environment, not your local environment.

That’s it!

PHP preg_match() and regex

by koesbong on March 5, 2011

While working on a personal project today, I learned something quite interesting. This might be a common knowledge, but it’s totally new to me.

<?php
$pattern = "/\$[\d]+\.?[\d]{0,2}/";
preg_match($pattern, "text $50 more 234", $matches);

// Result - array(1) { [0]=> array(0) { } }
$pattern = '/\$[\d]+\.?[\d]{0,2}/';
preg_match($pattern, "text $50 more 234", $matches);

// Result - array(1) { [0]=> array(1) { [0]=> string(3) "$50" } }

Apparently, when you want to use regex as a pattern in preg_match() or preg_match_all(), the regex pattern needs to be placed inside single quotes and not double quotes.

You learn something new everyday.

Python CSS Browser Selector

by koesbong on January 28, 2011

So you’ve got a site that looks great on your Firefox for Mac but not on your Firefox for Windows. Or you’ve got a site that looks mighty fine on Firefox 3.6 but not so mighty on Firefox 3.5. How do you deal with that? There is one particular trick that I know of to do a targeted IE specific CSS fixes, but that trick won’t work for the scenarios above.

IE Specific Conditional CSS

<!--[if IE]>
  <link rel="stylesheet" type="text/css" href="all-ie-only.css" />
<![endif]-->

Searching around on Google led me to Rafael Lima’s JavaScript CSS Browser Selector. Basically you load a Javascript file and after the page finished loading, the javascript detects what browser and machine you’re on and add those as class names to your <html > tag. Well, the problem with this approach is that it waits for the page to finish loading, then load the Javascript file. If for some reason your JS file takes a little bit longer to load after the page finished loading, you may see a flash of unstyled content.

Since I work with PHP a lot, I found out last year that there is a PHP version of that script written by Bastian Allgeier. He mentioned that his php script is a port from Rafael’s Javascript version. The advantage of the PHP version is that “there’s no additional request needed to include the javascript file and the selectors are available immediately“.

Fast forward to today, I have been working with Python and Django for the past few weeks and would like to use that script, but was not able to find the Python version. So I ported it and it is now on Github.

How to use

We will utilize Django’s TEMPLATE_CONTEXT_PROCESSORS so that it’ll be accessible to all templates. So, in your settings.py, add ‘app_folder.context_processors.get_ua’, where app_folder is the folder name your app lives in, and context_processors is the file in which we will call the get_ua() function.

settings.py

...

TEMPLATE_CONTEXT_PROCESSORS = (
    "django.core.context_processors.auth",
    "django.core.context_processors.debug",
    "django.core.context_processors.i18n",
    "django.core.context_processors.media",
    "app_folder.context_processors.get_ua",
)

...

context_processors.py

import css_selector

def get_ua(request):
    ua = request.META.get('HTTP_USER_AGENT', 'unknown')
    return css_selector.get_ua(ua)

And then in your base or index HTML file:

<!DOCTYPE HTML>
<html class="{{ css }}">
    ...
</html>

The output:

<!DOCTYPE HTML>
<html class="gecko mac ff36">
    ...
</html>

In your CSS file, you can then target the browsers and OS more specifically:

.mac.ff36 #header {}
.win.ff36 #header {}

Available Browser Codes

  • ie – Internet Explorer (All versions)
  • ie8 – Internet Explorer 8.x
  • ie7 – Internet Explorer 7.x
  • ie6 – Internet Explorer 6.x
  • ie5 – Internet Explorer 5.x
  • gecko – Mozilla, Firefox (all versions), Camino
  • ff2 – Firefox 2
  • ff3 – Firefox 3
  • ff35 – Firefox 3.5
  • ff35 – Firefox 3.6
  • ff35 – Firefox 4
  • opera – Opera (All versions)
  • opera8 – Opera 8.x
  • opera9 – Opera 9.x
  • opera10 – Opera 10.x
  • konqueror – Konqueror
  • webkit or safari – Safari (all versions), NetNewsWire, OmniWeb, Shiira, Google Chrome
  • chrome – Google Chrome
  • iron – SRWare Iron

Available OS Codes

  • win – Microsoft Windows
  • linux – Linux (x11 and linux)
  • mac – Mac OS
  • freebsd – FreeBSD
  • ipod – iPod Touch
  • iphone – iPhone
  • ipad – iPad
  • blackberry- BlackBerry
  • android – Android
  • webtv – WebTV
  • j2me – J2ME Devices (ex: Opera mini)
  • mobile – Other mobile devices

Inspiration

As I mentioned earlier, this is a port of the PHP script by Bastian Allgeier, which is a port of the Javascript by Rafael Lima, who was inspired by 37Signals. Big thanks to Adam Kuert for pointing out the usage of Django’s context processors to make things much easier and Nathan Ostgard for the porting guidance.

License

Following Bastian and Rafael’s step, this code is released under the following license: http://creativecommons.org/licenses/by/2.5/

Any feedback is greatly appreciated.

Sortable and Editable To-do List using HTML5′s localStorage

by koesbong on January 24, 2011

During a job interview I had a couple weeks ago, I was asked to create a to-do list for a coding test with these specifications within 2 hours:

  • Ability to add a new item
  • Ability to remove an item
  • Ability to edit an item
  • Ability to sort an item
  • Save the to-do list using browser’s local storage
  • Third party libraries or plugins are allowed

Long story short, I got everything done except saving the order of the list. It bugged me that I didn’t get to finish it all, so I spent some more time getting it done, polishing it, and here’s the result: Live Demo.

If you’d like to know how it was accomplished, keep on reading.

General Idea

So the general idea on how I approached this is:

  • Each item will be stored into localStorage with they key of “todo-uniqueID
  • The order of the list is tracked by an array, which then gets saved into localStorage with the key of “todo-orders”
  • localStorage with the key of “todo-counter” is used to track the last uniqueID used for the item so that on page refresh, it remembers what the next number should be

Now, the code assumes that you have some basic knowledge of JavaScript and jQuery, so here goes:

The HTML and the CSS

<!doctype HTML>
<html>
    <head>
        <title>CodingTest To-do List</title>
        <meta charset="utf-8" />
        <link rel="stylesheet" href="css/base.css" type="text/css" />
    </head>
    <body>
        <div id="container">
            <h1>CodingTest To-Do List</h1>
            <form id="todo-form">
                <input id="todo" type="text" />
                <input id="submit" type="submit" value="Add to List">
            </form>
            <ul id="show-items"></ul>
            <a href="#" id="clear-all">Clear All</a>
        </div>
        <script src="js/jquery-1.4.4.min.js"></script>
        <script src="js/jquery-ui-1.8.7.custom.min.js"></script>
        <script src="js/jquery.inlineedit.js"></script>
        <script src="js/pubsub.js"></script>
        <script src="js/base.js"></script>
    </body>
</html>

That’s a pretty straight forward HTML. There is an input field where users enter in their to-do item and a button to submit it, a blank ul tag where we will append the added to-do item later on using JavaScript, and a Clear All link to clear the list.

For the script includes, I use jQuery, jQuery UI Sortable, jQuery inlineEdit, pubsub, and base – which is where the magic happens.

You may ask why I use the inlineEdit plugin instead of just using the contenteditable attribute. The reason is because Sortable hijacks the mousedown event thus preventing the editable elements to receive focus when you click on it.

body {
    font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
    font-weight: 300;
    font-size: 12px;
}
a,
a:link {
    outline: none;
}
h1 {
    font-weight: 100;
    font-size: 72px;
    margin: 20px 0;
}
#container {
    width: 800px;
    text-align: center;
    margin: 20px auto;
}
input[type="text"] {
    height: 30px;
    width: 350px;
    padding: 10px;
    margin-right: 10px;
    font-size: 24px;
}
input[type="submit"] {
    height: 56px;
    padding: 10px;
    border: 1px solid #333;
    background-color: #ccc;
    font-size: 24px;
    cursor: pointer;
    outline: none;
}
ul {
    margin: 15px auto 0;
    padding: 0;
    width: 545px;
}
ul li {
    list-style-type: none;
    font-size: 24px;
    cursor: move;
    background-color: #efefef;
    margin-bottom: 5px;
    padding: 10px;
    text-align: left;
}
ul li span {
    cursor: text;
}
ul li a,
ul li a:link {
    float: right;
    display: none;
    text-decoration: none;
    color: #f03;
}
ul li a:hover {
    text-decoration: underline;
}

The CSS is also pretty straight forward and self-explanatory, nothing too fancy.

The Script – Add a new item

    // Add todo
    var i = Number(localStorage.getItem('todo-counter')) + 1,
          $form = $('#todo-form'),
          $itemList = $('#show-items'),
          $newTodo = $('#todo'),
          order = [];

    $form.submit(function(e) {
        e.preventDefault();
        $.publish('/add/', []);
    });

    $.subscribe('/add/', function() {
        if ($newTodo.val() !== "") {
            // Take the value of the input field and save it to localStorage
            localStorage.setItem( 
                "todo-" + i, $newTodo.val() 
            );
            
            // Set the to-do max counter so on page refresh it keeps going up instead of reset
            localStorage.setItem(
                'todo-counter', i
            );
            
            // Append a new list item with the value of the new todo list
            $itemList.append(
                "<li id='todo-" + i + "'>"
                + "<span class='editable'>"
                + localStorage.getItem("todo-" + i)
                + " </span><a href='#'>x</a></li>"
            );

            $.publish('/regenerate-list/', []);

            // Hide the new list, then fade it in for effects
            $("#todo-" + i).css('display', 'none').fadeIn();
            
            // Empty the input field
            $newTodo.val("");
            
            i++;
        }
    });

    $.subscribe('/regenerate-list/', function() {
        var $newTodoList = $('#show-items li');
        // Empty the order array
        order.length = 0;
        
        // Go through the list item, grab the ID then push into the array
        $newTodoList.each(function() {
            var $this = $(this).attr('id');
            order.push($this);
        });
        
        // Convert the array into string and save to localStorage
        localStorage.setItem(
            'todo-orders', order.join(',')
        );
    });

Breaking it down: We first declare the variables up top. Next step, we catch the submit event and announce to the app that the form has just been submitted using $.publish(). We then create a $.subscribe() for the add event that actually does all the dirty work.

It first checks to see if the value of the form submit is blank, if not, it retrieves the value then stores it into the localStorage with the key of “todo-uniqueId“, which is whatever the value of i is at that moment. It also stores the value of i into “todo-counter” for uniqueId tracking purposes. Afterward, it appends the new to-do item to the to-do list, displays it appropriately, and empties the input field. We also published “/regenerate-list/” which saves the order of the to-do items. The way that works is it empties the order array, go through the item list elements, get the ID and add to the array, then saving it to “todo-orders”. This “/regenerate-list/” will be used more than once in this app.

The Script – Remove an item

    var $removeLink = $('#show-items li a'),
          $itemList = $('#show-items');

    // Remove todo
    $itemList.delegate("a", "click", function(e) {
        var $this = $(this);

        e.preventDefault();
        $.publish('/remove/', [$this]);
    });

    $.subscribe('/remove/', function($this) {
        var parentId = $this.parent().attr('id');
        
        // Remove todo list from localStorage based on the id of the clicked parent element
        localStorage.removeItem(
            "'" + parentId + "'"
        );
        
        // Fade out the list item then remove from DOM
        $this.parent().fadeOut(function() { 
            $this.parent().remove();
            
            $.publish('/regenerate-list/', []);
        });
    });

Breaking it down: As always, variables declarations are up top. $.delegate() is used because we want to catch the click event on the current and the future $removeLink that gets created. “/publish/” then gets announced and what the app does next is it retrieves the ID of the parent of the clicked element and remove the entry in the localStorage based on it. It then fades out the list item element, removes the element from the DOM and publish “/regenerate-list/” to update “todo-orders”.

The Script – Edit and save item

    var $editable = $('.editable');

    // Edit and save todo
    $editable.inlineEdit({
        save: function(e, data) {
                var $this = $(this);
                localStorage.setItem(
                    $this.parent().attr("id"), data.value
                );
            }
    });

Breaking it down: After the variable declaration, we use the $.inlineEdit() call where upon save, it retrieves the ID of the parent of the edited element and re-save the value. Pretty straight forward.

The Script – Reorder and save

    var $itemList = $('#show-items');

    // Sort todo
    $itemList.sortable({
        revert: true,
        stop: function() {
            $.publish('/regenerate-list/', []);
        }
    });

Breaking it down: This is also pretty straight forward. $.sortable() is used to do the sorting functionality and then on the ‘stop’ event, it publishes “/regenerate-list/” to update “todo-orders”.

The Script – Clear all

    var $clearAll = $('#clear-all');

    // Clear all
    $clearAll.click(function(e) {
        e.preventDefault();
        $.publish('/clear-all/', []);
    });

    $.subscribe('/clear-all/', function() {
        var $todoListLi = $('#show-items li');

        order.length = 0;
        localStorage.clear();
        $todoListLi.remove();
    });

Breaking it down: We catch the click event and publish “/clear-all/”, which resets the order array back to 0, clear the localStorage, and remove all list elements from the DOM. Easy peasy.

The Script – The remove button

    var $itemList = $('#show-items');

    // Fade In and Fade Out the Remove link on hover
    $itemList.delegate('li', 'mouseover mouseout', function(event) {
        var $this = $(this).find('a');
        
        if(event.type === 'mouseover') {
            $this.stop(true, true).fadeIn();
        } else {
            $this.stop(true, true).fadeOut();
        }
    });

Breaking it down: We listen to the mouseover/mouseout event on the list elements and call fadeIn() or fadeOut() appropriately. Also very straight forward.

The Script – Load to-do list

    // Load to-do list
    var orderList,
          j = 0,
          k,
          $itemList = $('#show-items');

    orderList = localStorage.getItem('todo-orders');
    
    orderList = orderList ? orderList.split(',') : [];
                
    for( j = 0, k = orderList.length; j < k; j++) {
        $itemList.append(
            "<li id='" + orderList[j] + "'>"
            + "<span class='editable'>" 
            + localStorage.getItem(orderList[j]) 
            + "</span> <a href='#'>X</a></li>"
        );
    }

Breaking it down: After the variable declarations, we set the value of orderList to be the value of the “todo-orders” and convert it into an array. For each item in the array, we create a new list element and retrieve the value of the key using localStorage.getItem().

The Script

$(function() {
    var i = Number(localStorage.getItem('todo-counter')) + 1,
        j = 0,
        k,
        $form = $('#todo-form'),
        $removeLink = $('#show-items li a'),
        $itemList = $('#show-items'),
        $editable = $('.editable'),
        $clearAll = $('#clear-all'),
        $newTodo = $('#todo'),
        order = [],
        orderList;

    // Load todo list
    orderList = localStorage.getItem('todo-orders');
    
    orderList = orderList ? orderList.split(',') : [];
    
    for( j = 0, k = orderList.length; j < k; j++) {
        $itemList.append(
            "<li id='" + orderList[j] + "'>"
            + "<span class='editable'>" 
            + localStorage.getItem(orderList[j]) 
            + "</span> <a href='#'>X</a></li>"
        );
    }
        
    // Add todo
    $form.submit(function(e) {
        e.preventDefault();
        $.publish('/add/', []);
    });

    // Remove todo
    $itemList.delegate('a', 'click', function(e) {
        var $this = $(this);
        
        e.preventDefault();
        $.publish('/remove/', [$this]);
    });
    
    // Sort todo
    $itemList.sortable({
        revert: true,
        stop: function() {
            $.publish('/regenerate-list/', []);
        }
    });
    
    // Edit and save todo
    $editable.inlineEdit({
        save: function(e, data) {
                var $this = $(this);
                localStorage.setItem(
                    $this.parent().attr("id"), data.value
                );
            }
    });

    // Clear all
    $clearAll.click(function(e) {
        e.preventDefault();
        $.publish('/clear-all/', []);
    });

    // Fade In and Fade Out the Remove link on hover
    $itemList.delegate('li', 'mouseover mouseout', function(event) {
        var $this = $(this).find('a');
        
        if(event.type === 'mouseover') {
            $this.stop(true, true).fadeIn();
        } else {
            $this.stop(true, true).fadeOut();
        }
    });
        
    // Subscribes
    $.subscribe('/add/', function() {
        if ($newTodo.val() !== "") {
            // Take the value of the input field and save it to localStorage
            localStorage.setItem( 
                "todo-" + i, $newTodo.val() 
            );
            
            // Set the to-do max counter so on page refresh it keeps going up instead of reset
            localStorage.setItem('todo-counter', i);
            
            // Append a new list item with the value of the new todo list
            $itemList.append(
                "<li id='todo-" + i + "'>"
                + "<span class='editable'>"
                + localStorage.getItem("todo-" + i) 
                + " </span><a href='#'>x</a></li>"
            );

            $.publish('/regenerate-list/', []);

            // Hide the new list, then fade it in for effects
            $("#todo-" + i)
                .css('display', 'none')
                .fadeIn();
            
            // Empty the input field
            $newTodo.val("");
            
            i++;
        }
    });
    
    $.subscribe('/remove/', function($this) {
        var parentId = $this.parent().attr('id');
        
        // Remove todo list from localStorage based on the id of the clicked parent element
        localStorage.removeItem(
            "'" + parentId + "'"
        );
        
        // Fade out the list item then remove from DOM
        $this.parent().fadeOut(function() { 
            $this.parent().remove();
            
            $.publish('/regenerate-list/', []);
        });
    });
    
    $.subscribe('/regenerate-list/', function() {
        var $todoItemLi = $('#show-items li');
        // Empty the order array
        order.length = 0;
        
        // Go through the list item, grab the ID then push into the array
        $todoItemLi.each(function() {
            var id = $(this).attr('id');
            order.push(id);
        });
        
        // Convert the array into string and save to localStorage
        localStorage.setItem(
            'todo-orders', order.join(',')
        );
    });
    
    $.subscribe('/clear-all/', function() {
        var $todoListLi = $('#show-items li');
        
        order.length = 0;
        localStorage.clear();
        $todoListLi.remove();
    });
});

Wrapping up

Once again, you can see the live demo here. I have learned a lot through this project and hopefully can be beneficial for someone out there.

I’d like to thank Nithin Bekal whose code got me started thinking on this project, Nathan Ostgard for the mid-day and late night code help, as well as Josh Cody for code optimization help.