5
\$\begingroup\$

Here is the original question. I've incorporated a lot of the feedback and I'm ready for another round of feedback and suggestions.

Here is the PHP file's output. It creates a volunteer signup report (in HTML, viewable in your browser, based on data in an SQL database) for volunteer managers to review. I plan to use this page as a starting point, and expand it into a full-blown volunteer management website.

Volunteer Report

The PHP file directly below is probably what will need the most code review. I've broken the original code into a bunch of functions, hopefully increasing readability.

<?

// PAGE PURPOSE: Generates a volunteer signup report that a volunteer manager can use to track volunteer recruitment for an event.

if ( !defined('VIEWER') ) {
    http_response_code(403);
    die("403 Forbidden");
}

// FILE TODO:
// TODO: convert this entire file into a Report class, using OOP??
// TODO: add error handling (throwing and catching exceptions)

///// FUNCTIONS /////
/* TODO: Move some of the formatting stuff over to the templates. Examples from the Smarty manual:
    Name: {$name|capitalize}
    Addr: {$address|escape}
    Date: {$smarty.now|date_format:"%b %e, %Y"} */

function format_date($timestamp = 0) {
    if ( $timestamp == null || $timestamp == 0 ) {
        $timestamp = time();
    }
    return(date("D m/d/y", $timestamp));
}

function get_race_name($race_id, $db) {
    $stmt = $db->prepare("SELECT race_name FROM races WHERE race_id = ?;");
    $stmt->execute(array($race_id));
    $results = $stmt->fetchAll(PDO::FETCH_ASSOC);
    return($results[0]['race_name']);
}

function get_days_until_event($race_id, $db) {
    $stmt = $db->prepare("SELECT race_date FROM races WHERE race_id = ?;");
    $stmt->execute(array($race_id));
    $results = $stmt->fetchAll(PDO::FETCH_ASSOC);
    return(floor((strtotime($results[0]['race_date']) - time())/60/60/24));
}

function get_weeks_until_event($race_id, $db) {
    $stmt = $db->prepare("SELECT race_date FROM races WHERE race_id = ?;");
    $stmt->execute(array($race_id));
    $results = $stmt->fetchAll(PDO::FETCH_ASSOC);
    return(floor((strtotime($results[0]['race_date']) - time())/60/60/24/7));
}

function get_percent_formatting($percent) {
    if ( $percent < 50 ) {
        return("red_cell");
    } else if ( $percent < 90 ) {
        return("yellow_cell");
    } else {
        return("green_cell");
    }
}

function compute_race_output($races_sql, $db) {
    // TODO: check the rounding behavior on the weeks_until_event and days_until_event calculations
    $races_sql['race_name'] = get_race_name($races_sql['race_id'], $db);
    $races_sql['weeks_until_event'] = get_weeks_until_event($races_sql['race_id'], $db);
    $races_sql['days_until_event'] = get_days_until_event($races_sql['race_id'], $db);
    $races_sql['percent_average'] = ($races_sql['needed_total'] == 0) ? 0 : round($races_sql['signed_up_total'] / $races_sql['needed_total'] * 100);
    $races_sql['percent_formatting'] = get_percent_formatting($races_sql['percent_average']);
    $races_sql['percent_average'] .= "%";
    return($races_sql);
}

function compute_shift_output($shifts_sql) {
    $shifts_sql['shift_date'] = format_date(strtotime($shifts_sql['shift_date']));
    $shifts_sql['percent'] = ($shifts_sql['shift_needed'] == 0) ? 0 : round($shifts_sql['shift_enrolled'] / $shifts_sql['shift_needed'] * 100);
    $shifts_sql['percent_formatting'] = get_percent_formatting($shifts_sql['percent']);
    $shifts_sql['percent'] .= "%";
    return($shifts_sql);
}

function get_company_race_list($company_id, $db) {
    $stmt = $db->prepare("SELECT DISTINCT race_id FROM shifts WHERE company_id = ? ORDER BY race_id ASC;");
    $stmt->execute(array($company_id));
    return($stmt->fetchAll(PDO::FETCH_ASSOC));
}

function get_race_shift_list($race_id, $db) {
    $stmt = $db->prepare("SELECT * FROM shifts WHERE race_id = ? ORDER BY shift_date ASC;");
    $stmt->execute(array($race_id));
    return($stmt->fetchAll(PDO::FETCH_ASSOC));
}

