All posts by shannah

Steve Hannah is a 28-year-old software developer currently studying and working at Simon Fraser University in beautiful Vancouver British Columbia. He specializes in web information systems and prefers Java, PHP, and Python as programming languages. He is a Christian and worships at Christ Church of China in Vancouver.

The Steve Hannah Show Episode 1

I decided that it would be easy and kind of interesting to try to record my own little talk show. So I went out and bought a 2nd digicam, some lights, and a pair of tripods – reinstalled Final Cut Pro, and went straight to filming.

I’m not sure the direction that I will take the Steve Hannah show, but I would like to at least bring in some guests to discuss interesting issues with technology or local issues.

Now that I have everything set up I hope to be pumping one of these out once every couple of weeks. Also watch out for a series of instructional videos on Dataface and other web related topics.

Enjoy, and do comment if you have suggestions about what you’d like to see on the Steve Hannah show.

Clickable URLs with javascript

A client wants all email addresses and urls to be clickable in an application. I would like to develop a javascript solution for this.

I found this article that shows some example code for doing this on a particular piece of text, however, fixing the links on an entire page is a little bit more involved as it would require us to only convert the URLs that are static text – and leave the URLs, already inside an <a> tag, or appearing as tag attributes, alone.

My Solution


/**
 * A function that makes all static urls and email addresses in given DOM 
 * element's subtree to be converted to links.
 *
 * @param el DOM Element that we are checking
 *
 * This will recursively call itself until the entire subtree has been walked.
 * @author Steve Hannah .
 *
 * Some parts were adapted from http://www.arstdesign.com/articles/autolink.html
 */
