<?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>mitcho.com &#187; how to</title>
	<atom:link href="http://mitcho.com/blog/category/how-to/feed/" rel="self" type="application/rss+xml" />
	<link>http://mitcho.com</link>
	<description></description>
	<lastBuildDate>Fri, 10 Feb 2012 23:24:05 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.4-alpha-19719</generator>
		<item>
		<title>Checking mochitest test coverage</title>
		<link>http://mitcho.com/blog/projects/checking-mochitest-test-coverage/</link>
		<comments>http://mitcho.com/blog/projects/checking-mochitest-test-coverage/#comments</comments>
		<pubDate>Tue, 22 Mar 2011 23:05:19 +0000</pubDate>
		<dc:creator>mitcho</dc:creator>
				<category><![CDATA[how to]]></category>
		<category><![CDATA[projects]]></category>
		<category><![CDATA[Firefox]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[mochitest]]></category>
		<category><![CDATA[Mozilla Planet]]></category>
		<category><![CDATA[Panorama]]></category>

		<guid isPermaLink="false">http://mitcho.com/?p=4253</guid>
		<description><![CDATA[One of the last bugs for Firefox Panorama was bug 625818: &#8220;Check Panorama mochitest test suite coverage&#8221;. Our automated tests ensure that we do not regress on existing functionality, but it&#8217;s only as good as its coverage: how much of the Panorama code base is actually being &#8220;hit&#8221; through the process of running the test [...]
Related posts:<ol>
<li><a href='http://mitcho.com/blog/projects/performance-vs-responsiveness/' rel='bookmark' title='Performance vs Responsiveness —or— How I Made the Parser Twice As Fast in One Day'>Performance vs Responsiveness —or— How I Made the Parser Twice As Fast in One Day</a></li>
<li><a href='http://mitcho.com/blog/how-to/develop-with-jetpack-sdk-0-2/' rel='bookmark' title='Beginning development with Jetpack SDK 0.2'>Beginning development with Jetpack SDK 0.2</a></li>
<li><a href='http://mitcho.com/blog/projects/keep-up-with-yet-another-related-posts-plugin-with-rss/' rel='bookmark' title='Keep up with Yet Another Related Posts Plugin with RSS!'>Keep up with Yet Another Related Posts Plugin with RSS!</a></li>
</ol>

Related posts brought to you by <a href='http://yarpp.org'>Yet Another Related Posts Plugin</a>.]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.mozilla.org/firefox?WT.mc_id=aff_en08&amp;WT.mc_ev=click" style="float: right;margin-left:10px;"><img border="0" alt="Firefox Download Button" src="http://www.mozilla.org/contribute/buttons/120x240arrow_b.png"/></a>One of the last bugs for Firefox Panorama was <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=625818">bug 625818: &#8220;Check Panorama mochitest test suite coverage&#8221;</a>. Our automated tests ensure that we do not regress on existing functionality, but it&#8217;s only as good as its coverage: how much of the Panorama code base is actually being &#8220;hit&#8221; through the process of running the test suite.</p>

<p>Panorama went through a pretty rapid development cycle, making it into <a href="http://firefox.com">Firefox 4</a> which was <a href="http://blog.mozilla.com/blog/2011/03/22/mozilla-launches-firefox-4-and-delivers-a-fast-sleek-and-customizable-browsing-experience-to-more-than-400-million-users-worldwide-2/">released today</a> (yay!). Moreover, for a while we were developing outside of mozilla-central, without the regular &#8220;patches require tests&#8221; requirement. This makes checking its test coverage particularly important.</p>

<p>Check out the final result, the <a href="http://mitcho.com/code/panorama-coverage/">Panorama test coverage report</a>. The good news: our code coverage is 86%! (Some notes on what improvements can be made are in <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=625818">the bug</a>.)</p>

<p><a href="http://mitcho.com/code/panorama-coverage/"><img src="http://mitcho.com/blog/wp-content/uploads/2011/03/Screen-shot-2011-03-22-at-6.59.26-PM.png" alt="code coverage report" border="0" width="600" height="260" /></a></p>

<p>PhiliKON had previously worked on hooking into the <a href="https://developer.mozilla.org/en/Code_snippets/JavaScript_Debugger_Service">JS Debugger service</a>&#8217;s <code>interruptHook</code> to test <a href="https://developer.mozilla.org/en/Writing_xpcshell-based_unit_tests"><code>xpcshell</code> tests</a>. I modified this code to run instead in the <a href="https://developer.mozilla.org/en/Browser_chrome_tests">Mochitest browser chrome tests</a>. This code can be found <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=625818">on the bug</a>.</p>

<p>With this patch applied, I invoked the test suite with the following code: <code>TEST_PATH=browser/base/content/tests/tabview COVERAGE_FILTER="*tabview*" COVERAGE=true make -C obj-ff-dbg mochitest-browser-chrome</code> . That&#8217;s a regular <code>mochitest-browser-chrome</code> invocation with the <code>COVERAGE=true</code> flag which turns on code coverage checking, and <code>COVERAGE_FILTER=*tabview*</code> which filters out results from files which don&#8217;t have &#8220;tabview&#8221; in their paths. This creates a file called <code>coverage.json</code> in the working directory <em>of the test suite</em>, meaning, for me, <code>obj-ff-dbg/_tests/testing/mochitest/</code>.</p>

<p>This JSON file is a multidimensional array, with file paths and then line numbers as keys. The file paths here, as best as possible, have been converted into local filesystem paths. PhiliKON built a script which produces beautiful reports based on this output.</p>

<p>A word of warning: running with this JSD <code>interruptHook</code> is ridiculously slow. A number of tests for Panorama are timing-dependent (drag-drop tests, for example), making some of them fail, but that&#8217;s okay&#8230; as long as it completed not via a timeout, it actually did run through all the code. In order to get this to run through everything with some degree of control, I split up the mochitest tabview suite in to a few chunks. I then took the multiple resulting <code>coverage.json</code> files and passed them into another script, in <code>tools/coverage/aggregate.py</code>, which takes multiple JSON results like this and puts them together into a single JSON file. I then passed this aggregate JSON file to PhiliKON&#8217;s wonderful report script and—voila—the <a href="http://mitcho.com/code/panorama-coverage/">Panorama test coverage report</a>! Easy as pie.</p>
<p>Related posts:</p><ol>
<li><a href='http://mitcho.com/blog/projects/performance-vs-responsiveness/' rel='bookmark' title='Performance vs Responsiveness —or— How I Made the Parser Twice As Fast in One Day'>Performance vs Responsiveness —or— How I Made the Parser Twice As Fast in One Day</a></li>
<li><a href='http://mitcho.com/blog/how-to/develop-with-jetpack-sdk-0-2/' rel='bookmark' title='Beginning development with Jetpack SDK 0.2'>Beginning development with Jetpack SDK 0.2</a></li>
<li><a href='http://mitcho.com/blog/projects/keep-up-with-yet-another-related-posts-plugin-with-rss/' rel='bookmark' title='Keep up with Yet Another Related Posts Plugin with RSS!'>Keep up with Yet Another Related Posts Plugin with RSS!</a></li>
</ol>
<p>Related posts brought to you by <a href='http://yarpp.org'>Yet Another Related Posts Plugin</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://mitcho.com/blog/projects/checking-mochitest-test-coverage/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Beginning development with Jetpack SDK 0.2</title>
		<link>http://mitcho.com/blog/how-to/develop-with-jetpack-sdk-0-2/</link>
		<comments>http://mitcho.com/blog/how-to/develop-with-jetpack-sdk-0-2/#comments</comments>
		<pubDate>Wed, 14 Apr 2010 16:20:47 +0000</pubDate>
		<dc:creator>mitcho</dc:creator>
				<category><![CDATA[how to]]></category>
		<category><![CDATA[Jetpack]]></category>
		<category><![CDATA[Jetpack SDK]]></category>
		<category><![CDATA[Mozilla Labs]]></category>
		<category><![CDATA[Mozilla Planet]]></category>
		<category><![CDATA[Windows]]></category>

		<guid isPermaLink="false">http://mitcho.com/blog/?p=3504</guid>
		<description><![CDATA[This article is a translation of a recent article in Japanese by fellow Jetpack Ambassador Gomita which was published on the Mozilla Labs Jetpack blog. I&#8217;m cross-posting it here for posterity. Mozilla Labs recently released version 0.2 of the Jetpack SDK, which fixes some issues of the 0.1 release such as a glitch regarding development [...]
Related posts:<ol>
<li><a href='http://mitcho.com/blog/projects/jetpack-ambassadors-in-mv/' rel='bookmark' title='Jetpack Ambassadors in MV'>Jetpack Ambassadors in MV</a></li>
<li><a href='http://mitcho.com/blog/projects/ubiquity-localization-update/' rel='bookmark' title='Ubiquity Localization Update'>Ubiquity Localization Update</a></li>
<li><a href='http://mitcho.com/blog/projects/spring-is-for-speaking/' rel='bookmark' title='Spring is for Speaking: JSConf, WordCamp SF, IACL'>Spring is for Speaking: JSConf, WordCamp SF, IACL</a></li>
</ol>

Related posts brought to you by <a href='http://yarpp.org'>Yet Another Related Posts Plugin</a>.]]></description>
			<content:encoded><![CDATA[<p><em>This article is a translation of a <a href="http://www.xuldev.org/blog/?p=513">recent article in Japanese</a> by fellow Jetpack Ambassador <a href="http://www.xuldev.org/blog/">Gomita</a> which was published <a href="http://mozillalabs.com/jetpack/2010/04/13/develop-with-jetpack-sdk-0-2/">on the Mozilla Labs Jetpack blog</a>. I&#8217;m cross-posting it here for posterity.
</em></p>

<p>Mozilla Labs recently released <a href="http://mozillalabs.com/jetpack/2010/03/26/announcing-jetpack-sdk-0-2/">version 0.2</a> of the Jetpack SDK, which fixes some issues of the 0.1 release such as a glitch regarding development with Windows. SDK 0.2 doesn&#8217;t include the planned APIs for rapid development of new browser functionality, but you can still play with SDK 0.2 to get a flavor for development with the Jetpack SDK.</p>

<p>In this article we begin by setting up an SDK 0.2 development environment and explain the steps required to develop a simple, practical add-on using SDK 0.2. The instructions here are for Windows, but the basic steps are the same in every platform.</p>

<p><span id="more-3504"></span></p>

<h3>Installing Python</h3>

<p>The first step to using the Jetpack SDK is to install Python. How to install Python depends on your OS, but in Windows you can choose the &#8220;Python 2.6.5 Windows installer&#8221; from the <a href="http://www.python.org/download/">Python</a> site and follow the installation wizard. Here, I&#8217;ll use <code>C:\Python26\</code> as the installation path.</p>

<p>After the install, you can activate the <code>python</code> command in your command line by adding <code>C:\Python26</code> to the Windows <code>Path</code> preference. (If there is already another value, delimit with a semicolon: &#8220;;&#8221;.) Run the command &#8220;cmd&#8221; from the Start menu to start the command prompt and run <code>python -V</code> to confirm the Python version, <code>Python 2.6.2</code>:
<pre>C:\&gt;python -V
Python 2.6.2
</pre>
Note, the <a href="https://jetpack.mozillalabs.com/sdk/0.2/docs/#guide/getting-started">Jetpack SDK Docs</a> state that Python 2.5+ is required, but there seem to be some incompatibilities with Python 3.0.1 at this time. In addition, in my experience the SDK worked fine without the &#8220;Python for Windows extensions.&#8221;</p>

<h3>Setting up the Jetpack SDK</h3>

<p>Next, we set up the Jetpack SDK. Download the Jetpack SDK 0.2 package from the <a href="https://jetpack.mozillalabs.com/">Jetpack site</a>, unzip it, and place it somewhere convenient. Here, I used <code>C:\jetpack-sdk-0.2</code>.</p>

<p>To use the Jetpack SDK, it must be &#8220;activated.&#8221; From the command prompt, go to the Jetpack SDK folder and run <code>bin\activate</code>:
<pre>C:\jetpack-sdk-0.2&gt;bin\activate
Welcome to the Jetpack SDK. Run 'cfx docs' for assistance.
(C:\jetpack-sdk-0.2) C:\jetpack-sdk-0.2&gt;
</pre>
Next, run <code>cfx docs</code> to open the SDK documentation in the browser. The SDK documentation starts a local server on port 8888.
<pre>(C:\jetpack-sdk-0.2) C:\jetpack-sdk-0.2&gt;cfx docs
One moment.
Opening web browser to http://127.0.0.1:8888.
</pre></p>

<h3>The package directory structure</h3>

<p>Addons built with the Jetpack SDK are called &#8220;packages.&#8221; Let&#8217;s try building a simple &#8220;hello world&#8221;-style package, but first let&#8217;s see what the final directory structure of this package will look like:</p>

<table style="height: 279px; margin: 0pt 0pt 10px 80px;" width="450">
<tbody>
<tr>
<th><span style="float: left;">directory/file</span></th>
<th><span style="float: left;">Note</span></th>
</tr>
<tr>
<td><img style="float: left; margin: 4px 5px;" src="http://www.xuldev.org/common/folder.png" alt="フォルダ" width="16" height="16" />jetpack-sdk-0.2</td>
<td>the Jetpack SDK folder</td>
</tr>
<tr>
<td><img style="float: left; margin: 4px 5px; margin-left:21px;" src="http://www.xuldev.org/common/folder.png" alt="フォルダ" width="16" height="16" />packages</td>
<td>the main packages folder</td>
</tr>
<tr>
<td><img style="float: left; margin: 4px 5px; margin-left:37px;" src="http://www.xuldev.org/common/folder.png" alt="フォルダ" width="16" height="16" />hello-world</td>
<td>package root</td>
</tr>
<tr>
<td><img style="float: left; margin: 4px 5px; margin-left:53px;" src="http://www.xuldev.org/common/file.png" alt="ファイル" width="16" height="16" />package.json</td>
<td>package manifest file</td>
</tr>
<tr>
<td><img style="float: left; margin: 4px 5px; margin-left:53px;" src="http://www.xuldev.org/common/file.png" alt="ファイル" width="16" height="16" />README.md</td>
<td>package documentation</td>
</tr>
<tr>
<td><img style="float: left; margin: 4px 5px; margin-left:53px;" src="http://www.xuldev.org/common/folder.png" alt="フォルダ" width="16" height="16" />lib</td>
<td>the package code directory</td>
</tr>
<tr>
<td><img style="float: left; margin: 4px 5px; margin-left:69px;" src="http://www.xuldev.org/common/file.png" alt="ファイル" width="16" height="16" />main.js</td>
<td>main program code</td>
</tr>
<tr>
<td><img style="float: left; margin: 4px 5px; margin-left:69px;" src="http://www.xuldev.org/common/file.png" alt="ファイル" width="16" height="16" />simple-dialog.js</td>
<td>a custom code library</td>
</tr>
</tbody>
</table>

<p>The package&#8217;s root directory is placed in the &#8220;packages&#8221; directory in the Jetpack SDK folder, and includes the <code>package.json</code> manifest file and the <code>README.md</code> documentation file. The <code>lib</code> folder includes the package&#8217;s main program code and any custom libraries used by our addon.</p>

<h3>Creating the package</h3>

<p>We begin by creating the <code>hello-world</code> directory in <code>C:\jetpack-sdk-0.2\packages</code> . Next the manifest file <code>package.json</code> is created. The manifest file includes metadata about our package in JSON format. If you&#8217;ve ever created a XUL-style addon before, you can think of this as similar to the <code>install.rdf</code> file. Here, I used the following as the manifest:
<pre>{
    "id": "helloworld@xuldev.org",
    "version": "0.1",
    "description": "This is my first package.",
    "author": "Gomita &lt;gomita@xuldev.org&gt;"
}
</pre>
The <code>id</code> property is used as a unique ID for all addons including Jetpack packages and is often formatted as an email address. This corresponds to XUL-based addons&#8217; <code>&amp;lt;em:id&amp;gt;</code> tag.</p>

<p>Next, reload the SDK documentation in the browser and confirm that &#8220;hello-world&#8221; shows up under &#8220;Package Reference.&#8221;</p>

<h3>Writing the main code</h3>

<p>The next step is to add some working code to the hello-world package. Create a <code>lib</code> folder under the package root and create a <code>main.js</code> under <code>lib</code> with the following code:
<pre>exports.main = function(options, callbacks) {
    console.log("Hello, World!");
};
</pre>
The main program code is always loaded as a module called <code>main</code>. This <code>main</code> property is made accessible from outside code using the <a href="http://commonjs.org/">CommonJS</a>-style code <code>exports.main = ...</code>. <code>console.log</code> is a global function made available by Jetpack and the SDK prints the string to the command prompt.</p>

<p>It&#8217;s worth noting that, in the current Jetpack SDK, calling &#8220;<code>console.log("こんにちは");</code>&#8221; doesn&#8217;t yield the expected Japanese output. In the future such output will be handled through the planned <a href="https://wiki.mozilla.org/Labs/Jetpack/Reboot/JEP/113">localization API</a>.</p>

<h3>Testing our package</h3>

<p>With some simple code in our <code>main</code> function, it&#8217;s time to try this code out. To test this code, we run <code>cfx run -a firefox</code> in the command prompt. By running <code>cfx run</code> with the <code>-a firefox</code> option, we load our package into a brand new Firefox profile and launch Firefox.
<pre>(C:\jetpack-sdk-0.2) C:\jetpack-sdk-0.2&gt;cd packages\hello-world</p>

<p>(C:\jetpack-sdk-0.2) C:\jetpack-sdk-0.2\packages\hello-world&gt;cfx run -a firefox
info: Hello, World!
OK
Total time: 1.531000 seconds
Program terminated unsuccessfully.
</pre>
After Firefox loads, confirm that the command prompt reads <code>info: Hello, World!</code> When you quit Firefox, the testing will end.</p>

<h3>Using a standard library</h3>

<p>Now we&#8217;ll edit our code to invoke the timer library which is one of the Jetpack SDK&#8217;s standard libraries. The timer library is a module which abstracts various timer-related functionality, similar to the DOM&#8217;s <code>window.setTimeout</code>, <code>window.clearTimeout</code>. Details on this library are available in the <a href="https://jetpack.mozillalabs.com/sdk/0.2/docs/#module/jetpack-core/timer">SDK documentation</a>. Moreover, although not in the documentation, <code>timer.setInterval</code> and <code>timer.clearInterval</code> also work in this version.</p>

<p>To use this library in our main program code, we first must invoke this library with the CommonJS require function. We modify the <code>main.js</code> file as follows:
<pre>var timer = require("timer");
exports.main = function(options, callbacks) {
    timer.setInterval(function() {
        console.log(new Date().toLocaleTimeString());
    }, 1000);
};
</pre>
After this change, run <code>cfx run -a firefox</code> in the command prompt to test it. Check to make sure that the current time is being printed to the command prompt once a second:
<pre>(C:\jetpack-sdk-0.2) C:\jetpack-sdk-0.2\packages\hello-world&gt;cfx run -a firefox
info: 10:37:21
info: 10:37:22
info: 10:37:23
info: 10:37:24
info: 10:37:25
</pre></p>

<h3>Creating a custom library</h3>

<p>Next we&#8217;ll create a custom library to add some functionality not currently included in the Jetpack standard library. Implementing advanced functionality in add-ons, like filesystem access, involves using <a href="https://developer.mozilla.org/en/XPCOM">XPCOM</a> components. Jetpack encourages seprarating the use of XPCOM components into separate modules which are then used by the main program code. The Jetpack SDK doesn&#8217;t currently disallow direct XPCOM access within Jetpack add-on code, but such a restriction is forthcoming. Modularizing XPCOM code into separate libraries now allow you to easily migrate to equivalent standard libraries in the future.</p>

<p>Let&#8217;s create a <code>simple-dialog</code> library to display a modal dialog much like <code>window.alert</code> does. The Jetpack code&#8217;s runtime environment doesn&#8217;t include access to the regular <code>window</code> or <code>document</code> objects, so just calling <code>window.alert</code> doesn&#8217;t work. To create an alert from this context, we use the <code>&lt;a href="https://developer.mozilla.org/en/nsIPromptService"&gt;nsIPromptService&lt;/a&gt;</code> XPCOM component. In our package&#8217;s <code>lib</code> folder, create a <code>simple-dialog.js</code> file. Just like our main program code, we implement this library as a CommonJS module using <code>exports.&lt;em&gt;methodname&lt;/em&gt; = function(...){...}</code>.</p>

<p>The simple-dialog library will have these two methods:</p>

<table style="margin: 0 0 10px 80px;">
<tbody>
<tr>
<th><span style="float: left;">Method</span></th>
<th><span style="float: left;">Note</span></th>
</tr>
<tr>
<td><code>alert(<em>text</em>)</code></td>
<td>Displays an alert dialog with the string in <em>text</em> and an OK button. Equivalent to the DOM&#8217;s <code>window.alert</code>.</td>
</tr>
<tr>
<td><code>confirmYesNo(<em>text</em>)</code></td>
<td>Displays a confirmation dialog with the string in <em>text</em> and Yes and No buttons. The method returns <code>true</code> if the user presses &#8220;yes&#8221; and <code>false</code> if &#8220;no.&#8221;</td>
</tr>
</tbody>
</table>

<p>Here is the code for <code>simple-dialog.js</code>:
<pre>var promptSvc = Cc["@mozilla.org/embedcomp/prompt-service;1"].
                getService(Ci.nsIPromptService);</p>

<p>exports.alert = function(text) {
    promptSvc.alert(null, "[Jetpack]", text);
};</p>

<p>exports.confirmYesNo = function(text) {
    var pos = promptSvc.confirmEx(
        null, "[Jetpack]", text, promptSvc.STD_YES_NO_BUTTONS,
        null, null, null, null, {}
    );
    return pos == 0;
};
</pre>
Lines 1-2 are for calling <code>nsIPromptService</code>. Note that <code>Cc</code>, <code>Ci</code> are aliases for <code>Components.classes</code> and <code>Components.interfaces</code>, respectively, and are made available by Jetpack as global variables. Lines 4-6 implement the alert method for showing alert dialogs using <code>nsIPromptService</code>&#8217;s <code>alert</code> method. Lines 8-14 implement <code>simple-dialog</code>&#8217;s <code>confirmYesNo</code> method using <code>nsIPromptService</code>&#8217;s <code>confirmEx</code> method to display the dialog with yes and no buttons. <code>nsIPromptservice</code>&#8217;s <code>confirmEx</code> method returns 0 if the user presses &#8220;yes&#8221; and 1 if &#8220;no&#8221;, so we modify this value and return it.</p>

<h3>Using our custom library</h3>

<p>Let&#8217;s call this new custom library from our main program code and verify that it works. Here&#8217;s our updated <code>main.js</code> file:
<pre>var simpleDialog = require("simple-dialog");</p>

<p>exports.main = function(options, callbacks) {
    var adult = simpleDialog.confirmYesNo("Are you over 18 years old?");
    if (adult) {
        simpleDialog.alert("Welcome!");
    }
    else {
        simpleDialog.alert("Good bye!");
    }
};
</pre>
Run <code>cfx run -a firefox</code> and confirm that a confirmation dialog, as seen below, is displayed. Pressing &#8220;yes&#8221; and &#8220;no&#8221; should give you the appropriate alert dialogs as well.</p>

<p><img src="///var/folders/Xr/XrC2E9n7FcWD7pxxv8puwE+++TI/-Tmp-/com.apple.mail.drag-T0x10051fce0.tmp.Ppt6Wu/confirmyesno-en.png" alt="" /></p>

<h3><img src="///Users/mitcho/Desktop/confirmyesno-en.png" alt="" /></h3>

<h3><a href="http://mozillalabs.com/jetpack/files/2010/04/confirmyesno-en.png"><img class="alignnone size-medium wp-image-269" title="confirmyesno-en" src="http://mozillalabs.com/jetpack/files/2010/04/confirmyesno-en-300x111.png" alt="" width="300" height="111" /></a></h3>

<h3>Implementing a network status observer</h3>

<p>Now let&#8217;s use this hello-world package as a foundation for a more practical add-on. Using the <code>&lt;a href="https://jetpack.mozillalabs.com/sdk/0.2/docs/#module/jetpack-core/observer-service"&gt;observer-service&lt;/a&gt;</code> module included with the Jetpack SDK, we can monitor Firefox&#8217;s online/offline network status changes.</p>

<p>Firefox internally broadcasts various application events to observers via the <code>&lt;a href="https://developer.mozilla.org/ja/NsIObserverService"&gt;nsIObserverService&lt;/a&gt;</code> XPCOM component. When Firefox goes offline, a <code>network:offline-status-changed</code> notification is broadcast. To subscribe this notification and act on it, we use the <code>observer-service</code> library&#8217;s <code>add</code> method. <code>add</code>&#8217;s first argument is the name of the notification we want to subscribe to and the second argument is a callback function. The callback function is given two arguments, of which the second is a string equal to either &#8220;online&#8221; or &#8220;offline.&#8221; In our add-on, we&#8217;ll check this value and display an appropriate alert using <code>simple-dialog</code>.
<pre>var simpleDialog = require("simple-dialog");
var observer = require("observer-service");</p>

<p>exports.main = function(options, callbacks) {
    observer.add("network:offline-status-changed", function(sbj, data) {
        if (data == "online") {
            simpleDialog.alert("Firefox is now online.");
        }
        else if (data == "offline") {
            simpleDialog.alert("Firefox is now offline.");
        }
    });
};
</pre>
Launch Firefox by running <code>cfx run -a firefox</code> and then choose &#8220;File&#8221; &gt; &#8220;Work Offline&#8221; and you should get a notification:</p>

<p><a href="http://mozillalabs.com/jetpack/files/2010/04/offline-en.png"><img class="alignnone size-medium wp-image-270" title="offline-en" src="http://mozillalabs.com/jetpack/files/2010/04/offline-en-300x225.png" alt="" width="300" height="225" /></a></p>

<h3>Adding documentation</h3>

<p>If you add documentation to a package, you can view it by clicking that package in the SDK Documentation. To add documentation, create a <code>README.md</code> file in the package root directory. <code>README.md</code> is written in Markdown format which looks like this:</p>

<p><pre>This is my *first* package.
* foo
* bar
* baz
</pre></p>

<p>Now if you load the SDK documentation using <code>cfx docs</code> and click on the &#8220;hello-world&#8221; link, you&#8217;ll see this documentation together with the package metadata.</p>

<h3>Exporting an install package</h3>

<p>Jetpack add-ons which are created in this way can then be exported into Firefox-standard XPI files. To export an XPI, go to the package&#8217;s root directory in the command prompt and run <code>cfx xpi</code>.
<pre>(C:\jetpack-sdk-0.2) C:\jetpack-sdk-0.2\packages\hello-world&gt;cfx xpi
Exporting extension to hello-world.xpi.
</pre>
This creates an XPI file called <code>hello-world.xpi</code>. Opening this file in any Firefox profile will let you install it using the regular add-on install mechanism.</p>
<p>Related posts:</p><ol>
<li><a href='http://mitcho.com/blog/projects/jetpack-ambassadors-in-mv/' rel='bookmark' title='Jetpack Ambassadors in MV'>Jetpack Ambassadors in MV</a></li>
<li><a href='http://mitcho.com/blog/projects/ubiquity-localization-update/' rel='bookmark' title='Ubiquity Localization Update'>Ubiquity Localization Update</a></li>
<li><a href='http://mitcho.com/blog/projects/spring-is-for-speaking/' rel='bookmark' title='Spring is for Speaking: JSConf, WordCamp SF, IACL'>Spring is for Speaking: JSConf, WordCamp SF, IACL</a></li>
</ol>
<p>Related posts brought to you by <a href='http://yarpp.org'>Yet Another Related Posts Plugin</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://mitcho.com/blog/how-to/develop-with-jetpack-sdk-0-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Creating an image-sized iframe overlay with Shadowbox</title>
		<link>http://mitcho.com/blog/how-to/creating-an-image-sized-iframe-overlay-with-shadowbox/</link>
		<comments>http://mitcho.com/blog/how-to/creating-an-image-sized-iframe-overlay-with-shadowbox/#comments</comments>
		<pubDate>Wed, 13 Jan 2010 05:33:50 +0000</pubDate>
		<dc:creator>mitcho</dc:creator>
				<category><![CDATA[how to]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[iframe]]></category>
		<category><![CDATA[image]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[MIT]]></category>
		<category><![CDATA[shadowbox]]></category>

		<guid isPermaLink="false">http://mitcho.com/blog/?p=3293</guid>
		<description><![CDATA[I recently have been working with the Shadowbox JavaScript library for an upcoming revision to the MIT Edgerton Digital Collections website. Shadowbox is a nice lightbox library designed to work with various JavaScript libraries like jQuery, prototype, and mootools with a nice modular design. Shadowbox is organized around different &#8220;players&#8221;—one for each kind of media [...]
Related posts:<ol>
<li><a href='http://mitcho.com/blog/life/im-seriously-dreaming-of-a-white-christmas/' rel='bookmark' title='I&#8217;m Seriously Dreaming of a White Christmas'>I&#8217;m Seriously Dreaming of a White Christmas</a></li>
<li><a href='http://mitcho.com/blog/life/the-gift-that-keeps-on-giving/' rel='bookmark' title='The Gift That Keeps On Giving'>The Gift That Keeps On Giving</a></li>
<li><a href='http://mitcho.com/blog/life/atayal-cultural-festival/' rel='bookmark' title='Atayal cultural festival'>Atayal cultural festival</a></li>
</ol>

Related posts brought to you by <a href='http://yarpp.org'>Yet Another Related Posts Plugin</a>.]]></description>
			<content:encoded><![CDATA[<p>I recently have been working with the <a href="http://www.shadowbox-js.com/">Shadowbox</a> JavaScript library for an upcoming revision to the MIT <a href="http://edgerton-digital-collections.org">Edgerton Digital Collections</a> website. Shadowbox is a nice <a href="http://en.wikipedia.org/wiki/Lightbox (JavaScript)">lightbox</a> library designed to work with various JavaScript libraries like jQuery, prototype, and mootools with a nice modular design.</p>

<p>Shadowbox is organized around different &#8220;players&#8221;—one for each kind of media that will be displayed. The library by default comes with players for Flash, HTML fragments, <code>iframe</code>s, QuickTime, and Windows Media. Some of these players, like those for images and video, automatically recognize the media size and adjust the lightbox accordingly, while others such as the <code>iframe</code> player can use a set size or can fill the screen. For the Edgerton site, though, we had a need for displaying an <code>iframe</code> but in the dimensions of a set image, so that we could display the image with an overlay. Here are some notes on how to implement a custom player for Shadowbox.</p>

<p><span id="more-3293"></span>
The first step to getting Shadowbox to recognize a custom player is to modify the players loaded by <code>shadowbox.js</code>. I called my player &#8220;hybrid,&#8221; as it&#8217;s like a cross between the <code>iframe</code> and <code>img</code> players.</p>


<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;">Shadowbox.<span style="color: #660066;">options</span>.<span style="color: #660066;">players</span><span style="color: #339933;">=</span><span style="color: #009900;">&#91;</span><span style="color: #3366CC;">&quot;html&quot;</span><span style="color: #339933;">,</span><span style="color: #3366CC;">&quot;iframe&quot;</span><span style="color: #339933;">,</span><span style="color: #3366CC;">&quot;img&quot;</span><span style="color: #339933;">,</span><span style="color: #3366CC;">&quot;hybrid&quot;</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span></pre></div></div>


<p>Now Shadowbox will try to look for the file <code>players/shadowbox-hybrid.js</code>. That&#8217;s where we will create our custom player. Start by duplicating the <code>iframe</code> player file. Next, we can make one more change to <code>shadowbox.js</code>. In the <code>getPlayer</code> function, add a line that will return &#8220;<code>hybrid</code>&#8221; when it recognizes a lightbox which should use the hybrid player. I added the following line, which uses the hybrid player when the text &#8220;<code>galleries</code>&#8221; is in the <code>a</code> tag&#8217;s <code>href</code> attribute.</p>


<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #009966; font-style: italic;">/galleries/</span>.<span style="color: #660066;">test</span><span style="color: #009900;">&#40;</span>content<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #000066; font-weight: bold;">return</span> <span style="color: #3366CC;">&quot;hybrid&quot;</span><span style="color: #339933;">;</span></pre></div></div>


<p>Finally, you must actually write the new player. There&#8217;s a readme file which describes the interface:</p>

<blockquote>
<p>All players should implement the same interface. This makes it possible for the Shadowbox class to know what methods to call and properties to check on player objects.</p>
<p>The interface is described here, with some simple explanations of how each method and/or property is to be used.</p>
<dl>
<dt><strong>height</strong></dt>
    <dd>(Number) The height of the object (in pixels)</dd>
<dt><strong>width</strong></dt>
    <dd>(Number) The width of the object (in pixels)</dd>
<dt><strong>ready</strong></dt>
    <dd>(optional, Boolean) True if the content is ready to be loaded, false otherwise. Useful when the script should wait until the content loads before proceeding (see below)</dd>
<dt><strong>resizable</strong></dt>
    <dd>(optional, Boolean) True if the content can be dynamically resized by the script (e.g. images, but not most movie formats)</dd>
<dt><strong>append()</strong></dt>
    <dd>Appends this object to the DOM</dd>
<dt><strong>remove()</strong></dt>
    <dd>Removes this object from the DOM</dd>
<dt><strong>onLoad()</strong></dt>
    <dd>(optional) Called after the content is loaded and the loading layer is hidden</dd>
<dt><strong>onWindowResize()</strong></dt>
    <dd>(optional) Called when the window is resized</dd>
</dl>
</blockquote>

<p>I found these directions quite easy to follow. For our purposes, there was also a note on how to set an object&#8217;s height and width based on an image.</p>

<p>Hopefully these notes are helpful for others who hope to take full advantage of this very powerful JavaScript library.</p>
<p>Related posts:</p><ol>
<li><a href='http://mitcho.com/blog/life/im-seriously-dreaming-of-a-white-christmas/' rel='bookmark' title='I&#8217;m Seriously Dreaming of a White Christmas'>I&#8217;m Seriously Dreaming of a White Christmas</a></li>
<li><a href='http://mitcho.com/blog/life/the-gift-that-keeps-on-giving/' rel='bookmark' title='The Gift That Keeps On Giving'>The Gift That Keeps On Giving</a></li>
<li><a href='http://mitcho.com/blog/life/atayal-cultural-festival/' rel='bookmark' title='Atayal cultural festival'>Atayal cultural festival</a></li>
</ol>
<p>Related posts brought to you by <a href='http://yarpp.org'>Yet Another Related Posts Plugin</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://mitcho.com/blog/how-to/creating-an-image-sized-iframe-overlay-with-shadowbox/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Fixing Geshi on line 2132</title>
		<link>http://mitcho.com/blog/how-to/fixing-geshi-on-line-2132/</link>
		<comments>http://mitcho.com/blog/how-to/fixing-geshi-on-line-2132/#comments</comments>
		<pubDate>Sat, 13 Jun 2009 15:15:44 +0000</pubDate>
		<dc:creator>mitcho</dc:creator>
				<category><![CDATA[how to]]></category>
		<category><![CDATA[bug]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[error]]></category>
		<category><![CDATA[geshi]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[regular expression]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[WordPress Planet]]></category>
		<category><![CDATA[YARPP]]></category>

		<guid isPermaLink="false">http://mitcho.com/blog/?p=2233</guid>
		<description><![CDATA[I recently noticed that some of my blog posts, most notably my Templates in YARPP 3 article, was producing a PHP error: Warning: preg_match() [function.preg-match]: Compilation failed: unrecognized character after (?&#60; at offset 3 in /&#8230;/html/blog/wp-content/plugins/wp-syntax/geshi/geshi.php on line 2132 This seemed to be coming from the version 1.0.8.4 version of Geshi I had installed. A [...]
Related posts:<ol>
<li><a href='http://mitcho.com/blog/projects/keep-up-with-yet-another-related-posts-plugin-with-rss/' rel='bookmark' title='Keep up with Yet Another Related Posts Plugin with RSS!'>Keep up with Yet Another Related Posts Plugin with RSS!</a></li>
<li><a href='http://mitcho.com/blog/projects/yet-another-related-posts-plugin/' rel='bookmark' title='Yet Another Related Posts Plugin'>Yet Another Related Posts Plugin</a></li>
<li><a href='http://mitcho.com/blog/projects/modifiying-wordpress-plugin-activation-behavior/' rel='bookmark' title='Modifiying WordPress plugin activation behavior'>Modifiying WordPress plugin activation behavior</a></li>
</ol>

Related posts brought to you by <a href='http://yarpp.org'>Yet Another Related Posts Plugin</a>.]]></description>
			<content:encoded><![CDATA[<p>I recently noticed that some of my blog posts, most notably my <a href="http://mitcho.com/blog/projects/yarpp-3-templates/">Templates in YARPP 3</a> article, was producing a PHP error:</p>

<blockquote>
  <p><b>Warning:</b> preg_match() [function.preg-match]: Compilation failed: unrecognized character after (?&lt; at offset 3 in <b>/&#8230;/html/blog/wp-content/plugins/wp-syntax/geshi/geshi.php</b> on <b>line 2132</b></p>
</blockquote>

<p>This seemed to be coming from the version 1.0.8.4 version of Geshi I had installed. A quick <a href="http://www.google.com/search?q=geshi+line+2132">google search for &#8220;geshi line 2132&#8221;</a> gives you over a thousand errors, so this seems to be common issue. <a href="http://qbnz.com/highlighter/">Geshi</a> is a fabulous and popular syntax highlighter and is the core component of the <a href="http://wordpress.org/extend/plugins/wp-syntax/">WP-Syntax plugin</a> for WordPress.</p>

<p>I did some digging around and realized that the issue was with the compilation of this monstrosity of a regular expression, used (as far as I can tell) to identify PHP code snippets, for example the &lt;?php &#8230;&#160;?&gt; keywords:</p>


<div class="wp_syntax"><div class="code"><pre class="code" style="font-family:monospace;">/(?&lt;start&gt;&lt;\\?(?&gt;php\b)?)(?:
(?&gt;[^\&quot;'?\\/&lt;]+)|
\\?(?!&gt;)|
(?&gt;'(?&gt;[^'\\\\]|\\\\'|\\\\\\\|\\\\)*')|
(?&gt;\&quot;(?&gt;[^\&quot;\\\\]|\\\\\&quot;|\\\\\\\\|\\\\)*\&quot;)|
(?&gt;\\/\\*(?&gt;[^\\*]|(?!\\*\\/)\\*)*\\*\\/)|
\\/\\/(?&gt;.*?$)|
\\/(?=[^*\\/])|
&lt;(?!&lt;&lt;)|
&lt;&lt;&lt;(?&lt;phpdoc&gt;\w+)\s.*?\s\k&lt;phpdoc&gt;
)*(?&lt;end&gt;\\?&gt;|\Z)/sm</pre></div></div>


<p>Not knowing exactly where to start in diagnosing this crazy expression, I simply disabled those &#8220;script delimiters&#8221; in the <code>geshi/php.php</code> file. The sections I commented out are lines 1080-1101. Now the script delimiters like <code>&amp;lt;?php</code> don&#8217;t get highlighted nicely, but I feel that&#8217;s a small price to pay for eliminating these errors. <a href="http://wordpress.org/support/topic/274864">Another solution for the WP-Syntax users</a> seems to be to downgrade to 0.9.4. Hopefully in the near future an update to Geshi will come out which fixes this issue once and for all.</p>
<p>Related posts:</p><ol>
<li><a href='http://mitcho.com/blog/projects/keep-up-with-yet-another-related-posts-plugin-with-rss/' rel='bookmark' title='Keep up with Yet Another Related Posts Plugin with RSS!'>Keep up with Yet Another Related Posts Plugin with RSS!</a></li>
<li><a href='http://mitcho.com/blog/projects/yet-another-related-posts-plugin/' rel='bookmark' title='Yet Another Related Posts Plugin'>Yet Another Related Posts Plugin</a></li>
<li><a href='http://mitcho.com/blog/projects/modifiying-wordpress-plugin-activation-behavior/' rel='bookmark' title='Modifiying WordPress plugin activation behavior'>Modifiying WordPress plugin activation behavior</a></li>
</ol>
<p>Related posts brought to you by <a href='http://yarpp.org'>Yet Another Related Posts Plugin</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://mitcho.com/blog/how-to/fixing-geshi-on-line-2132/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Adding Your Language to Ubiquity Parser 2</title>
		<link>http://mitcho.com/blog/how-to/adding-your-language-to-ubiquity-parser-2/</link>
		<comments>http://mitcho.com/blog/how-to/adding-your-language-to-ubiquity-parser-2/#comments</comments>
		<pubDate>Wed, 29 Apr 2009 11:44:20 +0000</pubDate>
		<dc:creator>mitcho</dc:creator>
				<category><![CDATA[how to]]></category>
		<category><![CDATA[argument structure]]></category>
		<category><![CDATA[arguments]]></category>
		<category><![CDATA[case marking]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[French]]></category>
		<category><![CDATA[i18n]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[l10n]]></category>
		<category><![CDATA[linguistics]]></category>
		<category><![CDATA[localization]]></category>
		<category><![CDATA[Mozilla Planet]]></category>
		<category><![CDATA[parser]]></category>
		<category><![CDATA[semantic roles]]></category>
		<category><![CDATA[ubiquity]]></category>

		<guid isPermaLink="false">http://mitcho.com/blog/?p=1956</guid>
		<description><![CDATA[NOTE: This blog post has now been added to the Ubiquity wiki and is updated there. Please disregard this article and instead follow these instructions. You&#8217;ve seen the video. You speak another language. And you&#8217;re wondering, &#8220;how hard is it to add my language to Ubiquity with Parser 2?&#8221; The answer: not that hard. With [...]
Related posts:<ol>
<li><a href='http://mitcho.com/blog/projects/ubiquity-parser-the-next-generation-demo/' rel='bookmark' title='Ubiquity Parser: The Next Generation Demo'>Ubiquity Parser: The Next Generation Demo</a></li>
<li><a href='http://mitcho.com/blog/projects/rolling-out-the-roles/' rel='bookmark' title='Rolling out the Roles'>Rolling out the Roles</a></li>
<li><a href='http://mitcho.com/blog/projects/foxkeh-demos-ubiquity-parser-the-next-generation/' rel='bookmark' title='Foxkeh demos Ubiquity Parser: The Next Generation'>Foxkeh demos Ubiquity Parser: The Next Generation</a></li>
</ol>

Related posts brought to you by <a href='http://yarpp.org'>Yet Another Related Posts Plugin</a>.]]></description>
			<content:encoded><![CDATA[<p><strong>NOTE: This blog post has now been added to the Ubiquity wiki and is updated there. Please disregard this article and instead follow <a href="https://wiki.mozilla.org/Labs/Ubiquity/Parser_2/Localization_Tutorial">these instructions</a>.</strong></p>

<p>You&#8217;ve <a href="http://mitcho.com/blog/projects/a-demonstration-of-ubiquity-parser-2/">seen the video</a>. You speak another language. And you&#8217;re wondering, <strong>&#8220;how hard is it to add my language to Ubiquity with Parser 2?&#8221;</strong> The answer: <strong>not that hard.</strong> With a little bit of JavaScript and knowledge of and interest in your own language, you&#8217;ll be able to get at least rudimentary Ubiquity functionality in your language. Follow along in this step by step guide and please <a href="http://ubiquity.mozilla.com/trac/ticket/662">submit your (even incomplete) language files</a>!</p>

<p><em>As Ubiquity Parser 2 evolves, there is a chance that this specification will change in the future. Keep abreast of such changes on the <a href="http://ubiquity.mozilla.com/planet/">Ubiquity Planet</a> and/or <a href="http://mitcho.com/blog/">this blog</a> (<a href="http://mitcho.com/blog/feed/blog-only/">RSS</a>).</em></p>

<p><span id="more-1956"></span></p>

<h3>Set up your environment</h3>

<p>If you&#8217;re new to Ubiquity core development, you&#8217;ll want to first read the <a href="http://wiki.mozilla.org/Labs/Ubiquity/Ubiquity_0.1_Development_Tutorial">Ubiquity 0.1 Development Tutorial</a> to learn how to get a live copy of the Ubiquity repository using <a href="http://en.wikipedia.org/wiki/Mercurial">Mercurial</a>. Once you&#8217;ve set up your Firefox profile to use this development version, make sure to try changing the <code>extensions.ubiquity.parserVersion</code> value to 2 in <code>about:config</code> (as seen in <a href="(http://mitcho.com/blog/projects/a-demonstration-of-ubiquity-parser-2/)">this demo video</a>) to verify that Parser 2 is working for you.</p>

<p>As you read along, you may find it beneficial to follow along in the languages currently included in Parser 2: <a href="https://ubiquity.mozilla.com/hg/ubiquity-firefox/raw-file/08cf861ba79a/ubiquity/modules/parser/new/en.js">English</a>, <a href="https://ubiquity.mozilla.com/hg/ubiquity-firefox/raw-file/08cf861ba79a/ubiquity/modules/parser/new/ja.js">Japanese</a>, <a href="https://ubiquity.mozilla.com/hg/ubiquity-firefox/raw-file/08cf861ba79a/ubiquity/modules/parser/new/pt.js">Portuguese</a>, and <a href="https://ubiquity.mozilla.com/hg/ubiquity-firefox/raw-file/08cf861ba79a/ubiquity/modules/parser/new/sv.js">Swedish</a> (and the incomplete <a href="https://ubiquity.mozilla.com/hg/ubiquity-firefox/raw-file/08cf861ba79a/ubiquity/modules/parser/new/zh.js">Chinese</a> and <a href="https://ubiquity.mozilla.com/hg/ubiquity-firefox/raw-file/08cf861ba79a/ubiquity/modules/parser/new/fr.js">French</a>).</p>

<h3>The structure of the language file</h3>

<p>Each language in Parser 2 gets its own file which acts as a <a href="https://developer.mozilla.org/En/Using_JavaScript_code_modules">JavaScript module</a>. You&#8217;ll need to look up the <a href="http://en.wikipedia.org/wiki/List of ISO 639-1 codes">ISO 639-1 code for your language</a>&#8230; Here we&#8217;ll use English (code <code>en</code>) as an example here and the JavaScript language file would then be called <code>en.js</code> and go in the <code>/ubiquity/modules/parser/new/</code> directory of the repository.</p>

<p>Here is the basic template for a Ubiquity Parser 2 language file:</p>


<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #003366; font-weight: bold;">var</span> EXPORTED_SYMBOLS <span style="color: #339933;">=</span> <span style="color: #009900;">&#91;</span><span style="color: #3366CC;">&quot;makeEnParser&quot;</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">typeof</span> window<span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> <span style="color: #3366CC;">'undefined'</span><span style="color: #009900;">&#41;</span> <span style="color: #006600; font-style: italic;">// kick it chrome style</span>
  Components.<span style="color: #660066;">utils</span>.<span style="color: #003366; font-weight: bold;">import</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;resource://ubiquity/modules/parser/new/parser.js&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #003366; font-weight: bold;">function</span> makeEnParser<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
  <span style="color: #003366; font-weight: bold;">var</span> en <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">new</span> Parser<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'en'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
...
&nbsp;
  <span style="color: #000066; font-weight: bold;">return</span> en<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></td></tr></table></div>


<p>After lines 1-4 which set up the <a href="https://developer.mozilla.org/En/Using_JavaScript_code_modules">JavaScript module</a>, everything else is wrapped in a factory function called <code>makeLaParser</code> (for Latin) or <code>makeEnParser</code> (for English, <code>en</code>) or <code>makeFrParser</code> (for French, <code>fr</code>), etc. This function initializes the new <code>Parser</code> object (line 7) with the appropriate language code, sets a bunch of parameters (elided above) and returns it. That&#8217;s it!</p>

<p>Now let&#8217;s walk through some of the parameters you must set to get your language working. For reference, the properties the language parser object is required to have are: <code>branching</code>, <code>anaphora</code>, and <code>roles</code>.</p>

<h3>Identifying your branching parameter</h3>


<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;">  en.<span style="color: #660066;">branching</span> <span style="color: #339933;">=</span> <span style="color: #3366CC;">'right'</span><span style="color: #339933;">;</span> <span style="color: #006600; font-style: italic;">// or 'left'</span></pre></div></div>


<p>One of the first things you&#8217;ll have to set for your parser is <strong>the <code>branching</code> parameter</strong>. Ubiquity Parser 2 uses the branching parameter to decide which direction to look for an argument after finding a delimiter or &#8220;role marker&#8221; (most often, these are <a href="http://en.wikipedia.org/wiki/adposition">prepositions or postpositions</a>. For example, in English &#8220;from&#8221; is a delimiter for the <code>goal</code> role and its argument is on its right.</p>

<table>
<tr><td>&nbsp;</td><td>&nbsp;</td><td colspan='2' style='background: transparent url(http://mitcho.com/i/cccarrow-right.png) no-repeat right bottom'>&nbsp;</td></tr>
<tr><td><b>to</b></td><td>Mary</td><td><b>from</b></td><td>John</td></tr>
</table>

<p>So &#8220;John&#8221; is a possible argument for the <code>source</code> role, but &#8220;Mary&#8221; should not be. Ubiquity can figure this out because English has the property <code>en.branching = 'right'</code>.</p>

<p>In Japanese, on the other hand, the argument of a delimiter like から (&#8220;from&#8221;) is found on the left of that delimiter, so <code>en.branching = 'left'</code>.</p>

<table>
<tr><td colspan='2' style='background: transparent url(http://mitcho.com/i/cccarrow-left.png) no-repeat left bottom'>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td></tr>
<tr><td>メアリー</td><td><b>-から</b></td><td>ジョン</td><td><b>-に</b></td></tr>
<tr><td>Mary</td><td><b>from</b></td><td>John</td><td><b>to</b></td></tr>
</table>

<p>In general, if your language has prepositions, you should use <code>.branching = 'right'</code> and if your language has postpositions, you can use <code>.branching = 'left'</code>.</p>

<p><strong>For more info</strong>:</p>

<ul>
<li>see <a href="http://en.wikipedia.org/wiki/Branching (linguistics)">branching</a> on Wikipedia.</li>
</ul>

<h3>Defining your roles</h3>


<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;">  en.<span style="color: #660066;">roles</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#91;</span>
    <span style="color: #009900;">&#123;</span>role<span style="color: #339933;">:</span> <span style="color: #3366CC;">'goal'</span><span style="color: #339933;">,</span> delimiter<span style="color: #339933;">:</span> <span style="color: #3366CC;">'to'</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
    <span style="color: #009900;">&#123;</span>role<span style="color: #339933;">:</span> <span style="color: #3366CC;">'source'</span><span style="color: #339933;">,</span> delimiter<span style="color: #339933;">:</span> <span style="color: #3366CC;">'from'</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
    <span style="color: #009900;">&#123;</span>role<span style="color: #339933;">:</span> <span style="color: #3366CC;">'position'</span><span style="color: #339933;">,</span> delimiter<span style="color: #339933;">:</span> <span style="color: #3366CC;">'at'</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
    <span style="color: #009900;">&#123;</span>role<span style="color: #339933;">:</span> <span style="color: #3366CC;">'position'</span><span style="color: #339933;">,</span> delimiter<span style="color: #339933;">:</span> <span style="color: #3366CC;">'on'</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
    <span style="color: #009900;">&#123;</span>role<span style="color: #339933;">:</span> <span style="color: #3366CC;">'alias'</span><span style="color: #339933;">,</span> delimiter<span style="color: #339933;">:</span> <span style="color: #3366CC;">'as'</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
    <span style="color: #009900;">&#123;</span>role<span style="color: #339933;">:</span> <span style="color: #3366CC;">'instrument'</span><span style="color: #339933;">,</span> delimiter<span style="color: #339933;">:</span> <span style="color: #3366CC;">'using'</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
    <span style="color: #009900;">&#123;</span>role<span style="color: #339933;">:</span> <span style="color: #3366CC;">'instrument'</span><span style="color: #339933;">,</span> delimiter<span style="color: #339933;">:</span> <span style="color: #3366CC;">'with'</span><span style="color: #009900;">&#125;</span>
  <span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span></pre></div></div>


<p>The second required property is the inventory of semantic roles and their corresponding delimiters. Each entry has a <code>role</code> from the <a href="http://mitcho.com/blog/projects/rolling-out-the-roles/">inventory of semantic roles</a> and a corresponding delimiter. Note that this mapping can be <a href="http://en.wikipedia.org/wiki/many-to-many (data model)">many-to-many</a>, i.e., each role can have multiple possible delimiters and different roles can have shared delimiters. Try to make sure to cover all of the roles in the <a href="http://mitcho.com/blog/projects/rolling-out-the-roles/">inventory of semantic roles</a>.</p>

<p><strong>For more info:</strong></p>

<ul>
<li><a href="http://mitcho.com/blog/projects/writing-commands-with-semantic-roles/">Writing commands with semantic roles</a></li>
<li><a href="http://mitcho.com/blog/projects/rolling-out-the-roles/">the proposed inventory of semantic roles</a></li>
<li>Wikipedia entry on <a href="http://en.wikipedia.org/wiki/thematic relations">thematic relations</a></li>
</ul>

<h3>Entering your anaphora (&#8220;magic words&#8221;)</h3>


<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;">  en.<span style="color: #660066;">anaphora</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#91;</span><span style="color: #3366CC;">&quot;this&quot;</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">&quot;that&quot;</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">&quot;it&quot;</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">&quot;selection&quot;</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">&quot;him&quot;</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">&quot;her&quot;</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">&quot;them&quot;</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span></pre></div></div>


<p>The final required property is the <code>anaphora</code> property which takes a list of &#8220;magic words&#8221;. Currently there is no distinction between all the different <a href="http://en.wikipedia.org/wiki/deixis">deictic</a> <a href="http://en.wikipedia.org/wiki/anaphora (linguistics)">anaphora</a> which might refer to different things.</p>

<h3>Special cases</h3>

<p>Some special language features can be handled by overriding the default behavior from <code>Parser</code>. Many of these features are still in the works, however, so we&#8217;d love to get your comments!</p>

<h4>Languages with no spaces</h4>

<p>If your language does not delimit arguments (or words, more generally) with spaces, there will be a need to write a custom <code>wordBreaker()</code> function and set <code>usespaces = false</code> and <code>joindelimiter = ''</code>. For an example, please take a look at the <a href="https://ubiquity.mozilla.com/hg/ubiquity-firefox/raw-file/08cf861ba79a/ubiquity/modules/parser/new/ja.js">Japanese</a> or <a href="https://ubiquity.mozilla.com/hg/ubiquity-firefox/raw-file/08cf861ba79a/ubiquity/modules/parser/new/zh.js">Chinese</a>.</p>

<h4>Case marking languages</h4>

<p><strike>If you have a strongly <a href="http://en.wikipedia.org/wiki/grammatical case">case-marked</a> language, you&#8217;ll have to write some rules to identify those different cases in <code>wordBreaker()</code> and then add some extra <code>roles</code> for these case markers, but for a number of languages the current design does not allow an elegant solution for parsing such arguments. Updates to this issue will be posted to <a href="http://ubiquity.mozilla.com/trac/ticket/663">this trac ticket</a>.</p>

<p>In the mean time, however, if you could write a parser even with only the prepositions/postpositions in your language, that would be a great benefit in getting started in your language.</strike> <strong>UPDATE</strong>: a proposal on how to deal with strongly case-marked languages has been written here: <a href="http://mitcho.com/blog/projects/in-case-of-case/">In Case of Case&#8230;</a>.</p>

<h4>Stripping articles</h4>

<p>Some languages have some delimiters which combine with articles. For example, in French, the preposition &#8220;à&#8221; combines with the masculine definite article &#8220;le&#8221; but not &#8220;la&#8221;:</p>

<ol>
<li>à + la = à la</li>
<li>à + le = au</li>
</ol>

<p>You can add both &#8220;à&#8221; and &#8220;au&#8221; as delimiters of the <code>goal</code> role, but then you will get feminine arguments back with the determiner (e.g. &#8220;la table&#8221;) while masculine arguments would be parsed without a determiner (e.g. &#8220;chat&#8221;).</p>

<ol>
<li>&#8220;<b>à</b> la table&#8221; = &#8220;<b>to</b> the table&#8221;</li>
<li>&#8220;<b>au</b> chat&#8221; = &#8220;<b>to the</b> cat&#8221;</li>
</ol>

<p><strike>One possible solution to this is to write a custom <code>cleanArgument()</code> method. After arguments have been parsed and placed in their appropriate roles, each argument text (say, &#8220;la table&#8221; or &#8220;chat&#8221;) are passed to <code>cleanArgument()</code>. You can simply write a <code>cleanArgument()</code> to strip off any &#8220;la &#8221; at the beginning of the input and return it and both example inputs will get normalized arguments: &#8220;table&#8221; and &#8220;chat&#8221;, respectively.</strike> <strong>UPDATE</strong>: For more up-to-date information on how to deal with these types of articles, please see <a href="http://mitcho.com/blog/projects/solving-a-romantic-problem/">Solving a Romance Problem</a>.</p>

<h3>Test your parser</h3>

<p>Now you can go into <code>about:config</code> and change <code>extensions.ubiquity.language</code> to be your language code and restart. All the verbs and nountypes at this point will remain the same as in the English version, but it should obey the argument structure (the word order and delimiters) of your language.<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup> If you run into any trouble, feel free to ask for help on the <a href="http://groups.google.com/group/ubiquity-i18n">Ubiquity i18n listhost</a> or find me on the Ubiquity IRC channel (mitcho @ irc.mozilla.org#ubiquity). Of course, once you&#8217;re at a good stopping point, please <a href="http://ubiquity.mozilla.com/trac/ticket/662">contribute your language file to Ubiquity</a>!</p>

<h3>More to come&#8230;</h3>

<p>At this point, you&#8217;ve only localized the <a href="http://en.wikipedia.org/wiki/argument structure">argument structure</a> of your language&#8230; additional work will be required to localize the nountypes and verb names, which is <a href="http://groups.google.com/group/ubiquity-i18n/browse_thread/thread/ab4d876b1ea02d4">the subject of ongoing discussion</a>&#8230; <a href="http://groups.google.com/group/ubiquity-i18n">join the Google Group</a> to get in on the discussion!</p>

<div class="footnotes">
<hr />
<ol>

<li id="fn:1">
<p>At this point in time it&#8217;s also possible to test your parser at <code>chrome://parser-demo/content/index.html</code> if you make a couple other changes to your code&#8230; for more information, watch the <a href="http://mitcho.com/blog/projects/foxkeh-demos-ubiquity-parser-the-next-generation/">Foxkeh demos Ubiquity Parser TNG</a> video. This option gives you more debug info as well.&#160;<a href="#fnref:1" rev="footnote">&#8617;</a></p>
</li>

</ol>
</div>
<p>Related posts:</p><ol>
<li><a href='http://mitcho.com/blog/projects/ubiquity-parser-the-next-generation-demo/' rel='bookmark' title='Ubiquity Parser: The Next Generation Demo'>Ubiquity Parser: The Next Generation Demo</a></li>
<li><a href='http://mitcho.com/blog/projects/rolling-out-the-roles/' rel='bookmark' title='Rolling out the Roles'>Rolling out the Roles</a></li>
<li><a href='http://mitcho.com/blog/projects/foxkeh-demos-ubiquity-parser-the-next-generation/' rel='bookmark' title='Foxkeh demos Ubiquity Parser: The Next Generation'>Foxkeh demos Ubiquity Parser: The Next Generation</a></li>
</ol>
<p>Related posts brought to you by <a href='http://yarpp.org'>Yet Another Related Posts Plugin</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://mitcho.com/blog/how-to/adding-your-language-to-ubiquity-parser-2/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>External orders in WordPress queries</title>
		<link>http://mitcho.com/blog/how-to/external-orders-in-wordpress-queries/</link>
		<comments>http://mitcho.com/blog/how-to/external-orders-in-wordpress-queries/#comments</comments>
		<pubDate>Sat, 29 Nov 2008 15:34:40 +0000</pubDate>
		<dc:creator>mitcho</dc:creator>
				<category><![CDATA[how to]]></category>
		<category><![CDATA[algorithm]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[filter]]></category>
		<category><![CDATA[hook]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[order]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[query_posts]]></category>
		<category><![CDATA[ranking]]></category>
		<category><![CDATA[suggestions]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[WordPress Planet]]></category>
		<category><![CDATA[WP_Query]]></category>
		<category><![CDATA[YARPP]]></category>

		<guid isPermaLink="false">http://mitcho.com/blog/?p=1102</guid>
		<description><![CDATA[The advanced WordPress user is intimately familiar with query_posts, the function which controls which posts are displayed in &#8220;The Loop.&#8221; query_posts gives plugin and theme writers the ability to display only posts written in Janary (query_posts("monthnum=1")) or disallow posts from a certain category (query_posts("cat=-529")1). One of the parameters you can set here is orderby which [...]
Related posts:<ol>
<li><a href='http://mitcho.com/blog/projects/yet-another-related-posts-plugin-20/' rel='bookmark' title='Yet Another Related Posts Plugin 2.0'>Yet Another Related Posts Plugin 2.0</a></li>
<li><a href='http://mitcho.com/blog/projects/markdown-for-wordpress-and-bbpress/' rel='bookmark' title='Markdown for WordPress and bbPress'>Markdown for WordPress and bbPress</a></li>
<li><a href='http://mitcho.com/blog/projects/yet-another-related-posts-plugin/' rel='bookmark' title='Yet Another Related Posts Plugin'>Yet Another Related Posts Plugin</a></li>
</ol>

Related posts brought to you by <a href='http://yarpp.org'>Yet Another Related Posts Plugin</a>.]]></description>
			<content:encoded><![CDATA[<p>The advanced WordPress user is intimately familiar with <a href="http://codex.wordpress.org/Template_Tags/query_posts"><code>query_posts</code></a>, the function which controls which posts are displayed in &#8220;The Loop.&#8221; <code>query_posts</code> gives plugin and theme writers the ability to display only posts written in Janary (<code>query_posts("monthnum=1")</code>) or disallow posts from a certain category (<code>query_posts("cat=-529")</code><sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup>). One of the parameters you can set here is <code>orderby</code> which affects the ordering of the posts returned, with allowed values such as <code>author</code>, <code>date</code>, or <code>title</code>. But what if you want to order your posts in some other order, defined outside of your <code>wp_posts</code> table? Here I&#8217;m going to lay out some thoughts on rolling your own external ordering source for WordPress queries.</p>

<p>In order to introduce an external ordering source, we need to do four things:
1. create the external ordering source,
2. hook up (read &#8220;<code>join</code>&#8221;) the external ordering source
3. make sure we use that order, and
4. make it play nice. ^^</p>

<p>By the way, I&#8217;m going to assume you, dear reader, are PHP-savvy, proficient in MySQL, and already know a little about WordPress. This how-to is not for the PHPhobic.</p>

<p><span id="more-1102"></span></p>

<h3>The ordering source</h3>

<p>For this example, suppose we want to display posts by order of &#8220;interestingness.&#8221; We&#8217;ll just create a table called <code>wp_interestingness</code> with two columns, <code>ID</code> and <code>interestingness</code> and populate it with some data. We&#8217;ll even be nice to our database by making sure the <code>ID</code> is the primary key. Easy.</p>

<h3>Hook up the external ordering source</h3>

<p>When you run a query through <code>query_posts()</code> (or use <code>WP_Query</code>&#8217;s <code>query</code> method<sup id="fnref:2"><a href="#fn:2" rel="footnote">2</a></sup>), what it&#8217;s doing is taking your special request and translating it into a MySQL statement. This means a query like <code>"monthnum=1"</code> is turned into <code>SELECT ... wp_posts.* FROM wp_posts WHERE 1=1 AND MONTH(wp_posts.post_date)='1' ...</code>. Every different query introduces something new to the basic <code>SELECT</code> command—in this case, the <code>AND MONTH(wp_posts.post_date)='1'</code>.</p>

<p>We first want to introduce the <code>interestingness</code> for each post and that means <code>join</code>ing the new table into the query. We&#8217;ll do this using the <code>posts_join</code> <a href="http://codex.wordpress.org/Plugin_API/Filter_Reference">filter</a>. This filter lets you add a <code>join</code> statement to the MySQL request.</p>


<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
</pre></td><td class="code"><pre class="php" style="font-family:monospace;">add_filter<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'posts_join'</span><span style="color: #339933;">,</span><span style="color: #0000ff;">'my_join_filter'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">function</span> my_join_filter<span style="color: #009900;">&#40;</span><span style="color: #000088;">$arg</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #000088;">$arg</span> <span style="color: #339933;">.=</span> <span style="color: #0000ff;">&quot; natural join wp_interestingness &quot;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">return</span> <span style="color: #000088;">$arg</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></td></tr></table></div>


<p>Note that here we&#8217;re using <code>natural join</code> as <code>wp_posts</code> and <code>wp_interestingness</code> have only one key in common, <code>ID</code>, and that&#8217;s exactly the column we want to join them on.</p>

<h3>Use the new order</h3>

<p>Now that we&#8217;ve <code>join</code>ed <code>wp_interestingness</code> in, we can refer to <code>wp_interestingness.interestingness</code> in our query. Note now that, by default, an <code>$wpdb-&gt;posts.post_date</code> will be used to order the posts. We&#8217;ll use another filter here; this time <code>posts_orderby</code>, to patch this part of the query. We&#8217;ll search for the default <code>ORDER BY</code> value and replace it with our own <code>interestingness</code>.</p>


<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
</pre></td><td class="code"><pre class="php" style="font-family:monospace;">add_filter<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'posts_orderby'</span><span style="color: #339933;">,</span><span style="color: #0000ff;">'my_orderby_filter'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">function</span> my_orderby_filter<span style="color: #009900;">&#40;</span><span style="color: #000088;">$arg</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #000000; font-weight: bold;">global</span> <span style="color: #000088;">$wpdb</span><span style="color: #339933;">;</span>
	<span style="color: #000088;">$arg</span> <span style="color: #339933;">=</span> <span style="color: #990000;">str_replace</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;<span style="color: #006699; font-weight: bold;">$wpdb-&gt;posts</span>.post_date&quot;</span><span style="color: #339933;">,</span><span style="color: #0000ff;">&quot;wp_interestingness.interestingness&quot;</span><span style="color: #339933;">,</span><span style="color: #000088;">$arg</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">return</span> <span style="color: #000088;">$arg</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></td></tr></table></div>


<p>By the way, you can now check the resulting MySQL query by <code>echo</code>ing <code>$wp_query-&gt;request</code>. (If you&#8217;re using the <code>WP_Query</code> method I advocated below in footnote (2), you&#8217;ll of course have to change <code>$wp_query</code> to the <code>WP_Query</code> object you&#8217;re using.)</p>

<h3>Learn to play nice ^^</h3>

<p>The instructions above do indeed work, but they also cause some major breakdowns in other functions of your blog. Why? That&#8217;s because the current code will edit your queries for every instance of The Loop: your index page, your archives, and your RSS feeds. You probably only want to search by interestingness in certain situations. What we need is a way to tell our (admittedly stupid) <code>my_join_filter</code> and <code>my_orderby_filter</code> when they should apply their <code>interestingness</code> magic and when they shouldn&#8217;t. There are several ways to set up such a system but here I&#8217;ll lay out one that I feel is particularly elegant. We&#8217;ll set it up so you can actually use <code>query_posts("orderby=interestingness")</code> and it&#8217;ll know what you&#8217;re talking about.</p>

<p>One of the first things that happens in <code>query_posts</code>—indeed, way before even the <code>posts_join</code> and <code>posts_orderby</code> filters—is an action hook called <code>parse_query</code>. This lets us look at the initial state of the <code>WP_Query</code> object as it starts to run. In particular, we can look at the <code>orderby</code> query variable and see if we want to order by <code>interestingness</code>. If we do, we&#8217;ll set a global variable called <code>$use_interestingness_flag</code> to be <code>true</code>.</p>


<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
</pre></td><td class="code"><pre class="php" style="font-family:monospace;">add_action<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'parse_query'</span><span style="color: #339933;">,</span><span style="color: #0000ff;">'set_use_interestingness_flag'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">function</span> set_use_interestingness_flag<span style="color: #009900;">&#40;</span><span style="color: #000088;">$query</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #000000; font-weight: bold;">global</span> <span style="color: #000088;">$use_interestingness_flag</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$query</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">query_vars</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'orderby'</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">==</span> <span style="color: #0000ff;">'interestingness'</span><span style="color: #009900;">&#41;</span>
		<span style="color: #000088;">$yarpp_score_override</span> <span style="color: #339933;">=</span> <span style="color: #009900; font-weight: bold;">true</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">else</span>
		<span style="color: #000088;">$yarpp_score_override</span> <span style="color: #339933;">=</span> <span style="color: #009900; font-weight: bold;">false</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></td></tr></table></div>


<p>Now we just have to edit our filters so they only run when <code>$use_interestingness_flag == true</code>. We also will make sure to turn the flag back off in <code>my_orderby_filter</code>, as it&#8217;s our last filter to run during each query. It&#8217;s just like putting the seat back down after using a unisex bathroom.<sup id="fnref:3"><a href="#fn:3" rel="footnote">3</a></sup></p>


<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="code"><pre class="php" style="font-family:monospace;">add_filter<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'posts_join'</span><span style="color: #339933;">,</span><span style="color: #0000ff;">'my_join_filter'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">function</span> my_join_filter<span style="color: #009900;">&#40;</span><span style="color: #000088;">$arg</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #000000; font-weight: bold;">global</span> <span style="color: #000088;">$use_interestingness_flag</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$use_interestingness_flag</span><span style="color: #009900;">&#41;</span>
		<span style="color: #000088;">$arg</span> <span style="color: #339933;">.=</span> <span style="color: #0000ff;">&quot; natural join wp_interestingness &quot;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">return</span> <span style="color: #000088;">$arg</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
add_filter<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'posts_orderby'</span><span style="color: #339933;">,</span><span style="color: #0000ff;">'my_orderby_filter'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">function</span> my_orderby_filter<span style="color: #009900;">&#40;</span><span style="color: #000088;">$arg</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #000000; font-weight: bold;">global</span> <span style="color: #000088;">$wpdb</span><span style="color: #339933;">,</span> <span style="color: #000088;">$use_interestingness_flag</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$use_interestingness_flag</span><span style="color: #009900;">&#41;</span>
		<span style="color: #000088;">$arg</span> <span style="color: #339933;">=</span> <span style="color: #990000;">str_replace</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;<span style="color: #006699; font-weight: bold;">$wpdb-&gt;posts</span>.post_date&quot;</span><span style="color: #339933;">,</span><span style="color: #0000ff;">&quot;wp_interestingness.interestingness&quot;</span><span style="color: #339933;">,</span><span style="color: #000088;">$arg</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #000088;">$use_interestingness_flag</span> <span style="color: #339933;">=</span> <span style="color: #009900; font-weight: bold;">false</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">return</span> <span style="color: #000088;">$arg</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></td></tr></table></div>


<p>This method has a great advantage as you can just set it up once and invoke it whenever you want, even together with other parameters, without any additional code. For example, you can try <code>query_posts("monthnum=1&amp;orderby=interestingness")</code> or <code>query_posts("cat=-529&amp;orderby=interestingness")</code>.</p>

<h3>Conclusion</h3>

<p>Adding an external ordering source to your WordPress post queries can be relatively straightforward if you understand what <code>query_posts</code> does and take advantage of its <a href="http://codex.wordpress.org/Plugin_API">hooks</a>. This tutorial can also serve as the basis for many other patches to <code>WP_Query</code>, not just the <code>orderby</code> parameter. To better understand the way WordPress builds its MySQL queries and the many <code>posts_*</code> filters which you can take advantage of, go to the source: <code>wp-includes/query.php</code>. Finally, you can use the special <code>parse_query</code> hook and global variables as flags to only apply the filters when necessary.</p>

<div class="footnotes">
<hr />
<ol>

<li id="fn:1">
<p>This, incidentally, is precisely what I do to hide, by default, <a href="http://twitter.com/mitchoyoshitaka/">my tweets</a> in my <code>index.php</code> and <code>archives.php</code>.&#160;<a href="#fnref:1" rev="footnote">&#8617;</a></p>
</li>

<li id="fn:2">
<p>If you&#8217;re going to get serious about rolling your WordPress queries I highly recommend you follow <a href="http://weblogtoolscollection.com/archives/2008/04/13/define-your-own-wordpress-loop-using-wp_query/">Mark Ghosh&#8217;s advice</a> on initializing another object of the <code>WP_Query</code> class and using the <code>query</code> method, rather than just using the global <code>query_posts</code> function.&#160;<a href="#fnref:2" rev="footnote">&#8617;</a></p>
</li>

<li id="fn:3">
<p>The perceptive reader will note that we are still searching for the string <code>"$wpdb-&gt;posts.post_date"</code> in <code>my_orderby_filter</code>, instead of something like <code>"$wpdb-&gt;posts.interestingness"</code>. That&#8217;s because the <code>orderby</code> value of <code>interestingness</code> is not one of the allowed <code>orderby</code> values (search for <code>$allowed_keys</code> in <code>wp-includes/query.php</code> to see the list). Thus the MySQL <code>ORDER BY</code> value is set to the default of <code>"$wpdb-&gt;posts.post_date"</code> before it gets to the <code>posts_orderby</code> filter. Now you know.&#160;<a href="#fnref:3" rev="footnote">&#8617;</a></p>
</li>

</ol>
</div>
<p>Related posts:</p><ol>
<li><a href='http://mitcho.com/blog/projects/yet-another-related-posts-plugin-20/' rel='bookmark' title='Yet Another Related Posts Plugin 2.0'>Yet Another Related Posts Plugin 2.0</a></li>
<li><a href='http://mitcho.com/blog/projects/markdown-for-wordpress-and-bbpress/' rel='bookmark' title='Markdown for WordPress and bbPress'>Markdown for WordPress and bbPress</a></li>
<li><a href='http://mitcho.com/blog/projects/yet-another-related-posts-plugin/' rel='bookmark' title='Yet Another Related Posts Plugin'>Yet Another Related Posts Plugin</a></li>
</ol>
<p>Related posts brought to you by <a href='http://yarpp.org'>Yet Another Related Posts Plugin</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://mitcho.com/blog/how-to/external-orders-in-wordpress-queries/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Keep up with Yet Another Related Posts Plugin with RSS!</title>
		<link>http://mitcho.com/blog/projects/keep-up-with-yet-another-related-posts-plugin-with-rss/</link>
		<comments>http://mitcho.com/blog/projects/keep-up-with-yet-another-related-posts-plugin-with-rss/#comments</comments>
		<pubDate>Sat, 04 Oct 2008 16:43:34 +0000</pubDate>
		<dc:creator>mitcho</dc:creator>
				<category><![CDATA[how to]]></category>
		<category><![CDATA[projects]]></category>
		<category><![CDATA[automation]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[feed]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[RSS]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[YARPP]]></category>

		<guid isPermaLink="false">http://mitcho.com/blog/?p=789</guid>
		<description><![CDATA[As more and more people have been using my Yet Another Related Posts Plugin for WordPress, I thought it would be nice to have an RSS feed for users to stay on top of the latest releases. Yet Another Related Posts Plugin version log RSS 2.0 Clicking on a version&#8217;s permalink will let you download [...]
Related posts:<ol>
<li><a href='http://mitcho.com/blog/projects/yet-another-related-posts-plugin-20/' rel='bookmark' title='Yet Another Related Posts Plugin 2.0'>Yet Another Related Posts Plugin 2.0</a></li>
<li><a href='http://mitcho.com/blog/projects/yet-another-related-posts-plugin/' rel='bookmark' title='Yet Another Related Posts Plugin'>Yet Another Related Posts Plugin</a></li>
<li><a href='http://mitcho.com/blog/projects/modifiying-wordpress-plugin-activation-behavior/' rel='bookmark' title='Modifiying WordPress plugin activation behavior'>Modifiying WordPress plugin activation behavior</a></li>
</ol>

Related posts brought to you by <a href='http://yarpp.org'>Yet Another Related Posts Plugin</a>.]]></description>
			<content:encoded><![CDATA[<p>As <a href="http://wordpress.org/extend/plugins/yet-another-related-posts-plugin/stats/">more and more people</a> have been using my <a href="/code/yarpp">Yet Another Related Posts Plugin</a> for <a href="http://en.wikipedia.org/wiki/WordPress">WordPress</a>, I thought it would be nice to have an RSS feed for users to stay on top of the latest releases.</p>

<div class="files">
<div class="file rss">
<a href="http://mitcho.com/code/yarpp/yarpp.rss">Yet Another Related Posts Plugin version log</a><br />
<span class="specs">RSS 2.0</span>
</div>
</div>

<p>Clicking on a version&#8217;s permalink will let you download the plugin. Subscribe now and be the first to find out when the upcoming version 2.1 is released!</p>

<p>I decided to semi-automate this RSS-producing process as well. As a plugin developer using <a href="http://wordpress.org/extend">wordpress.org</a>&#8217;s plugin hosting, I sync a local copy of the plugin to their server using <a href="http://en.wikipedia.org/wiki/SVN">SVN</a>. I wrote a <a href="http://en.wikipedia.org/wiki/PHP">PHP</a> script to get the modification date information directly from the local files, parse the version log in the read me, and produce the RSS feed. If there&#8217;s an interest, perhaps I&#8217;ll release this code in the future.</p>
<p>Related posts:</p><ol>
<li><a href='http://mitcho.com/blog/projects/yet-another-related-posts-plugin-20/' rel='bookmark' title='Yet Another Related Posts Plugin 2.0'>Yet Another Related Posts Plugin 2.0</a></li>
<li><a href='http://mitcho.com/blog/projects/yet-another-related-posts-plugin/' rel='bookmark' title='Yet Another Related Posts Plugin'>Yet Another Related Posts Plugin</a></li>
<li><a href='http://mitcho.com/blog/projects/modifiying-wordpress-plugin-activation-behavior/' rel='bookmark' title='Modifiying WordPress plugin activation behavior'>Modifiying WordPress plugin activation behavior</a></li>
</ol>
<p>Related posts brought to you by <a href='http://yarpp.org'>Yet Another Related Posts Plugin</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://mitcho.com/blog/projects/keep-up-with-yet-another-related-posts-plugin-with-rss/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Doing your Taiwan Fulbright taxes</title>
		<link>http://mitcho.com/blog/how-to/doing-your-taiwan-fulbright-taxes/</link>
		<comments>http://mitcho.com/blog/how-to/doing-your-taiwan-fulbright-taxes/#comments</comments>
		<pubDate>Mon, 14 Jul 2008 10:11:52 +0000</pubDate>
		<dc:creator>mitcho</dc:creator>
				<category><![CDATA[how to]]></category>
		<category><![CDATA[abroad]]></category>
		<category><![CDATA[Fulbright]]></category>
		<category><![CDATA[IRS]]></category>
		<category><![CDATA[Taiwan]]></category>
		<category><![CDATA[taxes]]></category>

		<guid isPermaLink="false">http://mitcho.com/blog/?p=525</guid>
		<description><![CDATA[So you received a Fulbright fellowship to teach English in Taiwan. Congratulations! And they&#8217;re even going to pay you! Great! But if they&#8217;re paying you, you&#8217;ll have to pay taxes&#8230; so here&#8217;s my guide to doing your Taiwan Fulbright English Teaching Assistant taxes. Note: Many of the considerations here are specific to Fulbright English Teaching [...]
Related posts:<ol>
<li><a href='http://mitcho.com/blog/observation/obama-for-taiwan-2008/' rel='bookmark' title='Obama for Taiwan 2008'>Obama for Taiwan 2008</a></li>
<li><a href='http://mitcho.com/blog/how-to/survival-tips-for-visiting-taiwan/' rel='bookmark' title='Survival Tips for Visiting Taiwan'>Survival Tips for Visiting Taiwan</a></li>
<li><a href='http://mitcho.com/blog/life/family-in-taiwan/' rel='bookmark' title='Family in Taiwan'>Family in Taiwan</a></li>
</ol>

Related posts brought to you by <a href='http://yarpp.org'>Yet Another Related Posts Plugin</a>.]]></description>
			<content:encoded><![CDATA[<p>So you received a <a href="http://us.fulbrightonline.org/thinking_teaching.html">Fulbright fellowship to teach English</a> in <a href="http://us.fulbrightonline.org/program_country.php?id=103">Taiwan</a>. Congratulations! And they&#8217;re even going to pay you! Great! But if they&#8217;re paying you, you&#8217;ll have to pay taxes&#8230; so here&#8217;s my guide to doing your Taiwan Fulbright English Teaching Assistant taxes.</p>

<p>Note: Many of the considerations here are specific to Fulbright English Teaching Assistants (ETA&#8217;s) in Taiwan. If this exact grant doesn&#8217;t apply to you, you may be better off simply taking a look at <a href="http://www.irs.gov/businesses/small/international/article/0,,id=149708,00.html">the IRS&#8217;s guide for Fulbrighters</a> and also the <a href="http://www.irs.gov/pub/irs-pdf/p54.pdf">Tax Guide for U.S. Citizens and Resident Aliens Abroad</a>. I am not a <a href="http://en.wikipedia.org/wiki/Certified Public Accountant">Certified Public Accountant</a>, tax advisor, nor <a href="http://en.wikipedia.org/wiki/Enrolled Agent">Enrolled Agent</a><sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup> so your mileage may vary. Consider yourself warned.</p>

<p><span id="more-525"></span></p>

<h3>Leaving the States</h3>

<p>First, make sure that any employers you&#8217;ve had so far this year know some permanent address of yours (your parents&#8217;, say) and will send your income statements to that address come next January. If they only have some temporary address of yours, try to see if you can change that record with your employers before you leave. Worst case, you won&#8217;t get one in the mail, and you&#8217;ll call them up when you start your taxes and have them send it to you then.</p>

<h3>Your Taiwanese income</h3>

<p>In terms of actually doing your taxes: there are a few things to note, most of which they will tell you during orientation or afterwards:</p>

<ol>
<li>Your Fulbright payment will not exist in Taiwan for tax purposes—i.e., you do not pay Taiwan taxes—but they will count toward US taxes.</li>
<li>The ETA-ship payments, according to <a href="http://www.irs.gov/businesses/small/international/article/0,,id=149708,00.html">the IRS&#8217;s own guidelines</a> count as wages, not as scholarships, for tax purposes as, after all, they&#8217;re paying you to teach.</li>
<li>You will not, however, receive a W-2 form for your &#8220;wages&#8221;—instead you&#8217;ll get a nice letter from the Foundation (with your income in US$) which you can use as proof of those wages.</li>
</ol>

<p>All of the above are pretty straightforward. The bottom line is, you pay federal taxes just as if the job were in the US. The only major difference is that approximate payments will not be withheld from each paycheck like it would be in the US.</p>

<h3>Filing your federal taxes</h3>

<p>The fact that you&#8217;re <em>filing</em> from abroad, however, adds a few other issues:</p>

<ol>
<li>Whenever you file from abroad, you get an <a href="http://www.irs.gov/businesses/small/international/article/0,,id=96768,00.html"><em>automatic</em> two month extension on filing</a>. While there&#8217;s no penalty to do this and file by June 15th, if it turns out that you do owe taxes, those taxes will still have interest accruing since April 15th. So it pays to just figure it out earlier rather than later.</li>
<li>If you end up being out of the US for more than 330 days, as some ETA&#8217;s may (depending on their itineraries, or if they decide to stay in Taiwan afterwards), there&#8217;s an option to take a <a href="http://www.irs.gov/pub/irs-pdf/f2555.pdf">Foreign Earned Income Exclusion (IRS Form 2555)</a>, which lowers your tax liability in the US (and thus, lowers your taxes). It&#8217;s a bit complicated, however, so even if this option applies to you, if you&#8217;re like me and enjoy doing your taxes, you can calculate your taxes both ways: with and without Form 2555. My personal experience was that, as I was already filing for certain <a href="http://www.irs.gov/pub/irs-pdf/p970.pdf">educational credits</a> (Lifetime Learning), it didn&#8217;t matter whether I used it or not, so I didn&#8217;t.</li>
</ol>

<h3>State and local taxes</h3>

<p>Finally, there&#8217;s the issue of state and local taxes. The bottom line is that you pay state and local taxes only for wages that were made in those states. The way this is done in most states is by filing a non-resident/partial-year-resident form which makes you calculate your total adjusted income and also your your adjusted income <em>in that municipality</em>, and charges you the appropriate tax based on your income <em>in that place.</em> As your Taiwan wages weren&#8217;t in any US state, they won&#8217;t count toward any state or local taxes.</p>

<h3>Help is just a phone call away</h3>

<p>If you get confused along the way, there&#8217;s always the option of calling the IRS. The IRS has a 1-800 number for individual taxpayers (1-800-829-1040), and 1-800 calls are free from <a href="http://www.skype.com">Skype</a>.</p>

<div class="footnotes">
<hr />
<ol>

<li id="fn:1">
<p>Highest (coolness of title)/(actual coolness of the job) ratio ever.&#160;<a href="#fnref:1" rev="footnote">&#8617;</a></p>
</li>

</ol>
</div>
<p>Related posts:</p><ol>
<li><a href='http://mitcho.com/blog/observation/obama-for-taiwan-2008/' rel='bookmark' title='Obama for Taiwan 2008'>Obama for Taiwan 2008</a></li>
<li><a href='http://mitcho.com/blog/how-to/survival-tips-for-visiting-taiwan/' rel='bookmark' title='Survival Tips for Visiting Taiwan'>Survival Tips for Visiting Taiwan</a></li>
<li><a href='http://mitcho.com/blog/life/family-in-taiwan/' rel='bookmark' title='Family in Taiwan'>Family in Taiwan</a></li>
</ol>
<p>Related posts brought to you by <a href='http://yarpp.org'>Yet Another Related Posts Plugin</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://mitcho.com/blog/how-to/doing-your-taiwan-fulbright-taxes/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>My new scale</title>
		<link>http://mitcho.com/blog/how-to/my-new-scale/</link>
		<comments>http://mitcho.com/blog/how-to/my-new-scale/#comments</comments>
		<pubDate>Tue, 24 Jun 2008 13:17:40 +0000</pubDate>
		<dc:creator>mitcho</dc:creator>
				<category><![CDATA[how to]]></category>
		<category><![CDATA[life]]></category>
		<category><![CDATA[airline]]></category>
		<category><![CDATA[construction]]></category>
		<category><![CDATA[luggage]]></category>
		<category><![CDATA[Taiwan]]></category>
		<category><![CDATA[that is all]]></category>

		<guid isPermaLink="false">http://mitcho.com/blog/?p=456</guid>
		<description><![CDATA[ANA has draconian baggage restrictions (checked: 20kg total, US$10/kg thereafter) and I don&#8217;t own a scale. Problem solved! Verdict: suitcase 18kg, guitar 6kg. I think I&#8217;ll make one more box to send at the post office tomorrow morning. I feel like a caveman. THAT IS ALL. No related posts. Related posts brought to you by [...]
No related posts.

Related posts brought to you by <a href='http://yarpp.org'>Yet Another Related Posts Plugin</a>.]]></description>
			<content:encoded><![CDATA[<p><a href="http://en.wikipedia.org/wiki/ANA">ANA</a> has draconian baggage restrictions (checked: 20kg total, US$10/kg thereafter) and I don&#8217;t own a scale. Problem solved!</p>

<p><zp:taiwan/scale1.jpg><zp:taiwan/scale2.jpg></p>

<p>Verdict: suitcase 18kg, guitar 6kg. I think I&#8217;ll make one more box to send at the post office tomorrow morning.</p>

<p>I feel like a caveman. THAT IS ALL.</p>
<p>No related posts.</p>
<p>Related posts brought to you by <a href='http://yarpp.org'>Yet Another Related Posts Plugin</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://mitcho.com/blog/how-to/my-new-scale/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>St. Patrick&#8217;s Day Pilaf, brought to you by Sufjan Stevens</title>
		<link>http://mitcho.com/blog/how-to/st-patricks-day-pilaf-brought-to-you-by-sufjan-stevens/</link>
		<comments>http://mitcho.com/blog/how-to/st-patricks-day-pilaf-brought-to-you-by-sufjan-stevens/#comments</comments>
		<pubDate>Sat, 15 Mar 2008 17:34:20 +0000</pubDate>
		<dc:creator>mitcho</dc:creator>
				<category><![CDATA[how to]]></category>
		<category><![CDATA[life]]></category>
		<category><![CDATA[broccoli]]></category>
		<category><![CDATA[Chicago]]></category>
		<category><![CDATA[cook]]></category>
		<category><![CDATA[cooking]]></category>
		<category><![CDATA[food]]></category>
		<category><![CDATA[green]]></category>
		<category><![CDATA[Illinois]]></category>
		<category><![CDATA[parsley]]></category>
		<category><![CDATA[pilaf]]></category>
		<category><![CDATA[recipe]]></category>
		<category><![CDATA[rice]]></category>
		<category><![CDATA[St. Patrick's Day]]></category>
		<category><![CDATA[Sufjan Stevens]]></category>

		<guid isPermaLink="false">http://mitcho.com/blog/2008/03/15/st-patricks-day-pilaf-brought-to-you-by-sufjan-stevens/</guid>
		<description><![CDATA[St. Patrick&#8217;s Day for many means a wholesale celebration of faux-Irishness through Guinness and everything green. While I&#8217;m not a fan of beer, I decided to put something green together to eat today. One of my favorite food writers Mark Bittman of the New York Times made a chicken with salsa verde but since I [...]
Related posts:<ol>
<li><a href='http://mitcho.com/blog/observation/patricks-nortons-on-tekzillaz/' rel='bookmark' title='Patricks Nortons on Tekzillaz'>Patricks Nortons on Tekzillaz</a></li>
</ol>

Related posts brought to you by <a href='http://yarpp.org'>Yet Another Related Posts Plugin</a>.]]></description>
			<content:encoded><![CDATA[<p>St. Patrick&#8217;s Day for many means a wholesale celebration of faux-Irishness through <a href="http://en.wikipedia.org/wiki/Guinness">Guinness</a> and everything green. While I&#8217;m not a fan of beer, I decided to put something green together to eat today. One of my favorite food writers <a href="http://en.wikipedia.org/wiki/Mark Bittman">Mark Bittman</a> of the New York Times made a <a href="http://www.nytimes.com/2008/03/12/dining/12mini.html">chicken with salsa verde</a> but since I can&#8217;t find half of those ingredients in this country, I made a simpler non-Irish dish: a green pilaf, based on <a href="http://query.nytimes.com/gst/fullpage.html?res=9C04E0D91731F937A15753C1A9679C8B63&amp;sec=&amp;spon=&amp;partner=permalink&amp;exprod=permalink">Bittman&#8217;s own recipe</a>. Why not try a simple green vegetarian dish for St. Patrick&#8217;s Day?</p>

<p>As an added bonus, I set this recipe to <a href="http://www.amazon.com/gp/redirect.html?ie=UTF8&#038;location=http%3A%2F%2Fwww.amazon.com%2Fgp%2Fentity%2FSufjan-Stevens%2FB000APT3D0%3Fie%3DUTF8%26%252AVersion%252A%3D1%26%252Aentries%252A%3D0&#038;tag=mitchocom-20&#038;linkCode=ur2&#038;camp=1789&#038;creative=390957">Sufjan Stevens&#8217;</a><img src="https://www.assoc-amazon.com/e/ir?t=mitchocom-20&amp;l=ur2&amp;o=1" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /> <a href="http://www.amazon.com/gp/product/B0009R1T7M?ie=UTF8&#038;tag=mitchocom-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=B0009R1T7M">Illinoise</a><img src="http://www.assoc-amazon.com/e/ir?t=mitchocom-20&#038;l=as2&#038;o=1&#038;a=B0009R1T7M" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />. I was just in a Chicago-missing mood and listening to it while cooking, and it seemed to work so well.</p>

<p><zp:taiwan/pilaf/pilaf7.jpg></p>

<p>Time: 45 minutes (mostly waiting, though)<br />
2 tablespoons extra virgin olive oil<br />
1 medium onion, chopped<br />
2 cloves garlic, chopped<br />
Salt and pepper<br />
1&#160;1/2 cups (400cc) chicken stock (I used a <a href="http://en.wikipedia.org/wiki/bouillon cube">bouillon cube</a>—雞湯塊)<br />
1 medium head of broccoli, just the flowers, in small chunks (maybe 3/4-1 cup)<br />
1 cup (250cc) short grain white rice<br />
1/2 cup (130cc) chopped parsley, optional<br /></p>

<ol>
<li>Track 1: &#8220;Concerning the UFO Sighting Near Highland, Illinois.&#8221; Put oil in a pan on medium heat. Add the garlic, onion, and a pinch of salt and stir occasionally until the onion is translucent or until you hear the piano riff on track 3, &#8220;Come On! Feel the Illinoise!&#8221; In the down time, you can make sure your chicken broth is heated up in a pot.<br />
<zp:taiwan/pilaf/pilaf5.jpg></li>
<li>Track 3: &#8220;Come On! Feel the Illinoise!&#8221; Add rice to the onion pan and stir occasionally until they get clear and start to brown, sometime during the second half of track 3, &#8220;Come On! Feel the Illinoise! Pt. II: Carl Sandburg Visits Me In a Dream.&#8221; Throw the broccolli in the broth to let cook for the last minute of &#8220;Carl Sandburg.&#8221;<br />
<zp:taiwan/pilaf/pilaf1.jpg><zp:taiwan/pilaf/pilaf2.jpg></li>
<li>Track 4: &#8220;John Wayne Gacy, Jr.&#8221; Add the stock/broccolli to the rice/onion pan. Heat to a boil and then let cook for the rest of the track. Stir occasionally. Compare yourself to a serial killer as you watch the bubbles.<br />
<zp:taiwan/pilaf/pilaf3.jpg></li>
<li>Track 5: &#8220;Jacksonville.&#8221; Cover and cook until the end of track 9, &#8220;Chicago.&#8221;<br />
<zp:taiwan/pilaf/pilaf6.jpg><zp:taiwan/pilaf/pilaf4.jpg></li>
<li>Track 10: &#8220;Casimir Pulaski Day.&#8221; Enjoy my favorite song on the CD. Turn heat off and let sit, uncover, stir gently, cover again, and let sit on the burner until track 15, &#8220;The Predatory Wasp of the Palisades Is Out to Get Us.&#8221; Optionally mix in chopped parsley for additional green. Serve.</li>
</ol>

<p>Happy St. Patrick&#8217;s Day!</p>
<p>Related posts:</p><ol>
<li><a href='http://mitcho.com/blog/observation/patricks-nortons-on-tekzillaz/' rel='bookmark' title='Patricks Nortons on Tekzillaz'>Patricks Nortons on Tekzillaz</a></li>
</ol>
<p>Related posts brought to you by <a href='http://yarpp.org'>Yet Another Related Posts Plugin</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://mitcho.com/blog/how-to/st-patricks-day-pilaf-brought-to-you-by-sufjan-stevens/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Display your Last.fm rankings using PHP 4&#8217;s XSLT support</title>
		<link>http://mitcho.com/blog/how-to/display-your-lastfm-rankings-using-php-4s-xslt-support/</link>
		<comments>http://mitcho.com/blog/how-to/display-your-lastfm-rankings-using-php-4s-xslt-support/#comments</comments>
		<pubDate>Fri, 01 Feb 2008 11:23:27 +0000</pubDate>
		<dc:creator>mitcho</dc:creator>
				<category><![CDATA[how to]]></category>
		<category><![CDATA[metablog]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[music]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[XML]]></category>
		<category><![CDATA[XSLT]]></category>

		<guid isPermaLink="false">http://mitcho.com/blog/2008/02/01/display-your-lastfm-rankings-using-php-4s-xslt-support/</guid>
		<description><![CDATA[With all the exciting recent news about Last.fm, I thought I would document a simple bit of code I added to my site the other day. Last.fm offers a number of Flash-based widgets you can add to your website. Unfortunately, this doesn&#8217;t give you much flexibility and, of course, requires Flash. But you, dear friend, [...]
Related posts:<ol>
<li><a href='http://mitcho.com/blog/how-to/updating-your-zenphoto-theme-for-zenphoto-11/' rel='bookmark' title='Updating your zenphoto theme for zenphoto 1.1'>Updating your zenphoto theme for zenphoto 1.1</a></li>
<li><a href='http://mitcho.com/blog/life/buklavu/' rel='bookmark' title='Buklavu'>Buklavu</a></li>
<li><a href='http://mitcho.com/blog/projects/yet-another-related-posts-plugin/' rel='bookmark' title='Yet Another Related Posts Plugin'>Yet Another Related Posts Plugin</a></li>
</ol>

Related posts brought to you by <a href='http://yarpp.org'>Yet Another Related Posts Plugin</a>.]]></description>
			<content:encoded><![CDATA[<p>With all the <a href="http://blog.last.fm/2008/01/23/free-the-music">exciting recent news</a> about <a href="http://www.last.fm">Last.fm</a>, I thought I would document a simple bit of code I added to my site the other day.</p>

<p>Last.fm offers a number of <a href="http://en.wikipedia.org/wiki/Flash">Flash</a>-based <a href="http://www.last.fm/widgets/">widgets</a> you can add to your website. Unfortunately, this doesn&#8217;t give you much flexibility and, of course, requires Flash. But you, dear friend, have a site written in <a href="http://en.wikipedia.org/wiki/PHP">PHP</a>, and the rankings are just XML files. There is a better way.</p>

<p>Looking around on the web, there are <a href="(http://labs.silverorange.com/archives/2007/february/workingwithxml)">some good instructions and recommendations</a> for using PHP 5&#8217;s object-oriented XML support. But, as we know, <a href="http://www.nexen.net/chiffres_cles/phpversion/17965-evolution_de_php_sur_internet_decembre_2007.php">not everyone is using PHP 5</a>. Here&#8217;s what I did on my PHP install, which includes the <a href="http://www.php.net/domxml">DOM/XML and DOM/XSLT extensions</a>.<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup></p>

<h3>Write your XSL Transformation</h3>

<p>The first step is to write your <a href="http://en.wikipedia.org/wiki/XSL Transformation">XSL Transformation</a>, or XSLT, a special XML &#8220;program&#8221; which takes an XML file and reformats it into another XML file. Remember when you learned in Algebra class what a function was? An XSLT defines a function from XML to XML. In our case, we need to take a special proprietary XML file like <a href="http://ws.audioscrobbler.com/1.0/user/mitchoyoshitaka/weeklyartistchart.xml">the this one for my weekly top artists</a> and return some solid XHTML.</p>

<p>Let&#8217;s take a look at the XSLT I used: (it helps to take a look at the <a href="http://ws.audioscrobbler.com/1.0/user/mitchoyoshitaka/weeklyartistchart.xml">original XML file</a> at the same time.)</p>

<blockquote>
  <p><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;</code><br /><code>&lt;xsl:stylesheet version="1.0"
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;</code><br /><code>&lt;xsl:template match="weeklyartistchart"&gt;</code><br /><code>&lt;ol&gt;</code><br /><code>&lt;xsl:apply-templates select="artist[position() &amp;lt; 6]"/&gt;</code><br /><code>&lt;/ol&gt;</code><br /><code>&lt;/xsl:template&gt;</code><br /><code>&lt;xsl:template match="artist"&gt;</code><br /><code>&lt;li&gt;&lt;a&gt;&lt;xsl:attribute name="href"&gt;&lt;xsl:value-of select="./url"/&gt;&lt;/xsl:attribute&gt;&lt;xsl:value-of select="./name"/&gt;&lt;/a&gt;&lt;span&gt;&lt;xsl:value-of select="./artist"/&gt;&lt;/span&gt;: &lt;xsl:value-of select="./playcount"/&gt;&lt;/li&gt;</code><br /><code>&lt;/xsl:template&gt;</code><br /><code>&lt;/xsl:stylesheet&gt;</code></p>
</blockquote>

<p>The <a href="http://www.w3.org/TR/xslt">full spec description</a> will give you all the juicy details, but you really only need a few basic details (or you can just steal my code). First, we note the <code>&lt;xsl:template match="weeklyartistchart"&gt;</code> and <code>&lt;xsl:template match="artist"&gt;</code> items. Each of these code blocks describe what to do to each <code>&lt;weeklyartistchart&gt;</code> and <code>&lt;artist&gt;</code> nodes, respectively, in the input XML. In the code above, when a <code>&lt;weeklyartistchart&gt;</code> is found, an ordered list is opened and the artist template is applied to the first five (<code>position() &amp;lt; 6</code>) <code>&lt;artist&gt;</code> nodes. In the artist template (<code>&lt;xsl:template match="artist"&gt;</code>), the script takes each <code>&lt;artist&gt;</code> and prints a list item with the name of the artist, with a link to the url given in the original <code>&lt;artist&gt;</code>&#8217;s <code>&lt;url&gt;</code> subnode.</p>

<h3>Process the XML with your XSLT</h3>

<p>Once your XSLT is written, save it to a file, like <a href="/music/last.fm.xml"><code>last.fm.xml</code></a>. Now we&#8217;ll use the DOM/XSLT extension and apply this XSLT file to the live weekly artist chart XML file from last.fm. Here&#8217;s the code I used:</p>

<blockquote>
  <p><code>$chartxml = domxml_open_file("</code><a href="http://ws.audioscrobbler.com/1.0/user/mitchoyoshitaka/weeklyartistchart.xml">&#8230;<code>/weeklyartistchart.xml</code></a><code>");</code><br /><code>$xslt = domxml_xslt_stylesheet_file("last.fm.xsl");</code><br /><code>$charthtml = $xslt-&gt;process($chartxml);</code><br /><code>echo $charthtml-&gt;dump_mem();</code><br /></p>
</blockquote>

<p>The code is pretty self explanatory—just four lines: 1. open the remote XML file using <code>domxml_open_file</code>, 2. open the stylesheet (XSL transformation), 3. apply the stylesheet to the remote XML file, and 4. <code>echo</code> the output.</p>

<p>That&#8217;s it! You can see the results <a href="/music/">here on my music page</a>.</p>

<div class="footnotes">
<hr />
<ol>

<li id="fn:1">
<p>To see if these instructions will work for you, check your <a href="http://www.php.net/phpinfo">phpinfo</a> for the lines &#8220;DOM/XML enabled&#8221; and &#8220;DOM/XSLT enabled&#8221;. If the items aren&#8217;t even showing up, you&#8217;re out of luck. <img src='http://mitcho.com/blog/wp-includes/images/smilies/icon_sad.gif' alt=':(' class='wp-smiley' />  There are, however, other <a href="http://www.php.net/xslt">comparable methods</a> to process XML and XSLT in PHP 4.<br /><img class="limages" src='http://mitcho.com/blog/wp-content/uploads/2008/01/picture-8.png' alt='dom/xml check' />&#160;<a href="#fnref:1" rev="footnote">&#8617;</a></p>
</li>

</ol>
</div>
<p>Related posts:</p><ol>
<li><a href='http://mitcho.com/blog/how-to/updating-your-zenphoto-theme-for-zenphoto-11/' rel='bookmark' title='Updating your zenphoto theme for zenphoto 1.1'>Updating your zenphoto theme for zenphoto 1.1</a></li>
<li><a href='http://mitcho.com/blog/life/buklavu/' rel='bookmark' title='Buklavu'>Buklavu</a></li>
<li><a href='http://mitcho.com/blog/projects/yet-another-related-posts-plugin/' rel='bookmark' title='Yet Another Related Posts Plugin'>Yet Another Related Posts Plugin</a></li>
</ol>
<p>Related posts brought to you by <a href='http://yarpp.org'>Yet Another Related Posts Plugin</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://mitcho.com/blog/how-to/display-your-lastfm-rankings-using-php-4s-xslt-support/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Survival Tips for Visiting Taiwan</title>
		<link>http://mitcho.com/blog/how-to/survival-tips-for-visiting-taiwan/</link>
		<comments>http://mitcho.com/blog/how-to/survival-tips-for-visiting-taiwan/#comments</comments>
		<pubDate>Fri, 16 Nov 2007 06:04:17 +0000</pubDate>
		<dc:creator>mitcho</dc:creator>
				<category><![CDATA[how to]]></category>
		<category><![CDATA[life]]></category>
		<category><![CDATA[food]]></category>
		<category><![CDATA[Taiwan]]></category>
		<category><![CDATA[tips]]></category>

		<guid isPermaLink="false">http://mitcho.com/blog/2007/11/16/survival-tips-for-visiting-taiwan/</guid>
		<description><![CDATA[As my family and Bailey are coming to visit me soon, I decided to write up some basic survival tips for visiting Taiwan. While most aspects of living in Taiwan are very similar to the US or Japan, here are some things to keep in mind: Don&#8217;t flush the toilet paper in the toilets. For [...]
Related posts:<ol>
<li><a href='http://mitcho.com/blog/life/affirmative-action-taiwan-style/' rel='bookmark' title='Affirmative action, Taiwan style'>Affirmative action, Taiwan style</a></li>
<li><a href='http://mitcho.com/blog/life/fire-drill/' rel='bookmark' title='Fire drill!'>Fire drill!</a></li>
<li><a href='http://mitcho.com/blog/life/i-am-not-a-nazi/' rel='bookmark' title='I am not a Nazi'>I am not a Nazi</a></li>
</ol>

Related posts brought to you by <a href='http://yarpp.org'>Yet Another Related Posts Plugin</a>.]]></description>
			<content:encoded><![CDATA[<p>As my family and <a href="http://baileyinchicago.livejournal.com">Bailey</a> are coming to visit me soon, I decided to write up some basic survival tips for visiting Taiwan. While most aspects of living in Taiwan are very similar to the US or Japan, here are some things to keep in mind:</p>

<ol>
<li><p><em>Don&#8217;t flush the toilet paper in the toilets</em>. For this reason, almost every bathroom here has a trash basket. Also, <em>keep some tissue paper on you</em> as most public restrooms do not provide paper.</p></li>
<li><p><em>Don&#8217;t drink the tap water</em>. I don&#8217;t know what happens if you do, as I haven&#8217;t tried, but I&#8217;m not planning on it. The good news is that most public buildings and many other establishments have drinking water machines. <em>Bring a good water bottle</em>.<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup></p></li>
<li><p>If you&#8217;re visiting northern Taiwan in the winter, <em>bring some raingear</em>. Sure, you&#8217;ll probably buy a few umbrellas (I&#8217;m on number three), but rain coats are pretty useful too. You can also be really Taiwanese by wearing your raincoat backwards.<sup id="fnref:2"><a href="#fn:2" rel="footnote">2</a></sup></p></li>
<li><p><em>Taiwan is big on recycling</em>. Luckily, though, in most places there&#8217;s just a trash can (垃圾桶, pronounced lèsètǒng as opposed to the Mainland lājītǒng) and a recycling bin&#8230; in general, most paper and plastic containers that don&#8217;t have food waste can be recycled. Plastic bags and wrapping cannot be.</p></li>
<li><p><em>Keep all your receipts</em>. Taiwan has a <em><a href="http://www.tealit.com/lotto.htm">receipt lottery</a></em> (formally the Uniform-Invoice Prize). While you can claim it as a foreign visitor with a valid visa, as the winning numbers are released about a couple months after each time period ends, you&#8217;ll probably just want to give the receipts to me. ^^<br /><a rel="lightbox" href="http://mitcho.com/photos/taiwan/image/600/IMG_9844.jpg"><img class="images " alt="IMG_9844" title="IMG_9844" src="http://mitcho.com/photos/taiwan/image/thumb/IMG_9844.jpg"  /></a></p></li>
<li><p>Oh, and <em>cold tea is sweetened</em> by default. This freaked me out when I first tried some. Just a warning.</p></li>
</ol>

<p>Of course, if you&#8217;re not planning to visit me yet but have time between late-January and mid-February (my Chinese New Year break), <a href="mailto:mitcho@mitcho.com">let me know</a>. Let&#8217;s talk.</p>

<div class="footnotes">
<hr />
<ol>

<li id="fn:1">
<p>I love my <a href="http://www.nalgene-outdoor.com/">Nalgene</a>.&#160;<a href="#fnref:1" rev="footnote">&#8617;</a></p>
</li>

<li id="fn:2">
<p>This most likely comes from everyone riding scooters.&#160;<a href="#fnref:2" rev="footnote">&#8617;</a></p>
</li>

</ol>
</div>
<p>Related posts:</p><ol>
<li><a href='http://mitcho.com/blog/life/affirmative-action-taiwan-style/' rel='bookmark' title='Affirmative action, Taiwan style'>Affirmative action, Taiwan style</a></li>
<li><a href='http://mitcho.com/blog/life/fire-drill/' rel='bookmark' title='Fire drill!'>Fire drill!</a></li>
<li><a href='http://mitcho.com/blog/life/i-am-not-a-nazi/' rel='bookmark' title='I am not a Nazi'>I am not a Nazi</a></li>
</ol>
<p>Related posts brought to you by <a href='http://yarpp.org'>Yet Another Related Posts Plugin</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://mitcho.com/blog/how-to/survival-tips-for-visiting-taiwan/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Updating your zenphoto theme for zenphoto 1.1</title>
		<link>http://mitcho.com/blog/how-to/updating-your-zenphoto-theme-for-zenphoto-11/</link>
		<comments>http://mitcho.com/blog/how-to/updating-your-zenphoto-theme-for-zenphoto-11/#comments</comments>
		<pubDate>Sun, 04 Nov 2007 13:52:55 +0000</pubDate>
		<dc:creator>mitcho</dc:creator>
				<category><![CDATA[how to]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[gallery]]></category>
		<category><![CDATA[photo]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[theme]]></category>
		<category><![CDATA[Zenphoto]]></category>

		<guid isPermaLink="false">http://mitcho.com/blog/2007/11/04/updating-your-zenphoto-theme-for-zenphoto-11/</guid>
		<description><![CDATA[I use zenphoto as the backend to my photos section with a custom theme to hook into my site&#8217;s navigation and such. I chose zenphoto for my website a year ago based on it&#8217;s main strength: simplicity. It does much less than the competition, but it does what I need it to do&#8212;for the most [...]
Related posts:<ol>
<li><a href='http://mitcho.com/blog/life/exploring-nanao-part-2/' rel='bookmark' title='Exploring Nanao, part 2: hot springs, waterfall, and beach'>Exploring Nanao, part 2: hot springs, waterfall, and beach</a></li>
<li><a href='http://mitcho.com/blog/life/exploring-nanao-part-1/' rel='bookmark' title='Exploring Nanao, part 1'>Exploring Nanao, part 1</a></li>
<li><a href='http://mitcho.com/blog/life/field-trip-guang-xing-farm/' rel='bookmark' title='Field trip: Guang-xing Farm'>Field trip: Guang-xing Farm</a></li>
</ol>

Related posts brought to you by <a href='http://yarpp.org'>Yet Another Related Posts Plugin</a>.]]></description>
			<content:encoded><![CDATA[<p>I use <a href="http://www.zenphoto.org/">zenphoto</a> as the backend to my <a href="/photos">photos</a> section with a custom theme to hook into my site&#8217;s navigation and such. I chose zenphoto for my website a year ago based on it&#8217;s main strength: simplicity. It does much less than the competition, but it does what I need it to do&#8212;for the most part. It&#8217;s a fantastic bare-bones mysql/php photo gallery option.</p>

<p>Since then, though, I (along with many others) have been slightly disappointed by the lack of development in the promising project, without having the time or energy to pitch in myself. Such is life. But now the wait is over: <a href="http://www.trisweb.com/archives/2007/11/02/zenphoto-release-11">Zenphoto 1.1 is out</a>.</p>

<p>Zenphoto 1.1, I believe, does a good job balancing this tradition of simplicity with some popular new features. Highlights include (there are many) <em>tagging</em>, <em>subalbums</em>, <em>chronological archives</em>, <em>RSS feeds</em>, <em>EXIF</em> support, <em>Google Maps</em>, <em>search</em>, and preliminary <em>video</em> support. Exciting stuff.</p>

<p>As I maintain my own theme, though, some of these new features of course require me to update my theme. Below is my rough guide to editing your theme to take maximum advantage of zenphoto 1.1.</p>

<p><span id="more-38"></span></p>

<h3>First things first</h3>

<p>Between the core components moving from <code>/zen</code> to <code>/zp-core</code> and the addition of subalbums, your old <code>.htaccess</code> will no longer be valid. Make sure to copy over the new <code>.htaccess</code> (in the zenphoto 1.1 zip file, though probably invisible in your filesystem) with necessary updates for your setup, or else none of your images will show up. Ha!</p>

<h3>Search and Archives</h3>

<p>The first thing you&#8217;ll notice if you check out the default theme is the search and archive pages&#8230; the easy way to implement these, I think, is to copy over the <code>search.php</code> and <code>archives.php</code> pages and then make the necessary organizational changes. This is of course easier said than done, but everyone&#8217;s custom theme exists for different reasons.</p>

<p>To implement search, you first have to add the search box to the index page:</p>

<pre><code>if (getOption('Allow_search')) {  printSearchForm(); }
</code></pre>

<p>The default theme has this right above the gallery title, but I put it at the bottom of my main <code>div</code>.</p>

<h3>Subalbums</h3>

<p>Subalbums are an exciting new feature for organization buffs, but it does require some quick changes. First of all, the subalbum-path breadcrumb must be added to <code>album.php</code> and <code>image.php</code>, right before the current album/image name. Use the three parameters to change the delimiters.</p>

<pre><code>printParentBreadcrumb($before,$between,$after);
</code></pre>

<p>By default, subalbums are listed at the top of an album&#8217;s page, so we have a new <code>while(next_album())</code> loop there. The default theme&#8217;s additional loop<sup id="fnref:2"><a href="#fn:2" rel="footnote">1</a></sup> is:</p>

<pre><code>&lt;?php while (next_album()): ?&gt;
    &lt;a href="&lt;?php echo getAlbumLinkURL();?&gt;"&gt;
    &lt;?php printAlbumThumbImage(getAlbumTitle()); ?&gt;&lt;/a&gt;
    &lt;a href="&lt;?php echo getAlbumLinkURL();?&gt;"&gt;
    &lt;?php printAlbumTitle(); ?&gt;&lt;/a&gt;
    &lt;?php printAlbumDate(""); ?&gt;
    &lt;?php printAlbumDesc(); ?&gt;
&lt;?php endwhile; ?&gt;
</code></pre>

<h3>Tag Support</h3>

<p>Simply add this line to the bottom of <code>album.php</code> and <code>image.php</code>:</p>

<pre><code>printTags(true, 'Tags: ');
</code></pre>

<h3>RSS Support</h3>

<p>To enable RSS support, simply put the <code>printRSSHeaderLink()</code> in your headers. I used these three lines:</p>

<pre><code>printRSSHeaderLink('Gallery','RSS title');
printRSSHeaderLink('Album','RSS title');
printRSSHeaderLink('Image','RSS title');
</code></pre>

<p>Now your RSS feeds are added as links in your header, and will be recognized automagically by a smart browser. You can also add an explicit link:</p>

<pre><code>printRSSLink('Gallery','','RSS', ' | ');
</code></pre>

<h3>Maps and EXIF</h3>

<p>While I haven&#8217;t implemented these myself in my theme,<sup id="fnref:1"><a href="#fn:1" rel="footnote">2</a></sup> these look fairly simple to add to a theme. The key PHP code to include are:</p>

<pre><code>  if (getImageEXIFData()) {
    printImageMetadata('', false); 
  } 
</code></pre>

<p>and</p>

<pre><code>printImageMap();
</code></pre>

<h3>There&#8217;s more</h3>

<p>There&#8217;s of course more advanced stuff that might be of interest to other theme designers. The place to start is probably <code>normalizeColumns()</code>.</p>

<p>Feel free to check out my final product, the <a href="/photos">updated photos section</a>.</p>

<div class="footnotes">
<hr />
<ol>

<li id="fn:2">
<p>listed here as just the php logic, without the organizational html&#160;<a href="#fnref:2" rev="footnote">&#8617;</a></p>
</li>

<li id="fn:1">
<p>I use <a href="http://www.digitalia.be/software/slimbox">slimbox</a>, the <a href="http://mootools.net/">mootools</a>-based <a href="http://www.huddletogether.com/projects/lightbox2/">lightbox</a> clone and, thus, never use the image page or comments. What I have done, however, is just put some of that EXIF data in the title tag to be displayed by slimbox by manipulating the value of <code>getImageEXIFData()</code> in the <code>while(next_image())</code> loop.&#160;<a href="#fnref:1" rev="footnote">&#8617;</a></p>
</li>

</ol>
</div>
<p>Related posts:</p><ol>
<li><a href='http://mitcho.com/blog/life/exploring-nanao-part-2/' rel='bookmark' title='Exploring Nanao, part 2: hot springs, waterfall, and beach'>Exploring Nanao, part 2: hot springs, waterfall, and beach</a></li>
<li><a href='http://mitcho.com/blog/life/exploring-nanao-part-1/' rel='bookmark' title='Exploring Nanao, part 1'>Exploring Nanao, part 1</a></li>
<li><a href='http://mitcho.com/blog/life/field-trip-guang-xing-farm/' rel='bookmark' title='Field trip: Guang-xing Farm'>Field trip: Guang-xing Farm</a></li>
</ol>
<p>Related posts brought to you by <a href='http://yarpp.org'>Yet Another Related Posts Plugin</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://mitcho.com/blog/how-to/updating-your-zenphoto-theme-for-zenphoto-11/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

