Archive for the 'Technology' Category

How to Undo a Friend Request on Facebook

Wednesday, October 1st, 2008

Have you accidentally friend requested someone, or otherwise just decided you want to revert adding him or her to your Facebook friends list? There is no explicit option to take back the addition, but it is possible — you have to block him or her, then unblock (if you wish to do so). This is how it’s done through the new Facebook layout.

To block a person, hover over the Settings link (toward the top-right corner to the left of the search box), and go to Privacy Settings. From there, under Block List you should see a search box. Search the person’s name, and you can find and block them in the search results that follow. To unblock them, click the (remove) link by their name on the Privacy Settings page. The pending friend request confirmation should now be purged.

Google Chrome Freezing — A Temporary Fix

Monday, September 29th, 2008

Well, Google Chrome has impressed me enough to make it my default browser already — though normally there will be Firefox windows open as well, at least while Chrome is in its current state of development.

If you use Chrome regularly, you may have had it pause or freeze on you… especially when a tab utilizes a plugin such as Adobe Flash or Adobe Acrobat. IT enthusiast and expert Alex Wells has discovered, and it is verified to work on my setup as well, an easy method that seems to unfreeze Chrome in most cases:

When Chrome freezes, simply right-click on the Chrome icon in the Windows taskbar. Note that if you have the “Group similar taskbar buttons” option enabled (it’s enabled by default), then you must first left-click to expand the group before right-clicking.

As far as we have seen, this method has (oddly) cured the freezes in Chrome. Keeping that in mind, hopefully an update will be released that addresses the freezing issue. Since the right-clicking method seems to work, maybe it will be a pretty easy patch!

PHP Tag Cloud Remove Common Words

Friday, July 11th, 2008

If you have an application that allows a user to enter arbitrary tags for an entity, you might want to filter their tag input based on certain criteria. This tutorial assumes that you will be working with a space-separated list of tags, e.g. from a form input field. If your tag input is coming from an array, you can try $tags = implode(' ', $tag_array); to prepare it for the rest of the code presented here.

Firstly, we’ll cover the tag filter. A list of the most common English words was compiled by merging data from the following resources:
http://en.wikipedia.org/wiki/Most_common_words_in_English
http://www.askoxford.com/oec/mainpage/oec02/?view=uk
http://esl.about.com/library/vocabulary/bl1000_list1.htm
http://www.deafandblind.com/word_frequency.htm

We can look at the whole list later for reference, but to make an effective tag filter, many had to be removed by hand. Additionally, this script disregards words with less than three letters, so those were removed as well. Many search applications, including the one that inspired this coding, won’t mess with those. Here is a space-separated list of the common English-language words to be filtered:

able about after again all also and any are bad been before being between but came can cause change come could did differ different does don't down each end even every far few for form found four from get good great had has have her here him his how into its just keep let many may might more most much must near need never new next not now off one only other our out over part put said same say seem set should side some still such take than that the their them then there these they thing this three through too two upon use very was way went were what when where which while who will with would you your

Let’s use the following example for a space-separated tag string:

fluffy is freeze you Rocket don't cute boulder fry

The following PHP code will filter out the common words as well as words that contain less than three letters. It only pulls strings with alpha characters, and additionally converts all tags to lowercase:

$tag_filter = array('able', 'about', 'after', 'again', 'all', 'also', 'and', 'any', 'are', 'bad', 'been', 'before', 'being', 'between', 'but', 'came', 'can', 'cause', 'change', 'come', 'could', 'did', 'differ', 'different', 'does', 'don', 'down', 'each', 'end', 'even', 'every', 'far', 'few', 'for', 'form', 'found', 'four', 'from', 'get', 'good', 'great', 'had', 'has', 'have', 'her', 'here', 'him', 'his', 'how', 'into', 'its', 'just', 'keep', 'let', 'many', 'may', 'might', 'more', 'most', 'much', 'must', 'near', 'need', 'never', 'new', 'next', 'not', 'now', 'off', 'one', 'only', 'other', 'our', 'out', 'over', 'part', 'put', 'said', 'same', 'say', 'seem', 'set', 'should', 'side', 'some', 'still', 'such', 'take', 'than', 'that', 'the', 'their', 'them', 'then', 'there', 'these', 'they', 'thing', 'this', 'three', 'through', 'too', 'two', 'upon', 'use', 'very', 'was', 'way', 'went', 'were', 'what', 'when', 'where', 'which', 'while', 'who', 'will', 'with', 'would', 'you', 'your', );

