jQuery - Select element cheat sheet

Wednesday, 25 March 2009

View Comments

Download your free select element cheat sheet. Contains references and examples for doing anything you need with select elements and jQuery.

The download link to the cheat sheet can be found here.

This document goes through different sections of the jQuery API and provides practical examples of how to do anything you need with jQuery and select elements.

This is the result of several hours of hard work and research, I hope you will get a lot of benefit from it.

Includes:
* Find the index of a selected option
* Set the selected option by value
* Set the selected option by text
* Insert a new option before or after another
* Get the text or value of the selected option

Please let me know if you have any positive or constructive feedback

You may also be interested in my new CSS jQuery cheat sheet here
more...

jQuery - AJAX post - tips and usability ideas

Wednesday, 18 March 2009

View Comments

I'm going to go over some usability tips and ways you can improve your web applications that use AJAX.

As you have seen from my previous posts I like using jQuery and jQuery has some fantastic easy to use built in functions that help with AJAX requests.

Today I'm going to look at a simple form submit, but will give you some tips on how to implement AJAX that will help relieve user frustration. AJAX is great when everything runs smoothly, but we need to be able to handle any errors that come up in a graceful way and we also want to avoid users from getting annoyed.

For a working example of a successful submit go here

For a working example of a failed submit go here

Here's some of the ways that users deal with problems in AJAX:

* Clicking on the submit button several times until something happens
* Leave the site in a rage and never coming back again
* Sitting there staring at the screen for a long, long, long time (believe me this happens, some people are patient enough to wait 5 - 10 minutes even if nothing is happening)
* Refreshing the browser several times hoping that this will resolve the issue
* Try and contact support

All of these are bad and here is why:
* The form may have already submitted correctly, but there was a problem with the response so submitting several times could result in multiple / partial inserts
* This should be obvious, you don't want your users leaving your site less than satisfied
* Your users should have a good idea of exactly what is happening and if there is a delay or problem they should know about it
* Refreshing the browser in most cases won't fix an AJAX problem, this is usally done out of frustration
* If you can keep your users informed this will stop them from having to jump straight on the phone to support every time there is a small issue

In this example, I'm going to go over some ways that you can avoid these issues from happening. The main reason these things happen is because the user is not aware of what is going on. This is one of the biggest problems with AJAX. A typical scenario; User fills out form, clicks submit and wheel starts spinning and then it doesn't go anywhere from there... what to do?

Here are some good practices when dealing with AJAX:
* Show the spinning disc, many users are familiar with it and it looks like something is happening even though it is just an animated gif.
* Have a precise message telling the user what is going on at each step e.g. Saving your data now etc.
* If the AJAX save fails then send the details to support, don't just wait till your user contacts you, this might not happen until it's too late. The support email should have the following:
- What data the user was trying to send
- The URL that was used when making the AJAX call including all paramters
- Any user details you have, you may want to follow this up
- The time and date that it happened so you can track frequency
* If the AJAX save fails give the user some options. Let them try and submit the form again, if you are sending support emails for failed attempts you can always remove duplicate entries. Provide them with a support email address or phone number or a send button to "send error report to support" or something similar. Your user will feel like something is being done even when something breaks.
* Show a success message that tells the user that everything was successful. Again, if you are handling requests properly in the back end you can report to yourself if there are any issues.

The first step in getting jQuery to handle your submit request is attaching a submit function to your form as such:

$("#ajaxsubmit").submit(function() {

// Do your processing here

return false;
}

The return false is necessary so that it doesn't pass through to the forms action. You should add a working action to your forms in case the user doesn't have javascript enabled.

Let's have a look at my submit function:

$("#ajaxsubmit").submit(function() {
$("#messages").html("<img class="ajaxloadimg" src="http://www.blogger.com/images/ajax-loader.gif" /></code><p class="save">Please wait, saving your data...</p>");

$("#ajaxsubmit input[name=submit]").hide();

$("#ajaxsubmit input").attr("disabled", "disabled");

// Perform your validation error checking here
var formdata = $("#ajaxsubmit").serialize();

$.ajax({
type: "POST",
url: "process_ajax.php",
data: formdata,
timeout: 2000,
error: function (XMLHttpRequest, textStatus, errorThrown) {
ajaxSubmitError(XMLHttpRequest, textStatus, errorThrown);
},
success: function (data) {
ajaxSubmitSuccess(data);
}
});

return false;

});

I start off by showing a message to the user to let them know that we are in the process of saving their data.
I then hide the submit button so they can't go crazy resubmitting over and over again. We will give them this option back if the save fails, otherwise we don't want them to hit that button again.
I then lock all the fields, I don't want to remove them completely. I personally like to see what is being submitted even after I have hit submit just in case I screwed up big time and need to make a call or send an email to fix things up. Having the fields visible allows the user to reconfirm in their mind that they have submitted the correct info. Again, these will become unlocked if the AJAX call fails.

The next step is serializing the data so that it can be posted and then the call to the AJAX function. jQuery makes this so easy to do, if you want to see what other options are avaiable go the jQuery AJAX API reference at this page. The three options to take note of are these timeout, error and success.

- timeout This will stop our users from having to stare at their screens if something went wrong. Regardless of whether there was an error or things are just going slow (to the user there is no difference, there is just a problem!) it will throw a timeout error after x number of miliseconds have passed and it hasn't received a success message.

- error Your error handling function. jQuery will pass back some information about the error. Once we get this we need to do two things: Let the user know on their screen, and let your support team know via email

- success If everything went well the success function is called. In here you should report back to the user with a success message and then you can decide either to clear or remove the form, you don't want there old data sitting there, that's asking for trouble. One good idea is to have a success message appear and then slowly dissapear, this is a good idea particularly when someone needs to make multiple entries on the same form, you don't want the old success message hanging around.
more...

jQuery - Select elements - tips and tricks

Friday, 13 March 2009

View Comments

I'm going to go over some useful things that you can use jQuery for to maniuplate and retrieve values from a form select element.

I won't be providing complete examples, but these should be straight forward enough for you to build a test page and cut and paste whatever is appropriate.

I have had several pages that I have used to find answers to common questions that keep recurring and I thought it would be good to stick everything here.

If there is anything missing that you would like to see added to this list just add it to the comments.

Please check out my latest post here where you can download a cheat sheet that contains the examples below as well as many others.

// Add options to the end of a select
$("#myselect").append("<option value='1'>Apples</option>");
$("#myselect").append("<option value='2'>After Apples</option>");

// Add options to the start of a select
$("#myselect").prepend("<option value='0'>Before Apples</option>");

// Replace all the options with new options
$("#myselect").html("<option value='1'>Some oranges</option><option value='2'>More Oranges</option><option value='3'>Even more oranges</option>");

// Replace items at a certain index
$("#myselect option:eq(1)").replaceWith("<option value='2'>Some apples</option>");
$("#myselect option:eq(2)").replaceWith("<option value='3'>Some bananas</option>");

// Set the element at index 2 to be selected
$("#myselect option:eq(2)").attr("selected", "selected");

// Set the selected element by text
$("#myselect").val("Some oranges").attr("selected", "selected");

// Set the selected element by value
$("#myselect").val("2");

// Remove an item at a particular index
$("#myselect option:eq(0)").remove();

// Remove first item
$("#myselect option:first").remove();

// Remove last item
$("#myselect option:last").remove();

// Get the text of the selected item
alert($("#myselect option:selected").text());

// Get the value of the selected item
alert($("#myselect option:selected").val());

// Get the index of the selected item
alert($("#myselect option").index($("#myselect option:selected")));

// Alternative way to get the selected item
alert($("#myselect option:selected").prevAll().size());

// Insert an item in after a particular position
$("#myselect option:eq(0)").after("<option value='4'>Some pears</option>");

// Insert an item in before a particular position
$("#myselect option:eq(3)").before("<option value='5'>Some apricots</option>");

// Getting values when item is selected
$("#myselect").change(function() {
alert($(this).val());
alert($(this).children("option:selected").text());
});
more...

User Voice and SSO behind a firewall

Thursday, 12 March 2009

View Comments

Hi,
Our company is having a trial for a User Voice system that will be used for our internal apps and other things.

We ran into a few issues while getting our User Voice account up and running. The main problem was getting our User Voice to work from behind a firewall. We have a very strict security policy and we wanted to perform
the security checks using our SSO system from behind our firewall on our intranet and then forward users on to our User Voice account as an authorised user.

The problem: Up until a couple of weeks ago there was no way of doing this with a User Voice account. In order to get SSO to work with User Voice it was only possible using a cookie and to have domain aliasing set up.
This meant that we could not use our intranet to forward authorised users on to our User Voice account as the domain forwarding needed a valid contactable URL. (Note: There is also an API that you can use to validate and create users, but we had little success with getting this to work.
I could not find a working example anywhere and could only get back errors. Although I did manage to query an existing user.)

After sending an email to the team at User Voice they got back to us immediately with a solution! They were just in the middle of implementing a solution and had almost finished.

The solution is simple and easy to implement. Here is how it works:

* Validate the user behind the firewall
* Use your Account Key and API Key provided to encrypt the user information
* Pass the encrypted data along in the URL to your User Voice account
* This will either create the user if it doesn't exist or will log the user in automatically
* A cookie is generated on the user machine so they can access different areas of the site as necessary
* SSO attempt is also logged in the admin area
* User activity is also logged in the admin area

These are the steps that are used for passing our validated user through to our User Voice account:
* User goes to a specific URL on our intranet that has our php script to create the forwarding URL. The user includes a variable in the URL indicate which uservoice forum they would like to go to.
* User is validated by our SSO system
* Database query is performed to verify user has an active account, retrieves their email and display name (this could also be used to restrict access to a specific forum based upon user access level and the forum they specified in the URL)
* If user is valid send them through to the specified forum, if no forum was specified pass them through to our User Voice account general page
* User is created / logged in at User Voice site and log in attempt is recorded


Before I get in to the example, here is the link to user to view the SSO setup pdf which is good background information and will also explain about what attributes can be passed for the user, remember to replace:

http://uservoice.uservoice.com/files/sso_setup.pdf

Also note that SSO is only available for certain levels of account. Make sure you have a valid account and you can log in normally using your administrator account before trying to set up SSO.

Your Account Key and API Key are available from the Admin section under Setup >> Single Sign-On e.g. yoursite.uservoice.com/sso

OK, now let's see our example:

Firstly I set up a couple of functions; validateUser and getEncryptedData

validateUser is used to query the database and retrieve some details about our user base upon their $_SERVER['AUTH_USER'] value. Here is an example, but you will definitely need to cusomtise this.


function validateUser($auth_user, &$sqlsrv) {
if (!$auth_user) return false;

$strSQL = "SELECT * FROM person WHERE AuthName = '{$auth_user}' AND Active = 1";
$user = $sqlsrv->fetchrow($strSQL);

if ($user) {
return $user;
} else {
return false;
}
}


Here is getEncryptedData. Note, most of this comes from the SSO setup pdf document.


function getEncryptedData($username, $email, $displayname) {
$password = "<< ACCOUNT KEY GOES IN HERE! >>";
$salt = "<< API KEY GOES IN HERE! >>";
$salted = $salt . $password;
$hash = hash('sha1',$salted,true);
$saltedHash = substr($hash,0,16);

$expires = date('Y-m-d 00:00:00', strtotime('+14 days')); // expire the cookie in 14 days from now

$iv = "OpenSSL for Ruby";
$data = array(
"expires" => $expires,
"username" => $username,
"email" => $email,
"display_name" => $displayname
);
$data = json_encode($data);
// double XOR first block
for ($i = 0; $i < 16; $i++) {
$data[$i] = $data[$i] ^ $iv[$i];
}
$pad = 16 - (strlen($data) % 16);
$data = $data . str_repeat(chr($pad), $pad);
$cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128,'','cbc','');
mcrypt_generic_init($cipher, $saltedHash, $iv);
$encryptedData = mcrypt_generic($cipher,$data);
mcrypt_generic_deinit($cipher);
$encryptedData = urlencode(urlencode($encryptedData));

return $encryptedData;
}


So call validateUser first to validate the user and get their details.

Then pass these details in to getEncrypted data.

The final step is moving your validate user on (or some other message if they didn't validate).



$enckey = getEncryptedData($username, $email, $displayname);

header("Location: http://yoursite.uservoice.com/pages/{$forum}?sso={$enckey}");


Take note that the encrypted data goes into the sso variable of the URL.

I also add the following:


if (!$forum) $forum = 'general';


So that we just pass the user on to the main page if they didn't specify which forum they want to go to.

Filling in the rest should be straight forward.
more...

sqlsrv - How to install sqlsrv and fix MSSQL/php 255 multibyte issue

Tuesday, 3 March 2009

View Comments

For anyone looking for a solution to multi-byte characters and getting more than 255 characters from MSSQL and php and you don't want to read the background details, just skip down to the line that starts with "OK now on to the implementation..."

Well it's been a long time in between posts. Especially with our tight release cycle and family commitments.

Since I started working here we've had a whole bunch of issues trying to get multi-byte characters working with MSSQL and php.

The main issues with php and MSSQL are:
* Getting more than 255 characters from a varchar / nvarchar field.
* Getting nvarchar(MAX) and ntext fields to work at all
* Storing and retrieving multi-byte characters in combination with the above. (Using CAST or CONVERT to string seems to kill the multi-byte characters)
* Having to specify a collate when retrieving multi-byte characters
* When specifying a collate, can only display one type of characters e.g. can only show Thai characters and not Thai + Chinese characters in one field (believe me there are practical uses for this esp. in places like Malaysia & Singapore).

I won't go through all the dramas we've been having in detail as I'm sure if you are interested in this post you more than likely have gone through some of them yourself.

It has been suggested that http://www.freetds.org/ works. I am only mentioning it so that others who have had success with it won't say "Why don't you use freeTDS". Also note that we are using Windows servers here and I couldn't find any good documentation or solid examples of getting it to work on a Windows server. If you have any useful links then please add them in the comments section for others to try.

Our application is designed to run around the world and we have localized versions in many different countries. Part of our software is entering in details that are often long, descriptive and more than your average 255 characters so it was important that we got this working.

If you've read my other posts you will know that I am working on converting this application from asp to php. As asp and MSSQL play nice together there was no hassles retrieving these fields before, but since changing over to php we've had this issue and I don't want to keep half of the app in php and half in asp.

I'm hoping this solution is easy to implement so others that are having the same frustrations can get this solved in a matter of minutes. If you search around the web the most common answer is to use CAST or CONVERT which is an easier solution than mine, but will work fine only if you are using English.

OK now on to the implementation...

Firstly, before we go any further, I mention in this post installing software and drivers that come from third party sites. Just a disclaimer: Read all licenses, back up everything and don't install anything without consideration, don't do it just because I said so. Even in the best of conditions your machine may decide it's time to stop working. Do everything at your own risk.

My solution is using Microsoft's SQL Server 2005 Driver for PHP. The current link to the documentation is here. The link to download the driver is here.

The very first step is changing you php ini, so that this will allow you to retrieve the larger data from the database. It has been suggested that if these are left as the default they may result in the result being truncated:
mssql.textlimit = 65536
mssql.textsize = 65536
There is no recommended size here so you can play around with values, but just make sure they are not truncating by default.

Next follow the instructions provided to load the sqlsrv dll driver which is in the documentation under "Loading the SQL Server 2005 Driver for PHP".

It doesn't mention it anywhere, but if the driver has loaded correctly you should be able to do a search on your phpinfo page for the text sqlsrv and find something, then you will know that it has loaded.

Another thing to mention is that you may have troubles with connecting to the MSSQL database even after all this has done. The error I'm referring to is:
    [0] => Array
(
[0] => IM002
[SQLSTATE] => IM002
[1] => 0
[code] => 0
[2] => [Microsoft][ODBC Driver Manager] Data source name not found and no default driver specified
[message] => [Microsoft][ODBC Driver Manager] Data source name not found and no default driver specified
)

It took me a lot of digging round on Google to find out how to fix this. Especially as it already worked for me on our dev server and only decided to drive me insane on the day we pushed our BETA release <sigh>.

Here is a link to the post where someone else was having similar problems and after some painstaking reading through I managed to find the solution. The main problem is this: "The SQL Server Driver for PHP requires the SQL Server 2005 version of the SQL Native Client"

To fix IM002, follow this link here. If that link is no longer current search for something similar to "Feature Pack for Microsoft SQL Server 2005", there is also one for 2008. Search for the download link on the page for sqlncli.msi. Download and (at your own risk! back everything up!) install. That should fix this problem and you should be able to connect without problems if IIS and you MSSQL server are configured correctly.

Ok, now all the major dramas should be out of the way. You should now be able to, using the API provided in the documentation do everything you need to without issue. If you are still having issues let me know in the comments and I will see if I can point you in the right direction. Although I won't be able to help you with configuring your Windows or MSSQL database servers, these should be configured correctly before going anywhere with the sqlsrv driver.

Next I would like to go over my implementation of sqlsrv and the abstraction class I built to make things easier to use in php. I haven't benchmarked this against anything so please do your own testing for suitability before just plugging it in to your system, I'm providing this as an example only. I appreciate all feedback and positive criticism. Please let me know if there are any improvements you would make so we can all learn.

You can view / download the source here. Just remember to replace the <pre> tags with php tags before use.

One thing to note about the sqlsrv driver is that it retrieves dates as objects when calling sqlsrv_fetch_array which makes it incredibly slow. The way that I have gotten around this is retrieving each of the fields for every row as sqlsrv_get_field($stmt, $key, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR)), this seems to work a hell of a lot faster.

The two main functions are fetchrows and fetchutf8. The fetchutf8 function differs in that it takes the $fields variable which are the variables that are multibyte characters that need to be retrieved as utf-8, this requires an extra check so I split the functions for speed purposes.

I hope this is useful and saves people a lot of time and frustration. If you have ideas for improvements please share with everyone.
more...