CSS Buttons

January 27th, 2010

A common issue in developing web applications is that styling between different browsers often looks different. This hurts many designers who are trying to make things look a certain way, and it hurts many developers who have to listen to those designers complain. I’ve run into this issue in the past, and the designer I was working with insisted that we use Flash buttons that look and act a certain way. Do I even have to explain the issues associated with Flash buttons throughout your entire site?

This is a quick and easy solution to having buttons that all look the same, and function the same way regardless of browser or OS. Now, as a disclaimer, I have to say that the best way to go about doing rounded corners is through CSS3, but Internet Explorer does not support CSS3 (thank you Microsoft, way to stay ahead of the curve). One alternative is to use JavaScript to generate DIVs that gradually create a rounded corner effect, but at times it can be glitchy; especially when using Ajax in IE.This leaves us with one choice: images. I am not a proponent of using images to do rounded corners, but when you have no other options, you have to do something. That being said, this little bit of code uses tiny PNG images that stretch horizontally.

I am not going to run through how the code itself works, but I will explain the requirements, usage, and available functionality.

Requirements

  • jQuery 1.3.2+
  • jQuery UI 1.7.2+
    • Only uses “disableSelection()”. You can copy just this function if you don’t want to include the entire library.
  • Minimum of the transitional DOCTYPE because Quirks mode is not supported

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “DTD/xhtml1-transitional.dtd”>

Usage

Place a

<div class=”cssButton” caption=”Button Label”></div>

anywhere on the page, and then call

StyleButtons()

after the page loads.To use different features of the buttons, simply add the appropriate attributes to the div, and they will be used accordingly.

Functionality
The following attributes are implemented:

  • caption – The text on the button
  • onclick – Javascript to execute on click
  • width – Set an absolute, fixed width in px or pt
  • align – Aligns the caption text within the button
    • One of {“left”, “right”, “middle”}
    • Defaults to “middle”
  • padding – Padding for the text to the button border
    • Works just like standard CSS syntax [top, right, bottom, left]
    • Default values of “auto”
  • fontSize – Font size to use in px or pt
    • Defaults to the current font set on the page

Source Code

Example page

Source Code

Restarting my blog

January 23rd, 2010

Well, I haven’t written anything in a long time, but that’s because I don’t like writing. I’ve realized though, that I’m constantly running across different issues and finding interesting solutions that aren’t documented or explained very well. So, I’ve decided to start writing again in hopes that it will help whomever might be reading this.

Making Wizards in Web Apps

October 19th, 2007

There is a multitude of ways to make wizards in your web application. When not using AJAX, here is my favorite method.

First, lets lay down some base requirements:

  • We have 3 steps in the wizard
  • We have a “Complete” page
  • The user is able to navigate to previous steps and not lose data
  • Nothing is finalized until the last step is finished.

Now that we know what we want out of this wizard, what functions do we need?

  • GetWizardSteps($currentStep)
  • DisplayWizardSteps($steps)
  • SetWizardData($data)
  • GetWizardData()
  • SaveWizardData()

And, we’ll need the following pages:

  • wizardStep1.php
  • wizardStep2.php
  • wizardStep3.php
  • wizardControl.php
  • wizardComplete.php
  • wizardLibrary.php

What do these functions actually do?

$steps = GetWizardSteps($currentStep);

This function should take an integer for the current step, and spit out an array of steps. Each element in the array should have a step caption, a link (if step is navigable), and any css you want to use to designate past, current, or future steps.

list($html, $css) =  DisplayWizardSteps($steps);

You should then be able to take that array, and send it to this function. This function then takes that array of steps, and for every element, writes out appropriate HTML for it. You should return the HTML and CSS you need to display the list of steps, taking into account their status (past, current, future).

SetWizardData($data);

$data can be any object. For the purposes of the wizard, I recommend making it a simple stdClass. The purpose of this function is to allocate a unique section of the $_SESSION to store all the data from the wizard. ex. $_SESSION["wizard"] = $data

$data = GetWizardData();

This should look in the same place on the session and retrieve the data stored there. If you’re using stdClass to store the data, then: return $_SESSION["wizard"]; should be sufficient. Otherwise, you may need to implement custom conversion of a simple associative array to the value object of your choice. End result, is that all of the wizard data stored on the session is retrieved as a single object.