$tags = 'fluffy is freeze you rocket don\'t cute boulder fry';
preg_match_all('/([a-zA-Z]{3,})/', $tags, $matches);
$matches[0] = array_map('strtolower', $matches[0]);
$tags = array_diff($matches[0], $tag_filter);

The $tags array would then be filtered and lowercased, producing the following output with print_r($tags):

Array
(
    [0] => fluffy
    [1] => freeze
    [3] => rocket
    [5] => cute
    [6] => boulder
    [7] => fry
)

If you need to convert it from the array back to a space-separated string, try $tags = implode(' ', $tags);. You may also of course add more words to the word list — that could come in handy with other application-specific functions such as cursing filters.

Here is the full list of common words merged from the above-stated resources, separated by spaces, including those with less than three letters:

a able about act add after again air all also am an and animal answer any are as ask at back bad be been before being between big boy build but by call came can case cause change child city close come company could country cover cross day did differ different do does don't down draw each early earth end even every eye fact far farm father feel few find first follow food for form found four from get give go good government great group grow had hand hard has have he head help her here high him his home hot house how i if important in into is it its just keep kind know land large last late learn leave left let life light like line little live long look low made make man many may me mean men might more most mother move mr mrs much must my name near need never new next night no north not now number of off office old on one only or other our out over own page part people person picture place plant play point port press problem public put read real right round run said same saw say school sea see seem self sentence set she should show side small so some sound spell stand start state still story study such sun take tell than that the their them then there these they thing think this thought three through time to too tree try turn two under up upon us use very want was water way we week well went were what when where which while who why will with woman word work world would write year you young your

Javascript Age From Birthday Calculator

Tuesday, June 24th, 2008

Figured this might be handy to some folks… Just enter in a birthdate (or just any date for that matter, doesn’t necessarily have to be a birthday), and it will give you the number of years that have passed since then! Why use your head when we have computers!?

Month:

Day:

Year:
(YYYY)

UNIMPORTANT NOTE: This script uses the average number of days in a year as part of its calculation. If I hear about that causing any problems I’ll look into another way of doing it, but I think it’s the most reasonable, far more so than using a whole number like 365.

The JavaScript:

function getAgeFromBday() {
  var month = document.getElementById('bdc-month').value;
  var day   = document.getElementById('bdc-day').value;
  var year  = document.getElementById('bdc-year').value;
  var bdate = new Date(year, month, day);

  if (bdate.getDate() != day || bdate.getMonth() != month || bdate.getFullYear() != year) {
    alert('That date appears to be invalid!');
    return false;
  }

  var today = new Date();
  today.setHours(0);
  today.setMinutes(0);
  today.setSeconds(0);

  if (bdate > today) {
    alert('Provided date must fall before today\'s date!');
    return false;
  }

  alert(Math.floor((today - bdate) / 31556952000));
}

The markup:

<p>
  <strong>Month:</strong><br />
  <select name="bdc-month" id="bdc-month">
    <option value="0">January</option>
    <option value="1">February</option>
    <option value="2">March</option>
    <option value="3">April</option>
    <option value="4">May</option>
    <option value="5">June</option>
    <option value="6">July</option>
    <option value="7">August</option>
    <option value="8">September</option>
    <option value="9">October</option>
    <option value="10">November</option>
    <option value="11">December</option>
  </select>
</p>
<p>
  <strong>Day:</strong><br />
  <input name="bdc-day" id="bdc-day" style="width: 15px;" type="text" />
</p>
<p>
  <strong>Year:</strong><br />
  <input name="bdc-year" id="bdc-year" style="width: 30px;" type="text" /> (YYYY)
</p>
<p>
  <button type="submit" onclick="getAgeFromBday()"><strong>DO IT</strong></button>
</p>

See also the PHP function that returns an age from a birthdate.

.htaccess Add Trailing Slash to URL

Friday, January 11th, 2008

If you are rewriting your URLs using .htaccess and mod_rewrite, you might run into a problem where trailing slashes are ignored.

