20 July 2011

Microsoft Internet Explorer - a "special needs" browser

When developing a website I usually consider how long it will take to code the site, template it, and test it.  Then I add a whole lot of extra time to make it work in Internet Explorer.

A Microsoft User - ready to use the interweb
In a way Microsoft's refusal to believe that there is a world of computing outside of Redmond is a good thing - it means that I get more billable hours per project.  Of course those billable hours are spent in frustrating searches through Google to try and work out all of the various quirks and bugs in Internet Explorer.

I tend to think of Microsoft Internet Explorer users as "special needs" kids who are being set loose in the playground that is the Internet.  Barely able to prevent themselves from drooling on their keyboards and eating their mouses they demand that websites conform to their special needs.  Standards such as Javascript DOM level 2, HTML5, CSS, CSS3 are pretty much ignored because their parent doesn't believe in modern technology.  Microsoft and the Amish are pretty much similar in that respect.  They're both American, highly insulated, and refuse to believe in an outside world that is advancing without them.

BUT Microsoft insists on installing its browser into its Windows operating system.  And not only does it ship as a default browser, but it's so deeply entrenched into the OS that you can't uninstall it.  I'm not sure why Microsoft thinks that a web browser should be integrated into an operating system, but I'm sure there is a reason for this (like preventing users from choosing other browsers).

So the average Internet Explorer user is a Windows user (typically less savvy than Mac/Linux users) who is unaware that there are better ways to explore the Internet (less savvy than Firefox or Chrome users).  Who would choose a browser that is slower and less featured than a competing browser (like Chrome)?

So, if your target market consists of special needs people who "choose" inferior products then by all means target the gullible Internet Explorer users.  It's like taking candy from special needs kids.

Is there hope for the future?

An Internet Explorer is pulled over on the Internet Highway
Of course Microsoft is promising to change in IE9.  They are promising "native" support for HTML5.  Apart from the fact that HTML5 is an external standard and doesn't belong to Microsoft why would they want to  build this into their operating system?

Ah well, that's Microsoft for you.  But Microsoft also promised that IE8 would be standards compliant and would solve problems like world hunger.  I'm still hungry and margin:auto doesn't work, and neither do phase down events in Javascript, neither does HTML5 and CSS3 is also ignored.  So when Microsoft aims to be standards compliant they actually mean "Microsoft standards compliant" and not the standards that the rest of us use.

IE7 promised to offer a cure for cancer and be an improvement on IE6.  That's pretty much the only promise that Microsoft actually managed to keep, but having Satan poke my eyes out while Steve Ballmer (crazy monkey man) sodomizes me would be better than IE6.  So by aiming for the low hanging fruit Microsoft actually delivered on a promise.  Sodomy from the world's sweatiest billionaire would only be made worse by his screams "DEVELOPERS DEVELOPERS DEVELOPERS" which I imagine would be  his replacement for "oh God I'm coming".  I'm not suggesting Mr Ballmer enjoys anal sex (I know he's married with tadpoles) but he does appear to have a somewhat sadistic approach to web developers so the analogy is used.  I'm actually quite fond of the monkey man - he gives me about 5 hours extra on every project I do.

08 July 2011

CakePHP : Changing .htaccess to prevent one (or more) directory from being handled by Cake

This technique may be useful if for example you have decided to use Wordpress to handle your blog, rather than trying to code your own competing blog system.

This .htaccess file is based on the CakePHP default but simply adds a rewrite rule to exclude the "blog" directory from Cake's control.

<ifmodule mod_rewrite.c=""%gt;
   RewriteEngine on
   RewriteRule   ^(blog).* - [NC,L]
   RewriteRule    ^blog /blog    [L]
   RewriteRule    ^$ app/webroot/    [L]
   RewriteRule    (.*) app/webroot/$1 [L]

If you want to prevent Cake from controlling multiple directories you can simply modify the rewrite rule like such:

RewriteRule   ^(blog|my|other|directories|go|here).* - [NC,L]

In order to accomplish this on an IIS server you can email sballmer@microsoft.com and ask for support in getting his expensive software to do what it says it does on the box.

04 July 2011

CakePHP : Adding a file upload and adding a select list of URLs for users in a CMS

CakePHP automagically generates textboxes for users, but it's usually a project requirement that these boxes are "user friendly".

Adding CK editor to CakePHP is easy, but lets go a few steps further and give it the ability to allow users to upload images directly into their content and to select a list of pages when creating a link.

This article is based heavily on two articles (Adding file upload in CK editor and Adding a ‘Link to local page from site’ field) which I have simply modified to be CakePHP specific. So all credits to Ben Roberts and Zac.

Step 1 - Adding CK editor to CakePHP with the FileManager plugin
1) Download CK editor from the official site and unzip it into your /app/webroot/js directory. To make things easy I put it in /app/webroot/ckeditor directory.
2) Download FCK editor from the same site.  It was at the bottom of the CK downloads page (because it is deprecated).  Unzip it to a temporary directory and copy the filemanager directory (fckeditor/editor/) to /app/webroot/ckeditor/filemanager directory.  We will be using the FCK filemanager in CK editor.
3) Edit filemanager/connectors/php/config.php and enable the plugin and do whatever other configuration you may require. Take note of the document root option and remember that this is going through Javascript and not CakePHP, which means that you need to set it /app/webroot/userfiles and not the default of /userfiles
4) Open up filemanager/connectors/php/config.php and make the following changes:

Add this function:

function GetUrlParam( paramName )
 var oRegex = new RegExp( '[\?&]' + paramName + '=([^&]+)', 'i' ) ;
 var oMatch = oRegex.exec( window.top.location.search ) ;
 if ( oMatch && oMatch.length > 1 )
  return decodeURIComponent( oMatch[1] ) ;
  return '' ;
Replace the OpenFile function with the below function:

function OpenFile( fileUrl )
 //PATCH: Using CKEditors API we set the file in preview window. 
 funcNum = GetUrlParam('CKEditorFuncNum') ;
 //fixed the issue: images are not displayed in preview window when filename contain spaces due encodeURI encoding already encoded fileUrl 
 window.top.opener.CKEDITOR.tools.callFunction( funcNum, fileUrl);
 window.top.close() ;
 window.top.opener.focus() ;
Create a new helper in your helpers directory called fck.php and include the code below. Remember to use this helper in the controller you want to be able to use CK editor. This helper is based on the one available in the bakery but I have modified the code so that it uses the file manager we copied in from FCK.

// file /app/views/helpers/fck.php
class FckHelper extends Helper { 

    var $helpers = Array('Html', 'Javascript'); 

    function load($id) { 
        $did = ''; 
        foreach (explode('.', $id) as $v) { 
            $did .= ucfirst($v); 
        // filebrowser patch   
        $here = 'http://' .  $_SERVER['HTTP_HOST'] . $this->webroot; 

        $fileBrowser = "{\n
            filebrowserBrowseUrl :'".$here."js/ckeditor/filemanager/browser/default/browser.html?Connector=".$here."js/ckeditor/filemanager/connectors/php/connector.php',\n
            filebrowserImageBrowseUrl : '".$here."js/ckeditor/filemanager/browser/default/browser.html?Type=Image&Connector=".$here."js/ckeditor/filemanager/connectors/php/connector.php',\n
            filebrowserFlashBrowseUrl :'".$here."js/ckeditor/filemanager/browser/default/browser.html?Type=Flash&Connector=".$here."js/ckeditor/filemanager/connectors/php/connector.php'}\n";

        $code = "CKEDITOR.replace( '".$did."', $fileBrowser );"; 
        return $this->Javascript->codeBlock($code);  

When you want to include CK editor in a view you can now use the code provided in the bakery. I'm just quoting a snippet, but you can view the full source here

echo $javascript->link('ckeditor/ckeditor', NULL, false);  
 echo $form->input('body', array('cols' => '60', 'rows' => '3')); 
 echo $fck->load('News.body'); 

We should now be able to include CK editor and make use of the File Manager.

Step 2 - Allowing users to select a list of URL's on the site

This section is a CakePHP specific implementation of the guide published online here.

I'm using a menu table in my database to list a menu name and url. It doesn't really matter how you want to handle your pages just so long as you can get them into JSON formatted string of description/url pairs.

Here is an example of the desired output in your controller:

[['Home Page','home'],['About Us','about'],['Contact','contact']]

I used code like the following snippet, because for this project I am using a "slug" field to be the same as the page title (project specification). Slugs are stored in my database with a hyphen which is simply stripped to get the page title.

// get pages for ckEditor
$this->ContentItem->recursive = -1;
$contentItems = $this->ContentItem->find('all',array('fields'=>array('slug')));
$json_links = array();
    foreach ($contentItems as $contentItem) {
        $slug = $contentItem['ContentItem']['slug'];                                      
        $description = str_replace('-',' ',$slug);
        $json_links[] .= "['" . $description . "','". $slug ."']";
    $json_links = '[' . implode(',',$json_links) . ']';               

You will no doubt need to modify this to suit your needs, either in your model or controller depending on your coding style.

Open up the view and add a dummy input box like the tutorial suggests:

<input type='hidden' id='pageListJSON' value="<?php echo $json_links; ?>" >

Personally I feel that there is probably a better way to do this, but since this method works I'm not going to bother trying to improve on it. The only reason the author of the tutorial uses the input is to use a Javascript query on it later. So I wonder if setting a variable there and replacing this look-up later would be a better approach? Comments anybody? Anyway, back to work...

You can now follow the rest of the tutorial that this section is based on from step 3 as this all deals with the Javascript and so can be directly applied to Cake.

When following the tutorial make sure that you take note of the comment made by amosmos on 01.01.11 regarding the code addition in step 5... if you are using a database to pull your pages you may need to add a prefix to your URL (or you can do it in your Model/Controller when you encode in JSON):

attributes[ 'data-cke-saved-href' ] = 'pages/' + data.localPage; 

You should now have a workable CK Editor for your CakePHP CMS.