<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Scott Donnelly&#039;s Blog</title>
	<atom:link href="http://scott.donnel.ly/feed/" rel="self" type="application/rss+xml" />
	<link>http://scott.donnel.ly</link>
	<description>HTTP-EQUIV=&#34;CONTENT-TRIPE&#34;</description>
	<lastBuildDate>Fri, 23 Dec 2011 15:28:27 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=</generator>
		<item>
		<title>yepnope and jQuery &#8211; fix for $(document).ready calls</title>
		<link>http://scott.donnel.ly/yepnope-jquery-document-ready/</link>
		<comments>http://scott.donnel.ly/yepnope-jquery-document-ready/#comments</comments>
		<pubDate>Tue, 20 Dec 2011 20:54:12 +0000</pubDate>
		<dc:creator>Scotty</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[WebDev]]></category>
		<category><![CDATA[$(document).ready]]></category>
		<category><![CDATA[asynchronous]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[loader]]></category>
		<category><![CDATA[ready]]></category>
		<category><![CDATA[yepnope]]></category>
		<category><![CDATA[yepnope.js]]></category>

		<guid isPermaLink="false">http://scott.donnel.ly/?p=233</guid>
		<description><![CDATA[I recently added the fantastic yepnope.js to a site in order to give it a bit of a speedup. The site already had jQuery, including a few calls to $(document).ready() across a few different files. As anybody who has tried this knows, this breaks your $(document).ready() calls. jQuery is not loaded at the point that [...]]]></description>
			<content:encoded><![CDATA[<p>I recently added the fantastic <a title="yepnope.js" href="http://yepnopejs.com/" target="_blank">yepnope.js</a> to a site in order to give it a bit of a speedup. The site already had jQuery, including a few calls to $(document).ready() across a few different files. As anybody who has tried this knows, this breaks your $(document).ready() calls. jQuery is not loaded at the point that the browser tries to execute the document ready call, so the browser gives an error, complaining that $ is undefined. Here is my solution.</p>
<p>First, add this in your &lt;head&gt;, before yepnope and anything that could use jQuery. This sets up a placeholder that stores any calls to $(document).ready() in an array called docready.</p>
<pre class="brush: js">var docready=[],$=function(){return{ready:function(fn){docready.push(fn)}}};</pre>
<p>Secondly, in your yepnope call to load jQuery, change the &#8220;complete&#8221; call like so:</p>
<pre class="brush: js">yepnope({
	load: '//ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.min.js',
	callback: function (url, result, key) {
		if (!window.jQuery) yepnope('/js/jquery.min.js');
	},
	complete: function() {
		$ = jQuery;
		jQuery(document).ready(function() {
			for(n in docready) docready[n]();
		});
	}
});</pre>
<p>So now, once jQuery has loaded, $ is reassigned from our placeholder to jQuery, and the stored $(document).ready() calls are iterated through and executed once ready fires. Sweet!</p>
<p>Edit: Updated with new, more concise, complete callback.</p>
<img src="http://scott.donnel.ly/?ak_action=api_record_view&id=233&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://scott.donnel.ly/yepnope-jquery-document-ready/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>OpenSearch &#8211; A quick tip</title>
		<link>http://scott.donnel.ly/opensearch-a-quick-tip/</link>
		<comments>http://scott.donnel.ly/opensearch-a-quick-tip/#comments</comments>
		<pubDate>Tue, 10 May 2011 23:30:19 +0000</pubDate>
		<dc:creator>Scotty</dc:creator>
				<category><![CDATA[Apache]]></category>
		<category><![CDATA[WebDev]]></category>
		<category><![CDATA[OpenSearch]]></category>

		<guid isPermaLink="false">http://scott.donnel.ly/?p=206</guid>
		<description><![CDATA[I&#8217;ve learnt some great new skills today involving OpenSearch, and browser search plug-ins. Not enough time to put it all into an article right now, but I will work on one over the next few days. In the meantime, a quick tip &#8211; something that may save someone a bit of time. When linking to [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve learnt some great new skills today involving <a href="http://http://www.opensearch.org/">OpenSearch</a>, and browser search plug-ins. Not enough time to put it all into an article right now, but I will work on one over the next few days.</p>
<p>In the meantime, a quick tip &#8211; something that may save someone a bit of time.</p>
<p>When linking to an OpenSearch description document to enable auto-discovery, an HTML link tag to the XML OSDD is used. The type property is set to &#8220;application/opensearchdescription+xml&#8221;, as per the OpenSearch spec. By default, however, as the OSDD file generally has a .xml suffix, Apache will serve the image up as text/xml. This can cause the browser to not accept the OSDD. The solution, if you are using .htaccess files, is a simple addition to the .htaccess file. Replace opensearch.xml with the name of your OSDD file:</p>
<pre class="brush: text;" style="font-size: small;">
&lt;Files opensearch.xml&gt;
    ForceType application/opensearchdescription+xml
&lt;Files&gt;</pre>
<p>Simple, I know, but it may cause a bit of head-scratching. More to come.</p>
<img src="http://scott.donnel.ly/?ak_action=api_record_view&id=206&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://scott.donnel.ly/opensearch-a-quick-tip/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Email Validation &#8211; via Polymorphic PHP / JS hybrid script, internationalized CC TLD ready</title>
		<link>http://scott.donnel.ly/email-validation-via-polymorphic-php-js-hybrid-script-internationalized-cc-tld-ready/</link>
		<comments>http://scott.donnel.ly/email-validation-via-polymorphic-php-js-hybrid-script-internationalized-cc-tld-ready/#comments</comments>
		<pubDate>Mon, 20 Sep 2010 23:05:24 +0000</pubDate>
		<dc:creator>Scotty</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[WebDev]]></category>
		<category><![CDATA[email]]></category>
		<category><![CDATA[IANA]]></category>
		<category><![CDATA[JavScript]]></category>
		<category><![CDATA[polymorphism]]></category>
		<category><![CDATA[regex]]></category>
		<category><![CDATA[self-modification]]></category>
		<category><![CDATA[validation]]></category>

		<guid isPermaLink="false">http://scott.donnel.ly/?p=140</guid>
		<description><![CDATA[I was looking around for a RegEx to match an email address last night and headed straight to, what must be, the RegEx Mecca &#8211; http://www.regular-expressions.info . The page discussing email validation is very comprehensive and informative. The section that got my mind going was under the heading &#8220;Trade-Offs in Validating Email Addresses&#8221;; in particular [...]]]></description>
			<content:encoded><![CDATA[<p>I was looking around for a RegEx to match an email address last night and headed straight to, what must be, the RegEx Mecca &#8211; <a href="http://www.regular-expressions.info">http://www.regular-expressions.info</a> . The page discussing <a title="email validation" href="http://www.regular-expressions.info/email.html" target="_blank">email validation</a> is very comprehensive and informative. The section that got my mind going was under the heading &#8220;Trade-Offs in Validating Email Addresses&#8221;; in particular the lengthy RegEx and it&#8217;s associated critique:</p>
<blockquote><p><tt>^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.(?:[A-Z]{2}|com|org|net|edu|gov|mil|biz|info|mobi|name|aero|asia|jobs|museum)$</tt> <a href="http://www.regular-expressions.info/regexbuddy/emailtld.html"><img src="http://www.regular-expressions.info/img/rxb.gif" border="0" alt="Analyze this regular expression with RegexBuddy" width="16" height="16" align="middle" /></a> could be used to allow any two-letter country code top level domain,  and only specific generic top level domains.  By the time you read this,  the list might already be out of date.  If you use this regular  expression, I recommend you store it in a global constant in your  application, so you only have to update it in one place.  You could list  all country codes in the same manner, even though there are almost 200  of them.</p></blockquote>
<p>So I thought to myself, what if you could somehow automatically update the list of valid TLD&#8217;s, generate the RegEx from that, and then just use a simple function to test the RegEx? JS would be out of the window for the auto-update, unless you wanted the client to have an extra HTTP request for every visit to a page with your function. But how about a PHP script that could update the TLD list, direct from IANA, say, once per day, and cache this request. Combine the JS with the PHP by having the PHP script generate the JS programatically, by setting the script&#8217;s header&#8217;s content type attribute as  application/x-javascript. And for fun, and because I never get to do any self-modifying code, have the PHP script store its updates from IANA in itself &#8211; so that it would only need to refresh its TLD list when it checks it&#8217;s own file modification time and finds itself to be unmodified for over a day.</p>
<p>Well, here is the result (download link at bottom of post):</p>
<pre class="brush: php;" style="font-size: small;">< ?php
/*****************************************************
* valEmail.js.php
*
* @author Scott Donnelly <scott@donnel.ly>
* @version 1.0
* Released under the LGPL V3 license.
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
*
* See http://scott.donnel.ly/archive/140 for info
* thanks to http://www.regular-expressions.info
******************************************************/

// TLDs last updated: Never
$TLDs = Array();

// update list of top-level domains from IANA if current list is too old.
if (empty($TLDs) || filemtime('./valEmail.js.php') < time() - 86400) {

    // get the file from IANA; gracefully fall back to current data on fail
    if ($iana_TLD_file = file_get_contents('http://data.iana.org/TLD/tlds-alpha-by-domain.txt')) {

        $TLDs = Array();
        $iana_TLD_file = explode("\n", $iana_TLD_file);

        // parse all TLD's into TLD array - ignore comments/too short lines
        foreach($iana_TLD_file as $line)
            if (strlen(trim($line)) > 1 &#038;&#038; $line[0] != "#")
                array_push($TLDs, trim($line));

        // self-modify to update cached TLD Array
        if ($this_file = file_get_contents('valEmail.js.php')) {

            // rip the document block at the start of the file.
            $start_of_file = substr($this_file, 0, strpos($this_file, "// TLDs last updated"));

            // synthesize the first 3 lines of the file after the docblock...
            $start_of_file .= "// TLDs last updated: "
                . date('D/n/Y G:i:s T') . "\n"
                . '$TLDs = Array(';

            $first = true;
            foreach($TLDs as $TLD) {
                if ($first) $first = false; else $start_of_file .= ", ";
                $start_of_file .= "'$TLD'";
            }
            $start_of_file .= ");\n";

            // ... append the rest of the file as per current file...
            $this_file = $start_of_file . substr($this_file, strpos($this_file, ");\n") + 3);

            // .. and write it out to the file system.
            if ($fp = fopen('./valEmail.js.php', 'wt')) {
                fwrite($fp, $this_file);
                fclose($fp);
            }
        }
    }
}

// output the email check function as a JS file.
header('Content-type: application/x-javascript');

echo "function valEmail(email) {
        emailRE = new RegExp('[a-z0-9!#$%\&#038;\'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%\&#038;\'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+(?:";

$first = true;
foreach($TLDs as $TLD) {
    if ($first) $first = false; else echo "|";
    echo(strtolower($TLD));
}

echo ")$')\n\treturn emailRE.test(email);\n}\n";
?></pre>
<p>Executing the code the first time triggers the script to modify itself, rewriting lines 13 and 14 to:</p>
<pre class="brush: php; first-line:13;" style="font-size: x-small;">// TLDs last updated: Mon/9/2010 21:43:00 BST
$TLDs = Array('AC', 'AD', 'AE', 'AERO', 'AF', 'AG', 'AI', 'AL', 'AM', 'AN', 'AO', 'AQ', 'AR', 'ARPA', 'AS', 'ASIA', 'AT', 'AU', 'AW', 'AX', 'AZ', 'BA', 'BB', 'BD', 'BE', 'BF', 'BG', 'BH', 'BI', 'BIZ', 'BJ', 'BM', 'BN', 'BO', 'BR', 'BS', 'BT', 'BV', 'BW', 'BY', 'BZ', 'CA', 'CAT', 'CC', 'CD', 'CF', 'CG', 'CH', 'CI', 'CK', 'CL', 'CM', 'CN', 'CO', 'COM', 'COOP', 'CR', 'CU', 'CV', 'CX', 'CY', 'CZ', 'DE', 'DJ', 'DK', 'DM', 'DO', 'DZ', 'EC', 'EDU', 'EE', 'EG', 'ER', 'ES', 'ET', 'EU', 'FI', 'FJ', 'FK', 'FM', 'FO', 'FR', 'GA', 'GB', 'GD', 'GE', 'GF', 'GG', 'GH', 'GI', 'GL', 'GM', 'GN', 'GOV', 'GP', 'GQ', 'GR', 'GS', 'GT', 'GU', 'GW', 'GY', 'HK', 'HM', 'HN', 'HR', 'HT', 'HU', 'ID', 'IE', 'IL', 'IM', 'IN', 'INFO', 'INT', 'IO', 'IQ', 'IR', 'IS', 'IT', 'JE', 'JM', 'JO', 'JOBS', 'JP', 'KE', 'KG', 'KH', 'KI', 'KM', 'KN', 'KP', 'KR', 'KW', 'KY', 'KZ', 'LA', 'LB', 'LC', 'LI', 'LK', 'LR', 'LS', 'LT', 'LU', 'LV', 'LY', 'MA', 'MC', 'MD', 'ME', 'MG', 'MH', 'MIL', 'MK', 'ML', 'MM', 'MN', 'MO', 'MOBI', 'MP', 'MQ', 'MR', 'MS', 'MT', 'MU', 'MUSEUM', 'MV', 'MW', 'MX', 'MY', 'MZ', 'NA', 'NAME', 'NC', 'NE', 'NET', 'NF', 'NG', 'NI', 'NL', 'NO', 'NP', 'NR', 'NU', 'NZ', 'OM', 'ORG', 'PA', 'PE', 'PF', 'PG', 'PH', 'PK', 'PL', 'PM', 'PN', 'PR', 'PRO', 'PS', 'PT', 'PW', 'PY', 'QA', 'RE', 'RO', 'RS', 'RU', 'RW', 'SA', 'SB', 'SC', 'SD', 'SE', 'SG', 'SH', 'SI', 'SJ', 'SK', 'SL', 'SM', 'SN', 'SO', 'SR', 'ST', 'SU', 'SV', 'SY', 'SZ', 'TC', 'TD', 'TEL', 'TF', 'TG', 'TH', 'TJ', 'TK', 'TL', 'TM', 'TN', 'TO', 'TP', 'TR', 'TRAVEL', 'TT', 'TV', 'TW', 'TZ', 'UA', 'UG', 'UK', 'US', 'UY', 'UZ', 'VA', 'VC', 'VE', 'VG', 'VI', 'VN', 'VU', 'WF', 'WS', 'XN--0ZWM56D', 'XN--11B5BS3A9AJ6G', 'XN--80AKHBYKNJ4F', 'XN--9T4B11YI5A', 'XN--DEBA0AD', 'XN--FIQS8S', 'XN--FIQZ9S', 'XN--FZC2C9E2C', 'XN--G6W251D', 'XN--HGBK6AJ7F53BBA', 'XN--HLCJ6AYA9ESC7A', 'XN--J6W193G', 'XN--JXALPDLP', 'XN--KGBECHTV', 'XN--KPRW13D', 'XN--KPRY57D', 'XN--MGBAAM7A8H', 'XN--MGBAYH7GPA', 'XN--MGBERP4A5D4AR', 'XN--O3CW4H', 'XN--P1AI', 'XN--PGBS0DH', 'XN--WGBH1C', 'XN--XKC2AL3HYE2A', 'XN--YGBI2AMMX', 'XN--ZCKZAH', 'YE', 'YT', 'ZA', 'ZM', 'ZW');</pre>
<aside>(Note the interesting cluster of internationalised CC TLD&#8217;s at the end &#8211; all of the XN&#8211; beginning tld&#8217;s. There are undoubtedly a lot more of these on the way, since applications for them were only accepted beginning November 2009. Five were approved in January 2010 and came into service in May 2010. A further <emph>21</emph> were requested in May 2010. See the very interesting entry on Wikipedia regarding these <a href="http://en.wikipedia.org/wiki/Internationalized_country_code_top-level_domain">Internationalized CC TLD&#8217;s</a>. Since it is likely we will get a torrent of these i15ed CCTLD&#8217;s over the coming months and years, perhaps my implementation does not seem so silly after all! )</aside>
<p>
Anyway, the script produces (at the time of writing!) the following JavaScript function:</p>
<pre style="font-size: x-small;">function valEmail(email) {
		emailRE = new RegExp('[a-z0-9!#$%\&#038;\'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%\&#038;\'*+/=?^_`{|}~-]+)*@
(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+?:ac|ad|ae|aero|af|ag|ai|al|am|an|ao|aq|ar|arpa|as|asia|at|au|
aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|biz|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cat|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|
com|coop|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|edu|ee|eg|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|
gl|gm|gn|gov|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|info|int|io|iq|ir|is|it|je|jm|jo|jobs|jp|ke|
kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mil|mk|ml|mm|mn|mo|mobi|
mp|mq|mr|ms|mt|mu|museum|mv|mw|mx|my|mz|na|name|nc|ne|net|nf|ng|ni|nl|no|np|nr|nu|nz|om|org|pa|pe|
pf|pg|ph|pk|pl|pm|pn|pr|pro|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|
sy|sz|tc|td|tel|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|travel|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|
xn--0zwm56d|xn--11b5bs3a9aj6g|xn--80akhbyknj4f|xn--9t4b11yi5a|xn--deba0ad|xn--fiqs8s|xn--fiqz9s|
xn--fzc2c9e2c|xn--g6w251d|xn--hgbk6aj7f53bba|xn--hlcj6aya9esc7a|xn--j6w193g|xn--jxalpdlp|xn--kgbechtv|
xn--kprw13d|xn--kpry57d|xn--mgbaam7a8h|xn--mgbayh7gpa|xn--mgberp4a5d4ar|xn--o3cw4h|xn--p1ai|
xn--pgbs0dh|xn--wgbh1c|xn--xkc2al3hye2a|xn--ygbi2ammx|xn--zckzah|ye|yt|za|zm|zw)$')
	return emailRE.test(email);
}</pre>
<p>Not pretty, but &#8220;fire-and-forget&#8221; &#8211; you will never need to look at this ugly JS as it will never need editing (These sound like famous last words&#8230; no more than 640k anyone??! <img src='http://scott.donnel.ly/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' />  )</p>
<p>Anyways, you can give it a try here:</p>
<p><script type="text/javascript" language="JavaScript">function valEmail(email) {	
		emailRE = new RegExp('[a-z0-9!#$%\&#038;\'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%\&#038;\'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+(?:ac|ad|ae|aero|af|ag|ai|al|am|an|ao|aq|ar|arpa|as|asia|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|biz|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cat|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|com|coop|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|edu|ee|eg|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gov|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|info|int|io|iq|ir|is|it|je|jm|jo|jobs|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mil|mk|ml|mm|mn|mo|mobi|mp|mq|mr|ms|mt|mu|museum|mv|mw|mx|my|mz|na|name|nc|ne|net|nf|ng|ni|nl|no|np|nr|nu|nz|om|org|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|pro|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tel|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|travel|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|xn--0zwm56d|xn--11b5bs3a9aj6g|xn--80akhbyknj4f|xn--9t4b11yi5a|xn--deba0ad|xn--fiqs8s|xn--fiqz9s|xn--fzc2c9e2c|xn--g6w251d|xn--hgbk6aj7f53bba|xn--hlcj6aya9esc7a|xn--j6w193g|xn--jxalpdlp|xn--kgbechtv|xn--kprw13d|xn--kpry57d|xn--mgbaam7a8h|xn--mgbayh7gpa|xn--mgberp4a5d4ar|xn--o3cw4h|xn--p1ai|xn--pgbs0dh|xn--wgbh1c|xn--xkc2al3hye2a|xn--ygbi2ammx|xn--zckzah|ye|yt|za|zm|zw)$');
	return emailRE.test(email);
};
</script></p>
<form>
<input id="valEmailTest" value="type an email here" onClick='this.value=""' onKeyUp='var obj = getElementById("valEmailResult"); if (valEmail(this.value)) { obj.value = "Email Ok!"; obj.style.color = "green"; }  else { obj.value = "Email Invalid!"; obj.style.color = "red"; }'/>
<input id="valEmailResult" disabled/></form>
<p> You can downoad the zipped valEmail.js.php file <a href="http://scott.donnel.ly/valEmail.zip">using this link.</a></p>
<p><a class="FlattrButton" style="display:none;" href="http://scott.donnel.ly"></a></p>
<img src="http://scott.donnel.ly/?ak_action=api_record_view&id=140&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://scott.donnel.ly/email-validation-via-polymorphic-php-js-hybrid-script-internationalized-cc-tld-ready/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>passchk_fast &#8211; A JavaScript password strength utility based on passchk</title>
		<link>http://scott.donnel.ly/passchk_fast-a-javascript-password-strength-utility-based-on-passchk/</link>
		<comments>http://scott.donnel.ly/passchk_fast-a-javascript-password-strength-utility-based-on-passchk/#comments</comments>
		<pubDate>Sun, 19 Sep 2010 01:06:18 +0000</pubDate>
		<dc:creator>Scotty</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[WebDev]]></category>
		<category><![CDATA[entropy]]></category>
		<category><![CDATA[paschk_fast]]></category>
		<category><![CDATA[passchk]]></category>
		<category><![CDATA[password]]></category>
		<category><![CDATA[security]]></category>

		<guid isPermaLink="false">http://scott.donnel.ly/?p=117</guid>
		<description><![CDATA[I have been working on a little pet project to improve the registration form for a site I am working on, and came across passchk by Tyler Akins. Thanks Tyler, it has been a great use to me. This great bit of code had the kind of functionality that I was after, but the JavaScript-based [...]]]></description>
			<content:encoded><![CDATA[<p>I have been working on a little pet project to improve the registration form for a site I am working on, and came across <a href="http://rumkin.com/tools/password/passchk.php">passchk by Tyler Akins</a>. Thanks Tyler, it has been a great use to me. This great bit of code had the kind of functionality that I was after, but the JavaScript-based decompression routine seemed a bit slow for something to use in a registration form &#8211; I wanted something a bit more snappy. So after a couple of hours of coding, I have re-factored passchk, trading off space for speed. The common passwords file is uncompressed and stored as JSON, increasing the file size but doing away with the time required for decompression. This led to the common passwords being stored ROT13-encrypted, so that obscene common passwords do not cause the JS file to be blocked by any web filtering/firewalls. the frequency table is also stored as a JSON string, for rapid loading, in the main JS file.</p>
<p>I have also completely rewritten the character set size determination function to use regex instead, and made a minor tweak to the entropy calculation function to handle single character passwords more correctly. The functionality has been broken out into separate functions so that they can better be re-used. See how the final modified code behaves here (no password data is sent back to this server &#8211; all calculations are done in JavaScript from within your browser.)</p>
<p><script language="javascript" src="http://scott.donnel.ly/scripts/passchk_fast.js"></script><script language="javascript" src="http://scott.donnel.ly/scripts/passchk_test.js"></script></p>
<form action="#" method="POST"><strong>Enter your password or passphrase here:</strong></p>
<input id="passchk_field" size="40" type="password" /></form>
<p><span id="passchk_result">Loading &#8230;</span></p>
<p><a href="http://scott.donnel.ly/downloads/passchk_fast.zip">download here</a> if you want to use it. It is licensed under the GPL3.</p>
<p>To learn more about password entropy and information theory, see Tyler&#8217;s excellent <a href="http://rumkin.com/tools/password/passchk.php">original page</a>,  as well as Wikipedia&#8217;s page on <a href="http://en.wikipedia.org/wiki/Password_strength">Password Strength</a>.</p>
<img src="http://scott.donnel.ly/?ak_action=api_record_view&id=117&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://scott.donnel.ly/passchk_fast-a-javascript-password-strength-utility-based-on-passchk/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Limesurvey Ticks All My Boxes.</title>
		<link>http://scott.donnel.ly/best-e-surveys-limesurvey-wins-for-me/</link>
		<comments>http://scott.donnel.ly/best-e-surveys-limesurvey-wins-for-me/#comments</comments>
		<pubDate>Sat, 17 Apr 2010 03:10:44 +0000</pubDate>
		<dc:creator>Scotty</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[LAMP]]></category>
		<category><![CDATA[limesurvey]]></category>
		<category><![CDATA[survey]]></category>

		<guid isPermaLink="false">http://scott.donnel.ly/?p=95</guid>
		<description><![CDATA[I recently had to source and implement a means of creating, distributing, and reporting customer satisfaction surveys. I had a look around and after a bit of research narrowed things down to SurveyMonkey, Google Forms, and LimeSurvey. I settled for LimeSurvey in the end because: It is free &#8211; as a LAMP based web app, [...]]]></description>
			<content:encoded><![CDATA[<p>I recently had to source and implement a means of creating, distributing, and reporting customer satisfaction surveys. I had a look around and after a bit of research narrowed things down to <a href="http://www.surveymonkey.com/">SurveyMonkey</a>,<a href="http://http://docs.google.com/support/bin/topic.py?topic=15166"> Google Forms</a>, and <a href="http://www.limesurvey.org/">LimeSurvey</a>. I settled for LimeSurvey in the end because:</p>
<li>It is free &#8211; as a LAMP based web app, all you need is a web server to install it to and you get full functionality. SurveyMonkey and its ilk charge to be able to have more than ten question surveys; LimeSurvey allows you as many surveys with as many questions to as many recipients as you like.</li>
<li>It is extremely customizeable and flexible. 20 question types, skip logic, and a built in template editor.</li>
<li>Did I mention it&#8217;s free?</li>
<p>Google Forms is a great solution for simple, generic looking surveys with simpler question types, but LimeSurvey is a fully integrated survey application with comprehensive reporting, token based email invites / reminders, and plenty more besides. After a straightforward install and short learning period, I had knocked out a great looking survey in no time, with the template matching the company colours and badged with the logo. The surveys are hosted on a subdomain of our website and the emails appear to originate from our domain, so the overall feel is very professional. Anyone looking to implement something similar could do a lot worse than to get yourself over to <a href="http://www.limesurvey.org/">LimeSurvey</a> for the download.</p>
<img src="http://scott.donnel.ly/?ak_action=api_record_view&id=95&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://scott.donnel.ly/best-e-surveys-limesurvey-wins-for-me/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Astro resumption</title>
		<link>http://scott.donnel.ly/astro-resumption/</link>
		<comments>http://scott.donnel.ly/astro-resumption/#comments</comments>
		<pubDate>Thu, 08 Apr 2010 22:37:10 +0000</pubDate>
		<dc:creator>Scotty</dc:creator>
				<category><![CDATA[astro]]></category>
		<category><![CDATA[C6-SGT]]></category>
		<category><![CDATA[QCUIAG]]></category>
		<category><![CDATA[satellite]]></category>
		<category><![CDATA[SPC900NC]]></category>

		<guid isPermaLink="false">http://scott.donnel.ly/?p=87</guid>
		<description><![CDATA[It&#8217;s been ages since I&#8217;ve got my &#8216;scope out, but it&#8217;s ben gnawing at me for a while. Last night I decided to get it set up in my garden for the first time since moving house. I had one of those nights that reminds me why I got a scope in the first place [...]]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s been ages since I&#8217;ve got <a href="http://firstlightoptics.com/proddetail.php?prod=c6sgtxlt">my &#8216;scope</a> out, but it&#8217;s ben gnawing at me for a while. Last night I decided to get it set up in my garden for the first time since moving house. I had one of those nights that reminds me why I got a scope in the first place &#8211; I saw not only Saturn and Mars, as well as several <a href="http://en.wikipedia.org/wiki/Messier_object">Messier objects</a>, through the scope, but also spotted several satellites, a shooting star, and an <a href="http://en.wikipedia.org/wiki/Satellite_flare">Iridium flare</a> (see <a href="http://www.heavens-above.com/iridium.asp?Dur=7&#038;lat=52.485&#038;lng=-1.860&#038;loc=Birmingham&#038;alt=114&#038;tz=GMT">Heavens Above</a> to find out when you can see one) whist setting up, which was lucky.</p>
<p>I found that <a href="http://www.google.com/sky/skymap/">Google Sky Map</a> on my <a href="http://www.engadget.com/2009/07/23/htc-hero-review/">HTC Hero</a> is a great aid for setting up, in order to refresh my memory of where all the stars are when aligning the scope. I can instantly identify where the alignment stars and Polaris are as well as pick out which planets are in a good enough place for me to spot (I have several buildings around blocking large chinks of the sky). </p>
<p>I captured some video on my SP900NC using <a href="http://arnholm.org/astro/software/wxAstroCapture/">WxAstroCapture</a>, and was hoping to use <a href="http://www.avistack.de/index_en.html">AviStack</a> to stack the images but it tells me that the CODEC is unsupported &#8211; anyone got any ideas?</p>
<p>The quality was really poor though, I need to work on my collimation (<a href="http://www.astrophoto.fr/">Thierry Legault</a>&#8216;s fantastic collimation guide is always my first point of reference). There was a lot of crap on my collector plate which I have cleaned off, which I am hoping will mean a much improved view next time.</p>
<p>I have also <a href="http://www.home.zonnet.nl/m.m.j.meijer/D_I_Y/spc900nc.htm">LX-Modded</a> my SPC900NC, but do not have a proper parallel port to make use of it &#8211; I am going to have to make up a USB-Serial adapter as <a href="http://home.versatel.nl/m.m.j.meijer/D_I_Y/usb_serial.htm">described by Martin Meijer</a> (I think the <a href="http://www.store.shoestringastronomy.com/products_lx.htm">Shoestring Astronomy LXUSB</a> is a bit expensive for what it does for my liking). Once I have done this I will post my results.</p>
<img src="http://scott.donnel.ly/?ak_action=api_record_view&id=87&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://scott.donnel.ly/astro-resumption/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>WordPress PHP Fatal Error</title>
		<link>http://scott.donnel.ly/tweet-blender-need-mender/</link>
		<comments>http://scott.donnel.ly/tweet-blender-need-mender/#comments</comments>
		<pubDate>Thu, 08 Apr 2010 21:27:45 +0000</pubDate>
		<dc:creator>Scotty</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Wordpress]]></category>

		<guid isPermaLink="false">http://scott.donnel.ly/?p=78</guid>
		<description><![CDATA[Alex King strikes again, with an explanation and a fix!]]></description>
			<content:encoded><![CDATA[<p>Update &#8211; it seems that Tweet Blender or Twitter Tools does not want to play ball &#8211; when I hit the big blue Publish button, i get a PHP fatal error (<code>Cannot redeclare class services_json</code>).</p>
<p>On further investigation, it looks like this is down to WordPress rather than any of the plugins. Also, the afore mentioned <a href="http://alexking.org/blog">Alex King</a> strikes again &#8211; with an explanation for the problem and a fix, <a href="http://core.trac.wordpress.org/attachment/ticket/11827/patch-class-json.diff">Here</a> &#8211; which I have put into place. Thanks again, Alex!</p>
<img src="http://scott.donnel.ly/?ak_action=api_record_view&id=78&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://scott.donnel.ly/tweet-blender-need-mender/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>WP Update</title>
		<link>http://scott.donnel.ly/wp-update/</link>
		<comments>http://scott.donnel.ly/wp-update/#comments</comments>
		<pubDate>Thu, 08 Apr 2010 21:13:03 +0000</pubDate>
		<dc:creator>Scotty</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[Twitter]]></category>
		<category><![CDATA[Wordpress]]></category>

		<guid isPermaLink="false">http://scott.donnel.ly/?p=72</guid>
		<description><![CDATA[Thanks to <a href="http://alexking.org/blog">Alex King</a> for making a load of cool plugins, see <a href="http://alexking.org/projects/wordpress">here</a> for a list, there's some great stuff there.]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been putting it off for a while but I&#8217;ve now moved the back end DB to MySQL 5 from the old 4 DB at my hosts, allowing me to upgrade WP. Anyone else using wordpress, I&#8217;m sure I don&#8217;t need to underscore the importance of staying up-to-date &#8211; see <a href="http://scobleizer.com/2009/09/05/i-dont-feel-safe-with-wordpress-hackers-broke-in-and-took-things/">Scobleizer</a> for an example. </p>
<p>Plugins like <a href="http://semperfiwebdesign.com/plugins/wp-security-scan/">WP Security Scan</a> and <a href="http://akismet.com/">Akimset</a> are great ways to help improve your blog&#8217;s security. Also, <a href="http://www.lostinsearch.com/2009/01/18/10-wordpress-security-tips/">Lostinsearch.com</a> have some great tips. If anybody has any other tips, plugins, or even tales of what can happen with poor WP security, let us know below.</p>
<p>Anyways I took the time to change theme and have a freshen up of the blog, the old theme was starting to bug me, as well as adding some cool Twitter integration &#8211; via <a href="http://wordpress.org/extend/plugins/tweet-blender/">Tweet Blender</a>, and <a href="http://alexking.org/projects/wordpress/readme?project=twitter-tools">Twitter Tools</a> (anyone have any good suggestions for other Twitter plugins?)</p>
<p>Thanks to <a href="http://alexking.org/blog">Alex King</a> for making a load of cool plugins, see <a href="http://alexking.org/projects/wordpress">here</a> for a list, there&#8217;s some great stuff there.</p>
<p>Anyways I&#8217;m gonna try and post a bit more frequently (the blogger&#8217;s lament &#8211; if I had a pound for every time I&#8217;d read that on a blog (and another pound for each time I saw that written followed by a massive gap with no posts!) )&#8230; so watch this space.</p>
<img src="http://scott.donnel.ly/?ak_action=api_record_view&id=72&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://scott.donnel.ly/wp-update/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Asterisk to Voiceflex &#8211; an example on the NSLU2</title>
		<link>http://scott.donnel.ly/asterisk-to-voiceflex-an-example-on-the-nslu2/</link>
		<comments>http://scott.donnel.ly/asterisk-to-voiceflex-an-example-on-the-nslu2/#comments</comments>
		<pubDate>Thu, 17 Dec 2009 11:47:10 +0000</pubDate>
		<dc:creator>Scotty</dc:creator>
				<category><![CDATA[Asterisk]]></category>
		<category><![CDATA[BCM50]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[SIP]]></category>

		<guid isPermaLink="false">http://scott.donnel.ly/?p=63</guid>
		<description><![CDATA[I have had numerous comments and requests regarding Asterisk configuration for Voiceflex. I have neglected my blog somewhat lately, but when I googled for &#8220;BCM50 SIP&#8221; the other day, I was suprised to see my own blog high up the listings! I have decided to revisit this, as it has obviously generated a lot of [...]]]></description>
			<content:encoded><![CDATA[<p>I have had numerous comments and requests regarding Asterisk configuration for Voiceflex. I have neglected my blog somewhat lately, but when I googled for &#8220;BCM50 SIP&#8221; the other day, I was suprised to see my own blog high up the listings! I have decided to revisit this, as it has obviously generated a lot of interest.</p>
<p>I have a bash script that I used on the Linksys NSLU2 (&#8220;the SLUG&#8221;) in order to quickly configure Asterisk to act as a gateway between a Nortel BCM50 and Voiceflex&#8217;s SIP trunks. Although there is no longer a need to use the NSLU2 in this fashion, as Voiceflex can dispense with SIP authentication when the BCM50 is on a static IP, I will post the scripts, as well as the relevant Asterisk config files, as they may be useful for anybody that wants to configure Asterisk with SIP trunks.</p>
<p>Firstly,  the Asterisk configs: obviously, do not use these as-is, you will need to change the details to suit!<br />
<br />
sip.conf</p>
<pre class="brush:bash">[general]

context=default
bindport=5060
bindaddr=0.0.0.0
srvlookup=yes
domain=asterisk

disallow=all
allow=alaw,g729,g723
language=en
relaxdtmf=yes
trustrpid=no
useragent=Asterisk PBX
promiscredir=yes
canreinvite=yes

domain=146.101.248.200,incoming-voiceflex
domain=192.168.40.6,incoming-voiceflex
domain=10.10.10.10,incoming-bcm
domain=10.10.11.11,default

#include /etc/asterisk/sip_nat.conf

#include /etc/asterisk/sip_accounts.conf

[sip-bcm]
type=peer
host=10.10.10.10
context=incoming-bcm

#include /etc/asterisk/sip_og_context.conf

; sip ext for xlite, for testing
[200]
type=friend
regexten=200
secret=donttell
context=default
host=10.10.10.10
canreinvite=no
insecure=port,invite</pre>
<p>sip_nat.conf</p>
<pre class="brush:bash">externip=11.11.11.11
localnet=192.168.192.0/24
nat=yes</pre>
<p>sip_accounts.conf</p>
<pre class="brush:bash">;sip_accounts.conf generated by autoasterflex
register =&gt; 12345678:shhhsecret@sip.voiceflex.com

registertimeout=20
registerattempts=0

[authentication]
auth=12345678:shhhsecret@146.101.248.200
auth=12345678:shhhsecret@voiceflex
auth=12345678:shhhsecret@sip.voiceflex.com</pre>
<p>sip_og_context.conf</p>
<pre class="brush:bash">[voiceflex]
type=friend
callerid=01234567890
fromuser=01234567890
defaultuser=12345678@voiceflex
disallow=all
allow=alaw,ulaw,g729
fromdomain=voiceflex
secret=shhhsecret
host=sip.voiceflex.com
insecure=invite,port
context=incoming-voiceflex</pre>
<p>extensions_incoming_voiceflex.conf</p>
<pre class="brush:bash">[incoming-voiceflex]
exten =&gt; s,1,Dial(SIP/666666@sip-bcm,,r);
exten =&gt; 666666,1,Dial(SIP/666666@sip-bcm,,r);</pre>
<p>extensions.conf</p>
<pre class="brush:bash">; extensions.conf - asterisk dialplan

[general]
static=yes
writeprotect=no
autofallthrough=yes
clearglobalvars=no
priorityjumping=no

[globals]

[incoming-bcm]
exten =&gt; 200,1,Dial(SIP/200,,r);
exten =&gt; _9.,1,Dial(SIP/${EXTEN:1}@voiceflex,,r);
exten =&gt; _8.,1,SIPDtmfMode(inband);
exten =&gt; _8.,2,Dial(SIP/${EXTEN:1}@voiceflex,,r);

;this context must be absent prior to  running autoasterflex
;[incoming-voiceflex]
;exten =&gt; s,1,Dial(SIP/234567@sip-bcm,,r);
;exten =&gt; 234567,1,Dial(SIP/234567@sip-bcm,,r);

[default]
exten =&gt; _9.,1,Dial(SIP/${EXTEN:1}@voiceflex,,r);
exten =&gt; 300,1,Dial(SIP/234567@sip-bcm,,r);
exten =&gt; 234567,1,Dial(SIP/234567@sip-bcm,,r);

#include /etc/asterisk/extensions_incoming_voiceflex.conf</pre>
<p>Finally, here is a little shell script that I was using, in order to configure an NSLU2 with Asterisk already installed, to configure the SIP trunks. USE WITH CAUTION &#8211; answering &#8220;y&#8221; to &#8220;Change IP &amp; DNS settings (y/n) ?&#8221; will re-write the network interface config (/etc/network/interfaces) and DNS client config (/etc/resolf.conf)  on the machine that you run it on (if it is Linux based.) I would advise anybody using the script to exercise care doing this, and only to do so if you have read and understand the script.</p>
<p>autoasterflex.sh</p>
<pre class="brush:bash">#!/bin/bash

#autoasterflex

echo "Caller ID of incoming SIP Trunk?"
read CALLERID
echo "SIP Account number?"
read SIPACCT
echo "SIP Account password?"
read SIPPASS
echo "PBX IP Address?"
read BCMIP
echo "WAN IP Address?"
read EXTERNIP
echo "Local network? (eg 192.168.0.1/24)"
read LOCALNET
echo "Digits to send to pbx as received digits?"
read DDI
echo "Change IP &amp; DNS settings (y/n) ?"
read CHANGEIP

if [ "$CHANGEIP" == "y" ]; then
	echo "Slug IP?"
	read SLUGIP
	echo "Slug Netmask?"
	read SLUGSN
	echo "Slug Default Gateway?"
	read SLUGDGW
	echo "DNS Server?"
	read SLUGDNS
fi

# modify existing sip_og_context.conf
sed -i "s/callerid=.*/callerid=${CALLERID}/" /etc/asterisk/sip_og_context.conf
sed -i "s/fromuser=.*/fromuser=${CALLERID}/" /etc/asterisk/sip_og_context.conf
sed -i "s/defaultuser=.*/defaultuser=${SIPACCT}@voiceflex/" /etc/asterisk/sip_og_context.conf
sed -i "s/secret=.*/secret=${SIPPASS}/" /etc/asterisk/sip_og_context.conf

# delete existing si_accounts.conf, create a new one
rm -f /etc/asterisk/sip_accounts.conf
echo ";sip_accounts.conf generated by autoasterflex" &gt;&gt; /etc/asterisk/sip_accounts.conf
echo "register =&gt; ${SIPACCT}:${SIPPASS}@sip.voiceflex.com" &gt;&gt; /etc/asterisk/sip_accounts.conf
echo "" &gt;&gt; /etc/asterisk/sip_accounts.conf
echo "registertimeout=20" &gt;&gt; /etc/asterisk/sip_accounts.conf
echo "registerattempts=0" &gt;&gt; /etc/asterisk/sip_accounts.conf
echo "" &gt;&gt; /etc/asterisk/sip_accounts.conf
echo "[authentication]" &gt;&gt; /etc/asterisk/sip_accounts.conf
echo "auth=${SIPACCT}:${SIPPASS}@146.101.248.200" &gt;&gt; /etc/asterisk/sip_accounts.conf
echo "auth=${SIPACCT}:${SIPPASS}@voiceflex" &gt;&gt; /etc/asterisk/sip_accounts.conf
echo "auth=${SIPACCT}:${SIPPASS}@sip.voiceflex.com" &gt;&gt; /etc/asterisk/sip_accounts.conf

#delete existing sip_nat.conf,  create a new one
rm -f /etc/asterisk/sip_nat.conf
echo "externip=${EXTERNIP}" &gt;&gt; /etc/asterisk/sip_nat.conf
echo "localnet=${LOCALNET}" &gt;&gt; /etc/asterisk/sip_nat.conf
echo "nat=yes" &gt;&gt; /etc/asterisk/sip_nat.conf

#check extensions.conf has #include /etc/asterisk/extensions_incoming_voiceflex.conf, append if absent
INCVFLEXCONFPRESENT=`grep '/etc/asterisk/extensions_incoming_voiceflex.conf' /etc/asterisk/extensions.conf`
if [ "" == "$INCVFLEXCONFPRESENT" ]; then
	echo ""  &gt;&gt; /etc/asterisk/extensions.conf
	echo "#include /etc/asterisk/extensions_incoming_voiceflex.conf" &gt;&gt; /etc/asterisk/extensions.conf
fi

#delete existing extensions_incoming_voiceflex.conf, recreate
rm -f /etc/asterisk/extensions_incoming_voiceflex.conf
echo "[incoming-voiceflex]" &gt;&gt; /etc/asterisk/extensions_incoming_voiceflex.conf
echo "exten =&gt; s,1,Dial(SIP/${DDI}@sip-bcm,,r);" &gt;&gt; /etc/asterisk/extensions_incoming_voiceflex.conf
echo "exten =&gt; ${DDI},1,Dial(SIP/${DDI}@sip-bcm,,r);" &gt;&gt; /etc/asterisk/extensions_incoming_voiceflex.conf

# modify sip.conf
sed -i "s/domain=.*,incoming-bcm/domain=${BCMIP},incoming-bcm/" /etc/asterisk/sip.conf
sed -i "s/host=.*/host=${BCMIP}/" /etc/asterisk/sip.conf

if [ "$CHANGEIP" == "y" ]; then
	#recreate /etc/network/interfaces
	rm -f /etc/network/interfaces
	echo "# /etc/network/interfaces" &gt;&gt; /etc/network/interfaces
	echo "# recreated by autoasterflex" &gt;&gt; /etc/network/interfaces
	echo "#" &gt;&gt; /etc/network/interfaces
	echo "# the loopback interface" &gt;&gt; /etc/network/interfaces
	echo "auto lo" &gt;&gt; /etc/network/interfaces
	echo "iface lo inet loopback" &gt;&gt; /etc/network/interfaces
	echo "#" &gt;&gt; /etc/network/interfaces
	echo "# the interface used by default during boot" &gt;&gt; /etc/network/interfaces
	echo "auto eth0" &gt;&gt; /etc/network/interfaces
	echo "#" &gt;&gt; /etc/network/interfaces
	echo "# static entry for eth0" &gt;&gt; /etc/network/interfaces
	echo "iface eth0 inet static" &gt;&gt; /etc/network/interfaces
	echo "    address ${SLUGIP}" &gt;&gt; /etc/network/interfaces
	echo "    netmask ${SLUGSN}" &gt;&gt; /etc/network/interfaces
	echo "    gateway ${SLUGDGW}" &gt;&gt; /etc/network/interfaces

	#recreate /etc/resolf.conf
	rm -f /etc/resolv.conf
	echo "search workgroup" &gt;&gt; /etc/resolf.conf
	echo "nameserver ${SLUGDNS}" &gt;&gt; /etc/resolv.conf
fi

#done
echo "Changes done. execute shutdown -r now to restart with new settings."</pre>
<p>I hope this helps.</p>
<img src="http://scott.donnel.ly/?ak_action=api_record_view&id=63&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://scott.donnel.ly/asterisk-to-voiceflex-an-example-on-the-nslu2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>CakePHP ExtendAssociations &#8211; HABTM Update by HABTM primary key</title>
		<link>http://scott.donnel.ly/cakephp-extendassociations-habtm-update-by-habtm-primary-key/</link>
		<comments>http://scott.donnel.ly/cakephp-extendassociations-habtm-update-by-habtm-primary-key/#comments</comments>
		<pubDate>Wed, 16 Dec 2009 20:35:02 +0000</pubDate>
		<dc:creator>Scotty</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://scott.donnel.ly/?p=24</guid>
		<description><![CDATA[For a few years now I have been using Brandon Parise&#8217;s great CakePHP behaviour, ExtendAssociations, which I originally found here at the Bakery. Lately I have been developing an app that has a HABTM relationship that has extra data stored in the relationship, and also can have multiple entries for the same association. To explain [...]]]></description>
			<content:encoded><![CDATA[<p>For a few years now I have been using Brandon Parise&#8217;s great CakePHP behaviour, ExtendAssociations, which I originally found <a href="http://bakery.cakephp.org/articles/view/add-delete-habtm-behaviour">here at the Bakery.</a> Lately I have been developing an app that has a HABTM relationship that has extra data stored in the relationship, and also can have multiple entries for the same association.<br />
To explain further, I have a <strong>Sale</strong> model, that can be associated to multiple <strong>Item</strong>s. each association stores the sale price and the quantity of the item. Also, say for example a Sale has 4 of Item X associated to it at £99, this cound be added to at a later date with 2 Item X at £130 for example. Since each HABTM association cannot be identified uniquely by the tuple of sale_id and item_id, the HABTM table must have a primary key field for itself.</p>
<p>The original ExtendAssociations update action did not provide an update action. I provided one a while ago <a href="http://bakery.cakephp.org/articles/view/add-delete-habtm-behaviour#1932">here</a>, but this is not sufficient for the scenario mentioned above, so here is another action which can update an HABTM association using the HABTM join table primary key as a  reference:</p>
<pre class="brush: php;" style="font-size:small">function habtmUpdatePrimary(&amp;$model, $assoc, $ids = array(), $extra = array() ) {
		if(!is_array($ids)) {
			$extra = array($extra);
			$ids = array($ids);
		}

		// make sure the association exists
		if(isset($model-&gt;hasAndBelongsToMany[$assoc])) {
			// get association data
			$joinTable = $model-&gt;hasAndBelongsToMany[$assoc]['joinTable'];
			$associationForeignKey = $model-&gt;hasAndBelongsToMany[$assoc]['associationForeignKey'];
			$foreignKey = $model-&gt;hasAndBelongsToMany[$assoc]['foreignKey'];

			$joinClass = array($model-&gt;name, $assoc);
			sort($joinClass);
			$joinClass = Inflector::pluralize($joinClass[0]) . $joinClass[1];
			$joinTablePrimaryKey = $model-&gt;$joinClass-&gt;primaryKey;

			if( !empty($joinTable) &amp;&amp; !empty($joinTablePrimaryKey) ) {

				$success = true;
				foreach($ids as  $index =&gt; $id)
				{
					// execute SQL query for each $id
					$sql = array();
					foreach( $extra[$index] as $key =&gt; $value ) {
					    if ($value != null) {
							$sql[] = $key . " = '". addslashes($value) . "'";
						} else {
                			$sql[] = $key . " = NULL";
              			}
          			}
					$result = $model-&gt;query( "UPDATE `$model-&gt;tablePrefix.$joinTable` set ".implode( "," , $sql )." WHERE $joinTablePrimaryKey = $id");
					if (!$result) $success = false;
				}
				return $success;
			} else {
				// invalid join table name or primary key field name
				return false;
			}
		} else {
			// association doesn't exist, return false
			return false;
		}
	}</pre>
<p>Here is the associated action to delete a HABTM by join table  primary key:</p>
<pre class="brush: php;" style="font-size:small">function habtmDeletePrimary(&amp;$model, $assoc, $ids) {
if(!is_array($ids))
$ids = array($ids);

// make sure the association exists
if(isset($model-&gt;hasAndBelongsToMany[$assoc])) {

//get extra data
$joinTable = $model-&gt;hasAndBelongsToMany[$assoc]['joinTable'];
$foreignKey = $model-&gt;hasAndBelongsToMany[$assoc]['foreignKey'];
$associationForeignKey = $model-&gt;hasAndBelongsToMany[$assoc]['associationForeignKey'];

$joinClass = array($model-&gt;name, $assoc);
sort($joinClass);
$joinClass = Inflector::pluralize($joinClass[0]) . $joinClass[1];
$joinTablePrimaryKey = $model-&gt;$joinClass-&gt;primaryKey;

$sql = "DELETE FROM `$model-&gt;tablePrefix.$joinTable` WHERE ";
$first = true;
foreach($ids as $id)
{
$sql .= ($first?"":" OR ") . "`$joinTablePrimaryKey` = '$id'";
$first = false;
}

// execute SQL
$success = $model-&gt;query($sql);
return $success;
}

// association doesn't exist, return false
return false;
}</pre>
<p>Here is the full code for the version of ExtendAssociations that I currently use &#8211; any comments / criticisms / improvements welcome. I would again like to thank Brandon Parise for graciously publishing the original Behaviour, nice work!</p>
<pre class="brush: php; collapse:true;">/**
* Extend Associations Behavior
* Extends some basic add/delete function to the HABTM relationship
* in CakePHP.  Also includes an unbindAll($exceptions=array()) for
* unbinding ALL associations on the fly.
*
* Now also adds / updates extra data (ie fields in the HABTM join table other than the keys),
* as well as handling HABTMs that have multiple entries for the same association
* and their own primary key.
*
* This code is loosely based on the concepts from:
* http://rossoft.wordpress.com/2006/08/23/working-with-habtm-associations/
*
* @author Brandon Parise &lt;brandon@parisemedia.com&gt;, updated and extended by Scott Donnelly &lt;scott@donnel.ly&gt;
* @package CakePHP Behaviors
*
*/
class ExtendAssociationsBehavior extends ModelBehavior {
/**
* Model-specific settings
* @var array
*/
var $settings = array();

/**
* Setup
* Noething sp
*
* @param unknown_type $model
* @param unknown_type $settings
*/
function setup(&amp;$model, $settings = array()) {
// no special setup required
$this-&gt;settings[$model-&gt;name] = $settings;
}

/**
* Add an HABTM association
*
* @param Model $model
* @param string $assoc
* @param int $id
* @param mixed $assoc_ids
* @return boolean
*/
function habtmAdd(&amp;$model, $assoc, $id, $assoc_ids , $extra = array() ) {
if(!is_array($assoc_ids)) {
$assoc_ids = array($assoc_ids);
$extra = array($extra);
}

// make sure the association exists
if(isset($model-&gt;hasAndBelongsToMany[$assoc])) {

// get association data
$joinTable = $model-&gt;hasAndBelongsToMany[$assoc]['joinTable'];
$associationForeignKey = $model-&gt;hasAndBelongsToMany[$assoc]['associationForeignKey'];
$foreignKey = $model-&gt;hasAndBelongsToMany[$assoc]['foreignKey'];

$success = true;
$n = 0;

if( !empty( $joinTable ) ) {
foreach($assoc_ids as $assoc_id)
{
// execute SQL query for each $assoc_id
$values = array("'" . $id . "'", "'" . $assoc_id . "'");
$cols = array("`" . $foreignKey . "`", "`" . $associationForeignKey . "`");
foreach( $extra[$n] as $key =&gt; $value )
{
array_push($cols, '`' . $key . '`');
array_push($values, "'" . addslashes($value) . "'");
}

$n++;
$result = $model-&gt;query( "INSERT INTO `$model-&gt;tablePrefix.$joinTable` (" . implode( ", ", $cols) . ") VALUES (" . implode( "," , $values ) . ")");
if (!$result) $success = false;
}
} else {
return false;
}
return $success;
}

// association doesn't exist, return false
return false;
}

/**
* Delete an HABTM Association
*
* @param Model $model
* @param string $assoc
* @param int $id
* @param mixed $assoc_ids
* @return boolean
*/
function habtmDelete(&amp;$model, $assoc, $id, $assoc_ids) {
if(!is_array($assoc_ids))
$assoc_ids = array($assoc_ids);

// make sure the association exists
if(isset($model-&gt;hasAndBelongsToMany[$assoc])) {

//get extra data
$joinTable = $model-&gt;hasAndBelongsToMany[$assoc]['joinTable'];
$foreignKey = $model-&gt;hasAndBelongsToMany[$assoc]['foreignKey'];
$associationForeignKey = $model-&gt;hasAndBelongsToMany[$assoc]['associationForeignKey'];

//build SQL query
$sql = "DELETE FROM `$model-&gt;tablePrefix.$joinTable` WHERE `$foreignKey` = '$id'";
if($assoc_ids[0] != '*') {
$sql .= " AND (";
$first = true;
foreach($assoc_ids as $assoc_id)
{
$sql .= ($first?"":" OR ") . "`$associationForeignKey` = '$assoc_id'";
$first = false;
}
$sql .= ")";
}

// execute SQL
$success = $model-&gt;query($sql);
return $success;
}

// association doesn't exist, return false
return false;
}

/**
* Delete an HABTM Association, specified by primary key id, rather than foreignkey / associationforeignkey
*
* @param Model $model
* @param string $assoc
* @param mixed $ids
* @return boolean
*/
function habtmDeletePrimary(&amp;$model, $assoc, $ids) {
if(!is_array($ids))
$ids = array($ids);

// make sure the association exists
if(isset($model-&gt;hasAndBelongsToMany[$assoc])) {

//get extra data
$joinTable = $model-&gt;hasAndBelongsToMany[$assoc]['joinTable'];
$foreignKey = $model-&gt;hasAndBelongsToMany[$assoc]['foreignKey'];
$associationForeignKey = $model-&gt;hasAndBelongsToMany[$assoc]['associationForeignKey'];

$joinClass = array($model-&gt;name, $assoc);
sort($joinClass);
$joinClass = Inflector::pluralize($joinClass[0]) . $joinClass[1];
$joinTablePrimaryKey = $model-&gt;$joinClass-&gt;primaryKey;

$sql = "DELETE FROM `$model-&gt;tablePrefix.$joinTable` WHERE ";
$first = true;
foreach($ids as $id)
{
$sql .= ($first?"":" OR ") . "`$joinTablePrimaryKey` = '$id'";
$first = false;
}

// execute SQL
$success = $model-&gt;query($sql);
return $success;
}

// association doesn't exist, return false
return false;
}

/**
* update HABTM Associations, including extra data, referenced by joined models primary keys
*
* @param Model $model
* @param string $assoc
* @param int $id
* @param int $assoc_ids
* @param mixed $extra
* @return boolean
*/
function habtmUpdate(&amp;$model, $assoc, $id, $assoc_ids, $extra = array() ) {
if(!is_array($assoc_ids)) {
$assoc_ids = array($assoc_ids);
$extra = array($extra);
}

// make sure the association exists
if(isset($model-&gt;hasAndBelongsToMany[$assoc])) {

// get association data
$joinTable = $model-&gt;hasAndBelongsToMany[$assoc]['joinTable'];
$associationForeignKey = $model-&gt;hasAndBelongsToMany[$assoc]['associationForeignKey'];
$foreignKey = $model-&gt;hasAndBelongsToMany[$assoc]['foreignKey'];

if( !empty( $joinTable ) ) {

$success = true;
foreach($assoc_ids as $index =&gt; $assoc_id)
{
// build query for each $assoc_id
$sql = array();
foreach( $extra[$index] as $key =&gt; $value ) {
if ($value != null) {
$sql[] = $key . " = '". addslashes($value) . "'";
} else {
$sql[] = $key . " = NULL";
}
}
// update each association with the new values
$result = $model-&gt;query( "UPDATE `$model-&gt;tablePrefix.$joinTable` set ".implode( "," , $sql )." WHERE $foreignKey = $id AND $associationForeignKey = '$assoc_id'");
if (!$result) $success = false;
}

return $success;
} else {
// join table not specified, return false
return false;
}
} else {
// association doesn't exist, return false
return false;
}
}

/**
* retrieve HABTM Associations, accessed by join table primary key
*
* @param Model $model
* @param string $assoc
* @param int $id
* @return boolean
*/
function habtmFetch(&amp;$model, $assoc, $ids) {
if(!is_array($ids))
$ids = array($ids);

// make sure the association exists
if(isset($model-&gt;hasAndBelongsToMany[$assoc])) {

// get association data
$joinTable = $model-&gt;hasAndBelongsToMany[$assoc]['joinTable'];
$associationForeignKey = $model-&gt;hasAndBelongsToMany[$assoc]['associationForeignKey'];
$foreignKey = $model-&gt;hasAndBelongsToMany[$assoc]['foreignKey'];

$joinClass = array($model-&gt;name, $assoc);
sort($joinClass);
$joinClass = Inflector::pluralize($joinClass[0]) . $joinClass[1];
$joinTablePrimaryKey = $model-&gt;$joinClass-&gt;primaryKey;

$n = 0;
if( !empty( $joinTable ) )
{
$whereClause = "(";
foreach($ids as $id)
{
// craft where clause
if ($n &gt; 0) $whereClause .= " OR ";
$whereClause .= "$joinTablePrimaryKey = $id";
$n++;
}
$whereClause .=")";

$res = $model-&gt;query( "SELECT * FROM `$model-&gt;tablePrefix.$joinTable` WHERE $whereClause");
}
return $res;
}
// association doesn't exist, return false
return false;
}

/**
* Update HABTM Associations, accessed by join table primary key
*
* @param Model $model
* @param string $assoc
* @param int $id
* @param mixed $extra
* @return boolean
*/
function habtmUpdatePrimary(&amp;$model, $assoc, $ids = array(), $extra = array() ) {
if(!is_array($ids)) {
$extra = array($extra);
$ids = array($ids);
}

// make sure the association exists
if(isset($model-&gt;hasAndBelongsToMany[$assoc])) {
// get association data
$joinTable = $model-&gt;hasAndBelongsToMany[$assoc]['joinTable'];
$associationForeignKey = $model-&gt;hasAndBelongsToMany[$assoc]['associationForeignKey'];
$foreignKey = $model-&gt;hasAndBelongsToMany[$assoc]['foreignKey'];

$joinClass = array($model-&gt;name, $assoc);
sort($joinClass);
$joinClass = Inflector::pluralize($joinClass[0]) . $joinClass[1];
$joinTablePrimaryKey = $model-&gt;$joinClass-&gt;primaryKey;

if( !empty($joinTable) &amp;&amp; !empty($joinTablePrimaryKey) ) {

$success = true;
foreach($ids as  $index =&gt; $id)
{
// execute SQL query for each $id
$sql = array();
foreach( $extra[$index] as $key =&gt; $value ) {
if ($value != null) {
$sql[] = $key . " = '". addslashes($value) . "'";
} else {
$sql[] = $key . " = NULL";
}
}
$result = $model-&gt;query( "UPDATE `$model-&gt;tablePrefix.$joinTable` set ".implode( "," , $sql )." WHERE $joinTablePrimaryKey = $id");
if (!$result) $success = false;
}
return $success;
} else {
// invalid join table name or primary key field name
return false;
}
} else {
// association doesn't exist, return false
return false;
}
}

/**
* Delete All HABTM Associations
* Just a nicer way to do easily delete all.
*
* @param Model $model
* @param string $assoc
* @param int $id
* @return boolean
*/
function habtmDeleteAll(&amp;$model, $assoc, $id) {
return $this-&gt;habtmDelete($model, $assoc, $id, '*');
}

/**
* Find
* This method allows cake to do the dirty work to
* fetch the current HABTM association.
*
* @param Model $model
* @param string $assoc
* @param int $id
* @return array
*/
function __habtmFind(&amp;$model, $assoc, $id) {
// temp holder for model-sensitive params
$tmp_recursive = $model-&gt;recursive;
$tmp_cacheQueries = $model-&gt;cacheQueries;

$model-&gt;recursive = 1;
$model-&gt;cacheQueries = false;

// unbind all models except the habtm association
$this-&gt;unbindAll($model, array('hasAndBelongsToMany' =&gt; array($assoc)));
$data = $model-&gt;find(array($model-&gt;name.'.'.$model-&gt;primaryKey =&gt; $id));

$model-&gt;recursive = $tmp_recursive;
$model-&gt;cacheQueries = $tmp_cacheQueries;

if(!empty($data)) {
// use Set::extract to extract the id's ONLY of the $assoc
$data[$assoc] = array($assoc =&gt; Set::extract($data, $assoc.'.{n}.'.$model-&gt;primaryKey));
}

return $data;
}

/**
* UnbindAll with Exceptions
* Allows you to quickly unbindAll of a model's
* associations with the exception of param 2.
*
* Usage:
*   $this-&gt;Model-&gt;unbindAll(); // unbinds ALL
*   $this-&gt;Model-&gt;unbindAll(array('hasMany' =&gt; array('Model2')) // unbind All except hasMany-Model2
*
* @param Model $model
* @param array $exceptions
*/
function unbindAll(&amp;$model, $exceptions = array()) {
$unbind = array();
foreach($model-&gt;__associations as $type) {
foreach($model-&gt;{$type} as $assoc=&gt;$assocData) {
// if the assoc is NOT in the exceptions list then
// add it to the list of models to be unbound.
if(@!in_array($assoc, $exceptions[$type])) {
$unbind[$type][] = $assoc;
}
}
}
// if we actually have models to unbind
if(count($unbind) &gt; 0) {
$model-&gt;unbindModel($unbind);
}
}
}</pre>
<div id="_mcePaste" style="overflow: hidden; position: absolute; left: -10000px; top: 603px; width: 1px; height: 1px;"><code><span style="color: #000000;"><span style="color: #0000bb;">$model</span><span style="color: #007700;">-&gt;</span><span style="color: #0000bb;">tablePrefix</span><span style="color: #007700;">.</span></span></code></div>
<img src="http://scott.donnel.ly/?ak_action=api_record_view&id=24&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://scott.donnel.ly/cakephp-extendassociations-habtm-update-by-habtm-primary-key/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