Here is an example scenario. Say you have PHP script that takes query arguments like this (replace example.org with your domain, of course):

http://example.org/index.php?page=content&title=welcome

You are rewriting it to look like this:

http://example.org/content/welcome/

For reference, here’s an example .htaccess script that would do that (there are plenty of other ways, this is one basic example):

RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/$ /index.php?page=$1&title=$2 [QSA,L]

And here is the problem with that! If you try a URL like this (without the trailing slash):

http://example.org/content/welcome

404! OH NOES!!

So, one alternative is to change the .htaccess directive to look like this:

RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/?$ /index.php?page=$1&title=$2 [QSA,L]

So now both of these URLs work!

http://example.org/content/welcome/, AND
http://example.org/content/welcome

BUT WAIT! Trailing slashes are prettier, and we don’t want two separate URLs for the same content! Google doesn’t even like that! OOOH NOOOES!!

How to force a trailing slash in the URL:

  • Make your rewrites require the trailing slash, as with the first example:
    RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/$ /index.php?page=$1&title=$2 [QSA,L]
  • Add this code below your regular rewrites:
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_URI} !(\.[a-zA-Z0-9]{1,5}|/)$
    RewriteRule (.*)$ http://example.org/$1/ [R=301,L]

Note: This should work no matter how your application handles URLs. Some applications, however, such as WordPress, pull in the entire URL query regardless of slashes. If your application inherently does not care about trailing slashes in its RewriteRules, then you must put these directives before your other rewrites. The reason for that is because if it goes after them, then the application’s own RewriteRule will go into effect, accepting the URL without the trailing slash, and will likely ignore any subsequent rules (because of the [L] flag). The reason I suggest in this tutorial to put the script after your normal rewrites is for optimization and that can only apply when your normal rewrites require a trailing slash. If the URL has a trailing slash, as is the desired scenario, then it should go ahead and apply the rules and not even bother looking at the redirection directive.

Another point to keep in mind is that this script will redirect and add a trailing slash even for bogus URLs. E.g. - if you type this hypothetical non-existent URL into the address bar:

http://example.org/garhtrehstreetreetree

It is going to rewrite and forward it to the following URL with a trailing slash:

http://example.org/garhtrehstreetreetree/

Before displaying the expected 404 error. Note that this is not typical behaviour of Apache server. However; I do not consider this quirk to be a big deal at all, and I believe that it is unavoidable since we are handling this with .htaccess.

Explanation of the directives and regular expressions:

The two RewriteCond lines test to make sure the requested filename is not an actual file or directory, respectively. This makes sense because we’re rewriting URLs here; the requested URLs are not actual files, they are references to actual files and query arguments.

The regular expression in the third RewriteCond does two things. It checks to make sure the requested “filename” does not have an extension. If for whatever reason it has an extension, then we probably don’t want to add a trailing slash! Note: This expression considers an extension anything that has a period followed by between 1 and 5 letters and numbers. That covers your .js and your .phtml, at least. Feel free to change the 1 and 5 to suit your application. The other thing the third RewriteCond does is make sure that the URL doesn’t already have a trailing slash. If it didn’t do that check, the redirect could attempt to loop forever and that’s annoying!

The last line, the RewriteRule, will capture the entire URL path. It then does a 301 redirect to the same URL path with a trailing slash appended.

IMPORTANT UPDATE (IMPORTANT UPDATE TO IMPORTANT UPDATE: IMPORTANT UPDATE NOW IRRELEVANT — SEE EVEN MORE IMPORTANT UPDATE): It appears that some configurations do not support lookaheads in regular expressions! I have a strong suspicion but do not know for sure that the issue is the Apache version. This tutorial should work fine for Apache 2, and may not work for Apache 1.3. That, specifically, was the case with the configurations I tested. Here is an alternative route if you are getting internal server errors with the original method:

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !(\.[a-zA-Z0-9]{1,5}|/)$
RewriteRule (.*)([^/])$ http://example.org/$1$2/ [R=301,L]

I do not think it is as pretty, but ya gotta do what ya gotta do!

