Thursday, July 30, 2009

Smarty syntax error: unrecognized tag 'dynamic' and Block functions

I ran into this error while working with Smarty caching.  This error is caused by a "dynamic" block function not being registered with Smarty.  A block function is a function which essentially will take a section of content and execute a function on that content.  Because of this, Smarty will not cache it.  Notice in this example the smarty_block_dynamic function simply returns the content.  Because it was defined as "dynamic" you can use the Smarty dynamic tags to wrap content and avoid the caching engine.

This is not a default tag in Smarty and will fail if you have not registered the block function as provided in the example.

Tuesday, July 21, 2009

Feedburner Custom URL

Feedburner offers a custom URL service which will allow for you to link to any domain/subdomain combination instead of feeds*.feedburner.com. The good news is anyone can do it. You can find the Branding settings by logging info feedburner and clicking "My Account" and then "My Brand".

Google will then offer instructions on how to do this. On a shared hosting service you can submit a support request to have the DNS entries made for you, on a dedicated/self hosted you can do it yourself. These changes have to resolve just as any other DNS, so the effects are not immediate.

Note: I just changed web hosts and it appears that the DNS entry url that Google provided changed as well. That leads me to believe that if your primary domain changes nameservers that Google would issue a new entry value.

Monday, July 20, 2009

How to execute PHP files via cron in CPanel

There are two ways that I know of (I'm sure there's more) to execute php files within cron commands.

php -q /home/USER/public_html/cron/path-to-file.php

This first method will utilize relative paths. Meaning if within this file you utilize a require_once(../file.php); it will load the file accordingly. Whereas if you use the second route

/usr/bin/php -q /home/USER/public_html/cron/path-to-file.php

you must utilize absolute paths within your file. Including a file using a relative path (like ../) will throw an error, you must define the full path of the file. Save yourself a headache when you go to transfer hosts, utilize the first method.

Wednesday, July 15, 2009

Why do web developers support outdated browsers?

At the date of this post, IE 8 has been out for 4 months and IE 7 for 2.5 years.  Why am I still seeing threads about designing for IE 6?

The reality of it is that your average person knows little about what a browser is, and why it's important to keep your software up to date.  Part of this reason is that nothing ever breaks when they are running outdated technology.  Developers need to keep moving the web forward and stop supporting older browsers.  A step in the right direction is telling your user when their browser is out of date.  In our next update, users not using the latest major version of Chrome, IE, and Firefox will have a notice on their screen explaining that their browser is out of date with a provided link on how to upgrade.

By implementing features like this, you're saving yourself time and educating your visitors.

UPDATE: The day after I posted this Google announced they are discontinuing support for IE 6.

Tuesday, July 14, 2009

CSS Syntax Formatting Standards

Designing for readability is very important. Not only can it cut down on your own development time when you perform maintenance or make improvements to older sections of your site that you haven't seen in a while, but it also makes it much easier for other developers to read your code.

Before we get into standards its important to note that tabbing, comments, and spacing increase the file size of your style sheets. Before you implement these kinds of standards, ensure you have some kind of compression in place (I recommend Minify for both CSS and JS) to keep the file size under control. Minify is extremely important for shared hosts such as HostGator that do not permit gZip compression.

Tabbing
Always tab/nest your code accordingly. Below is a code snipplet from some CSS on one of our main templates. Notice it's easier to read when it's tabbed as you can quickly determine which elements are children of which. In a larger stylesheet this really helps because you can see #hd .nav and know that's not what you need and skip down to the next section.

#hd .nav {
  border-color: #223f4a;
  border-style: solid;
  border-width: 1px 1px 0 1px;
 }
  #hd .nav ul {
   margin-left: 98px;
  }
   #hd .nav ul li {
    float: left;
    margin: 2px 0 6px 0;
    padding-right: 2px;
    position: relative;
   }
CSS Selectors
Using the same snipplet above, you'll notice that our CSS Selector's anchor is unique and not a class. When available, use a unique anchor rather than a class. This will help prevent CSS from effecting elements it was not intended to effect.

