FREE Instant Email Standard Test Tool

Orite has created an online tool to emulate HTML/CSS rendering capacity of various Email clients. It references the Email CSS support Guide from Campaign Monitor, and utilizing the email-standards.org Email ACID test page as the example. You may paste in your own email template to test in.

Please note, this tool only provides a brief demonstration, by no means to be the exact reflection of how email clients show emails. Please remember this tool highly relies on the browsers capability as well, so be sure to use a standard compliant browsers. For more solid tests please try Litmus Email Testing or the Campaign Monitor Email Testing tool. If you want to convert any external CSS to inline style, Inline Styler and Emogrifier may come to help. Here goes the FREE tool, post your comments.

Email Client:


Currently tested under FF3/3.5, Safari4, Chrome2/3, IE7/8, Opera9.6


jQuery IE detection issue workaround

We recently discovered an issue with jQuery.browser.version detection, it reports wrong Internet Explorer versions on some corrupted IE userAgent strings. A corrupted IE8 userAgent string may look like,

Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; WOW64; Trident/4.0; GTB6; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30618; .NET CLR 1.1.4322; InfoPath.2)

In this case jQuery.browser.version will report browser version 6.0 instead of 8.0, this will result some jQuery plugins to show a 'not implemented' error in IE8. So far we know jqModal and FancyBox are affected. Because they rely on jQuery.browser.version to detect IE6, and run some IE6 specific codes, which caused IE8 to fail, another sad story from MS.

Although we believe it's not the problem with jQuery or IE (Microsoft) alone, the best fix will be patching the plugins to detect IE6. i.e. to detect IE6 properly with jQuery, you will need the following code, which has an extra check by using window.XMLHttpRequest.

ie6=$.browser.msie&&($.browser.version == "6.0")&&!window.XMLHttpRequest;

If you curious about how userAgnet string got corrupted, we don't have much clue about it, some say it's due to an earlier release of IE8 (however as we aware, some IE7 has got the same issue), but in our office 3 out of 10 PCs has got the corrupted string. To fix it, you need to look for the following registry keys,

HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\5.0\User Agent\Post Platform
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\User Agent\Post Platform
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\5.0\User Agent\Post Platform
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\User Agent\Post Platform

Or if you want to do more then just differentiate IEs, you may find a good browser detection Javascript here.

Some side note, you might notice jQuery has marked jQuery.browser deprecated since 1.3 in the favour of jQuery.support. We understand why jQuery would want to take this approach, and it's a good step forward. However jQuery.support would only be good if it tells all the feature differences among browsers, rendering engines, and standard modes. As web develops we don't learn the differences first then start developing a site, we only come across the differences and try to figure out what feature difference cause the different final result will be really time consuming. Until the time all the browsers come to the same standard and feature set, patching and hacking may be the only way for web developers, no one-stop solution would be available at any moment before that epoch. May God blessing all the web developers please.


Yii Framework controller and view path convention

Yii Framework has just upgraded to 1.0.5, really glad that Qiang could keep up the momentum to release an update every month. We strongly believe with this level of activity, this open source project will soon becoming the most popular and comprehensive PHP framework. However the only problem this brings is the question "To upgrade or NOT to upgrade?". For a real life project, it's bit hard to keep up with the monthly upgrade, especially when things are constantly changing and improving. Some upgrade really brings the benefit of stability and versatility to the framework, but there are bad times...

Yesterday as usual, we have upgraded our Yii version to 1.0.5, and one of the project we are working on suddenly stopped working almost completely. That was because of a little change in the view path naming convention. To read more about the issue please refer to Issue 307.

To avoid this, please keep this naming convention in mind:

  • Name controller ID in lowercase only with the very first letter/word capitalized.
  • If a controller ID has multiple words try to separate them using underscore '_' or hyphen '-' to concatenate them, NOT capitalize any following words
  • All view path/directory should be all in lowercase and with underscore '_' or hyphen '-' for concatenation.
This directory naming convention should prevent any problem on both Windows or *nix platform.

The only last thing we didn't really agree with Qiang is that a controller would only be accessible by only one URL, not both capitalized or lowercased word(s), simply because an URL should be unified and universal. We would like to leave this discussion to all the Yii enthusiast.


CSS Implementation Chart for IE6,IE7 and IE8

Since Internet Explorer8 has just joined the infamous IE family, which added bit of work (chaos) in the compatibility war. We have designed the following tool to show the difference between the "recent" IE versions, namely 6,7 and 8. Hope this would help web developers who care about browser compatibility.