EVEN MORE IMPORTANT UPDATE: After browsing the web for a minute, I came across another site that has a similar (but different) tutorial. After looking at the example therein, I metaphorically smacked my forehead and realized that a very small part of the original example (specifically, the lookahead ((?!/)) part) was not necessary, and it now works on all tested servers. The tutorial has been updated to reflect this change and is now even prettier than it was before. Thank you other person, I feel dumb now and hats off to you.

Installing Windows XP Professional SP2 on Acer Aspire 5315-2153 Laptop

Saturday, November 10th, 2007

This is for all of you who figured the Acer Aspire 5315-2153 (which comes packaged with Vista) would make a great Windows XP Professional notebook, for only $348 + the cost of windows… and then realized you had problems.

We have been receiving a large number of visits from folks with non-English-speaking configurations. As a convenience to visitors, we are offering a machine translation of this tutorial powered by N2H and Google Language Tools:  English flag French flag Portuguese flag Russian flag Spanish flag

There are two methods for getting this to work, one only requires the internet, a few minutes, and a CD burner. The other requires a USB floppy drive.

UPDATE: We’ve got a working modem driver now. Check out the tutorial on getting it up and running (should be installed last).

READ FULL TUTORIAL »

Working with server time in JavaScript (and PHP)

Friday, August 31st, 2007

Dealing with timezone offsets can be frustrating in any coding environment. It’s pretty easy to get the server time into the client side JavaScript using PHP. The following code will create a JavaScript Date object that holds the current server date/time:

<script type="text/javascript">
var dateServer = new Date('<?= date('F d, Y H:i:s') ?>');
<script type="text/javascript">

Once that’s done you can work with the dateServer Date object and it will reflect the server’s local date/time. However, what if I need to calculate the date multiple times on-the-fly (e.g. - from within in a loop)? That might come in handy for things like timer scripts. We’ll first need to know the offset between the server time and local time:

<script type="text/javascript">
var dateLocal  = new Date();
var dateServer = new Date('<?= date('F d, Y H:i:s') ?>');
var dateOffset = dateServer - dateLocal;
<script type="text/javascript">

dateOffset now holds the offset in milliseconds. Here’s how to make use of the offset for future JavaScript Date object instances:

<script type="text/javascript">
var dateLocal  = new Date();
var dateServer = new Date('<?= date('F d, Y H:i:s') ?>');
var dateOffset = dateServer - dateLocal;

var newDate = new Date();
newDate.setTime(newDate.getTime() + dateOffset);
<script type="text/javascript">

That’s it! The newDate variable now holds the server time using the calculated offset from the previous PHP date() call.

UPDATE: I have created an example script that illustrates its use in a recursive clock function. As a side note, the setTimeout() JavaScript function produced much better results than setInterval() for this particular application. setInterval() was causing it to skip seconds for some reason, possibly something to do with this computer being bogged down with processes. At any rate, while setInterval() looks cleaner and more elegant, it looks like setTimeout() is the ideal route for a live timer or clock script.

Great Circle Distance Function for Oracle

Thursday, March 29th, 2007

The Distance Learning folks wanted a provision to allow users to search for site locations based on the distance from a specified zip code. I did some research and found a few resources. Most of things I found use a single SQL statement, but that didn’t suit our needs so I made it into an Oracle function:

create or replace function gc_dist (lat1 IN number, lon1 IN number, lat2 IN number, lon2 IN number) RETURN number

is

A_lat number;
A_lon number;
B_lat number;
B_lon number;
delta_lat number;
delta_lon number;
pi number;
earth_radius number;
gcdist number;
distance number;

begin

pi := 3.14159265358979323;
earth_radius := 3963.189;

A_lat := (lat1 * pi) / 180;
A_lon := (lon1 * pi) / 180;
B_lat := (lat2 * pi) / 180;
B_lon := (lon2 * pi) / 180;

delta_lat := A_lat - B_lat;
delta_lon := A_lon - B_lon;

-- find great circle distance
gcdist := power(sin(delta_lat / 2), 2) + cos(A_lat) * cos(B_lat) * power(sin(delta_lon / 2), 2);

distance := ROUND(earth_radius * 2 * atan2(sqrt(gcdist),sqrt(1 - gcdist)), 1);

return distance;

end;

I found the “exact” Earth radius on Wikipedia. phpZipLocator was used to get it working with the site. I had to modify the functions a little to suit the specific needs of the application.