Also notice that within our selector, while the 3rd child could very easily be called #hd li instead of #hd .nav ul li, because of our tabbing and to increase readability we generally include tree elements in the selectors. This will also ensure that it only effects exactly what it was intended to effect rather than effecting any other lists.

Selectors by Class
In your CSS selectors it's better practice to use classes rather than elements.  For instance, the below example is restricted to a ul element and cannot be used on a ol element.  As more useful tags are being included with CSS, get in the habit of designing your code for flexability, you'll be surprised at the different ways you utilize the same CSS across multiple elements.

ul.bullet {
 background-image: url(resource/bullet.gif);
 background-position: 4px 6px;
}

Monday, July 13, 2009

CSS Form Element Standards

Having standards set within your code is probably one of the single most effective habits you can have as a developer. Even if your code is bad, having a standard way of executing things you know exactly how your code will be effected, and it makes it easier to improve your code because it's handled consistently across the whole site.  Consistency is key, and that applies in all areas, not just in your user experience.

With us in the middle of redesigning one of our sites we have moved our policy of creating standards to CSS, and really taken it to the next level.

Element Sizes
Form fields should not all be the same size.  Different form elements accept different types of data of varying lengths.  Because consistency is important both within your code and for the user experience we have implemented form standard sizes.

/*
 * Input
 */
.form input.small {
 width: 100px;
}
.form input.medium {
 width: 200px;
}
.form input.large {
 width: 300px;
}
.form textarea.small {
 width: 250px;
 height: 80px;
}
/*
 * Textarea
 */
.form textarea.medium {
 width: 360px;
 height: 120px;
}
.form textarea.large {
 width: 360px;
 height: 220px;
}
.form textarea.xlarge {
 width: 360px;
 height: 350px;
}

This allows us to globally control the size of our forms and our forms to always have consistent sizes so we don't end up with 4-5 different size fields on a single form.

Element Styles
 While it's generally not a good idea to style form elements too much, there are some good things you can do to enhance the user experience.  Web sites are becoming crisper, cleaner, and more interactive - Web 2.0.  To follow this trend we have modified all of our form elements to have a light gray border with 4px padding on the element.  The padding is there to emphasize focus on the content of the element.  By making the select box larger we make it easier to click on and make it easier to read because of the extra spacing between the form element borders and the text itself.


To make our forms for interactive, we use the psuedo class focus to make the border slightly darker.  The objective is to highlight the field but keep the focus on the content itself, thus the border is not solid black, but only a darker gray.

With 47% of the web using Firefox (according to W3Schools), using these styles will work very well in accordance with select boxes.

Sunday, July 12, 2009

Multiple MySQL statements with a single query

To run multiple statements within a single query you simply need to separate the statements with a semicolon to signify the end of the statement.

UPDATE `inventory` SET `value` = 2 WHERE `name` = "Strawberries";
UPDATE `inventory` SET `value` = 4 WHERE `name` = "Blueberries"

This is the best route to take with any ALTER TABLE or UPDATE queries.

For inserting multiple rows you should do the following.

INSERT INTO `inventory` (`id`, `name`, `value`) VALUES
(1, 'Strawberries', 4),
(2, 'Blueberries', 4);

Unfortunately, while PHP's mysqli_multi_query does support multiple queries, mysql_query does not.  A workaround for this is to explode the sql string by a semicolon and execute each query manually.

$sql = "UPDATE `inventory` SET `value` = 2 WHERE `name` = \"Strawberries\";
UPDATE `inventory` SET `value` = 4 WHERE `name` = \"Blueberries\"";

$pieces = explode(";", $sql);
foreach ($pieces as $query) {
     mysql_query($query) or die(mysql_error());
}

This can easily be put into a function to keep your code clean while still getting the usability you're looking for.

Valid RSS 2.0 Generator Class

I've been searching Google and couldn't find anything except documentation. So I'm posting this here in hopes that it will help someone else from having to go through a few hours of Trial and Error getting their feed to validate. This is tested only to pass the W3.org RSS 2.0 Validator.

I initially programmed it with required key values and things utilizing our custom api, but removed all of that for a stripped down basic version. If anyone has any updates/improvements to this feed or has any classes for other kinds of feeds I'd definitely be interested in hearing about them.