$success = SaveWizardData();

This function calls GetWizardData() and then does whatever it needs, to save the data to the database, or file, or where ever you want to save it to. Return true / false for success / failure.

How do the pages tie these functions together?

wizardStep1.php, wizardStep2.php, wizardStep3.php

For each step, there are several things that must be done.

  1. Set $currentStep
  2. Generate the steps: $steps = GetWizardSteps($currentStep);
  3. Display the steps: list($stepsHTML, $stepsCSS) = DisplayWizardSteps($steps);
  4. Create a <form method=”post” action=”wizardControl.php”>
  5. Add a hidden input field for the current step.
  6. Add all content and the submit button to the form.

wizardControl.php

The wizardControl receives all input to process. The task here is to GetWizardData, switch on the $currentStep, and SetWizardData appropriately. If the last step is submitting, then you validate the data, SaveWizardData, and clear out what’s stored as the current wizard data: SetWizardData(false). On success, redirect to the wizardComplete page; on failure, redirect to the step that failed.

wizardLibrary.php

This is just an include page. It doesn’t display anything, or process anything. It simply contains the functions we described earlier.

Common PHP Functions

September 5th, 2007

Practically all web-applications use UUIDs for one purpose or another. This generates a 40 character UUID that you can substring to get whatever length you want.

function GenUUID() {
    $uuid = uniqid(md5(rand()), false);
}

When you retrieve data from the database and spit it out to the screen through HTML, how often can you trust the data to not contain XSS (Cross-Site Scripting)? This blocks all simple XSS attacks that were submitted in input fields. Before displaying data, simply run it through this function.

function EscapeDisplay($str) {
    return @htmlspecialchars($str);
}

When you’re writing XML blocks, some data needs to be encased with CDATA, but how do you know what to encase? This does it for you. It returns validly encoded XML content.

function XMLEncode($val) {
    if (@htmlspecialchars($val,ENT_NOQUOTES) != $val) {
        return @utf8_encode("<![CDATA[$val]]>");
    } else {
        return @utf8_encode($val);
    }
}

Very often, you want to fire off a request to an arbitrary webpage, and not worry about the response. This shoots of the request, and within 1 second times out. Returns nothing.

function SubmitRequest($request) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $request);
    curl_setopt($ch, CURLOPT_FRESH_CONNECT, true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    # Enable / Disable a timeout for connection
//        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
    curl_setopt($ch, CURLOPT_TIMEOUT, 1);

    curl_exec($ch);

    curl_close($ch);
}

You retrieve 2 dates from MySQL, how do you know which date is more recent? The syntax and response is similar to Java’s compareTo(a,b) method. Returns -1 for a < b, 0 for a = b, 1 for a > b.

function CompareMySQLDates($a, $b) {
    $strA = split("[- ]", $a);
    $strB = split("[- ]", $b);
    # Make the dates into timestamps of US formatted dates
    $timeA = strtotime($strA[1] . "/" . $strA[2] . "/" . $strA[0]);
    $timeB = strtotime($strB[1] . "/" . $strB[2] . "/" . $strB[0]);
    # Compare timestamp integers
    return $timeA < $timeB ? -1 : ($timeA == $timeB ? 0 : 1);
}

Regex expression to make sure an email address has the right syntax: word@word.ext

function ValidateEmail($email) {
    return eregi("^[a-z0-9]+([-_\.]?[a-z0-9])+@[a-z0-9]+([-_\.]?[a-z0-9])+\.[a-z]{2,4}", $email) ? true : false;
}

Removes whitespace from a block of text

function CleanWhitespace($str) {
    return ereg_replace("[ \t]","",$str);
}

General function for securing data sent in a SQL Statement.
Usage: $sql = “SELECT * FROM table1 WHERE name = ‘” . SQLEscape($name) . “‘”;

function SQLEscape($data) {
    return mysql_real_escape_string($data);
}

Used for calling a Stored Procedure through a MySQLi connection ($database is a valid connection).

function CallSQLProcedure($sql) {
    global $database;
    # Create a temp array so we can free the result
    $arr = array();

    # Call the proc
    if ($database->multi_query($sql)) {
        $result = $database->store_result();
        while ($row = $result->fetch_assoc()) {
            $arr[] = $row;
        }
        while ($database->next_result()) {}

        # You have to free the resultset
        if ($result)
            $result->close();
    }
    return $arr;
}

