 |
|
Tuesday, July 1. 2008
After spending some time trying to figure out how I could move the 'My Account' menu link from the Navigation Menu within Drupal I soon came to realize that it wasn't budging.
After digging around for some solutions I came across the 'me' Aliases module. I must say, although simply in nature, its a nifty little utility module.
The way that it works is that where ever you place an me within the Path field for menu item the me will reference back to the current users uid.
Example: John Doe has a uid of 30.
In order for John to get to his account to manage his information he must go to:
http://domain.com/user/30 (without the me aliases module)
http://domain.com/user/me (with the me aliases module)
Here is a screen shot of what it will look like when you add a menu item using the module.
For additional information about this module or to download it, please visit Me Aliases Module.
Wednesday, June 25. 2008
 Most major websites nowadays have mobile versions. Some examples include Google, Yahoo, and Reddit. Up until recently, most mobile websites were built using the wap/wml (wireless application protocol) format. The WAP format can be thought of as a compressed version of HTML. If you view the source for the CNN site, you can see its header DOCTYPE set as so:
< ?xml version="1.0" ?>
< !DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.0//EN" "http://www.wapforum.org/DTD/xhtml-mobile10.dtd">
Most sites have a special URL dedicated to mobile devices (e.g. m.site.com, mobile.site.com). If the user decides to browse to site.com instead of m.site.com, some redirect code can be added to the main site to determine the user agent (browser), and redirect accordingly. Dev.mobi offers a PHP script to determine whether a browser comes from a mobile device, using variables such as php's $_SERVER['HTTP_USER_AGENT'].
Full HTML support is also being more widespread as more advanced mobile devices are hitting the market. Even as mobile browsers are becoming more indistinguishable from their full-size counterparts, and as cellular networks are getting faster, there are still several considerations to take seriously:
1. Download size is still an important issue.
There are people out there paying $2/MB for data usage. It's fine to show a small, compressed logo image, but if you stick on a useless 75KB stock image, you're going to inadvertently piss some people off. With that being said, code optimization is also extremely important. Many IA rules apply to both desktop and mobile websites (latest or featured content up top), but this matters more on mobile devices since it's often harder to scroll around and what not.
2. Design must be flexible
Mobile devices come in a myriad of different forms and screen sizes. Some browsers support javascript and/or Flash (lite), but not all. Fixed-width mobile sites are all but unheard of. If your site is complex in design and you'd prefer to keep it intact, then it may save headaches in the long-term by not supporting mobile versions and hoping that more iPhone-like devices (with full HTML support) roll their way onto shelves. To try out mobile versions without a lot of time invested, there are services such as MoFuse that'll automatically build a mobile website version for you.
Monday, June 23. 2008
This article is a valuable resource for clearly explaining how passwords, and other sensitive "secret" strings, can be stored in a database: A Hashing Primer. It may be useful in explaining non-technical users why we don't store passwords in the clear, that is, in a way that we easily know a user's password./p> A common task for any authentication system is to store and retrieve passwords. Doing this securely is key to building a system that is not only stable, but relatively safe in the even that it ever becomes unstable and allows potential attackers to view stored account information. Passwords should never (or rarely) be stored as plaintext: this is where one-way cryptographic hashing can save the day—or at least save plenty of difficult work.
Friday, June 20. 2008
Its amazing how simple this task is using PHP5, cURL, and SimpleXML
1:<?php
2:$ch = curl_init();
3:curl_setopt($ch, CURLOPT_URL, 'http://tech.forumone.com/atom.xml');
4:curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
5:if ($output = curl_exec($ch))
6:{
7: $xml = new SimpleXmlElement($output);
8:}
9:curl_close($ch);
10:?>
Monday, June 16. 2008
Sometimes it's just not wise to work on a database after midnight when your senses are not fully alert!
Anyhow, I don't know how I did this but I inserted 7,000+ records for a single content type (restaurants) half of which were duplicate entries.
After realizing that I can't perform a subquery against the same table to delete the duplicates I quickly turned to Temporary Tables (TT). The reason I used a TT was for the simple fact that I was temporarily using it and I was the only person who could use it. Additionally, once I was done fixing the issue I didn't want to have to worry about forgetting to drop the TT (keep in mind it is late, I'll forget anything). TT's are only accessible during a single db connection and drops when the connection is dropped reducing chances to pollute the db with garbage.
Some may be wondering, why didn't you just create a new table with a subquery selecting the distinct records to construct and populate a new table and then rename it. The reason being is that the 'records' table contains multiple content types with their own set of data and I didn't want to take any chances on messing them up.
So using a subquery SELECT DISTINCT tablefields FROM tablename WHERE criteria to select content type records I created a new TT along with the matching records.
CREATE TEMPORARY TABLE restaurants_temp
SELECT DISTINCT title,address,city,zipcode,state FROM records where datatype = 1;
Next, I dropped all of the original records from the 'records' table.
DELETE FROM records where datatype = 1;
Finally, I inserted all of the records from the 'restaurants_temp' table into the 'records' table via use of a subquery.
INSERT INTO records (title,address,city,zipcode,state)
SELECT title,address,city,zipcode,state FROM restaurants_temp;
Close connection and TT is dropped. Don't have to worry about the db being junked up with garbage.
Friday, June 13. 2008
Yahoo!'s Term Extraction Service can be used to extract significant words or phrases from some larger body of text. There are many uses for it, not the least of which is providing keywords, or tags in Web2.0 jargon, to help classify and organize a library of content. The following PHP script uses will use the Term Extraction service to analyze a PDF file. With a little more work, it could be expanded to work with Microsoft Word, Excel, and Powerpoint files. Extracting keywords automatically would be a helpful feature to build into your blog or CMS. There are modules to extract keywords for Drupal and Wordpress.
1:<?php
2:// discover where pdftotext tool is
3:$catpdf = trim(`which pdftotext`);
4:
5:// the PDF file to analyze
6:$source = 'http://example.com/my_file.pdf';
7:
8:// will copy file to a local temporary file
9:$temp_pdf_file = tempnam(sys_get_temp_dir(), "ek");
10:
11:// see below
12:download_file($source, $temp_pdf_file);
13:
14:// save text contents of pdf source to another temp file
15:$extract_file = tempnam(sys_get_temp_dir(), "ek");
16:exec($catpdf . ' ' . escapeshellarg($temp_pdf_file) . ' ' . escapeshellarg($extract_file));
17:
18:// fetch and output terms
19:$contents = file_get_contents($extract_file);
20:if ($terms = get_yahoo_terms($contents))
21:{
22: echo "\nYahoo terms for the file $source";
23: foreach ($terms as $term)
24: {
25: echo "\n$term";
26: }
27: echo "\n";
28:}
29:
30:// hide our footsteps
31:unlink($temp_pdf_file);
32:unlink($extract_file);
33:
34:/**
35: * Uses curl to copy $source to a local file $dest
36: * @param string
37: * @param string
38: */
39:function download_file($source, $dest)
40:{
41: $out = fopen($dest, 'wb');
42:
43: $ch = curl_init();
44:
45: curl_setopt($ch, CURLOPT_FILE, $out);
46: curl_setopt($ch, CURLOPT_HEADER, 0);
47: curl_setopt($ch, CURLOPT_URL, $source);
48:
49: curl_exec($ch);
50:
51: curl_close($ch);
52:}
53:
54:/**
55: * Uses curl to query yahoo term extraction service for meaninful terms
56: * @param string
57: * @return mixed, array on success or null on failure
58: */
59:function get_yahoo_terms($content)
60:{
61: $SERVICE_URL = 'http://api.search.yahoo.com/ContentAnalysisService/V1/termExtraction';
62: $app_id = 'F1_Testing';
63:
64: $ch = curl_init();
65: curl_setopt($ch, CURLOPT_URL, $SERVICE_URL);
66: curl_setopt($ch, CURLOPT_POST, 3);
67: curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
68:
69: curl_setopt( $ch, CURLOPT_POSTFIELDS, 'appid=' . $app_id . '&context=' . urlencode($content) . '&output=php');
70: $raw = curl_exec($ch);
71: curl_close($ch);
72:
73: if ($raw = unserialize($raw))
74: {
75: if (isset($raw['ResultSet']['Result']))
76: {
77: return $raw['ResultSet']['Result'];
78: }
79: }
80:}
81:?>
As a sample of what to expect, I used the script to look at Calculating CARMA: Global Estimation of CO2 Emissions from the Power Sector - Working Paper 145 and the list of terms returned is below. The list of words is fairly accurate, and even includes the name of one of the authors.
global estimation
geographical scales
carbon emissions
co2 emissions
global citizens
global poverty
david wheeler
rigorous research
power plants
power sector
fossil energy
poverty and inequality
solar wind
energy sources
monitoring system
keystrokes
groundwork
strengths and weaknesses
carbon dioxide
aggregation
Monday, June 9. 2008
At DCPHP, I saw several clever presentations about unit testing, which essentially reinforced what I already know: if I could go back in time and actually guide the development of the CMS we had built in-house, I'd structure it not to have clever self-references and three-level inheritance...which would, incidentally, make it something less than a three-year project to create unit tests for it.
But over the years, with many beginning developers coming through our doors, I've seen several things that the guys who advocate unit testing assume you, as a sane person, are doing...but mostly I've seen those things not being done. So here's an easy one:
<?php $sql = "SELECT id, name, type FROM people LIMIT 10 ORDER BY date_added DESC"; $result = mysql_query($sql); if (mysql_num_rows($result)) { // do stuff } ?>
Great, eh? Checking to see if there are actually rows returned before you do something, right?
But what if your query fails? Remember, you write code not just for right now, but for when you do things down the line. Just like you make unit tests to catch any screwups before you go to production, you can be testing any calls right inside your code. So later, when you add "just a quick fix" and screw up the query, it doesn't have to fail if, for example, it only fails when you pass a variable in and the tests you wrote originally don't cover that.
So always be paranoid about any queries you make:
<?php $sql = "SELECT id, name, type FROM people LIMIT 10 ORDER BY date_added DESC"; $result = mysql_query($sql); if (false === $result) { // handle gracefully } elseif (0 < mysql_num_rows($result)) { // do something } else { // handle the case of no data } ?>
Obviously, in the world of PHP 5 and OO, you should be using exceptions and try-catch, but it is amazing to me the number of developers who will blithely assume their queries are perfect and will remain so, and that there's no risk they'll ever see a production box. Testing is one of the first thing a schedule- or budget-pressed project will skimp on when resources go short. Most clients react less well to "we had to cut your feature, because the budget is running low" than "no problem with your feature, we'll just do some spot checks instead of the full-on testing process." That is, they will until you screw up and they discover a page is completely unusuable on a weekend when you're off paddling down a river.
So write fault-tolerant code. Make sure that even if your query fails, you handle it in a graceful way--and that you provide yourself enough information to quickly figure out what's going on on Monday and fix the thing.
Tuesday, May 27. 2008
Today, 37signals discussed how adding some padding to navigation links improves the user experience.
The end result is a feeling of comfort. It’s just really easy to click the links. It feels like the links are working with you instead of against you.
Its a simple application of Fitt's Law, which commenters were quick to bring up. Fitt's law has been around since 1954, and is used to predict how quickly someone can aim at target and its supported by a lot of experimental data.
The Microsoft Office team used it to analyze the usability of versions of Word and paid special attention to it during the redesign of Office 2007's user interface
First off, most controls in the Ribbon are labeled. This helps discoverability and usability considerably, but it also makes the buttons bigger and easier to target. As your screen resolution increases, the width of the Ribbon also increases, providing room for more labels and larger buttons.
Mezzoblue talks in detail about how it was used when the site went through its fifth redesign. More websites should use this simple technique to increase the clickable area around navigation links and buttons.
A larger clickable area means less precise mouse movement is required to focus on the link. Without impacting the design in any way (the halo is non-visible after all) the usability of the header improves. While the main goal is increased usability in this case, there are positive implications for accessibility as well. Those with motor skill disabilities may have difficulty using a mouse; larger areas to click makes the act easier, which makes a big difference to them.
Wednesday, May 21. 2008
I have a couple of projects that call for node content to be published outside of the normal center-of-the-page boundary. But the default template override system accounts for individual nodes, or even overriding the node display area for a given content type, but not the whole page. A Google search turned up some possibilities, but based on this page, I was able to find the right answer for Drupal 5.x, in templates.php: <?php function phptemplate_variables($hook, $vars = array()) {
| |