Referenced from MSDN: CSS Compatibility and IE

@ Rules



.value { sRules }Class
#value { sRules }ID
E { sRules }Type
* { sRules }Universal
ns|E { sRules }Namespaced
[att=val] { sRules }Equality [=]
[att] { sRules }Existence []
[att|=val] { sRules }Hyphen [|=]
[att~=val] { sRules }Whitespace [~=]
[ns|attr] { sRules }Namespaced
[att^=val] { sRules }Prefix [^=]
[att*=val] { sRules }Substring [*=]
[att$=val] { sRules }Suffix [$=]
E + F { sRules }Adjacent Sibling (+)
E > F { sRules }Child (>)
E F { sRules }Descendant
E ~ F { sRules }General Sibling (~)
:active { sRules }:active
:first-child { sRules }:first-child
:focus { sRules }:focus
:hover { sRules }:hover
:lang(C) { sRules }:lang()
:link { sRules }:link
:visited { sRules }:visited
@page :first { sRules }@page :first
@page :left { sRules }@page :left
@page :right { sRules }@page :right
:root { sRules }:root
:nth-child() { sRules }:nth-child()
:nth-last-child() { sRules }:nth-last-child()
:nth-of-type() { sRules }:nth-of-type()
:nth-last-of-type() { sRules }:nth-last-of-type()
:last-child { sRules }:last-child
:first-of-type { sRules }:first-of-type
:last-of-type { sRules }:last-of-type
:only-child { sRules }:only-child
:only-of-type { sRules }:only-of-type
:empty { sRules }:empty
:target { sRules }:target
:not(X) { sRules }:not()
:enabled { sRules }:enabled
:disabled { sRules }:disabled
:checked { sRules }:checked
:indeterminate { sRules }:indeterminate
:default { sRules }:default
:valid { sRules }:valid
:invalid { sRules }:invalid
:in-range { sRules }:in-range
:out-of-range { sRules }:out-of-range
:required { sRules }:required
:optional { sRules }:optional
:read-only { sRules }:read-only
:read-write { sRules }:read-write
:after { sRules }:after
:before { sRules }:before
:first-letter { sRules }:first-letter
:first-line { sRules }:first-line
::before { sRules }::before
::after { sRules }::after
::first-letter { sRules }::first-letter
::first-line { sRules }::first-line
::selection { sRules }::selection
::value { sRules }::value
::choices { sRules }::choices
::repeat-item { sRules }::repeat-item
::repeat-index { sRules }::repeat-index
{ sRule!important? }!important Declaration
{ list-style : sStyle }list-style
{ list-style-image : sStyle }list-style-image
{ list-style-position : sStyle }list-style-position
{ list-style-type : sStyle }list-style-type
{ color : sColor }color
{ background : sBackground }background
{ background-attachment : sAttachment }background-attachment
{ background-color : sColor }background-color
{ background-image : sLocation }background-image
{ background-position : sPosition }background-position
{ background-repeat : sRepeat }background-repeat
{ color-profile : sProfile }color-profile
{ rendering-intent : sIntent }rendering-intent
{ background : sBackground1,sBackground2, etc. }background (multiple)
{ background-clip : sClip }background-clip
{ background-origin : sOrigin }background-origin
{ background-break : sBreak }background-break
{ background-size : sSize }background-size
{ direction : sDirection }direction
{ font : sFont }font
{ font-family : sFamily }font-family
{ font-size : sSize }font-size
{ font-style : sStyle }font-style
{ font-variant : sVariant }font-variant
{ font-weight : sWeight }font-weight
{ letter-spacing : sSpacing }letter-spacing
{ line-height : sHeight }line-height
{ text-align : sAlign }text-align
{ text-decoration : sDecoration }text-decoration
{ text-indent : sIndent }text-indent
{ text-transform : sTransform }text-transform
{ unicode-bidi : sAlign }unicode-bidi
{ vertical-align : sAlign }vertical-align
{ white-space : sWrap }white-space
{ word-spacing : sSpacing }word-spacing
{ font-effect : sEffect }font-effect
{ font-emphasize : sEmphasize }font-emphasize
{ font-size-adjust : sSizeAdjust }font-size-adjust
{ font-smooth : sSmooth }font-smooth
{ font-stretch : sStretch }font-stretch
{ hanging-punctuation : sHangingPunctuation }hanging-punctuation
{ punctuation-trim : sTrim }punctuation-trim
{ ruby-align : sRubyAlign }ruby-align
{ ruby-overhang : sRubyOverhang }ruby-overhang
{ ruby-position : sRubyPlacement }ruby-position
{ ruby-span : sRubySpan }ruby-span
{ text-align-last : sAlignLast }text-align-last
{ text-emphasis : sEmphasis }text-emphasis
{ text-justify : sJustify }text-justify
{ text-outline : sOutline }text-outline
{ text-overflow : sOverflow}text-overflow
{ text-shadow : sShadow }text-shadow
{ text-wrap : sWrap }text-wrap
{ word-break : sBreak }word-break
{ word-wrap : sWrap }word-wrap
{ writing-mode : sFlow }writing-mode
{ content : sContent }content
{ counter-increment : sCounter }counter-increment
{ counter-reset : sCounter }counter-reset
{ quotes : sQuotes }quotes
{ border : sBorder }border
{ border-bottom : sBottom }border-bottom
{ border-bottom-color : sColor }border-bottom-color
{ border-bottom-style : sStyle }border-bottom-style
{ border-bottom-width : sWidth }border-bottom-width
{ border-collapse : sCollapse }border-collapse
{ border-color : sColor }border-color
{ border-left : sLeft }border-left
{ border-left-color : sColor }border-left-color
{ border-left-style : sStyle }border-left-style
{ border-left-width : sWidth }border-left-width
{ border-right : sRight}border-right
{ border-right-color : sColor }border-right-color
{ border-right-style : sStyle }border-right-style
{ border-right-width : sWidth }border-right-width
{ border-spacing : sSpacing }border-spacing
{ border-style : sStyle }border-style
{ border-top : sTop }border-top
{ border-top-color : sColor }border-top-color
{ border-top-style : sStyle }border-top-style
{ border-top-width : sWidth }border-top-width
{ border-width : sWidth }border-width
{ caption-side : sLocation }caption-side
{ clear : sClear }clear
{ empty-cells : sEmptyCells }empty-cells
{ float : sFloat }float
{ margin : sMargin }margin
{ margin-bottom : sHeight }margin-bottom
{ margin-left : sWidth }margin-left
{ margin-right : sWidth }margin-right
{ margin-top : sHeight }margin-top
{ padding : sPadding }padding
{ padding-bottom : sPadding }padding-bottom
{ padding-left : sPadding }padding-left
{ padding-right : sPadding }padding-right
{ padding-top : sPadding }padding-top
{ table-layout : sLayout }table-layout
{ border-break : sBreak }border-break
{ border-image : sImage }border-image
{ border-radius : sRadius }border-radius
{ box-shadow : sShadow }box-shadow
{ bottom : sBottom }bottom
{ clip : sClip }clip
{ display : sDisplay }display
{ height : sHeight }height
{ left : sPosition }left
{ max-height : sMaxHeight }max-height
{ max-width : sWidth }max-width
{ min-height : sMinHeight }min-height
{ min-width : sMinWidth }min-width
{ overflow : sOverflow }overflow
{ position : sPosition }position
{ right : sPosition }right
{ top : sTop }top
{ visibility : sVisibility }visibility
{ width : sWidth }width
{ z-index : vOrder }z-index
{ overflow-x : sOverflow }overflow-x
{ overflow-y : sOverflow }overflow-y
{ orphans : nLines }orphans
{ page-break-after : sBreak }page-break-after
{ page-break-before : sBreak }page-break-before
{ page-break-inside : sBreak }page-break-inside
{ widows : nLines }widows
{ fit : sFit }fit
{ fit-position : sPosition }fit-position
{ image-orientation : sOrientation }image-orientation
{ page : sPage }page
{ size : sSize }size
{ cursor : sCursor }cursor
{ outline : sOutline }outline
{ outline-color : sColor }outline-color
{ outline-style : sStyle }outline-style
{ outline-width : sWidth }outline-width
{ appearance : sAppearance }appearance
{ box-sizing : sSizing }box-sizing
{ icon : sIcon }icon
{ nav-down : sNavDown }nav-down
{ nav-index : sIndex }nav-index
{ nav-left : sNavLeft }nav-left
{ nav-right : sNavRight }nav-right
{ nav-up : sNavUp }nav-up
{ outline-offset : sOffset }outline-offset
{ outline-radius : sRadius }outline-radius
{ resize : sResize }resize
{ column-break-after : sSize }column-break-after
{ column-break-before : sSize }column-break-before
{ column-break-inside : sSize }column-break-inside
{ column-count : sCount }column-count
{ column-gap : sSize }column-gap
{ column-rule : sRule }column-rule
{ columns : sColumns }columns