When you have a SQL statement that simply returns a single value, like the count of rows in a table, use this. ($database is a valid PearDB Connection)

function ScalarSQL($sql) {
    global $database;

    $result = $database->query($sql);
    if ($result instanceof DB_Error) {
        return false;
    }
    if ($row = $result->fetchRow()) {
        return $row[0];
    } else {
        return false;
    }
}

Everyone hates dealing with messy Apache error logs. Use this function to write to your own logs. It uses the current date to create a directory structure, and a new log file everyday. Make sure the directory pointed to by $ERROR_LOG_DIR has rights for the current user to create files and folders.

function LogError($str) {
    global $ERROR_LOG_DIR;

    $str = date("H:i:s") . " - " . $_SERVER["REQUEST_URI"] . " - $str\n";
    $filename = date("Y-m-d") . ".log";
    $logdir = "$ERROR_LOG_DIR/" . date("Y-m");
    if (!@file_exists($logdir)) {
        @mkdir($logdir);
    }
    @error_log($str,3,"$logdir/$filename");
}

The built-in PHP function does not return valid filesizes for large files, this does (only works on Linux).

function LinuxFilesize($filename) {
    @exec("filesize $filename",$printout,$return);
    return $return != '0' ? 0 : $printout[0];
}

Upload a file asyncronously to an FTP site. $locaFile and $remoteFile can be paths to files, or just filenames.

function FTPFileUploadAsync($localFile, $remoteFile, $ftpServer, $username, $password){
    # Set up basic connection
    $connID = ftp_connect($ftpServer);

    if ($conn_id) {
        # Login with username and password
        $loginResult = ftp_login($connID, $username, $password);

        if ($loginResult) {
            # Upload the file
            ftp_nb_put($connID, $remoteFile, $localFile, FTP_BINARY);
            return true;
        }
    }

    return false;
}

Makes a directory at the specified $path on an FTP server. It makes all directories in the $path to get to the last node in the $path. $conn has to be an active / open FTP connection.

function FTPMkDir($conn, $path) {
    $dir = split("/", $path);
    $path = "";
    $ret = true;

    for ($i=1;$i<count($dir);$i++) {
        $path .= "/" . $dir[$i];
        if(!@ftp_chdir($conn,$path)) {
            @ftp_chdir($conn,"/");

            if(!@ftp_mkdir($conn,$path)) {
                $ret=false;
                break;
            }
        }
    }
    return $ret;
}

Asynchronous PHP calls

July 30th, 2007

PHP has no built-in support for making Asynchronous calls similar to Ajax style requests. You can use functions such as fopen, file_get_contents, and stream_get_contents to open URLs and process them, but you would need to sit there and wait for the entire page to get done before you can move on with your life. Say you wanted to copy file from one folder to another, you would do something like this:

$source = "original file";
$dest = "destination file";
copy($source, $dest);

If you’re writing a webapp, or a webpage with PHP and want to copy a 2 or 3 MB file, that’s perfectly fine. It’ll be done within a few seconds and you can be on your way, but what about copying an 800MB file? You wouldn’t want your client sitting there for 3 or 4 minutes with a “processing” cursor, trying to figure out if his computer crashed. This is where you need to copy the file asynchronously.

To do this, you could make a page, say “processor.php” which accepts 2 parameters: source, dest.
processor.php:

$source = urldecode($_GET["source"]);
$dest = urldecode($_GET["dest"]);
copy($source, $dest);
echo "Done!";

Then you need to make something to call that page. The best approach is to use cURL for a pseudo-asynchronous call.
Set CURLOPT_TIMEOUT to be 1 second. This way, you will run this request, and it will timeout within 1 second, while continuing to process the requested page in the background.

$source = urlencode("original file");
$dest = urlencode("destination file");
$request = "fullURL/processor.php?source=$source&dest=$dest";$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $request);
curl_setopt($ch, CURLOPT_FRESH_CONNECT, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 1);
curl_exec($ch);
curl_close($ch);

Now you have an Asynchronous calling system for which you can keep making pages to make more and more processes asynchronous. Or, you can use a single page for all processes, and encrypt it while you’re at it (using any 2-way encryption algorithm you want, I’m using base64 for simplicity purposes).