function get_company_name($company_id, $db) {
    $stmt = $db->prepare("SELECT * FROM companies WHERE company_id = ?;");
    $stmt->execute(array($company_id));
    $results = $stmt->fetch(PDO::FETCH_ASSOC);
    return($results['company_name']);
}

// TODO: script a login system, and pull the $company_id from the user's account instead of hard-coding it
$company_id = 1;

$smarty->assign('company_name', get_company_name($company_id, $db));
$smarty->assign('today', format_date());

foreach ( get_company_race_list($company_id, $db) as $races_sql ) {
    // TODO: change Signed Up and Needed amounts from integer in SQL table to SQL COUNT(*) later on
    $shifts_smarty = NULL;

    foreach ( get_race_shift_list($races_sql['race_id'], $db) as $shifts_sql ) {
        $races_sql['signed_up_total'] += $shifts_sql['shift_enrolled'];
        $races_sql['needed_total'] += $shifts_sql['shift_needed'];

        $shifts_smarty[] = compute_shift_output($shifts_sql);
    }
    $races_sql['shifts'] = $shifts_smarty;

    $races_smarty[] = compute_race_output($races_sql, $db);
}
$smarty->assign('races', $races_smarty);

$smarty->display('report.html');

?>

After this code review, I will probably charge ahead and start scripting the other pages of the website, so if you have any suggestions for how I should structure the pages, please mention them.

Current structure:

  • I have index.php as a viewer, and then pages/pagename.php for each page.

  • I also have a system directory (for config.php and other global includes), a skins directory (for smarty files, one folder for each skin, the current skin I named "default"), and a Smarty directory for all its files. Later on I could add directories for uploads, etc.

  • I am not currently using classes nor a giant function file.

  • I am using Smarty as my template engine and passing all output variables to Smarty.

Questions:

  • What are the pros and cons of converting this code to OOP, and what would be the best way to go about doing it? Just create a giant report class and make all these functions its methods? What's the benefit of doing this when having this code in report.php already encapsulates the data the same way a class would, but without the extra step of needing to create objects every time?

  • What are the pros and cons of converting this code to MVC, and what would be the best way to go about doing it? I've already isolated the "view" part nicely. This file and index.php are some kind of mix of Controller and Model, I think. How would I go about separating the code, and what are the benefits of this kind of separation?

  • "Code re-usability" was mentioned in the last code review. Would moving these functions into a giant functions file that is included into every page be a good move? On my last website I only put functions that I used in more than one place in the global functions file.

  • Smarty has helped make my PHP code cleaner and more readable by extracting all the HTML junk and putting it in its own file. However it is not clear without a thorough analysis (print_r) exactly what variables are passed to Smarty. Should I refactor my code in a way that makes these variable assignments clearer? For example, doing one $smarty->assign() per variable instead of passing a giant array?

  • Open to any and all other feedback. I want to fix all the bad habits and learn best practices, so I can get my code up to professional level.


Here are the rest of the files:

Raw HTML:

<!DOCTYPE html>
<html lang="en-US">
    <!-- TODO: add <time> element -->
    <!-- TODO: investigate other ways I could mark up the company name rather than a table -->
    <!-- TODO: move the HTML header into its own Smarty file so that it can easily be changed for the entire website -->

    <head>
        <title>Acme Events, Inc. Volunteer Report</title>
        <meta charset="utf-8" />
        <link rel="stylesheet" type="text/css" href="skins/default/style.css">
    </head>

    <body>
        <table>
            <tr class="title">
                <td>
                    Acme Events, Inc.
                </td>
            </tr>
            <tr class="title">
                <td>
                    Volunteer Report
                </td>
            </tr>
            <tr class="subtitle">
                <td>
                    Generated on Tue 08/25/15
                </td>
            </tr>
        </table>

        <br />

        <table>
            <tr class="title">
                <td colspan="6">
                    September Shuffle 5K
                </td>
            </tr>
            <tr class="bold_upper_border">
                <th class="column_shift">
                    Shift
                </th>
                <th class="column_date">
                    Date
                </th>
                <th class="column_time">
                    Time
                </th>
                <th class="column_signedup">
                    Signed Up
                </th>
                <th class="column_needed">
                    Needed
                </th>
                <th class="column_percent">
                    %
                </th>
            </tr>

            <tr>
                <td>
                    Packet Pick Up 10AM
                </td>
                <td>
                    Sat 09/12/15
                </td>
                <td class="bold_right_border">
                    10AM-1:30PM
                </td>
                <td>
                    40
                </td>
                <td>
                    40
                </td>
                <td class="green_cell">
                    100%
                </td>
            </tr>
            <tr>
                <td>
                    Packet Pick Up 1PM
                </td>
                <td>
                    Sat 09/12/15
                </td>
                <td class="bold_right_border">
                    1PM-5:30PM
                </td>
                <td>
                    40
                </td>
                <td>
                    40
                </td>
                <td class="green_cell">
                    100%
                </td>
            </tr>
            <tr>
                <td>
                    Kid's Fun Run
                </td>
                <td>
                    Sun 09/13/15
                </td>
                <td class="bold_right_border">
                    10AM-12PM
                </td>
                <td>
                    15
                </td>
                <td>
                    15
                </td>
                <td class="green_cell">
                    100%
                </td>
            </tr>
            <tr>
                <td>
                    Pre & Late Registration (Race Day)
                </td>
                <td>
                    Sun 09/13/15
                </td>
                <td class="bold_right_border">
                    6AM-10:30AM
                </td>
                <td>
                    40
                </td>
                <td>
                    50
                </td>
                <td class="yellow_cell">
                    80%
                </td>
            </tr>
            <tr>
                <td>
                    Start & Finish Line Support
                </td>
                <td>
                    Sun 09/13/15
                </td>
                <td class="bold_right_border">
                    6AM-11AM
                </td>
                <td>
                    75
                </td>
                <td>
                    75
                </td>
                <td class="green_cell">
                    100%
                </td>
            </tr>
            <tr>
                <td>
                    Water Station 1 (Inside Stadium)
                </td>
                <td>
                    Sun 09/13/15
                </td>
                <td class="bold_right_border">
                    6AM-10AM
                </td>
                <td>
                    41
                </td>
                <td>
                    40
                </td>
                <td class="green_cell">
                    103%
                </td>
            </tr>
            <tr>
                <td>
                    Volunteer Check In (Race Day)
                </td>
                <td>
                    Sun 09/13/15
                </td>
                <td class="bold_right_border">
                    6AM-10:30AM
                </td>
                <td>
                    5
                </td>
                <td>
                    5
                </td>
                <td class="green_cell">
                    100%
                </td>
            </tr>
            <tr>
                <td>
                    Water Station 2 (Outside Stadium)
                </td>
                <td>
                    Sun 09/13/15
                </td>
                <td class="bold_right_border">
                    6AM-10AM
                </td>
                <td>
                    25
                </td>
                <td>
                    40
                </td>
                <td class="yellow_cell">
                    63%
                </td>
            </tr>

            <tr class="bold_upper_border">
                <td class="center_text">
                    2 Weeks Until Event
                </td>
                <td colspan="2" class="center_text bold_right_border">
                    18 Days Until Event
                </td>
                <td>
                    281
                </td>
                <td>
                    305
                </td>
                <td class="green_cell">
                    92%
                </td>
            </tr>
        </table>
        <br />

        <table>
            <tr class="title">
                <td colspan="6">
                    November Turkey Trot 5K/10K
                </td>
            </tr>
            <tr class="bold_upper_border">
                <th class="column_shift">
                    Shift
                </th>
                <th class="column_date">
                    Date
                </th>
                <th class="column_time">
                    Time
                </th>
                <th class="column_signedup">
                    Signed Up
                </th>
                <th class="column_needed">
                    Needed
                </th>
                <th class="column_percent">
                    %
                </th>
            </tr>

            <tr>
                <td>
                    Packet Pick Up - Pre/Late Reg
                </td>
                <td>
                    Fri 11/06/15
                </td>
                <td class="bold_right_border">
                    3PM - 7:30PM
                </td>
                <td>
                    16
                </td>
                <td>
                    50
                </td>
                <td class="red_cell">
                    32%
                </td>
            </tr>
            <tr>
                <td>
                    Packet Pick Up - Registratio
                </td>
                <td>
                    Fri 11/06/15
                </td>
                <td class="bold_right_border">
                    11AM - 3:30PM
                </td>
                <td>
                    14
                </td>
                <td>
                    50
                </td>
                <td class="red_cell">
                    28%
                </td>
            </tr>
            <tr>
                <td>
                    Start/Finish Line Area: Post Race F
                </td>
                <td>
                    Sat 11/07/15
                </td>
                <td class="bold_right_border">
                    6AM - 10AM
                </td>
                <td>
                    15
                </td>
                <td>
                    50
                </td>
                <td class="red_cell">
                    30%
                </td>
            </tr>
            <tr>
                <td>
                    Pre/Late- Registration
                </td>
                <td>
                    Sat 11/07/15
                </td>
                <td class="bold_right_border">
                    5AM - 8AM
                </td>
                <td>
                    7
                </td>
                <td>
                    50
                </td>
                <td class="red_cell">
                    14%
                </td>
            </tr>
            <tr>
                <td>
                    Volunteer Check-In
                </td>
                <td>
                    Sat 11/07/15
                </td>
                <td class="bold_right_border">
                    5AM - 10AM
                </td>
                <td>
                    5
                </td>
                <td>
                    5
                </td>
                <td class="green_cell">
                    100%
                </td>
            </tr>
            <tr>
                <td>
                    Water Station 1
                </td>
                <td>
                    Sat 11/07/15
                </td>
                <td class="bold_right_border">
                    6AM - 10AM
                </td>
                <td>
                    1
                </td>
                <td>
                    40
                </td>
                <td class="red_cell">
                    3%
                </td>
            </tr>
            <tr>
                <td>
                    Water Station 2
                </td>
                <td>
                    Sat 11/07/15
                </td>
                <td class="bold_right_border">
                    6AM - 10AM
                </td>
                <td>
                    1
                </td>
                <td>
                    40
                </td>
                <td class="red_cell">
                    3%
                </td>
            </tr>

            <tr class="bold_upper_border">
                <td class="center_text">
                    10 Weeks Until Event
                </td>
                <td colspan="2" class="center_text bold_right_border">
                    73 Days Until Event
                </td>
                <td>
                    59
                </td>
                <td>
                    285
                </td>
                <td class="red_cell">
                    21%
                </td>
            </tr>
        </table>
    </body>