Post a comment if you use this and find it useful.

<?php
class FeedRss {
 
 var $feed = "";
 
 /*===========================================*/
 /* Public Functions
 /*===========================================*/
 public function create ($paramList) {
     $paramList = $this->cleanParamList($paramList);    
 
     $this->feed .= "<?xml version='1.0' encoding='iso-8859-1'?>\n";
     $this->feed .= "    <rss version='2.0' xmlns:atom='http://www.w3.org/2005/Atom'>\n";
     $this->feed .= "        <channel>\n";
     $this->feed .= "            <atom:link href='".$paramList['location']."' rel='self' type='application/rss+xml' />\n";
     $this->feed .= "            <title>".$paramList['title']."</title>\n";
  
  if (array_key_exists("copyright", $paramList)) {
       $this->feed .= "            <copyright>".$paramList['copyright']."</copyright>\n";
  }
  
  $this->feed .= "            <link>".$paramList['link']."</link>\n";
  $this->feed .= "            <description>".$paramList['description']."</description>\n";
  
  if (is_numeric($paramList["updated"])) {
   $this->feed .= "            <lastbuilddate>".date('D, d M Y H:i:s O', $paramList['updated'])."</lastBuildDate>\n";
  } else {
   $this->feed .= "            <lastbuilddate>".$paramList['updated']."</lastBuildDate>\n";
  }
  
  $this->feed .= "            <language>".$paramList['language']."</language>\n";
  
  if (array_key_exists("image", $paramList)) {
   $imageInput = array_merge($paramList["image"], array(
    "title" => $paramList["title"]
   ));
   $this->addImage($imageInput);
  }
 }
 
 public function addItem ($paramList) {
  $paramList = $this->cleanParamList($paramList);    
 
  $this->feed .= "        <item>\n";
  $this->feed .= "            <title><![CDATA[".$paramList['title']."]]></title>\n";
  $this->feed .= "            <link>".$paramList['link']."</link>\n";
  $this->feed .= "            <description><![CDATA[".$paramList['description']."]]></description>\n";
  
  if (array_key_exists("author", $paramList)) {
   $this->feed .= "            <author>".$paramList['author']."</author>\n";
  }
  
  if (array_key_exists("guid", $paramList)) {
   $this->feed .= "            <guid>".$paramList['guid']."</guid>\n";
  }
  
  if (is_numeric($paramList["updated"])) {
   $this->feed .= "            <pubdate>".date('D, d M Y H:i:s O', $paramList['updated'])."</pubDate>\n";
  } else {
   $this->feed .= "            <pubdate>".$paramList['updated']."</pubDate>\n";
  }
  
  if (array_key_exists("category", $paramList)) {
   $this->feed .= "            <category>".$paramList['category']."</category>\n";
  }
  
  $this->feed .= "        </item>\n";
 }
 
 public function get () {
  $this->feed .= "        </channel>\n";
  $this->feed .= "    </rss>";
  
  return $this->feed;
 }
 
 /*===========================================*/
 /* Private Functions
 /*===========================================*/
 
 private function addImage ($paramList) {
  $this->feed .= "            <image>\n";
  $this->feed .= "                <url>".$paramList["url"]."</url>\n";
  $this->feed .= "                <width>".$paramList["width"]."</width>\n";
  $this->feed .= "                <height>".$paramList["height"]."</height>\n";
  $this->feed .= "                <title>".$paramList["title"]."</title>\n";
  $this->feed .= "                <link>".$paramList["link"]."</link>\n";
  $this->feed .= "            </image>\n";
 }    
 private function cleanParamList ($paramList) {
  $ignoreParam = array("updated", "description");
  
  $replaceList = array(
   "&" => "&amp;",
   "–" => "&ndash;",
   "—" => "&mdash;",
   "¡" => "&iexcl;",
   "¿" => "&iquest;",
   "«" => "&raquo;",
   "<" => "&lt;",
   ">" => "&gt;",
   "$" => "&#36;",
   "»" => "&laquo;",
   "¢" => "&cent;",
   "©" => "&copy;",
   "÷" => "&divide;",
   "ยต" => "&micro;",
   "·" => "&middot;",
   "¶" => "&para;",
   "±" => "&plusmn;",
   "€" => "&euro;",
   "£" => "&pound;",
   "®" => "&reg;",
   "§" => "&sect;",
   "¥" => "&yen;",
   "’" => "'",
   "“" => "\"",
   "”" => "\""
  );
  
  foreach ($paramList as $key => $value) {
   if (!in_array($key, $ignoreParam)) {
    $paramList[$key] = str_replace(array_keys($replaceList), array_values($replaceList), $str);
   }
  }
  
  return $paramList;
 }
}
?>