Make a library.
Crypt.lib.php:

function EncryptRequest($func, $params) {
    $uri = urlencode($func);
    foreach ($params as $name => $data) {
        $uri .= "&&" . urlencode($name) . "=" . urlencode($data);

    }

    $enc = Encrypt($uri);
    return urlencode($enc);
}

function DecryptRequest($str) {
    $uri = Decrypt($str);
    $opts = explode("&&", $uri);
    # Add error checking here
    $func = array_shift($opts);
    $params = array();

    foreach ($opts as $opt) {
        $param = split("=", $opt);
        # Add error checking here
        $params[urldecode($param[0])] = urldecode($param[1]);
    }

    return array($func, $params);
}

# Any 2-way encryption algorithm works here
function Encrypt($str) {
    return base64_encode($str);
}

function Decrypt($str) {
    return base64_decode($str);
}

So, now your request looks something like this:

require_once("Crypt.lib.php");

function RequestAsync($func, $params) {
    $request = "fullURL/processor.php?request=" . EncryptRequest($func, $params);

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $request);
    curl_setopt($ch, CURLOPT_FRESH_CONNECT, true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 1);
    curl_exec($ch);
    curl_close($ch);
}

RequestAsync("CopyFile", array("source" => "original file", "dest" => "destination file"));

Now you have to do the opposite on the processor.php page:

require_once("Crypt.lib.php");

list($func, $params) = DecryptRequest(urldecode($_GET["request"]));

# Make sure the parameters are ordered properly
$rf = new ReflectionFunction($func);
$rps = $rf->getParameters();

$paramsOrdered = array();

if (is_array($params))
foreach ($rps as $rp) {
    $rpname = $rp->getName();
    $paramsOrdered[] = isset($params[$rpname]) ? $params[$rpname] : false;
}

call_user_func_array($func, $paramsOrdered);

function CopyFile($source, $dest) {
    copy($source, $dest);
}

echo "Done!";

I’ve left out a lot of the error checking for simplicity’s sake. When you implement this, you may want to error check the function names, parameter counts, parameter value types, files existing, etc…

I used base64 for the encryption because it’s simple, PHP has native support for it, and it’s fast. However, it is not secure. If you want to ensure that no one else can sniff out your request and forge other requests, you’ll need to change the encryption algorithm. Another idea, is to store these requests in the database, and simply pass an integer id for executing requests. When a request is picked up for execution, that database row is invalidated or deleted forcing a uniqueness to requests. This way, if someone sniffs out your request, and re-executes the same one, it won’t work. It’s up to you how secure you want the requests to be.

Flash 8 File Uploader Timeout bypass

July 24th, 2007

Flash has built in timeout support.

Apparently, it’s baked into the SWF and cannot be modified by anything other than eating through compiled SWF binary.

This timeout is very evident when you’re using a flash uploading component, and you’re uploading files of close to or over 60MB. This problem can be averted if you make a background process that tricks Flash. See, Flash thinks that while this long upload is processing, the script “times out”.

The following is one method I’ve used successfully to bypass this issue:

var timeoutInt:Number; // Interval handle

runner1 = function() {
clearInterval(timeoutInt);
var time:Number = Math.round(Math.random() * 10 + 10);
timeoutInt = setInterval(“runner2″, time);
}

runner2 = function() {
clearInterval(timeoutInt);
var time:Number = Math.round(Math.random() * 10 + 10);
timeoutInt = setInterval(“runner3″, time);
}

runner3 = function() {
clearInterval(timeoutInt);
var time:Number = Math.round(Math.random() * 10 + 10);
timeoutInt = setInterval(“runner1″, time);
}

Those functions will make a valid background process, but you also need to start and stop it:
file_fr is a FileReference object.
list_obj is a listener attached to file_fr.

list_obj.onComplete = function(){
clearInterval(timeoutInt);
}
list_obj.onCancel = function(){
clearInterval(timeoutInt);
}
uploadFile = function() {
runner1();
file_fr.upload(“targeturl”);
}

About

July 24th, 2007

This is an example of a WordPress page, you could edit this to put information about yourself or your site so readers know where you are coming from. You can create as many pages like this one or sub-pages as you like and manage all of your content inside of WordPress.