<color> (names)
<color> (#rrggbb or #rgb Notation)
<color> (rgb(r,g,b) Notation)
<color> (system colors)
<color> (rgba(r,g,b,a) Notation)
<color> (hsl(h,s,l) Notation)
<color> (hsla(h,s,l,a) Notation)

Good news or Bad news, IE8 is here

To those who worked hard on getting IE8 released on schedule, congratulations! In the same time we feel sorry to those who care about website compatibility, this means you have one more IE to hack on. Maybe this is the way how Microsoft helps to create jobs in the crisis, thanks! Anyway here's some critics from my own opinion. Firstly it took over 15 minutes to upgrade my IE8 RC1 to IE8 with two restarts. If you haven't noticed IE8 by default is not backward compatible to its predecessors. A good example is the 'opacity' in css, to get your page displayed correctly in IE8 you need to. Either in the <head> tag put,
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
or write something like this in the CSS,
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
However this may break PNG images with alpha channel from displaying properly. Yes, another PNG issue, :-< Read more on Bb RealTech If you want find out more about difference IE8 has to offer, please check out CSS3.info Enjoy hacking another IE!

mwbModelCommand, Yii Framework and MySQL Workbench

In the past few weeks we have been exploring with Yii Framework, and two of us have started porting some of our project to Yii Framework. One of the first few things that caught our attention is the CActiveRecord model classes, which require relational rules to describe relationships among database tables. We are glad that Yiic can help to create project scaffolds and create controller, model and CRUD, however it's not very practical to manually add all the relationship if your project has over 10 tables.

Our normal practice prefers to design database with relational diagram, so it not only sets out the table schemas but also visually demonstrates the relationship of database tables. However in this fast changing world, without the right tool most the relational diagram tends to out of sync with real production database. For most the of our project, we create ERD and generate database from the diagram in the design phase and later on in the development phase, most the developer choose to update the schema on the table directly, since that's quick, and most of them don't have the habit to update the ERD with relavant changes. You will feel lucky if your database design tool comes with reverse engineering, which can help you syn with your database at a later point still, otherwise you'll be purely rely on your bare eye and hand to syn among database table your model classes and the ERD.

So I believe when it comes to database design it should always be diagram driven, therefore if there's any changes need to be made, always update the digram first and carry on to your physical table and model class. Although we are still looking for good database migration tools to manage schema diffs. We have written a little script to help Yii Framework to work with MySQL Workbench (which we use to model MySQL databases), the purpose of the script is to batch create model classes for Yii Framework (although Yiic can generate models one by one, we want it happen at once and for all), and with the help of mwb file it can create the relational rules for the models, so there's no manual translation needed while you creating the models. Since it's a Yii shell command, we have named it 'mwbModelCommand'.

It's available for download at this Yii Extensions page, or Yii Framework mwbModelCommand (direct link)

Hope this script will save your time on relational model creation, if anyone have issues or suggestions mwbModelCommand please feel free to post back.

Wish List (Todo),
A tool (set) to stream line the database migration, so it would not only creates the models, but may also update (revert) models as well as database table schema at once, and data safe if possible. Please advice if you know something good out there.


Useful PHP Tools

Smashing Magazine did it again, a good collection of php tools, 50 Extremely Useful PHP Tools Please comment on your faviourate ones.
Written by in: Web Development | Tags:

Setting up wildcard virtual hosts for web development environment

We used to have dev site hosted under sub directories of localhost, e.g. http://localhost/project/abc/. When it comes to development and testing, we really want to keep the environment as close as possible to the production environment, so it would lot easier to deploy and maintain. Imagine you have to refer to /images/logo.png from a page like /we/have/got/a/test/page/index.html, you really want absolute path in this senario, however in order to work with sub folder based dev environment you either having awful relative links (which would be easily break down when you relate files), or different absolute path as if you would deploy that on the production server. Which is not very practical at all. To solve the problem, we thought about having separate virtual hosts for each project, ie. abc.localhost for abc project and so forth. There are several ways you could achieve this in apache configuration.
  1. With vhost_alias_module enabled
    <VirtualHost *:80>
        VirtualDocumentRoot c:/projects/%1/
    take abc.localhost for example, where %1 represents abc, and %2 would represent lcoalhost
  2. With mod_rewrite enabled
    <VirtualHost *:80>
        ServerAdmin root@localhost
        DocumentRoot "c:/projects/"
        ServerName localhost
        ServerAlias *.localhost
        RewriteEngine on
        RewriteCond %{HTTP_HOST} ^([^.]+)\.localhost$ [NC]
        RewriteRule  ^/(.*)$ /%1/$1
  3. With mod_rewrite and rewritemap
    <VirtualHost *:80>
        ServerAdmin root@localhost
        DocumentRoot "c:/projects/"
        ServerName localhost
        ServerAlias *.localhost
        RewriteEngine on
        RewriteMap   lowercase  int:tolower
        # define the map file
        RewriteMap   vhost      txt:vhosts.map
        # deal with aliases as above
        RewriteCond  ${lowercase:%{SERVER_NAME}}  ^(.+)$
        # this does the file-based remap
        RewriteCond  ${vhost:%1}                  ^(/.*)$
        RewriteRule  ^/(.*)$                      %1/$1
    Then create the vhosts.map file to contain the hosts to documentroot map, e.g. abc.localhost    c:/project_08/abc bcd.localhost    c:/project_09/bcd
Both method 1 and 2 would wildcard map *.localhost to c:/projects/*, and method 3 will allow you to specify documentroot of each domain in the vhosts.map file. With extra virtualhost you don't need to alter httpd.conf any more, all you need to do is create the subdomain and create the project folder in appropirate location. Since the hosts file doesn't support wildcard entry, for each of the subdomain of localhost you may need to create a separate entry, however it should only take few seconds, and nothing needs to be restart to take effect, which is easy and good enough. Alternatively if you have access to your DNS server, you may setup wildcard on the localhost zone, so everyone in the dev team will have access to *.localhost without alter anything in the local hosts file. Hope whis will in a way help web developers to deliver project faster.
Written by in: Web Development | Tags:

45 Useful and Fancy jQuery Techniques

Please check out this post on Smashing Magazine, 45+ New jQuery Techniques For Good User Experience really good collection of jQuery tricks for web development.
Written by in: Web Development | Tags:

Install Ruby on Rails (RoR) on Windows (zlib.dll error)

Although Orite has been focusing on PHP development for quite few years now, we are open to new web technologies and always exploring new tools and learning new skills. I still remember when we first heard Ruby on Rails few years back, we were quite amazed about the raid development framework, and the simplicity of the Ruby syntax (which is really OO and human readable). After the recent PHP framework research, we still not 100% happy with any framework in the PHP world. So we decide to take a look at RoR again and try to get some inspiration, and if someday when RoR rules the world we would not be too far behind hopefully. After digging the net, there's no one RoR package that's up to date and easy to install for windows, I guess that's because most the developers are on Macs and Linux. Although we have Linux as testing servers and Mac as graphic stations, all our web developer are happy windows users. We were trying to follow the instructions on RoR wiki, however the One-Click Installer is bit out dated (since official RoR website recommends Ruby 1.8.7). So here's our own instruction of getting RoR on Windows,
  1. Download the binary package from official Ruby download page
  2. Unzip and put in to a folder, say c:\ruby
  3. Add c:\ruby\bin into the environment path (if you don't know how, here's the official Microsoft How To Manage Environment Variables help)
  4. Get the latest RubyGems from RubyForge, and unzip it into a folder
  5. Now launch CMD and goto the RubyGems folder, then type >ruby setup.rb to setup RubyGems
  6. Here comes the problem, when we try to install rails by issuing the command >gem install rails Window alerts an error about missing zlib.dll, and ruby only comes with the zlib.so file under the lib\i386-mswin32 directory. To fix it you need the zlib.dll file under ruby\bin\ folder, if you have apache for windows installed, simply grab the zlib.dll from apache\bin folder, which should work. Otherwise you need to download zlib.dll from any of the dll hosts by search on Google.
  7. The the >gem install rails command should work
  8. And you may install mongrel, the http server for RoR application by >gem install mongrel
Then the RoR is ready to go, you may follow instructions on http://rubyonrails.org to start coding in Ruby for web development. If we don't get too busy with PHP dev work or too bored with RoR, we will post back any interesting findings in RoR from the perspective of PHP developers.

Copyright Orite Group Pty Ltd | Powered by WordPress | Theme: Aeros 2.0