Usage:
//if writing to a file via cron or any other method, comment out this header
header("Content-Type: application/xml; charset=ISO-8859-1");

$feed->create(array(
 "title" => "Domain.com News",
 "copyright" => "Copyright (c) ".date("Y")." Site Inc. All rights reserved.",
 "link" => "http://www.domain.com",
 "location" => "http://feed.domain.com/feed.xml",
 "description" => "All of the latest news",
 "language" => "en-us",
 "image" => array(
 "url" => "http://www.domain.com/image/branding/rss-logo.jpg",
 "height" => 50,
 "width" => 50,
 "title" => "RSS feed from Domain.com",
 "link" => "http://www.domain.com"
)
));

//add as many items as you'd like
$feed->addItem(array(
 "title" => $news["title"],
 "link" => "http://www.domain.com/index.php?sec=news&act=view&id=".$news["id"],
 "guid" => "http://www.domain.com/index.php?sec=news&act=view&id=".$news["id"],
 "description" => $tempNewsItem["preview_cache"],
 "author" => "news@domain.com (author)", //for proper validation you should have an email AND an author's name of some sort
 "category" => "Computer Hardware",
 "updated" => $news["posted"]
));

//this can also be used to write to a file
echo $feed->get(); 

Saturday, July 11, 2009

What's the point of RSS?

This may sound like a stupid question to those that use it, but having just started using it myself I can understand the perspective.  RSS seems to confusing and pointless when you don't know anything about it.  In reality, it's rather simple, and extremely powerful.

RSS is a method of delivering your content, whether in full or partial, to an external source (a visitor's RSS reader or another web site) without them having to visit your site.  Having just become an RSS fan with my favorite reader, SharpReader, I now understand the importance of this increased usability.  I post news on our gaming network and have over 40 feeds that I check every day.  If I were to check each of these sites every day, it would take hours and I wouldn't do it.  By having these RSS feeds these sites are ensuring that their content is delivered to me whether I check their site or not.  Generally, RSS feeds only contain a snipplet, or preview and a Read More link to visit their web site.

But what about losing traffic?  The answer is, you're not losing traffic.  Of the sites I have feeds for I would essentially only check a handful of them if it weren't for the feeds but because of the feeds I am kept up to date on all of them.  And with FeedBurner (now owned by Google) offering to track the number of subscribers I can't think of a reason why you wouldn't have an RSS feed on your site.



If you want to add one, I have posted a Valid RSS 2.0 Generator Class you can use.

Success Messages

In almost all of my web sites I have setup forms on our site so that upon submitting successfully, they would either show the success message at the top of the page you were just at or show only a success message on the page and nothing else. While this wasn't the best practice, I couldn't think of a better route to take. My partner recently presented a new idea. Instead of doing it that way and requiring more user input as to what the next step would be, he suggested that we use session variables to store the success message and forward the user to a page they would likely want to be on. So when sending an email, rather than loading a page that just says "Email Sent!", we forward them back to their inbox and show the success message there.
When doing this in PHP, if you use a header() forward your session variable will not save unless you force PHP to write the session. To do this, you would use session_write_close().
$_SESSION["success"] = "Your email has been sent"];
session_write_close(); //without this, the session variable will not be saved before header() executes

header("Location: http://www.domain.com/my-inbox/");

From the Ground Up

Through my years of web development experience I have made significant improvements to my style and methods of web programming along the way. I will use this blog to share my findings in all areas of web development including MySQL, PHP, Javascript, HTML, and CSS.