</html>

Smarty HTML:

<!DOCTYPE html>
<html lang="en-US">
    <!-- TODO: add <time> element -->
    <!-- TODO: investigate other ways I could mark up the company name rather than a table -->
    <!-- TODO: move the HTML header into its own Smarty file so that it can easily be changed for the entire website -->

    <head>
        <title>{$company_name} Volunteer Report</title>
        <meta charset="utf-8" />
        <link rel="stylesheet" type="text/css" href="skins/default/style.css">
    </head>

    <body>
        <table>
            <tr class="title">
                <td>
                    {$company_name}
                </td>
            </tr>
            <tr class="title">
                <td>
                    Volunteer Report
                </td>
            </tr>
            <tr class="subtitle">
                <td>
                    Generated on {$today}
                </td>
            </tr>
        </table>

{foreach item=race from=$races}
        <br />

        <table>
            <tr class="title">
                <td colspan="6">
                    {$race.race_name}
                </td>
            </tr>
            <tr class="bold_upper_border">
                <th class="column_shift">
                    Shift
                </th>
                <th class="column_date">
                    Date
                </th>
                <th class="column_time">
                    Time
                </th>
                <th class="column_signedup">
                    Signed Up
                </th>
                <th class="column_needed">
                    Needed
                </th>
                <th class="column_percent">
                    %
                </th>
            </tr>

{foreach item=shift from=$race.shifts}
            <tr>
                <td>
                    {$shift.shift_name}
                </td>
                <td>
                    {$shift.shift_date}
                </td>
                <td class="bold_right_border">
                    {$shift.shift_time}
                </td>
                <td>
                    {$shift.shift_enrolled}
                </td>
                <td>
                    {$shift.shift_needed}
                </td>
                <td class="{$shift.percent_formatting}">
                    {$shift.percent}
                </td>
            </tr>
{/foreach}

            <tr class="bold_upper_border">
                <td class="center_text">
                    {$race.weeks_until_event} Weeks Until Event
                </td>
                <td colspan="2" class="center_text bold_right_border">
                    {$race.days_until_event} Days Until Event
                </td>
                <td>
                    {$race.signed_up_total}
                </td>
                <td>
                    {$race.needed_total}
                </td>
                <td class="{$race.percent_formatting}">
                    {$race.percent_average}
                </td>
            </tr>
        </table>
{/foreach}
    </body>
