CSS

5 Tips to Write Better CSS

I’m not normally a fan of crosslinking to other sites, but I ran across a really good article for people new to CSS that may need a little direction on how to organize their CSS. These are things that would have been hugely helpful to me as an early user of CSS. You can check the article out here.

openview

Working with the OpenView Database Part 1

To be a rockstar administrator of an OpenView installation, you’ll need to be comfortable working with the raw data in the OpenView database. This article is going to cover the basics of the OVO database and which tables you should be familiar with.

To begin, connect to your OVO database using SQL Server Management Studio.

The main tables that we are going to be looking at are:

OV_MS_Annotation - The table that stores annotations for any message in the console
OV_MS_Message - The table that stores all the messages and their related information
sto_ov_managednode - The table that stores all the data on the nodes managed by this management server
sto_ov_nodegroup - The table that stores all the data about the node groups for this management server

OV_MS_Message - Many of the fields in this table are self explanatory and we won’t be going into much detail here. The key fields/columns in this table are:

  • Id - The guid of the message. This value corresponds to what you see on the General tab of the Message Properties dialog box.
  • State - This corresponds to the current state of the message and is an integer value and the available options are: 2 - Unowned, 3 - Owned , and 4 - Acknowledged.
  • TimeOfStateChange/TimeOfStateChangeTimeStamp - The time corresponding to the last time that the message’s state change
  • NodeName - The guid of the node that this message is for.
  • MessageGroup - The message group for this message. Corresponds to the value also viewable in the console.
  • Object - The object for this message. Corresponds to the value also viewable in the console.
  • Application - The application for this message. Corresponds to the value also viewable in the console.
  • Severity - The severity level of this message. This is an integer value and the available options are: 2 - Normal, 4 - Warning, 8 - Minor, 16 - Major, and 32 - Critical.
  • Text - This is the body of the message text for the message.

OV_MS_Annotation

  • MessageId - This is the guid for the corresponding message that this annotation is for.
  • OrderNumber - The sequential number corresponding to the order of the annotations.
  • Text - The annotation text.
  • UserOfAnnotation - The user that created the annotation.
  • TimeCreated/TimeCreatedTimeStamp - The time the annotation was created.

sto_ov_managednode

  • object_text - A text field containing all the data about the node including the AgentId, caption, and CommunicationPath if necessary.
  • name - The guid for the node. Also referred to as NodeName in other tables.

sto_ov_nodegroup

  • object_text - A text field containing all the data about the node group including the description and caption.
  • name - The guid for the node group.

Stay tuned for Part 2 as we start talking about how to write queries to pull the data that you need.

openview

New Content: HP OpenView

Starting today, I am going to be expanding the content of this site to include information about working with and administering an HP OpenView environment. By day, I manage an enterprise level monitoring infrastructure running HP OpenView/Operations Manager and there seems to be a discerning lack of information on the internet with relation to some of the finer points of OVO. I plan on filling in some of that gap so look for those posts if that is your cup of tea.

Database, SQL

Sort Data with NULLs at the Bottom in Microsoft SQL Server

One of my clients has a reporting system that orders the data by a column that is a date. They wanted the sort to be ascending, but this places columns with a NULL value at the top followed by the data ascending. They wanted the NULL dates to appear at the bottom. To achieve this, you have to use a syntax that I’ve never used before. Here is the original query:

SELECT * from Jobs as j ORDER BY Date ASC

Here is the modified query that makes use of the CASE statement:

SELECT * from Jobs as j ORDER BY CASE WHEN Date IS NULL THEN 1 ELSE 0 END, Date ASC

The revised query does the initial sort based on the result of the CASE statement and then based on the value in the Date field. The CASE statement itself returns a value of 1 if the Date field is NULL and 0 otherwise. The result of this is that all of the rows with data in Date are first, ordered by Date followed by the remaining rows. It works great and produces the exact result that the client was after.

For additional information on the CASE statement, you can check out this Database Journal article.

Javascript, PHP

jQuery Superfish Menu and Jeditable in IE7

Since I’m carrying on a love affair with jQuery right now, I’m moving most of my customers to jQuery based plugins to simplify the JS code that I am currently using on their sites. One customer happens to make extensive use of the jQuery Jeditable plugin for dynamic/ajax based content updates. One issue I ran into with IE7 was that with the previous menu system, FSMenu, the Jeditable objects would overlap the menu making it unusable. I tried everything under the sun to make it work and finally just dropped FSMenu in favor up the awesome Superfish plugin.

Unfortunately, Superfish had the same problem with the Jeditable objects overlapping the menu system. I tried tweaking the z-index on both of these items with no luck. Firefox displays these properly so I needed to find a workaround for IE since most of the users are still using IE.

