<?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>Hotchkiss Consulting &#187; How-To</title>
	<atom:link href="http://hotchkissconsulting.net/category/how-to/feed/" rel="self" type="application/rss+xml" />
	<link>http://hotchkissconsulting.net</link>
	<description>Just another WordPress site</description>
	<lastBuildDate>Mon, 02 Jan 2012 18:15:09 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3</generator>
		<item>
		<title>Migrating WordPress sites between dev and live servers</title>
		<link>http://hotchkissconsulting.net/2011/09/migrating-wordpress-sites-dev-live-servers/</link>
		<comments>http://hotchkissconsulting.net/2011/09/migrating-wordpress-sites-dev-live-servers/#comments</comments>
		<pubDate>Mon, 19 Sep 2011 13:38:48 +0000</pubDate>
		<dc:creator>Sam Hotchkiss</dc:creator>
				<category><![CDATA[How-To]]></category>

		<guid isPermaLink="false">http://hotchkissconsulting.net/?p=289</guid>
		<description><![CDATA[In my work, at least once a week I&#8217;m either copying a live site onto my development server or moving a site back to the live server. I&#8217;ve figured out a few tricks along the way. Notice: This post assumes that you have a basic comfort level with the command line, it assumes you have [...]]]></description>
			<content:encoded><![CDATA[<p>In my work, at least once a week I&#8217;m either copying a live site onto my development server or moving a site back to the live server.  I&#8217;ve figured out a few tricks along the way.</p>
<p><strong>Notice:</strong> This post assumes that you have a basic comfort level with the command line, it assumes you have a linux web host, and it assumes you&#8217;re not going to sue me or hold me responsible if you break something.  If that&#8217;s not the case, please stop reading now!<br />
<span id="more-289"></span></p>
<h3>Choosing your dev server URL:</h3>
<p>I <strong>strongly</strong> recommend that the URLs for your dev and production environments are the same number of characters.  So, for my site hotchkissconsulting.net, I&#8217;ll choose a dev server URL like hconsultdev01.hdev1.com (same number of characters in the server name).  This is very important because WordPress stores a <strong>lot</strong> of information as serialized values.  Serialized values are, essentially, arrays of data being flattened into a string.  They use the length of the string as a checkvalue, so if you have a longer (or shorter) dev site URL, when you go to find/replace, you&#8217;ll end up breaking that serialized value.</p>
<p><em>A good option for some sites&#8211; if your primary URL is www.yoursite.com, just use dev.yoursite.com for the dev server.</em></p>
<h3>Prerequisites:</h3>
<ol>
<li>Back up a LOT.  Back up before you start, and back up continually as you go.  It may seem like overkill, but when you break something, and, every now and then, EVERYONE breaks things, you&#8217;ll be glad you have it.</li>
<li>Make sure both servers are up and running you have apache (or your server of choice) configured, etc</li>
<li>These instructions assume that your dev and live URLs are the same length (see above).  If they are not, following these instructions will probably break your site.  Be careful!</li>
<li>You need to have SSH access to follow these steps</li>
</ol>
<h3>Alright, let&#8217;s move your site:</h3>
<p>Note, throughout this list I will refer to your &#8220;source&#8221; and &#8220;target&#8221; servers.  The source is the server you&#8217;re moving from, the target is the server you&#8217;re moving to</p>
<h4>Moving Your Database</h4>
<ol>
<li>Connect to your source database through phpMyAdmin</li>
<li>Download a full export of your database in SQL format</li>
<li>Open your exported file in your favorite text editor, do a find/replace to replace the source domain name with the target domain name (find: www.yoursite.com, replace: dev.yoursite.com) and save it</li>
<li>Connect to your target database through phpMyAdmin</li>
<li>Drop any existing tables from the database (if applicable&#8211; if this is a new database, you won&#8217;t have any to drop</li>
<li>Import your modified export file from step 3</li>
</ol>
<h4>Moving Your Files</h4>
<ol>
<li>Connect to your source server using SSH, then cd to your web root (often times this will just be cd public_html, but not always&#8230;)</li>
<li>Zip all of your files together for easy moving.  The command for this is: <code>zip -9r websitebackup *</code> Be VERY careful with this, as you&#8217;re allowing someone to download all of your files directly from your web server.  You should always add a few random characters after &#8220;websitebackup&#8221; so that someone can&#8217;t easily guess how to download this backup.  So, for example:  <code>zip -9r websitebackup67sadga *</code> creates an archive at your web root with the name websitebackup67sadga.zip</li>
<li>Congratulations, you&#8217;re now finished with your source server!</li>
<li>Connect to your target server using SSH, then cd to your web root</li>
<li>Download the archive you created&#8211; on most servers, you can use the command <code>wget sourceserver.com/websitebackup67sadga.zip</code> (Replacing sourceserver.com with your source server&#8217;s domain name, and websitebackup67sadga.zip with the proper archive file name)</li>
<li>Unzip your archive file: <code>unzip websitebackup67sadga.zip</code></li>
<li>Edit wp-config.php with your text editor of choice to point it at the proper MySQL db/user/host/password</li>
<li>Find/replace the source URL out of all of the files you just moved.  You can do this in one fell swoop with <code>find ./ -type f -exec sed -i 's/www.yoursite.com/dev.yoursite.com/' {} \;</code> (replacing the URLs with your own, first source, then target)</li>
<li>Finally, delete the archive files off of both your source and target servers, and you&#8217;re in business!</li>
</ol>
<p>This may all seem overwhelming at first, but with practice and time, you&#8217;ll be able to clone a site in 10 minutes flat!  Need a hand?  Leave a comment or send an email!</p>
]]></content:encoded>
			<wfw:commentRss>http://hotchkissconsulting.net/2011/09/migrating-wordpress-sites-dev-live-servers/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Checking for (and blocking) free email accounts with jQuery Validation plugin</title>
		<link>http://hotchkissconsulting.net/2011/09/checking-for-free-email-accounts-with-jquery-validation-plugin/</link>
		<comments>http://hotchkissconsulting.net/2011/09/checking-for-free-email-accounts-with-jquery-validation-plugin/#comments</comments>
		<pubDate>Fri, 09 Sep 2011 19:01:16 +0000</pubDate>
		<dc:creator>Sam Hotchkiss</dc:creator>
				<category><![CDATA[How-To]]></category>

		<guid isPermaLink="false">http://hotchkissconsulting.net/?p=285</guid>
		<description><![CDATA[The jQuery Validation plugin is one of the most commonly used and useful jQuery plugins. One of the nicest things about it is how easily you can extend it. I recently had a client ask me to build a form which wouldn&#8217;t allow users to sign up with free email addresses&#8211; in particular, no Gmail, [...]]]></description>
			<content:encoded><![CDATA[<p>The <a href="http://bassistance.de/jquery-plugins/jquery-plugin-validation/" target="_blank">jQuery Validation plugin</a> is one of the most commonly used and useful jQuery plugins.  One of the nicest things about it is how easily you can extend it.</p>
<p>I recently had a client ask me to build a form which wouldn&#8217;t allow users to sign up with free email addresses&#8211; in particular, no Gmail, Yahoo!, or Hotmail accounts.  After playing with it for a little bit, I came up with the following jQuery Validation method to check for, and block, Gmail, Yahoo!, and Hotmail (you can easily add other free email providers):</p>
<pre>
$.validator.addMethod(&#x27;nofreeemail&#x27;, function (value) {
    return /^([\w-\.]+@(?!gmail.com)(?!yahoo.com)(?!hotmail.com)([\w-]+\.)+[\w-]{2,4})?$/.test(value);
}, &#x27;Free email addresses are not allowed.&#x27;);
</pre>
<p>Once you&#8217;ve added the method, you can easily call it:</p>
<pre>
$(&quot;#signup_form&quot;).validate({
		rules: {
			&#x27;user[first_name]&#x27;: &quot;required&quot;,
			&#x27;user[last_name]&#x27;: &quot;required&quot;,
			&#x27;user[title]&#x27;: &quot;required&quot;,
			&#x27;user[phone]&#x27;: &quot;required&quot;,
			&#x27;user[email]&#x27;: {
				required: true,
				email: true,
				nofreeemail: true
			}
		}
});
</pre>
<p>So&#8211; to wrap up, the final code is:</p>
<pre>
&lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot;&gt;
$(document).ready(function() {
	$.validator.addMethod(&#x27;nofreeemail&#x27;, function (value) {
	    return /^([\w-\.]+@(?!gmail.com)(?!yahoo.com)(?!hotmail.com)([\w-]+\.)+[\w-]{2,4})?$/.test(value);
	}, &#x27;Free email addresses are not allowed.&#x27;);

	$(&quot;#signup_form&quot;).validate({
		rules: {
			&#x27;user[first_name]&#x27;: &quot;required&quot;,
			&#x27;user[last_name]&#x27;: &quot;required&quot;,
			&#x27;user[title]&#x27;: &quot;required&quot;,
			&#x27;user[phone]&#x27;: &quot;required&quot;,
			&#x27;user[email]&#x27;: {
				required: true,
				email: true,
				nofreeemail: true
			}
		}
	});
});
&lt;/script&gt;
</pre>
]]></content:encoded>
			<wfw:commentRss>http://hotchkissconsulting.net/2011/09/checking-for-free-email-accounts-with-jquery-validation-plugin/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Accessing content for non-active languages with qTranslate</title>
		<link>http://hotchkissconsulting.net/2010/01/accessing-content-for-non-active-languages-with-qtranslate/</link>
		<comments>http://hotchkissconsulting.net/2010/01/accessing-content-for-non-active-languages-with-qtranslate/#comments</comments>
		<pubDate>Thu, 14 Jan 2010 21:17:15 +0000</pubDate>
		<dc:creator>Sam Hotchkiss</dc:creator>
				<category><![CDATA[How-To]]></category>
		<category><![CDATA[Technologies]]></category>

		<guid isPermaLink="false">http://hotchkissconsulting.net/?p=271</guid>
		<description><![CDATA[I&#8217;ve recently been working on a new site for a large Jewish philanthropic organization, developing a (very) custom WordPress theme for their site&#8211; when it came to coming up with a way to handle bilingual English/Hebrew content, we tried a few WordPress plugins before settling on Qian Qin&#8217;s qTranslate, which has been wonderful&#8211; great user [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve recently been working on a new site for a large Jewish philanthropic organization, developing a (very) custom WordPress theme for their site&#8211; when it came to coming up with a way to handle bilingual English/Hebrew content, we tried a few WordPress plugins before settling on Qian Qin&#8217;s <a href="http://www.qianqin.de/qtranslate/">qTranslate</a>, which has been wonderful&#8211; great user interface, easy for the client to grasp, easy to work with.  The one problem I encountered way that there is no easy way to reach across languages for content, which was necessary for one section of the site where English content needed to appear alongside Hebrew content.  Without any further ado, here&#8217;s the snippet of code I came up with to get at both languages&#8211; you are left with $p_title and $p_body which are both arrays, and you need to make sure to substitute your language in for Hebrew, and your language 2 letter code in for iw on line 3:</p>
<pre>$enddelim = '&lt;!--:--&gt;';
 $englishdelim = '&lt;!--:en--&gt;';
 $hebrewdelim = '&lt;!--:iw--&gt;';

 $getpost = get_posts('post_type=page&amp;include='.$post-&gt;ID);
 foreach($getpost as $p2) :
 setup_postdata($p2);
 $both_titles = 'blah'.$p2-&gt;post_title;
 $both_bodies = 'blah'.$p2-&gt;post_content;
 endforeach;

 if(strpos($both_titles, $englishdelim)) :
 $pt1 = explode($englishdelim, $both_titles);
 $pt2 = explode($enddelim, $pt1[1]);
 $p_title['english'] = $pt2[0];
 endif;

 if(strpos($both_titles, $hebrewdelim)) :
 $pt1 = explode($hebrewdelim, $both_titles);
 $pt2 = explode($enddelim, $pt1[1]);
 $p_title['hebrew'] = $pt2[0];
 endif;

 if(strpos($both_bodies, $englishdelim)) :
 $pt1 = explode($englishdelim, $both_bodies);
 $pt2 = explode($enddelim, $pt1[1]);
 $p_body['english'] = $pt2[0];
 endif;

 if(strpos($both_bodies, $hebrewdelim)) :
 $pt1 = explode($hebrewdelim, $both_bodies);
 $pt2 = explode($enddelim, $pt1[1]);
 $p_body['hebrew'] = $pt2[0];
 endif;</pre>
]]></content:encoded>
			<wfw:commentRss>http://hotchkissconsulting.net/2010/01/accessing-content-for-non-active-languages-with-qtranslate/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Calculating age with PHP using a birth date</title>
		<link>http://hotchkissconsulting.net/2009/11/calculating-age-with-php-using-a-birth-date/</link>
		<comments>http://hotchkissconsulting.net/2009/11/calculating-age-with-php-using-a-birth-date/#comments</comments>
		<pubDate>Sun, 15 Nov 2009 08:21:52 +0000</pubDate>
		<dc:creator>Sam Hotchkiss</dc:creator>
				<category><![CDATA[How-To]]></category>

		<guid isPermaLink="false">http://hotchkissconsulting.net/?p=258</guid>
		<description><![CDATA[UPDATED 12/2: Code updated for efficiency and code download link added. Ran into a problem today&#8211; there&#8217;s no easy way to add and subtract dates in PHP and be left with standard units (year, month, day, etc), so I whipped up a quick script to do it&#8211; the biggest problem is months, here, since PHP [...]]]></description>
			<content:encoded><![CDATA[<p>UPDATED 12/2: Code updated for efficiency and <a href="http://tr.im/Gqgq">code download link</a> added.</p>
<p>Ran into a problem today&#8211; there&#8217;s no easy way to add and subtract dates in PHP and be left with standard units (year, month, day, etc), so I whipped up a quick script to do it&#8211; the biggest problem is months, here, since PHP deals with second-based timestamps, and months don&#8217;t possess a standard number of seconds.  Without further ado, here&#8217;s the code&#8211; I hope it&#8217;s helpful to your projects:<br />
<span id="more-258"></span><br />
<code></p>
<p>//Set the $year, $month, and $day variables to the date you're calculating time since.</p>
<p>/////////////////////////////////////////////////////////////////////<br />
// Calculate age<br />
/////////////////////////////////////////////////////////////////////<br />
	//Set your time zone so PHP5 doesn't balk<br />
	date_default_timezone_set('America/Denver');<br />
	$birthday = strtotime($year.'-'.$month.'-'.$day);<br />
	$current_time = time();<br />
	$curr['month'] = date('n', $current_time);<br />
	$curr['lastmonth'] = $curr['month'] - 1;<br />
	$curr['year'] = date('Y', $current_time);<br />
	$curr['lastyear'] = $curr['year'] - 1;<br />
	$curr['day'] = date('j', $current_time);</p>
<p>	//get the time difference in seconds<br />
	$diff = $current_time - $birthday;<br />
	$age['years'] = intval($diff/31556926);<br />
	//get the remaining seconds<br />
	$diff = $diff - (31556926 * $age['years']);<br />
	//Now for the tricky part-- number of months<br />
	if($curr['month'] > $month) {<br />
		$age['months'] = $curr['month'] - $month;<br />
		if($curr['day'] < $day) {<br />
			$age['months']--;<br />
			$month_temp = strtotime($curr['year'].'-'.$curr['lastmonth'].'-'.$day);<br />
		} else {<br />
			$month_temp = strtotime($curr['year'].'-'.$curr['month'].'-'.$day);<br />
		}<br />
		//Get the remaining seconds<br />
		$diff = $current_time - $month_temp;<br />
	} elseif($curr['month'] == $month) {<br />
		if($curr['day'] >= $day) {<br />
			$age['months'] = 0;<br />
			//since months are 0, we don't need to alter diff<br />
		} else {<br />
			$age['months'] = 11;<br />
			//Get the remaining seconds<br />
			$month_temp = strtotime($curr['year'].'-'.$curr['lastmonth'].'-'.$day);<br />
			$diff = $current_time - $month_temp;<br />
		}<br />
	} else {<br />
		$age['months'] = $curr['month'] - $month + 12;<br />
		if($curr['day'] < $day) {<br />
			$age['months']--;<br />
			$month_temp = strtotime($curr['year'].'-'.$curr['lastmonth'].'-'.$day);<br />
		} else {<br />
			$month_temp = strtotime($curr['year'].'-'.$curr['month'].'-'.$day);<br />
		}<br />
		$diff = $current_time - $month_temp;<br />
	}</p>
<p>	//calculate days<br />
	$age['days'] = intval($diff/86400);<br />
	//get the remaining seconds<br />
	$diff = $diff - (86400 * $age['days']);</p>
<p>	//calculate hours<br />
	$age['hours'] = intval($diff/3600);<br />
	//get the remaining seconds<br />
	$diff = $diff - (3600 * $age['hours']);</p>
<p>	//calculate minutes<br />
	$age['minutes'] = intval($diff/60);<br />
	//get the remaining seconds<br />
	$diff = $diff - (60 * $age['minutes']);</p>
<p>	//and we're left with seconds<br />
	$age['seconds'] = $diff;</p>
<p>	print_r($age);<br />
</code><br />
Or you can <a href="http://tr.im/Gqgq">download the source file here</a>, if you're having copy/paste troubles.</p>
]]></content:encoded>
			<wfw:commentRss>http://hotchkissconsulting.net/2009/11/calculating-age-with-php-using-a-birth-date/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>An easy way to handle previous and next product links</title>
		<link>http://hotchkissconsulting.net/2009/11/an-easy-way-to-handle-previous-and-next-product-links/</link>
		<comments>http://hotchkissconsulting.net/2009/11/an-easy-way-to-handle-previous-and-next-product-links/#comments</comments>
		<pubDate>Sat, 14 Nov 2009 08:50:05 +0000</pubDate>
		<dc:creator>Sam Hotchkiss</dc:creator>
				<category><![CDATA[How-To]]></category>

		<guid isPermaLink="false">http://hotchkissconsulting.net/?p=254</guid>
		<description><![CDATA[I&#8217;m working on a custom eCommerce site for a customer right now, and ran into an issue&#8211; they wanted to have, on every product page, a forward and back button to get to the previous and next items. The problem with this is that they may have arrived on the product page in a couple [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m working on a custom eCommerce site for a customer right now, and ran into an issue&#8211; they wanted to have, on every product page, a forward and back button to get to the previous and next items.  The problem with this is that they may have arrived on the product page in a couple of different ways, through search or browsing, with different criteria set.</p>
<p>I came up with this easy routine which goes through the result set, whether it&#8217;s search result or category view, then creates an array where the key is the current product ID, and the values are the IDs of the product before and the product afterward.  So if I&#8217;m on product 157, $ordering[157]['next'] will give me the ID of the next product.  Just pop it in a session, and you&#8217;re good to go.</p>
<p>Simple and straightforward&#8211; feel free to reuse all you like!<br />
<span id="more-254"></span><br />
<code><br />
			foreach($results as $row) {<br />
					/////////////////////////////////////////////////////////////////////<br />
					// so that we can capture previous, current, and next, we're going<br />
					// to process one row behind, so for the first row, don't put anything into<br />
					// our array<br />
					/////////////////////////////////////////////////////////////////////<br />
					if(!$curr) {<br />
						$curr = 'first';<br />
						$next = $row['id'];<br />
					} else {<br />
					// Everything moves back one space<br />
						$prev = $curr;<br />
						$curr = $next;<br />
						$next = $row['id'];<br />
					// It gets saved into the array<br />
						$ordering[$curr]['prev'] = $prev;<br />
						$ordering[$curr]['next'] = $next;<br />
					}<br />
				}<br />
				// Here we're handling the last row<br />
				$ordering[$next]['prev'] = $curr;<br />
				$ordering[$next]['next'] = 'last';<br />
</code></p>
]]></content:encoded>
			<wfw:commentRss>http://hotchkissconsulting.net/2009/11/an-easy-way-to-handle-previous-and-next-product-links/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ditch the Bacn, get the email you want.</title>
		<link>http://hotchkissconsulting.net/2009/05/filter-out-bacn-gmail/</link>
		<comments>http://hotchkissconsulting.net/2009/05/filter-out-bacn-gmail/#comments</comments>
		<pubDate>Wed, 27 May 2009 20:59:24 +0000</pubDate>
		<dc:creator>Sam Hotchkiss</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[How-To]]></category>

		<guid isPermaLink="false">http://hotchkissconsulting.net/?p=205</guid>
		<description><![CDATA[The term &#8216;Bacn&#8216; has been around for a couple of years to refer to, according to wikipedia: electronic messages which have been subscribed to and are therefore not unsolicited but are often unread by the recipient for a long period of time, if at all. Bacn has been described as &#8220;email you want but not [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://hotchkissconsulting.net/wp-content/uploads/2009/05/bacon1.jpg"><img class="alignleft size-medium wp-image-206" title="Mmmm, Bacn" src="http://hotchkissconsulting.net/wp-content/uploads/2009/05/bacon-242x300.jpg" alt="Mmmm, Bacn" width="242" height="300" /></a>The term &#8216;<a href="http://en.wikipedia.org/wiki/Bacon_(electronic)">Bacn</a>&#8216; has been around for a couple of years to refer to, according to wikipedia:</p>
<blockquote><p>electronic messages which have been subscribed to and are therefore not unsolicited but are often unread by the recipient for a long period of time, if at all. Bacn has been described as &#8220;<a class="mw-redirect" title="Email" href="http://en.wikipedia.org/wiki/Email">email</a> you want but not right now.&#8221;<sup id="cite_ref-0" class="reference"><a href="http://en.wikipedia.org/wiki/Bacon_%28electronic%29#cite_note-0"><span>[</span>1<span>]</span></a></sup><sup id="cite_ref-1" class="reference"><a href="http://en.wikipedia.org/wiki/Bacon_%28electronic%29#cite_note-1"><span>[</span>2<span>]</span></a></sup></p>
<p>Bacn differs from <a title="E-mail spam" href="http://en.wikipedia.org/wiki/E-mail_spam">spam</a> in that the emails are not unsolicited: the recipient has somehow signed up to receive it. Bacn is also not necessarily sent in bulk. Bacn derives its name from the idea that it is &#8220;better than spam, but not as good as a personal email&#8221;</p></blockquote>
<p>Bacn is, generally, non-urgent, not terribly exciting, but may still be information that you want and need.</p>
<p><em><strong>So we&#8217;ve got Spam, Bacn, and then email that actually matters&#8211; personal email from one person to another (you!).  I&#8217;ll call that chops.  So, how do you keep current on your chops without getting distracted by the spam and bacn?</strong></em></p>
<p>At Hotchkiss Consulting we use Google Apps to manage our email,  and it does a great job of removing the Spam.  But I can&#8217;t stand when I&#8217;m in the middle of a project and I get distracted by the constant inflow of Bacn, so I decided to get rid of it!  Since Google Apps is built on the Gmail core, these instructions should work for any gmail user.  Feel free to modify to fit your needs!<span id="more-205"></span></p>
<p><strong>a) Add a couple categories to keep stuff straight</strong></p>
<p style="padding-left: 30px;">I created two new categories, &#8220;Bacn&#8221; and &#8220;Chops&#8221;.  You can call these whatever you want.</p>
<p><strong>b) Create filters to get that unruly Bacn in its place</strong></p>
<p style="padding-left: 30px;">I was able to handle everything in 4 filters (add filters by clicking on &#8216;Settings&#8217; then the &#8216;Filters&#8217; tab):</p>
<h2 style="padding-left: 30px;">1) Identify and label the chops:</h2>
<p style="padding-left: 60px;"><strong>From</strong>: -{wordpress, support, no-reply, noreply, do-not-reply, help, null, owner, updates, sales, messages, jobs, notification, info, team, research, invitations, hello, webmaster, service, member, root, digest, email@, daemon, talktous, news, newsletter, anonredir, jagent, admin, cpanel, helpdesk, specials, postmaster, bounce, autocreate, linkedin.com, billing, auto}</p>
<p style="padding-left: 60px;"><em>This tells it that if the email contains any of the above strings, all of which are common in Bacn, that it is not chops</em> <em>(the -{ at the beginning indicates that it needs to EXCLUDE email with any of these strings in the from field)</em></p>
<p style="padding-left: 60px;"><strong>To</strong>: My email address</p>
<p style="padding-left: 60px;"><em>So&#8211; put in your email address here.  This will keep many mailing lists excluded.  Depending on your email setup, this may or may not work for you.</em></p>
<p style="padding-left: 60px;"><strong>Doesn&#8217;t have</strong>: &#8220;If you didn’t want to receive this email&#8221;, &#8220;If you prefer not to receive email&#8221;, &#8220;Validation Email&#8221;, &#8220;Dear Customer&#8221;, &#8220;You have received this email because&#8221;, &#8220;This email was sent to &#8220;, &#8220;fill out this short survey&#8221;, &#8220;You are receiving this email because&#8221;, &#8220;Unsubscribe at&#8221;, &#8220;To stop receiving these emails&#8221;, &#8220;to be removed from further&#8221;, &#8220;To unsubscribe or change&#8221;, &#8220;If you choose not to receive&#8221;, &#8220;simply unsubscribe&#8221;, &#8220;subscription via the link&#8221;, &#8220;If you would like to be removed from future&#8221;</p>
<p style="padding-left: 60px;"><em>These strings are common in Bacn, and uncommon in Chops.  By looking for them, we&#8217;re further reducing the Bacn in your Chops.</em></p>
<p style="padding-left: 30px;">Do a test search and check out all your chops.  Looking good, huh?  If you think so, click on &#8220;Next Step&#8221;, and tell it to apply the label &#8220;Chops&#8221; and check the box next to &#8220;Also apply filter to &#8230; conversations below.&#8221;</p>
<h2 style="padding-left: 30px;">2) Send the bacn packing:</h2>
<p style="padding-left: 30px;">These 3 filters will identify bacn, and you can do what you want with it.  I have my gmail mark it as Bacn and archive it.  This way it bypasses my email notification tool, but is still available for later review.</p>
<h3 style="padding-left: 60px;">Filter 1</h3>
<p style="padding-left: 60px;"><strong>From</strong>: {wordpress OR support OR no-reply OR noreply OR do-not-reply OR help OR null OR owner OR updates OR sales OR messages OR jobs OR notification OR info OR team OR research OR invitations OR hello OR webmaster OR service OR member OR root OR digest OR email@ OR daemon OR talktous OR news OR newsletter OR anonredir OR jagent OR admin OR cpanel OR helpdesk OR specials OR postmaster OR bounce OR autocreate OR linkedin.com OR billing OR auto}</p>
<h3 style="padding-left: 60px;">Filter 2</h3>
<p style="padding-left: 60px;"><strong>Has the Words</strong>: &#8220;If you didn’t want to receive this email&#8221; OR &#8220;If you prefer not to receive email&#8221; OR &#8220;Validation Email&#8221; OR &#8220;Dear Customer&#8221; OR &#8220;You have received this email because&#8221; OR &#8220;This email was sent to &#8221; OR &#8220;fill out this short survey&#8221; OR &#8220;You are receiving this email because&#8221; OR &#8220;Unsubscribe at&#8221; OR &#8220;To stop receiving these emails&#8221; OR &#8220;to be removed from further&#8221; OR &#8220;To unsubscribe or change&#8221; OR &#8220;If you choose not to receive&#8221; OR &#8220;simply unsubscribe&#8221; OR &#8220;subscription via the link&#8221; OR &#8220;If you would like to be removed from future&#8221;</p>
<h3 style="padding-left: 60px;">Filter 3</h3>
<p style="padding-left: 60px;">The last filter I use is to put my email in both the from and to field.  This picks up any messages that aren&#8217;t either from or to me.</p>
<h2>Did this work for you?  Do you have any other suggestions?  Let me know!  Together, we&#8217;ll beat the spam, we&#8217;ll beat the bacn, and we&#8217;ll be rolling in chops!</h2>
]]></content:encoded>
			<wfw:commentRss>http://hotchkissconsulting.net/2009/05/filter-out-bacn-gmail/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Controlling the Third Gen iPod Shuffle</title>
		<link>http://hotchkissconsulting.net/2009/04/ipod-shuffle-control/</link>
		<comments>http://hotchkissconsulting.net/2009/04/ipod-shuffle-control/#comments</comments>
		<pubDate>Fri, 24 Apr 2009 18:09:11 +0000</pubDate>
		<dc:creator>Sam Hotchkiss</dc:creator>
				<category><![CDATA[How-To]]></category>

		<guid isPermaLink="false">http://hotchkissconsulting.net/?p=187</guid>
		<description><![CDATA[Becky got me a 3g iPod shuffle back when they first came out, and it&#8217;s been great.  It&#8217;s TINY, super light, battery lasts all day&#8230; etc etc etc.  It also has no buttons, so you use a little block in the headphone cable by your right cheek to control it.  Apple tells you you can [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://hotchkissconsulting.net/wp-content/uploads/2009/04/apple-ipod-shuffle-1713951.jpg"><img class="size-medium wp-image-188 alignright" title="apple-ipod-shuffle" src="http://hotchkissconsulting.net/wp-content/uploads/2009/04/apple-ipod-shuffle-1713951.jpg" alt="apple-ipod-shuffle" width="150" height="170" /></a>Becky got me a 3g iPod shuffle back when they first came out, and it&#8217;s been great.  It&#8217;s TINY, super light, battery lasts all day&#8230; etc etc etc.  It also has no buttons, so you use a little block in the headphone cable by your right cheek to control it.  Apple tells you you can use this to skip tracks forward, skip tracks backward, pause, play, change the volume, and change playlists.  But there are a couple of hidden features, too!</p>
<p>Skip forward within a song: Click the button once, then hold it.  This will fastforward within the song or podcast.</p>
<p>Skip backwards within a song: Click the button twice, then hold it (you have to do this quickly).   This will rewind within the song or podcast.</p>
<p>Skip through songs while hearing the name of every song.  Hold the button until it starts reading the name of the song to you, then double-click the button.  It will speak the name of the new songs as you get to them.</p>
<p>Skip through playlists: Once you hold the button long enough that it starts reading back your playlists, release the button and you can use the volume up and down buttons to quickly scroll through your playlists.</p>
<p>Anybody know any other neat tricks?  Let me know in the comments!</p>
<p>Update: I guess the fast forward/rewind tricks ARE in the online manual&#8230; but I could have sworn they&#8217;re not in the little booklet.  I&#8217;ll have to check tonight.</p>
]]></content:encoded>
			<wfw:commentRss>http://hotchkissconsulting.net/2009/04/ipod-shuffle-control/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Remember the Milk Email Scripts</title>
		<link>http://hotchkissconsulting.net/2009/04/remember-the-milk-email-scripts/</link>
		<comments>http://hotchkissconsulting.net/2009/04/remember-the-milk-email-scripts/#comments</comments>
		<pubDate>Mon, 13 Apr 2009 00:00:45 +0000</pubDate>
		<dc:creator>Sam Hotchkiss</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[How-To]]></category>
		<category><![CDATA[Technologies]]></category>

		<guid isPermaLink="false">http://hotchkissconsulting.net/?p=177</guid>
		<description><![CDATA[I know I&#8217;ve been promising these for a while, but here are the three PHP scripts that make up my PHP -&#62; Email integration.  I&#8217;m still working on converting this to a full multi-user system.  You can see I already started with the conversion on the schedule dispatch email.  Full scripts after the jump! Schedule [...]]]></description>
			<content:encoded><![CDATA[<p>I know I&#8217;ve been promising these for a while, but here are the three PHP scripts that make up my PHP -&gt; Email integration.  I&#8217;m still working on converting this to a full multi-user system.  You can see I already started with the conversion on the schedule dispatch email.  Full scripts after the jump!</p>
<p><span id="more-177"></span></p>
<h2>Schedule Sending</h2>
<p><em>This script sends out an email to you with your full upcoming schedule.</em></p>
<p>[php]&lt;?php<br />
mysql_connect(&#8216;{SQL SERVER}&#8217;,&#8217;[SQL USER}','{SQL PASSWORD}');<br />
@mysql_select_db('rtm_memory') or die( &quot;Unable to select database&quot;);<br />
$sql = &quot;select * from users where active = 1;&quot;;<br />
$users = mysql_query($sql);<br />
while($user = mysql_fetch_array($users)) {</p>
<p>//what's the current hour?<br />
$curHour = date(G);<br />
$userID = $user['id'];<br />
$sql = &quot;select * from hours where userID = &#8216;$userID&#8217; and hour = &#8216;$curHour&#8217;;&quot;;<br />
$hours = mysql_query($sql);<br />
$hourc = mysql_num_rows($hours);<br />
if ($hourc == 0){<br />
exit;<br />
}</p>
<p>//First, get the list of incomplete tasks, this list is sorted in reverse chronoligical order by due date. (The higher an item&#8217;s value in the array, the sooner it needs to be done)<br />
//I opted to get the list in JSON format for easy manipulation. It&#8217;s what I&#8217;m comfortable with. It&#8217;s also available in REST.</p>
<p>//This API call is static, so we can just go ahead and calculate the sign<br />
$apiCallVars = &#8216;{SHARED SECRET}api_key{API KEY}auth_token&#8217;.$user['token'].&#8217;filterstatus:incompleteformatjsonfrob&#8217;.$user['frob'].&#8217;methodrtm.tasks.getList&#8217;;</p>
<p>$sig = md5($apiCallVars);</p>
<p>//And build the URI<br />
$url = &#8216;http://www.rememberthemilk.com/services/rest/?api_key={API KEY}&amp;auth_token=&#8217;.$user['token'].&#8217;&amp;filter=status:incomplete&amp;format=json&amp;frob=&#8217;.$user['frob'].&#8217;&amp;method=rtm.tasks.getList&amp;api_sig=&#8217;.$sig;<br />
//echo $url;<br />
//And retrieve and decode the response<br />
$response = file_get_contents($url);<br />
$tasks = json_decode($response);</p>
<p>//Make sure the string is good, if it&#8217;s not, keep trying until it is (up to 100 tries)!<br />
$rt = 0;<br />
while ($tasks-&gt;rsp-&gt;stat != &#8216;ok&#8217; &amp;&amp; $rt &lt; 100) {<br />
$response = file_get_contents($url);<br />
$tasks = json_decode($response);<br />
$rt++;<br />
}<br />
//var_dump($tasks);<br />
if ($tasks-&gt;rsp-&gt;stat != &#8216;ok&#8217;) {<br />
//Start building our email with a failure notice<br />
$body = &quot;Communications error with Remember The Milk. Sorry!<br />
&quot;;<br />
} else {<br />
//Start building our email with a Past Due header for the first tasks. I almost always have something past due, so I keep this there always<br />
$body = $body . &quot;Past Due:<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
&quot;;<br />
}</p>
<p>//I opted to break up my list into Past Due, Upcoming, and Future events, so I need to get the current time<br />
$now = time();</p>
<p>//And set the future time 1 week from now (604,800 seconds)<br />
$future = time() + 604800;</p>
<p>//Have we reached current events?<br />
$nt = 0;</p>
<p>//Have we reached future events? (we set nt and ft so that it doesn&#8217;t show the headers more than once)<br />
$ft = 0;</p>
<p>//How many tasks are there? Find out and subtract 1 because our counting starts at 0<br />
//Note: I indicate the use of list 1 because that&#8217;s the list that all of my tasks are in (my inbox), YMMV. If you build this<br />
//and get errors, try switching list[$listCount] to list['0'] thoughout the script</p>
<p>$tc = count($tasks-&gt;rsp-&gt;tasks-&gt;list-&gt;taskseries);</p>
<p>$tc&#8211;;</p>
<p>//Process each task<br />
while ($tc &gt; -1) {<br />
//Pull out the name and due date&#8230; that&#8217;s all I use on RTM&#8230; if you use more, then you can include them. Not a big problem.</p>
<p>$name = $tasks-&gt;rsp-&gt;tasks-&gt;list-&gt;taskseries[$tc]-&gt;name;<br />
$date = $tasks-&gt;rsp-&gt;tasks-&gt;list-&gt;taskseries[$tc]-&gt;task-&gt;due;</p>
<p>//Convert the due date/time to my timezone, MST (my server is in PST), and then put it into a friendly format<br />
$ts = strtotime($date) + 3600;<br />
$time = date(&#8216;D, M j g:ia&#8217;, $ts);</p>
<p>//Check to see if this is the first &quot;Upcoming&quot; or &quot;Future&quot; task, if so, put on the appropriate header<br />
if ($now &lt; $ts &amp;&amp; $nt == 0) {<br />
$nt = 1;<br />
$body = $body.&quot;<br />
Upcoming:<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
&quot;;<br />
}<br />
if ($future &lt; $ts &amp;&amp; $ft == 0) {<br />
$ft = 1;<br />
$body = $body.&quot;<br />
Future:<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
&quot;;<br />
}</p>
<p>//Build the string we&#8217;re going to add to the body. This is just the time string &#8211; the task name and a line break, for example<br />
//Thu, Mar 5 3:30pm &#8211; Meet the Qwest Installer<br />
$bodyl = $time.&quot; &#8211; &quot;.$name.&quot;<br />
&quot;;</p>
<p>//Pin this onto the body<br />
$body = $body.$bodyl;</p>
<p>//Reduce the count<br />
$tc&#8211;;<br />
}</p>
<p>//I now use the unix &quot;cal&quot; command to generate the next 4 months in plaintext calendar format to append to the end of the message<br />
ob_start();<br />
$cM = date(&#8216;m&#8217;);<br />
$com = &quot;cal &quot;.$cM.&quot; 2009&quot;;<br />
passthru($com, $cal1);<br />
$cM++;<br />
$com = &quot;cal &quot;.$cM.&quot; 2009&quot;;<br />
passthru($com, $cal2);<br />
$cM++;<br />
$com = &quot;cal &quot;.$cM.&quot; 2009&quot;;<br />
passthru($com, $cal3);<br />
$cM++;<br />
$com = &quot;cal &quot;.$cM.&quot; 2009&quot;;<br />
passthru($com, $cal4);<br />
$calData = ob_get_contents();<br />
ob_end_clean();</p>
<p>//Pin that calendar data on<br />
$body = $body.&quot;</p>
<p>&quot;.$calData;</p>
<p>//Lets also put a note at the bottom about the number of communications tries it took:<br />
$body = $body.&quot;</p>
<p>RTM reached after &quot;.$rt.&quot; retries.</p>
<p>This schedule sent to you courtesy of Hotchkiss Consulting (http://hotchkissconsulting.net/)&quot;;<br />
//Output the email body<br />
echo $body;</p>
<p>//Build and send the email<br />
$to = $user['email'];<br />
$subject = &#8216;My Schedule&#8217;;<br />
$message = $body;<br />
$headers = &#8216;From: rtm@hotchkissconsulting.net&#8217; . &quot;rn&quot; . &#8216;Reply-To: rtm@hotchkissconsulting.net&#8217; . &quot;rn&quot; . &#8216;X-Mailer: PHP/&#8217; . phpversion();<br />
mail($to, $subject, $message, $headers);<br />
} ?&gt;[/php]</p>
<h2>Notification Sending</h2>
<p><em>This script sends out a notification email to you with appointments coming up within the next 30 minutes.</em></p>
<p>[php]&lt;?php</p>
<p>//Lets get ourselves a timeline<br />
$key = md5(&#8216;{SHARED SECRET}api_key{API KEY}auth_token{AUTH TOKEN}formatjsonfrob{FROB}methodrtm.timelines.create&#8217;);<br />
$url = &#8216;http://www.rememberthemilk.com/services/rest/?api_key={API KEY}&amp;auth_token={AUTH TOKEN}&amp;format=json&amp;frob={FROB}&amp;method=rtm.timelines.create&amp;api_sig=&#8217;.$key;<br />
$response1 = file_get_contents($url);<br />
$tl = json_decode($response1);<br />
$timeline = $tl-&gt;rsp-&gt;timeline;</p>
<p>//And download the tasks list<br />
//This API call is static, so we can just go ahead and calculate the sign<br />
$sig = md5(&#8216;{SHARED SECRET}api_key{API KEY}auth_token{AUTH TOKEN}filterstatus:incompleteformatjsonfrob{FROB}methodrtm.tasks.getList&#8217;);</p>
<p>//And build the URI<br />
$url = &#8216;http://www.rememberthemilk.com/services/rest/?api_key={API KEY}&amp;auth_token={AUTH TOKEN}&amp;filter=status:incomplete&amp;format=json&amp;frob={FROB}&amp;method=rtm.tasks.getList&amp;api_sig=&#8217;.$sig;</p>
<p>//And retrieve and decode the response<br />
$response = file_get_contents($url);<br />
$tasks = json_decode($response);</p>
<p>//Make sure the string is good, if it&#8217;s not, keep trying until it is (up to 30 tries)!<br />
$rt = 0;<br />
while ($tasks-&gt;rsp-&gt;stat != &#8216;ok&#8217; &amp;&amp; $rt &lt; 30) {<br />
$response = file_get_contents($url);<br />
$tasks = json_decode($response);<br />
$rt++;<br />
}</p>
<p>//Get the current time so we know whether or not to send an alert<br />
$now = time();</p>
<p>//How many tasks are there?  Find out and subtract 1 because our counting starts at 0<br />
//Note: I indicate the use of list 1 because that&#8217;s the list that all of my tasks are in (my inbox), YMMV.  If you build this<br />
//and get errors, try switching list to list['0'] thoughout the script<br />
$tc = count($tasks-&gt;rsp-&gt;tasks-&gt;list-&gt;taskseries);<br />
$tc&#8211;;</p>
<p>//Set the email headers now so it doesn&#8217;t do it repeatedly through the while statement<br />
$headers = &#8216;From: {FROM ADDRESS}&#8217; . &quot;rn&quot; .<br />
 &#8216;Reply-To: {FROM ADDRESS}&#8217; . &quot;rn&quot; .<br />
 &#8216;X-Mailer: PHP/&#8217; . phpversion();<br />
$to = &#8216;sam@samandbecky.net&#8217;;</p>
<p>//Run this for every task<br />
while ($tc &gt; -1) {<br />
//Get the list ID<br />
$lid = $tasks-&gt;rsp-&gt;tasks-&gt;list-&gt;id;</p>
<p>//Get the task title<br />
$name = $tasks-&gt;rsp-&gt;tasks-&gt;list-&gt;taskseries[$tc]-&gt;name;</p>
<p>//get the task notes<br />
$notes = $tasks-&gt;rsp-&gt;tasks-&gt;list-&gt;taskseries[$tc]-&gt;notes;</p>
<p>//Get the due date<br />
$date = $tasks-&gt;rsp-&gt;tasks-&gt;list-&gt;taskseries[$tc]-&gt;task-&gt;due;</p>
<p>//Get the estimated time field which I use to see if I&#8217;ve sent a notification for this task yet<br />
$notified = $tasks-&gt;rsp-&gt;tasks-&gt;list-&gt;taskseries[$tc]-&gt;task-&gt;estimate;</p>
<p>//Get the task series ID<br />
$tsid = $tasks-&gt;rsp-&gt;tasks-&gt;list-&gt;taskseries[$tc]-&gt;id;</p>
<p>//Get the task ID<br />
$tid = $tasks-&gt;rsp-&gt;tasks-&gt;list-&gt;taskseries[$tc]-&gt;task-&gt;id;</p>
<p>//Convert both the task time and the current time into my time zone, MST from PST and make the due date pretty<br />
$ts = strtotime($date) + 3600;<br />
$now = time() + 3600;<br />
$time = date(&#8216;D, M j g:ia&#8217;, $ts);</p>
<p>//Calculate the difference, in seconds, between now and when the task is due<br />
$tt = $ts &#8211; $now;</p>
<p>//Build a notify string that indicates the day so that we are reminded more than once<br />
//for recurring tasks<br />
$notificationVal = date(&#8216;zy&#8217;) . &#8216;min&#8217;;</p>
<p>//Notify if the task is due in fewer than 30 minutes (1,800 seconds) and a notification hasn&#8217;t already been sent.<br />
//We also check to make sure that the task wasn&#8217;t due more than 2 hours ago.  This prevents an issue with repeating<br />
//tasks where they will notify over and over.<br />
if ($tt &lt; 1800 &amp;&amp; $tt &gt; -7200 &amp;&amp; $notified != $notificationVal) {</p>
<p>//The email subject will be the task name, then a dash, then the Task ID, Task Series ID, and List ID delimited by commas<br />
$subject = $name.&quot; &#8211; &quot;.$tid.&#8217;.&#8217;.$tsid.&#8217;.&#8217;.$lid;</p>
<p>//The email body contains the due time and the notes<br />
$message = &quot;Due: &quot;.$time.&quot;<br />
Notes: &quot;.$notes['0'];</p>
<p>//send the email<br />
mail($to, $subject, $message, $headers);</p>
<p>//Now we&#8217;re going to make it as notification sent by setting the estimated time to complete to our notification val</p>
<p>//first build the sig<br />
$toHash = &#8216;{SHARED SECRET}api_key{API KEY}auth_token{AUTH TOKEN}estimate&#8217;.$notificationVal.&#8217;formatjsonfrob{FROB}list_id{LIST ID}methodrtm.tasks.setEstimatetask_id&#8217;.$tid.&#8217;taskseries_id&#8217;.$tsid.&#8217;timeline&#8217;.$timeline;<br />
$sig = md5($toHash);</p>
<p>//then build our query URL<br />
$url =&#8217;http://www.rememberthemilk.com/services/rest/?api_key={API KEY}&amp;auth_token={AUTH TOKEN}&amp;estimate=&#8217;.$notificationVal.&#8217;&amp;format=json&amp;frob={FROB}&amp;list_id={LIST ID}&amp;method=rtm.tasks.setEstimate&amp;task_id=&#8217;.$tid.&#8217;&amp;taskseries_id=&#8217;.$tsid.&#8217;&amp;timeline=&#8217;.$timeline.&#8217;&amp;api_sig=&#8217;.$sig;</p>
<p>//then submit the query.  Voila!<br />
//And retrieve and decode the response<br />
$response3 = file_get_contents($url);<br />
$result = json_decode($response3);</p>
<p>//Make sure the string is good, if it&#8217;s not, keep trying until it is (up to 30 tries)!<br />
$rt = 0;<br />
while ($result-&gt;rsp-&gt;stat != &#8216;ok&#8217; &amp;&amp; $rt &lt; 100) {<br />
$response3 = file_get_contents($url);<br />
$result = json_decode($response3);<br />
$rt++;<br />
}<br />
}</p>
<p>//Move on to the next task<br />
$tc&#8211;;<br />
}</p>
<p>?&gt;</p>
<p>[/php]</p>
<h2>Notification Reply Processing</h2>
<p><em>This script processing replies that you send to the notification message, and submits any needed changes to RTM</em></p>
<p><em>[php]&lt;/em&gt;</p>
<p>&lt;em&gt;&lt;?php</p>
<p>//Lets get ourselves a timeline<br />
$key = md5(&#8216;{SHARED SECRET}api_key{API KEY}auth_token{AUTH TOKEN}formatjsonfrob{FROB}methodrtm.timelines.create&#8217;);<br />
$url = &#8216;http://www.rememberthemilk.com/services/rest/?api_key={API KEY}&amp;auth_token={AUTH TOKEN}&amp;format=json&amp;frob={FROB}&amp;method=rtm.timelines.create&amp;api_sig=&#8217;.$key;<br />
$response1 = file_get_contents($url);<br />
$tl = json_decode($response1);<br />
$timeline = $tl-&gt;rsp-&gt;timeline;</p>
<p>//Now we&#8217;re going to check our secret RTM email account that you&#8217;ve set up with gmail<br />
$connection = imap_open (&quot;{imap.gmail.com:993/imap/ssl}INBOX&quot;, &quot;{FROM ADDRESS}&quot;, &quot;{EMAIL PASSWORD}&quot;) or die(&quot;can&#8217;t connect: &quot; . imap_last_error());</p>
<p>//How many messages are there, if 0, it will just skip the while statement, close the connection, and be done.<br />
$num_mgs = imap_num_msg($connection);<br />
$c = 1;</p>
<p>while ($c &lt;= $num_mgs) {</p>
<p>//Get the email subject line<br />
$headers = imap_headerinfo($connection, $c);<br />
$subject = $headers-&gt;subject;</p>
<p>//Get the first character of the email body, this is your &quot;procCode&quot; which will tell the script what you want to do<br />
$body = imap_body($connection, $c);<br />
$procCode = strtolower(substr($body,0,1));</p>
<p>//Out of the subject line, extract the List ID, Task Series ID, and Task ID<br />
$lid = substr($subject, -7);<br />
$tsid = substr($subject, -16, -8);<br />
$tid = substr($subject, -25, -17);</p>
<p>//if the procCode is C, we&#8217;ll mark the task completed<br />
if ($procCode == &#8216;c&#8217;) {</p>
<p>// Tell RTM it&#8217;s finished</p>
<p>//Build our hash<br />
$toHash = &#8216;{SHARED SECRET}api_key{API KEY}auth_token{AUTH TOKEN}estimate1minformatjsonfrob{FROB}list_id&#8217;.$lid.&#8217;methodrtm.tasks.completetask_id&#8217;.$tid.&#8217;taskseries_id&#8217;.$tsid.&#8217;timeline&#8217;.$timeline;<br />
$sig = md5($toHash);</p>
<p>//Build our URL<br />
$url =&#8217;http://www.rememberthemilk.com/services/rest/?api_key={API KEY}&amp;auth_token={AUTH TOKEN}&amp;estimate=1min&amp;format=json&amp;frob={FROB}&amp;list_id={LIST ID}&amp;method=rtm.tasks.complete&amp;task_id=&#8217;.$tid.&#8217;&amp;taskseries_id=&#8217;.$tsid.&#8217;&amp;timeline=&#8217;.$timeline.&#8217;&amp;api_sig=&#8217;.$sig;</p>
<p>//Submit it and check the response<br />
$response3 = file_get_contents($url);<br />
$alterResp = json_decode($response3);</p>
<p>//If it worked, delete the email, if not, we&#8217;ll try again next time the script runs<br />
if ($alterResp-&gt;rsp-&gt;stat == &#8216;ok&#8217;) {<br />
//Move the message to the trash if it&#8217;s successful<br />
$move = &quot;[Gmail]/Trash&quot;;<br />
@imap_mail_move($connection, $c, $move);<br />
}<br />
}</p>
<p>//if the procCode is P, we&#8217;ll postpone the task<br />
if ($procCode == &#8216;p&#8217;) {</p>
<p>// Postpone it</p>
<p>//Build our hash<br />
$toHash = &#8216;{SHARED SECRET}api_key{API KEY}auth_token{AUTH TOKEN}estimate1minformatjsonfrob{FROB}list_id&#8217;.$lid.&#8217;methodrtm.tasks.postponetask_id&#8217;.$tid.&#8217;taskseries_id&#8217;.$tsid.&#8217;timeline&#8217;.$timeline;<br />
$sig = md5($toHash);</p>
<p>//Build our URL<br />
$url =&#8217;http://www.rememberthemilk.com/services/rest/?api_key={API KEY}&amp;auth_token={AUTH TOKEN}&amp;estimate=1min&amp;format=json&amp;frob={FROB}&amp;list_id={LIST ID}&amp;method=rtm.tasks.postpone&amp;task_id=&#8217;.$tid.&#8217;&amp;taskseries_id=&#8217;.$tsid.&#8217;&amp;timeline=&#8217;.$timeline.&#8217;&amp;api_sig=&#8217;.$sig;</p>
<p>//Submit it and check the response<br />
$response3 = file_get_contents($url);<br />
$alterResp = json_decode($response3);</p>
<p>//If it worked, delete the email, if not, we&#8217;ll try again next time the script runs<br />
if ($alterResp-&gt;rsp-&gt;stat == &#8216;ok&#8217;) {<br />
//Move the message to the trash if it&#8217;s successful<br />
$move = &quot;[Gmail]/Trash&quot;;<br />
@imap_mail_move($connection, $c, $move);<br />
}<br />
}</p>
<p>//if the procCode is S, we&#8217;ll resend the schedule<br />
if ($procCode == &#8216;s&#8217;) {</p>
<p>// Resend Schedule</p>
<p>include(&#8216;sendRTMsched.php&#8217;);<br />
$move = &quot;[Gmail]/Trash&quot;;<br />
@imap_mail_move($connection, $c, $move);</p>
<p>}</p>
<p>$c++;<br />
}<br />
imap_close($connection);<br />
?&gt;</p>
<p>[/php]</p>
<p></em></p>
<p><em>Post questions in the comments and I will be happy to help you out!</em></p>
<pre></pre>
]]></content:encoded>
			<wfw:commentRss>http://hotchkissconsulting.net/2009/04/remember-the-milk-email-scripts/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Building an eMail interface for Remember the Milk using PHP</title>
		<link>http://hotchkissconsulting.net/2009/03/email-interface-remember-the-milk/</link>
		<comments>http://hotchkissconsulting.net/2009/03/email-interface-remember-the-milk/#comments</comments>
		<pubDate>Fri, 06 Mar 2009 20:32:10 +0000</pubDate>
		<dc:creator>Sam Hotchkiss</dc:creator>
				<category><![CDATA[Consulting]]></category>
		<category><![CDATA[General]]></category>
		<category><![CDATA[How-To]]></category>
		<category><![CDATA[Technologies]]></category>
		<category><![CDATA[email]]></category>
		<category><![CDATA[peek]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[remember the milk]]></category>
		<category><![CDATA[rtm]]></category>

		<guid isPermaLink="false">http://www.hotchkissconsulting.net/?p=89</guid>
		<description><![CDATA[UPDATE: Scripts are available here: http://hotchkissconsulting.net/177/remember-the-milk-email-scripts/ So, in accordance with my plan to switch client communication from phone to email, I have decided that, instead of a Cell Phone, I will carry a Peek with me.  While it has been a rocky road so far, I trust that the people at Peek have some good [...]]]></description>
			<content:encoded><![CDATA[<p><strong>UPDATE: Scripts are available here: <a href="http://hotchkissconsulting.net/177/remember-the-milk-email-scripts/">http://hotchkissconsulting.net/177/remember-the-milk-email-scripts/</a></strong></p>
<p><img class="alignleft size-full wp-image-112" title="rtm" src="http://hotchkissconsulting.net/wp-content/uploads/2009/04/rtm1.jpg" alt="rtm" width="263" height="224" />So, in accordance with my plan to switch client communication from phone to email, I have decided that, instead of a Cell Phone, I will carry a <a href="http://getpeek.com/" target="_blank">Peek </a>with me.  While it has been a rocky road so far, I trust that the people at Peek have some good updates coming down the pipeline, so I&#8217;m going to stick it out for a couple months and see how I like it.</p>
<p>The thing about the Peek is that it&#8217;s eMail only.  And I really do mean only.  No calculator, no games, no web browser, and no calendar.  For me, that just won&#8217;t do&#8211; I am an avid fan of <a href="http://rememberthemilk.com/">Remember the Milk</a>.  I keep it in my OS X dashboard and on my desktop email screen (I use GFYD, Google For Your Domain).  Having my RTM to go is a must.</p>
<p>Currently, RTM offers a couple email-based options.  You can email in new tasks, you can receive a list of your tasks for the day every morning via email, and you can receive an email reminder before a task is due.  This is a good start, but there are some problems:</p>
<p>1) With the daily schedule emails, you can only see what&#8217;s going on that day, you can&#8217;t see your entire schedule.  This information is crucial for setting appointments.</p>
<p>2) Reminder emails don&#8217;t have the task name in the subject line, meaning you have to open the email to see what you&#8217;re supposed to do.</p>
<p>3) You can&#8217;t do anything with the tasks&#8211; no marking them completed or postponing, you have to do this from the computer.</p>
<p>So, I wrote my own email interface, and will go over it and include my source files so you, too, can build your own (if you&#8217;ve got some <a href="http://php.net/">PHP </a>know-how).  If there&#8217;s enough interest, I will build a standalone, hosted solution for everyone of every skill level.<span id="more-89"></span></p>
<p>Now, you&#8217;ll note that I didn&#8217;t mention any problems with the task submission, that&#8217;s because it works pretty well already&#8230; the only issue I ran into there is that the Peek gives an alert every time you try to submit a new task because your RTM task submission email address contains a &#8220;+&#8221; sign.  So I simply added an email address to one of my domains that forwards to my RTM address.  Problem solved.</p>
<p>Issues with my RTM email interface:</p>
<p>1) I use the time estimate field to store my flag as to whether or not a notification has been sent.  This isn&#8217;t a field that I, personally, use.</p>
<p>2) I have built this to use only one list, my inbox.  If you want to use more lists, go for it, this script should give you a good starting point.</p>
<p>3) Even with my source code, this is a pretty technical process to get this up and running on your server.  I&#8217;m providing this to help those of you who have a basic understanding of PHP and REST/JSON data structure save a little bit of time.  If there is sufficient interest, I will consider building a service to do this for you&#8211; if you are interested, please post a comment letting me know.</p>
<p>4) These instructions assume that you already have an <a href="http://www.rememberthemilk.com/services/api/keys.rtm">API Key and Shared Secret</a> and a frob and token with read/write access to your user account.  If there are a lot of questions about this, I can post a separate how-to.</p>
<p>You are welcome to use this information for personal projects and distribute it with proper accreditation.</p>
<p>This interface uses 3 scripts, I will go through each in a separate blog post:</p>
<p><strong>1) Schedule </strong></p>
<p>This script sends me my full schedule at 7am, noon, and 5pm (It is triggered by crontab).   Here&#8217;s what one of those emails might look like:</p>
<blockquote>
<pre>Past Due:
-------------------------
Wed, Mar 4 2:00pm - call leanne smith re: PSD files
Thu, Mar 5 6:00pm - Send Back Candles

Upcoming:
-------------------------
Fri, Mar 6 10:00am - Change netflix plan
Fri, Mar 6 10:00am - Pay Care Credit Bill
Fri, Mar 6 10:00am - Pay Sallie Mae
Fri, Mar 6 12:00pm - call kennebec re contract
Sat, Mar 7 10:00am - Haircut!
Sat, Mar 7 11:00am - Go see an unlikely weapon at noon
Wed, Mar 11 8:30am - Dog Grooming @ Happy Paws
Wed, Mar 11 4:00pm - Meet with B. Peterson @ 430
Thu, Mar 12 5:15pm - Take out the Trash

Future:
-------------------------
Wed, Mar 18 3:45pm - Meeting w/ J. Doe at 4:30
Fri, Mar 20 8:15am - Dentist Appt
Sat, Jun 6 1:00am - Call Barbara to set up meeting on the 10th

     March 2009
Su Mo Tu We Th Fr Sa
 1  2  3  4  5  6  7
 8  9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31

     April 2009
Su Mo Tu We Th Fr Sa
          1  2  3  4
 5  6  7  8  9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30

      May 2009
Su Mo Tu We Th Fr Sa
                1  2
 3  4  5  6  7  8  9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31

     June 2009
Su Mo Tu We Th Fr Sa
    1  2  3  4  5  6
 7  8  9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30</pre>
</blockquote>
<p>You can see, also, that at the emails I&#8217;ve tacked on the next 4 months worth of calendars so I can see what&#8217;s coming up!</p>
<p><strong>2) </strong><strong>Reminders</strong></p>
<p>To replace the reminder emails, I have a script that runs every 5 minutes to check to see if I have any tasks coming up within the next 30 minutes.  It then generates an email in which the subject is the name of my task followed by the Task ID, Task Series ID, and List ID (all of which are period delimitted).  This allows my next script to work its magic as well as putting my task info right there in the subject line so I can see what&#8217;s going on.</p>
<p><strong>3) Modifications</strong></p>
<p>This script checks a secret gmail account every 5 minutes, reads my messages and processes them based on the Process Code I send in.  The process code is the first letter in an email&#8211; if it&#8217;s &#8220;C&#8221; then this script tells RTM that the task is Completed.  If it&#8217;s &#8220;P&#8221;, the script tells RTM that the task is Postponed.  It figures out what the task is by parsing the numbers at the end of the subject line.</p>
<p>So&#8230; that&#8217;s what we&#8217;ve got!  Scripts are on their way and should be up today, please let me know what you think, and feel free to leave any questions in the comments field below.</p>
]]></content:encoded>
			<wfw:commentRss>http://hotchkissconsulting.net/2009/03/email-interface-remember-the-milk/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>