</html>

CSS:

/*

TODO: investigate using CSS shorthand, for example, font instead of font-family and font-size 
TODO: think about if there is a better way to do thick table borders than bold_X_border classes

*/

body {
    font-family: Calibri, Tahoma, Verdana, Sans-Serif;
    font-size: 11pt;
}

table {
    width: 600px;
    border-collapse: collapse;
    border: 2px solid black;
    white-space: nowrap;
}

th {
    font-weight: bold;
    font-style: italic;
    text-decoration: underline;
    border: 1px solid black;
    text-align: left;
}

td {
    border: 1px solid black;
}

.title {
    text-align: center;
    font-weight: bold;
    font-size: 16pt;
}

.subtitle {
    text-align: center;
    font-style: italic;
}

.column_shift {
    width: 36%;
}

.column_date {
    width: 16%;
}

.column_time {
    width: 17%;
    border-right: 2px solid black;
}

.column_signedup {
    width: 12%;
}

.column_needed {
    width: 10%;
}

.column_percent {
    width: 10%;
}

.bold_upper_border {
    border-top: 2px solid black;
}

.bold_right_border {
    border-right: 2px solid black;
}

.red_cell {
    color: #9C0006;
    background-color: #FFC7CE;
}

.yellow_cell {
    color: #9C6500;
    background-color: #FFEB9C;
}

.green_cell {
    color: #006100;
    background-color: #C6EFCE;
}

.center_text {
    text-align: center;
}

index.php:

<?php

define('VIEWER', TRUE);

include('system/config.php');