The solution turned out to be super simple. All of the fields I was using Jedtiable with had the class “editableNote” assigned to them so it was going to be easy to target just these items. I figured I would just hide them from view when the Superfish menu was activated and show them again when it was hidden. I didn’t want this to be the case in Firefox, just IE.

To make this change, open up your superfish.js file and look at the top where the superfish function is defined. Here is the full snippit of code that we are working with:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var sf = $.fn.superfish,
	c = sf.c,
	$arrow = $(['<span class="',c.arrowClass,'"> &#187;</span>'].join('')),
	over = function(){
		var $$ = $(this), menu = getMenu($$);
		clearTimeout(menu.sfTimer);
		$$.showSuperfishUl().siblings().hideSuperfishUl();
	},
	out = function(){
		var $$ = $(this), menu = getMenu($$), o = sf.op;
		clearTimeout(menu.sfTimer);
		menu.sfTimer=setTimeout(function(){
			o.retainPath=($.inArray($$[0],o.$path)>-1);
			$$.hideSuperfishUl();
			if (o.$path.length && $$.parents(['li.',o.hoverClass].join('')).length&lt;1){over.call(o.$path);}
		},o.delay);	
	},
	getMenu = function($menu){
		var menu = $menu.parents(['ul.',c.menuClass,':first'].join(''))[0];
		sf.op = sf.o[menu.serial];
		return menu;
	},
	addArrow = function($a){ $a.addClass(c.anchorClass).append($arrow.clone()); };

What you will want to look for is the over (line 4) and out (line 9) sections of the above code snippit. Since we only want to target IE, we are going to include the following 2 commands to show and hide the Jeditable objects on the over and out events for the Superfish menu.

To hide it:

if ($.browser.msie) $(".editableNote").hide();

To show it:

if ($.browser.msie) $(".editableNote").show();

Remember to change “editableNote” to whatever common class you are also targeting for your Jeditable objects.

Here is the updated snippit with the show and hide functions in place.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var sf = $.fn.superfish,
	c = sf.c,
	$arrow = $(['<span class="',c.arrowClass,'"> &#187;</span>'].join('')),
	over = function(){
		var $$ = $(this), menu = getMenu($$);
		clearTimeout(menu.sfTimer);
		if ($.browser.msie) $(".editableNote").hide();
		$$.showSuperfishUl().siblings().hideSuperfishUl();
	},
	out = function(){
		var $$ = $(this), menu = getMenu($$), o = sf.op;
		clearTimeout(menu.sfTimer);
		menu.sfTimer=setTimeout(function(){
			o.retainPath=($.inArray($$[0],o.$path)>-1);
			$$.hideSuperfishUl();
			if ($.browser.msie) $(".editableNote").show();
			if (o.$path.length && $$.parents(['li.',o.hoverClass].join('')).length&lt;1){over.call(o.$path);}
		},o.delay);	
	},
	getMenu = function($menu){
		var menu = $menu.parents(['ul.',c.menuClass,':first'].join(''))[0];
		sf.op = sf.o[menu.serial];
		return menu;
	},
	addArrow = function($a){ $a.addClass(c.anchorClass).append($arrow.clone()); };

A working before and after demo is forthcoming.

Javascript, PHP, Programming

Validating reCaptcha with jQuery and AJAX

I recently did some work for one of my clients that involved securing some simple web forms. These forms were to request literature or request a call back, simple stuff. Incidentally they were getting hammered with spam. The clients didn’t seem to care for this so they asked me to stop it.

My first move was to setup some sort of Captcha. I settled on reCaptcha and got to work on implementing it. I’d be on a bit of a jQuery kick lately so I wanted to see if I could validate the Captcha fields dynamically and only submit the form if they matched. It turned out that this was much easier than I’d thought.

To make this work, we’ll be using the following files:

  • recaptcha.php - The page that holds our form
  • ajax.recaptcha.php - The file that handles the ajax requests and outputs the result

Prerequisites

  1. The first thing we’ll do is setup our form. I’m going to assume you already have your form in place and that you are merely trying to secure it.
  2. Next, you’ll want to download the newest version of jQuery. You can put this file wherever you normally store your js files and include this in your page.
  3. Now it’s time to get yourself a reCaptcha keyset. A keyset is nothing more than a public and private key that allows you to use the reCaptcha service. It’s free. Head on over to http://recaptcha.net and click “Get reCaptcha” and fill out the necessary fields. Under domain, either fill out your site’s domain or set this up as a global key if you plan on using reCaptcha on multiple domains. Make a note of your public and private keys.