function makeLinksClickable(el){

    //If no element is provided, then we will traverse the entire document.
    if ( el == null ) el = document.body;
    
    
    if ( el.nodeType == 3 /* text node */ ){
        // We only want to replace urls in text nodes
        var s = el.nodeValue;
        
        if ( s.replace(/^\s+|\s+$/g, '').length == 0) return;
            // If there is no text in this text node, we will just return and do
            // nothing.
        
        var hlink = /\b(ht|f)tp:\/\/([^ \,\;\:\!\)\(\"\'\< \>\f\n\r\t\v])+/g;
            // regex for URLs
        var mlink = /\b([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+\b/g;
            // regex for e-mail addresses
            // Copied from http://www.quirksmode.org/js/mailcheck.html
        
        var frag = document.createElement('span');
            // Because we are having difficulty putting just the A tag into
            // the document after conversion, we create a wrapper <span> tag
            // that will hold the resulting <a> tag.  Then we will 
            // insert the <span> tag into the document.

        
        // Replace URLs in this text node.
        var new_s = s.replace(hlink, 
                        function($0,$1,$2){ 
                            //s = $0.substring(1,$0.length); 
                                // remove trailing dots, if any
                            s = $0;
                            while (s.length>0 && s.charAt(s.length-1)=='.') 
                                s=s.substring(0,s.length-1);
                                // add hlink
                                                   
                            return " " + s.link(s); 
                        }
                     ) ;
                     
        // Replace Email addresses in this text node.
        new_s = new_s.replace(mlink,
                    function($0,$1,$2){ 
                            //s = $0.substring(1,$0.length); 
                                // remove trailing dots, if any
                            s = $0;
                            while (s.length>0 && s.charAt(s.length-1)=='.') 
                                s=s.substring(0,s.length-1);
                                // add hlink
                                                
                            return " " + s.link('mailto:'+s); 
                        }
                     ) ;
        
        if ( new_s != s ){
            // We only want to perform the switch if a change was made
            frag.innerHTML = new_s;
            
            var parent = el.parentNode;
            parent.replaceChild(frag, el);
            
        }
    } else {
        // This is not a text node, so we will loop through all of its child
        // nodes and recursively change the urls and email addresses in them.
        for ( var i=0, len=el.childNodes.length; i<len ; i++ ){
            if ( !el.childNodes[i] ) continue;
                // If for some reason this node is not set we skip it
            if ( el.childNodes[i].tagName == 'A' ) continue;
                // We don't want to change anything that is already inside
                // an <a> tag
            if ( el.childNodes[i].tagName == 'SCRIPT' ) continue;
                // We leave scripts alone because we don't want to screw up
                // any javascripts.
            
        
            makeLinksClickable(el.childNodes[i]);
                // Now we recursively call ourselves.
        }
    }

}


// Now we register this function to be called as soon as the document is finished
// loading.
registerOnloadHandler(makeLinksClickable);

But Safari has trouble with string.replace() callbacks:

Apparently Safari has some trouble with the string.replace() method when it comes to passing a function as the second parameter, as we have done in our solution above. See This post for information on how to beat this problem. Then we have a fully-workable, cross-browser solution for converting static URLs and email addresses to links using javascript.

Software Patents Must Stop

Here is another riduculous law-suit over a patent that should never have been granted.

It involves a company named Polaris IP suing a collection of companies (including Google and Yahoo) for using software to automatically respond to email using a sort of artificial intelligence. Polaris IP did not create the software to do this, they merely patented the idea of using computers to automatically respond to email.

Ridiculous!

American mainstream media spreading fear about Canada’s immigration policies

I was listening to the Glenn Beck show on CNN XM radio, August 21st. He and his guest (MICHAEL SCHEUER, FORMER SENIOR CIA OPERATIVE) were discussing the SPP (security and prosperity) meeting between the U.S., Canada, and Mexico. In the course of criticizing the “northern border” security, Scheuer stated that
“immigration rules are such that almost anyone who speaks French can get into [Canada]” and because of this “there is a large community of Tunisians, Moroccans and Algerians in Montreal and Quebec City, and those communities have ties into the Boston area in the United States, so it`s a dangerous situation.”, implying that Canada is a major terror threat to the United States. He underhandedly connected the security of the northern border with a possibility of “Bin Laden delivering another 20,000 to 40,000 dead Americans inside the United States”.

The transcript for this program can be found here.

The section that troubles me is:

–begin snippet —

SCHEUER: He came through Vancouver. That`s exactly right, sir. There is a large community of Tunisians, Moroccans and Algerians in Montreal and Quebec City, and those communities have ties into the Boston area in the United States, so it`s a dangerous situation.

BECK: OK. And tie in birth rates for me, Michael, because I know Canada is having not as extreme as Europe is having, but they`re having a problem with birth rates in Canada. Why is that dangerous?

SCHEUER: Well, especially in the French-speaking province of Quebec, their immigration rules are such that almost anyone who speaks French can get into the country in order to beef up the numbers of the French community, and so people from French-speaking Africa, French-speaking Asia come in very easily, but also, French-speaking North Africans from those three countries I just mentioned, Glenn.

— end snippet —

I am not an expert on Canadian immigration policy. But I have some concerns as a result of hearing this program. (Frankly, since subscribing to XM radio about a month ago I have been surprised by the ultra-protectionist opinions held by most of the mainstream media in the United States – including a veiled (and sometimes open) mistrust of Canada).

My concerns are as follows:

  1. If the claims about Quebec’s immigration policy are founded, and almost anyone who speaks French can get into the country, then this seems like an important issue that needs to be dealt with and changed.
  2. If the claims are false, then something should be done to hold people like Michael Scheuer responsible for their slanderous statements about Canada. We, as Canadians, can not afford to underestimate importance of American public opinion towards us.

Insulating the ZODB from bad products

The ZODB (Zope Object Database) is a wonderful little invention that provides Zope and Plone with a lot of flexibility. Because it is uses a heirarchical format, it is intuitive and easy to move and copy objects around.

However, it seems that the proper functioning of the ZODB depends heavily on all of the objects stored therein being in good health. This means that if you inadvertently install a product that doesn’t cover all of its bases, you could be up the creek without a paddle when it comes time to copy or migrate the site.

I am currently attempting to upgrade our Faculty’s plone web site to use the new SFU look and feel. I set up a development server a couple of months ago to work on the new skin. Now that it is ready, I would like to create a copy of our site on the same Zope instance so that I can install the skin on that instance, then just change the path so that the change can happen instantaneously.

This strategy would work perfectly if we were working directly on the file system. However, I have encountered a basket full of problems in trying to make this copy. It seems easy enough. You click the little box beside the site in the ZMI, press the "Copy" button, then click the "Paste" button. If only it were that simple.

In my first attempt, it churned for about 30 minutes before returning an error that it couldn’t find a transform for the image/pcx type. After some searching, I found an obscure fix for this issue, involving the temporary removal of one of the python source files for the PortalTransforms package.

My next attempt resulted in some errors relating to an old product (CoreBlog) that was no longer installed in the system. Apparently there were still some remnants left in the ZODB. I couldn’t find any actual CoreBlog objects, but the error seemed to indicate that there were some remnants left in the portal catalog.

So I tried updating the portal catalog to see if that would fix anything. After about 30 minutes of thinking it returned an read-write error.

Next I tried to clear and then rebuilt the catalog. This worked. Now I’m back trying to make a copy of the site… It is still thinking….

Getting to the point

So the point of this post was two-fold.

  1. To rant about Plone
  2. To suggest to those who might be reading this and have a hand in the direction of Zope and Plone, that the ZMI should be insulated from bad products. Imagine if, when copying files from your hard disk to a flash drive, the operating system crashed because one of the files was corrupt. This would make computers nearly impossible. How about an error log to inform me that one of the files couldn’t be copied – but let the rest of the copy go through. Or better yet, let the copy go through unhindered, allowing whatever problems were existent on the original file to be copied through to the copy. I could live with that.

Logic and the Word of God

How do we use logic in reading scripture? A simplistic view might be that we should not rely on logic to understand scripture. Instead we should accept scripture at face value and submit to it. On a verse-by-verse basis, this is a feasible technique, however, when it comes to understanding the Bible as a whole we cannot simply read verses at face value. We must take context into account, and where two verses appear to contradict each other, we must use logic to reconcile them.

Hence we must use logic to aid in our understanding of scripture. However we must not make the mistake of assuming that our human logical deductions which are based on the infallible word of God are, themselves, infallible.

Why I am finished with Plone

I have a client that I set up on Plone about 9 months ago. It was a substantial amount of work moving them over to Plone, what with having to make a skin and copy all of the pages into Plone pages. It was all towards making my life easier in the long run, because, in theory, the users would be able to add their own news items and events.

For the first few months this was “sort of” working, but somewhere along the line I started having problems with Zope hanging, and hogging the processor and memory. Now it is at the point where this is happening hourly.

I have played with caching, session timeouts, and packing the ZODB, and anything else I can think of, but the site still seems to hang. The error logs are not helpful as they don’t give any indication that I can see as to what is causing the hanging.

So I decide to try to upgrade to the latest version (Plone 3 – I’m running 2.1). So I install a newer version of Python (2.4.4), and a new version of Zope (2.10), and finally I try to import and migrate one of my sites into the new instance. 2 hours of torture later I find that Plone 3 changed the way it handles its workflows and it is completely incompatible with the way that they were handled in Plone 2.5 and lower – so none of my add-on products will work (including the Forum, Ploneboard).

Well, so much for Plone 3. I guess I won’t EVER be upgrading to that one.

So what about Plone 2.5. Okay.. same deal. Installed different version of Zope (2.9.7) as recommended, then tried to import and migrate the site… But again – since Plone 2.5 changes the way it handles user accounts in an incompatible way, I need to uninstall the CAS module that I use for authentication…. Not today..

So I decide that maybe i just need to install the same version.. So I reinstall Zope 2.8.7, and Plone 2.1.4 and move the ZODB over to this new instance… It appears to be working ok for the first few minutes… however.. an hour later, it too is locking up.

So maybe it’s Python. I install Python 2.3.5 again in its own folder, then install Zope 2.8.7 on top of it, then I try to run my site on this instance…. Oh… but some of the extensions that I had compiled into my other Python 3.5 still need to be compiled in… so I compile/install PIL – seems to work ok — but then it comes to MySQL-Python so that I can access a MySQL database from Plone. This I spent 8 hours on, as there were missing libraries and header files.

Finally when I get it installed, this instance just locks up before I can even do anything…

Perhaps I’m not “smart” enough to run Plone.

I have been fed up with Plone before but keep on persisting because of all of the great features it appears to have. However, the single most important feature for me is that the site stays running. I’m at the end of my rope and really don’t know what to do at this point other than restart the server every hour until I manage to port the site back to PHP – a technology that just works!

Google top paying search word hoax

Ran across this article talking about some of the recent sites that claimed to know the top paying search words on Google. In this article, the author claims that these figures are a hoax – which makes sense because there are some pretty ridiculously high payouts listed for legal search terms. My guess is that the person who originally released this list made some good money off the page because lots of people are looking for this information…. and if the information that people want isn’t available, why not fabricate it, right?

PHP: call_user_func no good if you need to pass by reference

In PHP the call_user_func function is used to call a function whose name you won’t know until runtime.   In Dataface I need to do this sort of thing quite a bit because the developers are allowed to implement methods in their delegate classes that follow naming conventions. For example, they might define a method called firstname__permissions() that returns the permissions for the firstname field.   Since Dataface is written to be generic, I can’t very well hard code a call to the firstname__permissions() method inside the Dataface core.

This is where I turned to call_user_func so that I can do something like this:

// Suppose the field name we want to check was passed to us in the $fieldname variable.
$delegate =& $this->getDelegate(); // gets the delegate class
$permissions =& call_user_func( array(&$delegate, $fieldname."__permissions"), $this);

call_user_func will accept a function call as either a string function name (e.g. ‘trim’) or as a 2-element array, where the first element is an object, and the second element is a string which is the name of the method to call on that object. So call_user_func(array(&$delegate, $fieldname."__permissions")) is actually calling something like $delegate->firstname__permissions() (if the value of $fieldname was ‘firstname’).

Problem: Objects in PHP 4 are passed by value.

Now suppose our firstname__permissions() is defined like:
function firstname__permissions(&$record){ ... }. Note that the ‘&’ prepended to the $record parameter means that we want to pass the object by reference. This is necessary in PHP since objects are passed by value by default. There are many reasons why we don’t want to pass objects by value. For starters, if we make changes to the object inside our function those changes will be lost because they would have been applied to a copy of the object and not the object itself.

More problems: call_user_func passes everything by value

So you think you’ve solved your problem by defining firstname__permissions(&$record) to take the $record parameter by reference. WRONG! If we call it like:
call_user_func(array(&$delegage, 'firstname__permissions'), $record) the $record parameter will still be passed by value because call_user_func passes everything by value.

Solution: Don’t use call_user_func

One nice feature of PHP that most responsible PHP developers avoid like the plague is variable variables and variable functions. However this is one case where these come in handy. For instance, suppose we have the case above where we are calling call_user_func(array(&$delegate, $fieldname.'__permissions'), $record);. Call it this way will result in $record being passed by value.

However, we can use a variable method name as follows:

$methodname = $fieldname.'__permissions';
$out = $delegate->$methodname($record);

This solution will call our variable function AND pass $record by reference so everyone is happy.

But be responsible. Don’t over-use variable method calls.