Using something like this requires a database of zip codes along with the corresponding latitude and longitude for each. One can be found on the phpZipLocator site, but I don’t think I ended up using that one. I did a lot of research to find the best free one, but unfortunately I don’t remember which one was used (here’s another). As far as commercial solutions, I thought this one looks like one of the better ones. I’ll likely get back into it at some point and look this stuff up again.

Here’s an example usage of the function:

  1. Extract the latitude and longitude from the given zipcode:SELECT lat, lon FROM zipcodes WHERE zip = :ZIP“:ZIP” is the specified zipcode and “zipcodes” is the name of the zipcode table.
  2. Build the first part of the query sans the WHERE clause. In the WHERE clause we’ll specify the radiusSELECT gc_dist(zipcodes.lat, zipcodes.lon, :LAT, :LON) AS distance FROM zipcodes“:LAT” and “:LON” represent the returned values from the first query
  3. Use phpZipLocator’s inradius function to build the WHERE clause that will restrict the results within the specified radius of the specified zipcode. Here is what’s inside the function (I don’t remember what I modified in the function so I’ll just paste the main part):WHERE (POWER((69.1 * (lon - :LON) * COS(:LAT / 57.3)), 2) + POWER((69.1 * (lat - :LAT)), 2)) < (:RADIUS * :RADIUS);“:LON” and “:LAT” were found in the first query, and “:RADIUS” represents the specified radius in miles.
  4. Put them together and run the query!
NOTE: This post is for my IT450 journal; we are required to write database-related journals and submit the URL at the end of the semester.

I Got to Help Install ODU’s Google Mini!

Friday, March 23rd, 2007

So ODU recently purchased a shiny new Google Search Appliance. We’re talking $10,000 worth of web-searching power. It came in a box with the Google logo on the site. Most rack server machines look the same — plain black rectagles in a shape that could probably be used as a food tray. If you look through the racks in the ODU server room you won’t see much variance in the mounted machines.

In comes the Google Mini. Needless to say, its shade of cerulean blue really sticks out in the racks. When you first fire it up it takes about 10 minutes to boot. Once it’s booted you interface it with a laptop using a crossover cable (it comes with some cables). We used my work laptop to set it up. I got to type in the configuration settings for it, but unfortunately I had to pass the computer to David when it came time to set the password :-(
The Google Appliance will soon replace the current Google Custom Search the site is using. Having an in-house search appliance will exponentially increase the flexibility, alowing us to tightly integrate current databases such as those for faculty directories, courses and calenders. The possibilities are endless. The operating system internals of the Google Mini are locked down pretty tightly; they still don’t want people finding out how their actual search algorithms work or what kind of database system they are using.

I took some pictures of the installation process and will probably post them at some point.

It came with a Google T-Shirt! At the next Web Group meeting they’re going to announce who gets it. *crosses fingers*

Google Mini
NOTE: This post is for my IT450 journal; we are required to write database-related journals and submit the URL at the end of the semester.

SQL Injection

Monday, March 5th, 2007

A major concern when developing database-driven websites is SQL Injection. Poorly written code could result in a malicious user running custom queries from his or her browser! Obviously the effects of that sort of security hole could be devasting for a web application.

Since I deal mostly with PHP/MySQL/Oracle, I’ll just be talking about PHP/MySQL/Oracle.

PHP has a built-in MySQL function called mysql_real_escape_string(). If that’s the injection-prevent method you go with, any time user-provided data from a URL query string is going into a database query it should be passed through that function.

If you’re using Oracle, the best and most elegant bet is to take advantage of the query binding features.

An example from the Wikipedia article on SQL injection (modified to fit the Oracle syntax):

$query = $sql->prepare("select * from users where name = :NAME");
$query->execute($user_name);

The execute member function here takes the value of $user_name and “binds” it to “:NAME” in the query. In other words it replaces the data stored in $user_name with the bind variable “:NAME.” However, before dumping it into the query it makes sure that the data in $user_name is not SQL. Regardless of the contents it treats it as query-safe data. When using native Oracle binds there is no need to use other functions such as mysql_real_escape_string().

NOTE: This post is for my IT450 journal; we are required to write database-related journals and submit the URL at the end of the semester.