Now it’s time to put this all together. First we have to get our form to display our reCaptcha challenge field so the user can actually submit the form. My site uses PHP so I’ve downloaded the reCaptcha PHP Library and stored it on my server. At the top of my page, I have included this PHP file and setup my public and private keys. On this page, you really only need the public key, but I went ahead and included both so I don’t lose one of them. Here is what the top of my recaptcha.php looks like (with my keys blanked out):

recaptcha.php

require_once('/inc/recaptchalib.php');
$publickey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXX"; // you got this from the signup page
$privatekey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXX";

Next, you will include this in your page where you want the reCaptcha box to appear in the form:

recaptcha.php

echo recaptcha_get_html($publickey);

Next, we need to write our function that handles the dynamic/AJAX reCaptcha validation for us. I’ve done that already and you can see it below:

recaptcha.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function validateCaptcha()
{
    challengeField = $("input#recaptcha_challenge_field").val();
    responseField = $("input#recaptcha_response_field").val();
    //alert(challengeField);
    //alert(responseField);
    //return false;
    var html = $.ajax({
    type: "POST",
    url: "ajax.recaptcha.php",
    data: "recaptcha_challenge_field=" + challengeField + "&amp;recaptcha_response_field=" + responseField,
    async: false
    }).responseText;
 
    if(html == "success")
    {
        $("#captchaStatus").html(" ");
        // Uncomment the following line in your application
        return true;
    }
    else
    {
        $("#captchaStatus").html("Your captcha is incorrect. Please try again");
        Recaptcha.reload();
        return false;
    }
}

The above function grabs the reCaptcha data, submits it via a POST request to a file called ajax.recaptcha.php on our server, and waits for the repsonse. When it gets the response it either updates the status telling your user that the captcha didn’t match and then it reloads a new captcha for them, or it submits the form. The running demo code is slightly different to prevent the form from actually being submitted.

The last step to this part is to make sure your form runs this function when the user clicks that submit button. To do this, update the form’s opening tag and add an onsubmit event like this:

recaptcha.php

form name="loginform" id="loginform" action="#" method="post" onSubmit="return validateCaptcha()"

You’ll notice that we return true or false from the validateCaptcha() function. This controls if the form is submitted or not and is important for making this work.

Our final step in this process is to build the ajax.recaptcha.php file that we mentioned earlier to handle the requests. I’ve done this already and here is the final product (again with my keys blanked out):

ajax.recaptcha.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
require_once('/inc/recaptchalib.php');
$publickey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXX"; // you got this from the signup page
$privatekey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXX";
 
$resp = recaptcha_check_answer ($privatekey,
                                $_SERVER["REMOTE_ADDR"],
                                $_POST["recaptcha_challenge_field"],
                                $_POST["recaptcha_response_field"]);
 
if ($resp->is_valid) {
    ?>success< ?
}
else 
{
    die ("The reCAPTCHA wasn't entered correctly. Go back and try it again." .
       "(reCAPTCHA said: " . $resp->error . ")");
}

This file outputs only the word “success” if the captcha matches and a message and the response from reCaptchta if it fails. This is important because we are looking for the word success in our validateCaptcha() function.

You can see the functioning demo for this or download a zip file of the files used in the demo including full PHP source code.

Site News

New Blog

Most of my posts over the past few months have been of a technical nature and it’s time for this blog to officially find it’s place. From this point forward I will be focusing my writing efforts on PHP, jQuery, or whatever else I am working with at the time. My new personal writing will be located at http://chris.rascotopia.com

Stay tuned.

Life in General

Taking a Break

No, I promise that I didn’t die or forget about this site. The reality is that in the past few weeks I’ve been swamped with freelance work, taking a much needed vacation, and working on some of my personal sites to improve them. I’ve got a few things lined for this site and I’ll try and finish them up in the next week or so. Until then, stay tuned.

Internet

What is your preferecens?

I don’t know what mine is either, but apparently Yahoo was unable to save them:

Movies

The Dark Knight

Last week the wife and I went to dinner and went to see The Dark Knight. I don’t know that I would really call myself a huge fan of the Batman movies. I mean, sure, I enjoy them, but I’m not up to speed on the whole Batman saga.

That said, I have always liked Heath Ledger and I really wanted to see his final performance and boy did he deliver. I don’t want to take anything away from the original Joker, Jack, because The Dark Knight had a totally different tone than the first movie with Michael Keaton. Let it be said though, Ledger owned that part. He was by far the most unpredictable villian I’ve seen on the big screen in a long time and I honestly hope the man wins an Oscar for his part.

i think that he did such a good job only serves to make his death even more tragic. That his life was cut short is sad, but that we are all deprived of the best that he had to offer is even worse.

If you haven’t seen the movie, you need to.

Older Entries »