// TODO: Switch to a whitelist system for security reasons.
// TODO: Sanitize the $_GET data? Probably need to remove all forward slashes since we only want to load code from the /pages/ directory
// TODO: Handle "page does not exist" errors
if(isset($_GET['p'])) {
    include('./pages/' . $_GET['p'] . '.php');
} else {
    $directory = './pages';
    $pages = scandir($directory);

    foreach($pages as $pagename) {
        if ( $pagename == "." || $pagename == ".." ) {
            continue;
        } else {
            $pagename = rtrim($pagename, ".php");
            $pagename_pretty = str_replace("_", " ", $pagename);
            $pagename_pretty = ucwords($pagename_pretty);
            print("

                <a href='index2.php?p=$pagename'>$pagename_pretty</a><br />

            ");
        }
    }
}

?>

config.php:

<?php

if ( !defined('VIEWER') ) {
    http_response_code(403);
    die("403 Forbidden");
}

//// SECURITY /////
// http://wiki.hashphp.org/16_Tips_for_Secure_Code This tutorial says to keep register globals and magic quotes off. So throw warnings if they are on.
if ( ini_get('register_globals') == 1 ) {
    print("WARNING: Register globals is on.<br />");
}
if ( get_magic_quotes_gpc() == 1 ) {
    print("WARNING: Magic quotes is on.<br />");
}
// TODO: Check to see if indexes are on by reading the .htaccess file and using search or RegEx to see if "Options -Indexes" is present. Throw a warning if they are off. http://stackoverflow.com/questions/3618764/parsing-an-htaccess-file-with-php

///// MYSQL /////
$database_name = "voluntee_volunteers";
$database_username = "voluntee_volunte";
$database_password = "this_is_not_my_real_password";
$db = new PDO('mysql:host=localhost;dbname=' . $database_name . ';charset=utf8', $database_username, $database_password);
// $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);

///// SMARTY /////
include('./smarty/Smarty.class.php');
$smarty = new Smarty();

// TODO: allow the template directory to change based on a logged in user's skin settings
$smarty->setTemplateDir('./skins/default/pages');
$smarty->setCompileDir('./smarty/templates_c');
$smarty->setCacheDir('./smarty/cache');
$smarty->setConfigDir('./smarty/configs');

?>
\$\endgroup\$
1
\$\begingroup\$

What are the pros and cons of converting this code to OOP? What's the benefit of doing this when having this code in report.php already encapsulates the data the same way a class would, but without the extra step of needing to create objects every time?

The difference between objects and functions is that you can pass objects and their state on to other stuff, and their state is actually saved in the object, so it doesn't require a db interaction each time. For example, you could pass it on to the view/smarty files instead of passing arrays/single values.

Note also that currently, you perform more db accesses than necessary, because you use your db functions as getter. So you call eg get_race_name which issues a query, and then get_days_until_event which issues the same query. Instead, you could do something like $race = raceDAO->getById($id); $race_name = $race->getName(); $race_days = $race->getDaysUntilEvent(); which would only be one query.

As for further advantages: For one, this would decouple your view from your database. So if column names or your whole dbms change, your objects would still be able to interact with the view correctly.

If code completion works correctly (I'm not sure how well it works with smarty) that's also an advantage, as is the clear structure of a class vs the vague structure of an array.

Another advantage is testability.

If these advantages are really worth it for you depends on your preferences and the size of your project.

what would be the best way to go about doing it? Just create a giant report class and make all these functions its methods?

That would be one option, but I wouldn't. You currently have helper function that are not specific to reports with format_date, you have kind of a mix of DAO methods and getters like get_race_name, and then calculation methods.

Your current file also combines the access to very different things, such as reports, companies, and shifts. With OOP, each of these would get their own class. I would probably structure it something like I outlined above. The db access go into a DAO file (one for each object), each object gets a class with getter, the calculations also go here.

What are the pros and cons of converting this code to MVC, and what would be the best way to go about doing it? I've already isolated the "view" part nicely. This file and index.php are some kind of mix of Controller and Model, I think. How would I go about separating the code, and what are the benefits of this kind of separation?

The benefits are testabiliy, readability, and easier changeability. It's easier to find the relevant code pieces if they are separated like this.

"Code re-usability" was mentioned in the last code review. Would moving these functions into a giant functions file that is included into every page be a good move? On my last website I only put functions that I used in more than one place in the global functions file.

No, one giant file containing various stuff is never a good idea. Functions should be separated into logical blocks, which can then be put into their own file/class.

Smarty has helped make my PHP code cleaner and more readable by extracting all the HTML junk and putting it in its own file. However it is not clear without a thorough analysis (print_r) exactly what variables are passed to Smarty. Should I refactor my code in a way that makes these variable assignments clearer? For example, doing one $smarty->assign() per variable instead of passing a giant array?

Yes, I would. I really don't like these kinds of giant arrays. They are almost never properly documented, so all you can do is guess or search for the place they are created.

The solution is to either pass each value in its own variable (which could also get messy quickly), or to use objects. Those are well documented by themselves via the method signatures.

Misc

  • if magic quotes are on, I wouldn't print a warning (it's not relevant to endusers), but just clean the input myself via strip slashes.
  • register globals should indeed be off (it shouldn't exist as an option in the first place), but echoing this only gives attackers additional information, so I wouldn't do that either.
  • if you use a whitelist for your include pages, you do not need to further sanitize the p parameter.
  • I'm not that familiar with smarty, but I think it still doesn't HTML encode stuff by default, so you should think about doing it yourself.
|improve this answer|||||
\$\endgroup\$
  • \$\begingroup\$ Make each SQL table a class, rather than each PHP page? That. Is. Awesome. I never thought of that. It all makes sense now. 1) Let's say there's 500 races in the table and I need to iterate through all of them. Do I create 500 race objects and run 500 MySQL queries (because of the constructor)? There's gotta be a more efficient way to do that. 2) Should I try to link the classes together as parent/child, i.e. company contains all races, races contain all shifts? If parent/child classes are used, is it possible to have a shift without instantiating the company class for it? \$\endgroup\$ – AdmiralThrawn Aug 26 '15 at 9:43
  • 1
    \$\begingroup\$ @AdmiralAdama you mean iterate over all of them because you want to print them all? In that case, you are right, you really don't want to run 500 queries. Add a getAll($from, $to) method to the race DAO, which returns a list of objects. It only needs to perform one query. And yes, often, you want to link those classes, so you would have a field in an object that references a different object or list of objects. That's a whole new topic. But yes, generally, you don't want all linked objects to be loaded by default, but only if needed. This is generally called lazy loading. \$\endgroup\$ – tim Aug 26 '15 at 9:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.