<?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>Everything In Between &#187; Programming</title>
	<atom:link href="http://maymay.net/blog/category/programming/feed/" rel="self" type="application/rss+xml" />
	<link>http://maymay.net/blog</link>
	<description>The brutally honest, first-person account of Meitar Moscovitz&#039;s life.</description>
	<lastBuildDate>Thu, 19 Jan 2012 08:54:24 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Quick and Dirty: Clone Custom Field, Template Linked Files on Movable Type</title>
		<link>http://maymay.net/blog/2010/06/08/quick-and-dirty-clone-custom-field-template-linked-files-on-movable-type/</link>
		<comments>http://maymay.net/blog/2010/06/08/quick-and-dirty-clone-custom-field-template-linked-files-on-movable-type/#comments</comments>
		<pubDate>Wed, 09 Jun 2010 01:19:02 +0000</pubDate>
		<dc:creator>Meitar</dc:creator>
				<category><![CDATA[Crosspost]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Movable Type]]></category>

		<guid isPermaLink="false">http://maymay.net/blog/?p=1238</guid>
		<description><![CDATA[Movable Type is a pretty frustrating platform to work with because every so often (or, &#8220;way too often,&#8221; depending on who you ask) a function of the system simply doesn&#8217;t do what you&#8217;d expect it to do. Such is the case with the &#8220;Clone Blog&#8221; functionality. Although it dutifully copies most of a website from [...]]]></description>
			<content:encoded><![CDATA[<p>Movable Type is a pretty frustrating platform to work with because every so often (or, &#8220;way too often,&#8221; depending on who you ask) a function of the system simply doesn&#8217;t do what you&#8217;d expect it to do. Such is the case with the &#8220;Clone Blog&#8221; functionality. Although it dutifully copies most of a website from one &#8220;blog&#8221; object to another, a few things are missing.</p>
<p>Most notably, custom fields and templates&#8217; linked files are not copied. This is a deal-breaker for any large installation that uses the built-in MT &#8220;Clone&#8221; feature.</p>
<p>To get around this limitation, I wrote a stupid, quick &#8216;n&#8217; dirty <acronym title="PHP Hypertext Preprocessor; an HTML-embedded scripting language">PHP</acronym> script to finish the cloning process, called <code>finishclone.php</code>. It takes only 1 argument: the &#8220;new ID&#8221; you are cloning to. If all goes well, you&#8217;ll see output like this:</p>
<pre><samp>[root@dev www]$ </samp><kbd>php finishclone.php <var>28</var></kbd>
<samp>Cloning process complete.
[root@dev www]$ </samp></pre>
<p>In this example, <var>28</var> is the newly created blog&#8217;s ID. The blog you want to clone <em>from</em> is set as a constant within the script. I&#8217;ll leave modifying the script to support more flexible command line arguments as an exercise for the reader.</p>
<pre><code class="php">&lt;?php
/**
 * Ease the final steps in cloning a Movable Type blog.
 *
 * Description:   This script should be run after Movable Type's "Clone Blog"
 *                function has completed and before the cloned blog is used.
 *
 * Author:        "Meitar Moscovitz" &lt;meitar@maymay.net&gt;
 */

// Set constants.
define('MT_ORIG_BLOG', 0); // the ID of the blog you are cloning from
define('MYSQL_HOST', 'localhost');
define('MYSQL_USER', 'movabletype');
define('MYSQL_PASS', '<var>PASSWORD_HERE</var>');
define('MYSQL_DB', 'movabletype');

// Get command line arguments.
if (2 &gt; $_SERVER['argc']) { die('Tell me the ID of the blog to clone into.'); }

$blog_id = (int) $argv[1];

// Connect to db
if ( !mysql_pconnect( MYSQL_HOST, MYSQL_USER, MYSQL_PASS ) ) {
	die( 'Connection to the database has failed: ' . mysql_error( ) );
}
mysql_select_db( MYSQL_DB );

// Clone custom fields.
$result = mysql_query('SELECT * FROM mt_field WHERE field_blog_id='.MT_ORIG_BLOG.';');
while ($row = mysql_fetch_object($result)) {
	mysql_query(
		sprintf("INSERT INTO mt_field ("
			."field_basename,"
			."field_blog_id,"
			."field_default,"
			."field_description,"
			."field_name,"
			."field_obj_type,"
			."field_options,"
			."field_required,"
			."field_tag,"
			."field_type) "
			."VALUES('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s');",
			mysql_real_escape_string($row-&gt;field_basename),
			mysql_real_escape_string($blog_id),
			mysql_real_escape_string($row-&gt;field_default),
			mysql_real_escape_string($row-&gt;field_description),
			mysql_real_escape_string($row-&gt;field_name),
			mysql_real_escape_string($row-&gt;field_obj_type),
			mysql_real_escape_string($row-&gt;field_options),
			mysql_real_escape_string($row-&gt;field_required),
			mysql_real_escape_string($row-&gt;field_tag),
			mysql_real_escape_string($row-&gt;field_type)
		)
	) OR print mysql_error() . "\n";
}

// Link template files to filesystem.
$arr = array();
$result = mysql_query('SELECT template_name,template_linked_file FROM mt_template WHERE template_blog_id='.MT_ORIG_BLOG.';');
while ($row = mysql_fetch_object($result)) {
	$arr[$row-&gt;template_name] = $row-&gt;template_linked_file;
}
foreach ($arr as $k =&gt; $v) {
	mysql_query("UPDATE mt_template SET template_linked_file='$v' WHERE template_blog_id=$blog_id AND template_name='$k';");
}

print "Cloning process complete.\n";</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://maymay.net/blog/2010/06/08/quick-and-dirty-clone-custom-field-template-linked-files-on-movable-type/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Cross-post: Edenfantasys&#8217;s unethical technology is a self-referential black hole</title>
		<link>http://maymay.net/blog/2010/05/19/web-merchants-inc-edenfantasys-unethical-technology/</link>
		<comments>http://maymay.net/blog/2010/05/19/web-merchants-inc-edenfantasys-unethical-technology/#comments</comments>
		<pubDate>Wed, 19 May 2010 23:20:04 +0000</pubDate>
		<dc:creator>Meitar</dc:creator>
				<category><![CDATA[Business & E-Commerce]]></category>
		<category><![CDATA[Content Syndication]]></category>
		<category><![CDATA[Crosspost]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Maybe Maimed]]></category>
		<category><![CDATA[Search Engine Optimization]]></category>
		<category><![CDATA[Tech/Computing]]></category>
		<category><![CDATA[Web Design]]></category>

		<guid isPermaLink="false">http://maymay.net/blog/?p=1217</guid>
		<description><![CDATA[This entry was originally published at my other blog. I&#8217;m cross-posting it here in order to make sure it gets copied to more servers, as some people have suggested I&#8217;ll face a cease and desist order for publishing it in the first place. Please help distribute this important information by freely copying and republishing this [...]]]></description>
			<content:encoded><![CDATA[<p><em>This entry was originally published at <a href="http://maybemaimed.com/2010/05/19/edenfantasyss-unethical-technology-is-a-self-referential-black-hole/">my other blog</a>. I&#8217;m cross-posting it here in order to make sure it gets copied to more servers, as some people have suggested I&#8217;ll face a cease and desist order for publishing it in the first place. Please help distribute this important information by freely copying and republishing this post under the conditions of my <acronym title="Columbia College">CC</acronym>-BY-NC-ND license: provide me with attribution and a (real) back link, and you are free to republish an unaltered version of this post wherever you like. Thanks.</em></p>
<p>A few nights ago, I received an email from Editor of EdenFantasys&#8217;s SexIs Magazine, Judy Cole, asking me to modify <a href="http://kinkontap.com/?p=676">this Kink On Tap brief</a> I published that cites Lorna D. Keach&#8217;s writing. Judy asked me to &#8220;provide attribution and a link back to&#8221; SexIs Magazine. An ordinary enough request soon proved extraordinarily unethical when I discovered that <strong>EdenFantasys has invested a staggering amount of time and money to develop and implement a technology platform that actively denies others the courtesy of link reciprocity</strong>, <a href="http://www.ted.com/talks/jonathan_zittrain_the_web_is_a_random_act_of_kindness.html">a courtesy on which the ethical Internet is based</a>.</p>
<p>While what they&#8217;re doing may not be illegal, EdenFantasys has proven itself to me to be an unethical and unworthy partner, in business or otherwise. Its actions are blatantly hypocritical, as I intend to show in detail in this post. Taking willful and self-serving advantage of those not technically savvy is a form of inexcusable oppression, and none of us should tolerate it from companies who purport to be well-intentioned resources for a community of sex-positive individuals.</p>
<p>For busy or non-technical readers, see the next section, <a href="#executive-summary">Executive Summary</a>, to quickly understand what EdenFantasys is doing, why it&#8217;s unethical, and <a href="#how-this-affects-you">how it affects you</a> whether you&#8217;re a customer, a contributor, or a syndication partner. For the technical reader, the <a href="#technical-details">Technical Details</a> section should provide ample evidence in the form of a walkthrough and sample code describing the unethical Search Engine Optimization (<acronym title="Search Engine Optimization">SEO</acronym>) and Search Engine Marketing (<acronym title="Search Engine Marketing">SEM</acronym>) techniques EdenFantasys, <acronym title="Also Known As">aka</acronym>. Web Merchants, Inc., is engaged in. For anyone who wants to read further, I provide an <a href="#editorial">Editorial</a> section in which I share some thoughts about what you can do to help combat these practices and bring transparency and trust&mdash;not the sabotage of trust EdenFantasys enacts&mdash;to the market.</p>
<h2 id="executive-summary">EXECUTIVE SUMMARY</h2>
<p>Internet sex toy retailer Web Merchants, Inc., which bills itself as the &#8220;sex shop you can trust&#8221; and does business under the name EdenFantasys, has implemented technology on their websites that actively interferes with contributors&#8217; content, intercepts outgoing links, and alters republished content so that links in the original work are redirected to themselves. Using techniques widely acknowledged as unethical by Internet professionals and that are arguably in violation of major search engines&#8217; policies, EdenFantasys&#8217;s publishing platform has effectively outsourced the task of <a href="http://en.wikipedia.org/wiki/Spamdexing#Types_of_Link_Spam">&#8220;link farming&#8221; (a questionable Search Engine Marketing [<acronym title="Search Engine Marketing">SEM</acronym>] technique)</a> to sites with which they have &#8220;an ongoing relationship,&#8221; such as <a href="http://AlterNet.org/">AlterNet.org</a>, other large news hubs, and individual bloggers&#8217; blogs.</p>
<p>Articles published on EdenFantasys websites, such as the &#8220;community&#8221; website SexIs Magazine, contain <acronym title="HyperText Markup Language">HTML</acronym> crafted to look like links, but aren&#8217;t. When visited by a typical human user, a program written in JavaScript and included as part of the web pages is automatically downloaded and intercepts clicks on these &#8220;link-like&#8221; elements, fetching their intended destination from the server and redirecting users there. Due to the careful and deliberate implementation, the browser&#8217;s status bar is made to appear as though the link is legitimate, and that a destination is provided as expected.</p>
<p>For non-human visitors, including automated search engine indexing programs such as Googlebot, the &#8220;link&#8221; remains non-functional, making the article a search engine&#8217;s dead-end or &#8220;orphan&#8221; page whose only functional links are those whose destination is EdenFantasys&#8217;s own web presence. <strong>This makes EdenFantasys&#8217; website(s) a self-referential black hole that provides no reciprocity for contributors who author content, nor for any website ostensibly &#8220;linked&#8221; to from article content.</strong> At the same time, EdenFantasys editors actively solicit inbound links from individuals and organizations through &#8220;link exchanges&#8221; and incentive programs such as &#8220;awards&#8221; and &#8220;free&#8221; sex toys, as well as syndicating SexIs Magazine content such that the content is programmatically altered in order to create multiple (real) inbound links to EdenFantasys&#8217;s websites after republication on their partner&#8217;s media channels.</p>
<h3 id="how-this-affects-you">How EdenFantasys&#8217;s unethical practices have an impact on you</h3>
<p>Regardless of who you are, EdenFantasys&#8217;s unethical practices have a negative impact on you and, indeed, on the Internet as a whole.</p>
<div class="admonition tip" style="float: right; width: 33%; margin: 0 0 1em 1em;">
<strong>See for yourself</strong>: First, <em>log out of any and all EdenFantasys websites</em> or, preferably, use a different browser, or even a proxy service such as <a href="http://torproject.org/">the Tor network</a> for greater anonymity. Due to EdenFantasys&#8217;s technology, <em>you cannot trust that what you are seeing on your screen is what someone else will see on theirs.</em> Next, temporarily disable JavaScript (<a href="http://www.tucows.com/article/1690">read instructions for your browser</a>) and then try clicking on the links in SexIs Magazine articles. If clicking the intended off-site &#8220;links&#8221; doesn&#8217;t work, you know that your article&#8217;s links are being hidden from Google and that your content is being used for shady practices. In contrast, with JavaScript still disabled, navigate to another website (such as this blog), try clicking on the links, and note that the links still work as intended.</p>
<p><strong>Here&#8217;s another verifiable example</strong> from the EdenFantasys site showing that many other parts of Web Merchants, Inc. pages, not merely SexIs Magazine, are affected as well: With JavaScript disabled, visit the <a href="http://www.edenfantasys.com/sex-community/companies/aslan-leather/" rel="nofollow">EdenFantasys company page on Aslan Leather</a> (note, for the sake of comparison, the link in this sentence will work, even with JavaScript off). Try clicking on the link in the &#8220;Contact Information&#8221; section in the lower-right hand column of the page (shown in the screenshot, below). This &#8220;link&#8221; <em>should</em> take you to the Aslan Leather homepage but in fact it does not. So much for that &#8220;link exchange.&#8221;<br />
<a href="http://maybemaimed.com/wp-content/uploads/2010/05/edenfantasys-company-contact-information.png"><img src="http://maybemaimed.com/wp-content/uploads/2010/05/edenfantasys-company-contact-information-300x266.png" alt="" title="edenfantasys-company-contact-information" width="300" height="266" class="size-medium wp-image-1752" /></a><br />
(Click to enlarge.)
</div>
<ul>
<li><strong>If you&#8217;re an EdenFantasys employee</strong>, people will demand answers from you regarding the unethical practices of your (hopefully former) employer. While you are working for EdenFantasys, you&#8217;re seriously soiling your reputation in the eyes of ethical Internet professionals. Ignorance is no excuse for the lack of ethics on the programmers&#8217; part, and it&#8217;s a shoddy one for everyone else; you should be aware of your company&#8217;s business practices because you represent them and they, in turn, represent you.</li>
<li><strong>If you&#8217;re a partner or contributor</strong> (reviewer, affiliate, blogger), while you&#8217;re providing EdenFantasys with inbound links or writing articles for them and thereby propping them up higher in search results, EdenFantasys is not returning the favor to you (when they are supposed to be doing so). Moreover, they&#8217;re attaching your handle, pseudonym, or real name <em>directly</em> to all of their link farming (i.e., spamming) efforts. They <em>look</em> like they&#8217;re linking to you and they <em>look</em> like their content is syndicated fairly, but they&#8217;re actually playing dirty. They&#8217;re going the extra mile to ensure search engines like Google do not recognize the links in articles you write. They&#8217;re trying remarkably hard to make certain that all roads lead to EdenFantasys, but none lead outside of it; no matter what the &#8220;link,&#8221; search engines see it as stemming from and leading to EdenFantasys. The technically savvy executives of Web Merchants, Inc. are using you without giving you a fair return on your efforts. Moreover, EdenFantasys is doing this in a way that preys upon people&#8217;s lack of technical knowledge—potentially your own as well as your readership&#8217;s. Do you want to keep doing business with people like that?</li>
<li><strong>If you&#8217;re a customer</strong>, you&#8217;re monetarily supporting a company that essentially amounts to a glorified yet subtle spammer. If you hate spam, you should hate the unethical practices that lead to spam&#8217;s perpetual reappearance, including the practices of companies like Web Merchants, Inc. EdenFantasys&#8217;s unethical practices may not be illegal, but they are unabashedly a hair&#8217;s width away from it, just like many spammers&#8217;. If you want to keep companies honest and transparent, if you really want a &#8220;sex shop you can trust,&#8221; this is relevant to you because EdenFantasys is not it. If you want to purchase from a retailer that truly strives to offer a welcoming, trustworthy community for those interested in sex positivity and sexuality, pay close attention and take action. For ideas about what you can do, please see <a href="#what-you-can-do">the &#8220;What you can do&#8221; section, below</a>.</li>
<li><strong>If you&#8217;ve never heard about EdenFantasys before</strong>, but you care about a fair and equal-opportunity Internet, this is relevant to you because what EdenFantasys is doing takes advantage of non-tech-savvy people in order to slant the odds of winning the search engine game in their favor. They could have done this fairly, and I personally believe that they would have succeeded. Their sites are user-friendly, well-designed, and solidly implemented. However, they chose to behave maliciously by not providing credit where credit is due, failing to follow through on agreements with their own community members and contributors, and sneakily utilizing other publishers&#8217; web presences to play a very sad zero-sum game that they need not have entered in the first place. In the Internet I want, nobody takes malicious advantage of those less skilled than they are because their own skill should speak for itself. Isn&#8217;t that the Internet and, indeed, the future you want, too?</li>
</ul>
<h2 id="technical-details">TECHNICAL DETAILS</h2>
<p>What follows is a technical exploration of the way the EdenFantasys technology works. It is my best-effort evaluation of the process in as much detail as I can manage within strict self-imposed time constraints. If any of this information is incorrect, I&#8217;d welcome any and all clarifications provided by the EdenFantasys CTO and technical team in an appropriately transparent, public, and ethical manner. (You&#8217;re welcome—nay, <em>encouraged</em>—to leave a comment.)</p>
<p>Although I&#8217;m unconvinced that EdenFantasys understands this, it is the case that honesty is the best policy&mdash;especially on the Internet, where <em>everyone</em> has the power of &#8220;View source.&#8221;</p>
<h3>The &#8220;EF Framework&#8221; for obfuscating links</h3>
<p>Article content written by contributors on SexIs Magazine pages is published after all links are replaced with a <code>&lt;span&gt;</code> element bearing the <code>class</code> of <code>linklike</code> and a unique <code>id</code> attribute value. This apparently happens across any and all content published by Web Merchants, Inc.&#8217;s content management system, but I&#8217;ll be focusing on Lorna D. Keach&#8217;s post entitled <cite>SexFeed:Anti-Porn Activists Now Targeting Female Porn Addicts</cite> for the sake of example.</p>
<p>These fake links look like this in HTML:</p>
<pre><code class="html">And according to Theresa Flynt, vice president of marketing for Hustler video, &lt;span class="linklike" ID="EFLink_68034_fe64d2"&gt;female consumers make up 56% of video sales.&lt;/span&gt;</code></pre>
<p>This originally published <acronym title="HyperText Markup Language">HTML</acronym> is what visitors without JavaScript enabled (and what search engine indexers) see when they access the page. Note that the <code>&lt;span&gt;</code> is not a real link, even though it is made to look like one. (See Figure 1; click it to enlarge.)</p>
<p><strong>Figure 1:</strong></p>
<p><a href="http://maybemaimed.com/wp-content/uploads/2010/05/figure-11.png"><img src="http://maybemaimed.com/wp-content/uploads/2010/05/figure-11-300x241.png" alt="" title="figure-1" width="300" height="241" class="alignnone size-medium wp-image-1759" /></a></p>
<p>In a typical user&#8217;s browser, when this page is loaded, a JavaScript program is executed that mutates these &#8220;linklike&#8221; elements into <code>&lt;a&gt;</code> elements, retaining the &#8220;linklike&#8221; <code>class</code> and the unique <code>id</code> attribute values. However, no value is provided in the <code>href</code> (link destination) attribute of the <code>&lt;a&gt;</code> element. See Figure 2.</p>
<p><strong>Figure 2:</strong></p>
<p><a href="http://maybemaimed.com/wp-content/uploads/2010/05/figure-2.png"><img src="http://maybemaimed.com/wp-content/uploads/2010/05/figure-2-300x241.png" alt="" title="figure-2" width="300" height="241" class="alignnone size-medium wp-image-1760" /></a></p>
<p>The JavaScript program is downloaded in two parts from the endpoint at <code>http://cdn3.edenfantasys.com/Scripts/Handler/jsget.ashx</code>. The first part, retrieved in this example by accessing the <acronym title="Uniform Resource Identifier">URI</acronym> at <code>http://cdn3.edenfantasys.com/Scripts/Handler/jsget.ashx?i=jq132_cnf_jdm12_cks_cm_ujsn_udm_stt_err_jsdm_stul_ael_lls_ganl_jqac_jtv_smg_assf_agrsh&#038;v_14927484.12.0</code>, loads the popular <a href="http://jquery.org/">jQuery JavaScript framework</a> as well as custom code called the &#8220;EF Framework&#8221;.</p>
<p>The EF Framework contains code called the <code>DBLinkHandler</code>, an object that parses the <code>&lt;span&gt;</code> &#8220;linklike&#8221; elements (called &#8220;pseudolinks&#8221; in the EF Framework code) and retrieves the real destination. The entirety of the <code>DBLinkHandler</code> object is shown in <a href="#code-listing-1">code listing 1</a>, below. Note the code contains a function called <code>handle</code> that performs the mutation of the <code>&lt;span&gt;</code> &#8220;linklike&#8221; elements (seen primarily on lines 8 through 16) and, based on the prefix of each elements&#8217; <code>id</code> attribute value, two key functions (<code>BuildUrlForElement</code> and <code>GetUrlByUrlID</code>, whose signatures are on lines 48 and 68, respectively) interact to set up the browser navigation after responding to clicks on the fake links.</p>
<pre id="code-listing-1"><code class="javascript">var DBLinkHandler = {
    pseudoLinkPrefix: "EFLink_",
    generatedAHrefPrefix: "ArtLink_",
    targetBlankClass: "target_blank",
    jsLinksCssLinkLikeClass: "linklike",
    handle: function () {
        var pseudolinksSpans = $("span[id^='" + DBLinkHandler.pseudoLinkPrefix + "']");
        pseudolinksSpans.each(function () {
            var psLink = $(this);
            var cssClass = $.trim(psLink.attr("class"));
            var target = "";
            var id = psLink.attr("id").replace(DBLinkHandler.pseudoLinkPrefix, DBLinkHandler.generatedAHrefPrefix);
            var href = $("&lt;a&gt;&lt;/a&gt;").attr({
                id: id,
                href: ""
            }).html(psLink.html());
            if (psLink.hasClass(DBLinkHandler.targetBlankClass)) {
                href.attr({
                    target: "_blank"
                });
                cssClass = $.trim(cssClass.replace(DBLinkHandler.targetBlankClass, ""))
            }
            if (cssClass != "") {
                href.attr({
                    "class": cssClass
                })
            }
            psLink.before(href).remove()
        });
        var pseudolinksAHrefs = $("a[id^='" + DBLinkHandler.generatedAHrefPrefix + "']");
        pseudolinksAHrefs.live("mouseup", function (event) {
            DBLinkHandler.ArtLinkClick(this)
        });
        pseudolinksSpans = $("span[id^='" + DBLinkHandler.pseudoLinkPrefix + "']");
        pseudolinksSpans.live("click", function (event) {
            if (event.button != 0) {
                return
            }
            var psLink = $(this);
            var url = DBLinkHandler.BuildUrlForElement(psLink, DBLinkHandler.pseudoLinkPrefix);
            if (!psLink.hasClass(DBLinkHandler.targetBlankClass)) {
                RedirectTo(url)
            } else {
                OpenNewWindow(url)
            }
        })
    },
    BuildUrlForElement: function (psLink, prefix) {
        var psLink = $(psLink);
        var sufix = psLink.attr("id").toString().substring(prefix.length);
        var id = (sufix.indexOf("_") != -1) ? sufix.substring(0, sufix.indexOf("_")) : sufix;
        var url = DBLinkHandler.GetUrlByUrlID(id);
        if (url == "") {
            url = EF.Constants.Links.Url
        }
        var end = sufix.substring(sufix.indexOf("_") + 1);
        var anchor = "";
        if (end.indexOf("_") != -1) {
            anchor = "#" + end.substring(0, end.lastIndexOf("_"))
        }
        url += anchor;
        return url
    },
    ArtLinkClick: function (psLink) {
        var url = DBLinkHandler.BuildUrlForElement(psLink, DBLinkHandler.generatedAHrefPrefix);
        $(psLink).attr("href", url)
    },
    GetUrlByUrlID: function (UrlID) {
        var url = "";
        UrlRequest = $.ajax({
            type: "POST",
            url: "/LinkLanguage/AjaxLinkHandling.aspx",
            dataType: "json",
            async: false,
            data: {
                urlid: UrlID
            },
            cache: false,
            success: function (data) {
                if (data.status == "Success") {
                    url = data.url;
                    return url
                }
            },
            error: function (xhtmlObj, status, error) {}
        });
        return url
    }
};</code></pre>
<p>Once the mutation is performed and all the content &#8220;links&#8221; are in the state shown in Figure 2, above, an event listener has been bound to the anchors that captures a click event. This is done using prototypal extension, <acronym title="Also Known As">aka</acronym>. classic prototypal inheritance, in another part of the code, the <code>live</code> function on line 2,280 of the (de-minimized) <code>jsget.ashx</code> program, as shown in code listing 2, here:</p>
<pre id="code-listing-2"><code class="javascript">        live: function (G, F) {
            var E = o.event.proxy(F);
            E.guid += this.selector + G;
            o(document).bind(i(G, this.selector), this.selector, E);
            return this
        },
</code></pre>
<p>At this point, clicking on one of the &#8220;pseudolinks&#8221; triggers the EF Framework to call code set up by the <code>GetUrlByUrlID</code> function from within the <code>DBLinkHandler</code> object, initiating an <a href="http://en.wikipedia.org/wiki/XMLHttpRequest">XMLHttpRequest (XHR)</a> connection to the <code>AjaxLinkHandling.aspx</code> server-side application. The request is an <acronym title="HyperText Transfer Protocol">HTTP</acronym> POST containing only one parameter, called <code>urlid</code>, and its value matches a substring from within the <code>id</code> value of the &#8220;pseudolinks.&#8221; In this example, the <code>id</code> attribute contains a value of <code>EFLink_68034_fe64d2</code>, which means that the unique ID POST&#8217;ed to the server is <code>68034</code>. This is shown in Figure 3, below.</p>
<p><strong>Figure 3:</strong></p>
<p><a href="http://maybemaimed.com/wp-content/uploads/2010/05/figure-3.png"><img src="http://maybemaimed.com/wp-content/uploads/2010/05/figure-3-300x199.png" alt="" title="figure-3" width="300" height="199" class="alignnone size-medium wp-image-1761" /></a></p>
<p>The response from the server, shown in Figure 4, is also simple. If successful, the intended destination is retrieved by the <code>GetUrlByUrlID</code> object&#8217;s <code>success</code> function (on line 79 of <a href="#code-listing-1">Code Listing 1</a>, above) and the user is redirected to that web address, as if the link was a real one all along. The real destination, in this case to CNN.com, is thereby only revealed after the XHR request returns a successful reply.</p>
<p><strong>Figure 4:</strong></p>
<p><a href="http://maybemaimed.com/wp-content/uploads/2010/05/figure-4.png"><img src="http://maybemaimed.com/wp-content/uploads/2010/05/figure-4-300x199.png" alt="" title="figure-4" width="300" height="199" class="alignnone size-medium wp-image-1762" /></a></p>
<p>All of this obfuscation effectively blinds machines such as the Googlebot who are not JavaScript-capable from seeing and following these links. It deliberately provides no increased Pagerank for the link destination (as a real link would normally do) despite being &#8220;linked to&#8221; from EdenFantasys&#8217;s SexIs Magazine article. While the intended destination in this example link was at CNN.com, it could just as easily have been—and is, in other examples—links to the blogs of EdenFantasys community members and, indeed, everyone else linked to from a SexIs Magazine article or potentially any website operated by Web Merchants, Inc. that makes use of this technology.</p>
<h3>The EdenFantasys Outsourced Link-Farm</h3>
<p>In addition to creating a self-referential black hole with no gracefully degrading outgoing links, EdenFantasys also actively performs link-stuffing through its syndicated content &#8220;relationships,&#8221; underhandedly creating an outsourced and distributed link-farm, just like a spammer. The difference is that this spammer (Web Merchants, Inc. <acronym title="Also Known As">aka</acronym> EdenFantasys) is cleverly crowd-sourcing high-value, high-quality content from its own &#8220;community.&#8221;</p>
<p>Articles published at SexIs Magazine are syndicated in full to other large hub sites, such as AlterNet.org. Continuing with the above example post by Lorna D. Keach, <cite>Anti-Porn Activists Now Targeting Female Porn Addicts</cite>, we can see that <a href="http://www.alternet.org/story/146774/christian_anti-porn_activists_now_targeting_female_">this content was republished on AlterNet.org</a> shortly after original publication through EdenFantasys&#8217; website on May 3<sup>rd</sup> at <code>http://www.alternet.org/story/146774/christian_anti-porn_activists_now_targeting_female_</code>. However, a closer look at the <acronym title="HyperText Markup Language">HTML</acronym> code of the republication shows that each and every link contained within the article points to the same destination: the same article published on SexIs Magazine, as shown in Figure 5.</p>
<p><strong>Figure 5:</strong></p>
<p><a href="http://maybemaimed.com/wp-content/uploads/2010/05/figure-5.png"><img src="http://maybemaimed.com/wp-content/uploads/2010/05/figure-5-300x199.png" alt="" title="figure-5" width="300" height="199" class="alignnone size-medium wp-image-1763" /></a></p>
<p>Naturally, these syndicated links provided to third-party sites by EdenFantasys are real and function as expected to both human visitors and to search engines indexing the content. The result is &#8220;natural,&#8221; high-value links to the EdenFantasys website from these third-party sites; EdenFantasys doesn&#8217;t merely scrounge pagerank from harvesting the sheer number of incoming links, but as each link&#8217;s anchor text is different, they are setting themselves up to match more keywords in search engine results, keywords that the original author likely did not intend to direct to them. Offering search engines the implication that EdenFantasys.com contains the content described in the anchor text, when in fact EdenFantasys merely acts as an intermediary to the information, is very shady, to say the least.</p>
<p>In addition to syndication, EdenFantasys employs human editors to do community outreach. These editors follow up with publishers, including individual bloggers (such as myself), and request that any references to published material <q>provide attribution and a link back to us</q>, to use the words of Judy Cole, Editor of SexIs Magazine in an email she sent to me (see below), and presumably many others. EdenFantasys has also been known to request &#8220;link exchanges,&#8221; and offer incentive programs that encouraged bloggers to add the EdenFantasys website to their blogroll or sidebar in order to help raise both parties search engine ranking, when in fact EdenFantasys is not actually providing reciprocity.</p>
<p><a href="http://aagblog.com/2005/10/17/problems-with-edenfantasyscom/">More information about EdenFantasys&#8217;s unethical practices</a>, which are not limited to technical subterfuge, can be <a href="http://aagblog.com/?s=edenfantasys">obtained via AAGBlog.com</a>.</p>
<h3 id="editorial">EDITORIAL</h3>
<p>It is unsurprising that the distributed, subtle, and carefully crafted way EdenFantasys has managed to crowd-source links has (presumably) remained unpenalized by search engines like Google. It is similarly unsurprising that nontechnical users such as the contributors to SexIs Magazine would be unaware of these deceptive practices, or that they are complicit in promoting them.</p>
<p>This is no mistake on the part of EdenFantasys, nor is it a one-off occurrence. The amount of work necessary to implement the elaborate system I&#8217;ve described is also not even remotely feasible for a rogue programmer to accomplish, far less accomplish covertly. No, this is the result of a calculated and decidedly underhanded strategy that originated from the direction of top executives at Web Merchants, Inc. <acronym title="Also Known As">aka</acronym> EdenFantasys.</p>
<p>It is unfortunate that technically privileged people would be so willing to take advantage of the technically uneducated, particularly under the guise of providing a <em>trusted</em> place for the community which they claim to serve. These practices are exactly the ones that &#8220;the sex shop you can trust&#8221; should in no way support, far less be actively engaged in. And yet, here is unmistakable evidence that EdenFantasys is doing <em>literally</em> everything it can not only to bolster its own web presence at the cost of others&#8217;, but to hide this fact from its understandably non-tech-savvy contributors.</p>
<p>On a personal note, I am angered that I would be contacted by the Editor of SexIs Magazine, and asked to properly &#8220;attribute&#8221; and provide a link to <em>them</em> when it is precisely that reciprocity which SexIs Magazine would clearly deny me (and everyone else) in return. It was this request originally received over email from Judy Cole, that sparked my investigation outlined above and enabled me to uncover this hypocrisy. The email I received from Judy Cole is republished, in full, here:</p>
<blockquote><p>From: Judy Cole &lt;luxuryholmes@gmail.com&gt;<br />
Subject: Repost mis-attributed<br />
Date: May 17, 2010 2:42:00 PM PDT<br />
To: kinkontap+viewermail@gmail.com<br />
Cc: Laurel &lt;laurelb@edenfantasys.com&gt;</p>
<p>Hello Emma and maymay,</p>
<p>I am the Editor of the online adult magazine SexIs (http://www.edenfantasys.com/sexis/). You recently picked up and re-posted a story of ours by Lorna Keach that Alternet had already picked up: </p>
<p>http://kinkontap.com/?s=alternet</p>
<p>We were hoping that you might provide attribution and a link back to us, citing us as the original source (as is done on Alternet, with whom we have an ongoing relationship), should you pick up something of ours to re-post in the future.</p>
<p>If you would be interested in having us send you updates on stories that might be of interest, I would be happy to arrange for a member of our editorial staff to do so. (Like your site, by the way. TBK is one of our regular contributors.)</p>
<p>Thanks and Best Regards,</p>
<p>Judy Cole<br />
Editor, SexIs</p></blockquote>
<p>Judy&#8217;s email <em>probably</em> intended to reference the new <a href="http://kinkontap.com/?cat=11">Kink On Tap briefs</a> that my co-host Emma and I publish, not a search result page on the Kink On Tap website. Specifically, she was talking about this brief: <a href="http://KinkOnTap.com/?p=676">http://KinkOnTap.com/?p=676</a>. I said as much in my reply to Judy:</p>
<blockquote><p>Hi Judy,</p>
<p>The <acronym title="Uniform Resource Locator">URL</acronym> in your email doesn&#8217;t actually link to a post. We pick up many stories from AlterNet, as well as a number from SexIs, because we follow both those sources, among others. So, did you mean this following entry?</p>
<p>   <a href="http://KinkOnTap.com/?p=676">http://KinkOnTap.com/?p=676</a></p>
<p>If so, you should know that we write briefs as we find them and provide links to where we found them. We purposefully do not republish or re-post significant portions of stories and we limit our briefs to short summaries in deference to the source. In regards to the brief in question, we do provide attribution to Lorna Keach, and our publication process provides links automatically to, again, the source where we found the article. :) As I&#8217;m sure you understand, this is the nature of the Internet. Its distribution capability is remarkable, isn&#8217;t it?</p>
<p>Also, while we&#8217;d absolutely be thrilled to have you send us updates on stories that might be of interest, we would prefer that you do so in the same way the rest of our community does: by contributing to the community links feed. You can find detailed instructions for the many ways you can do that on our wiki:</p>
<p>   <a href="http://wiki.kinkontap.com/wiki/Community_links_feed">http://wiki.kinkontap.com/wiki/Community_links_feed</a></p>
<p>Congratulations on the continued success of SexIs.</p>
<p>Cheers,<br />
-maymay</p></blockquote>
<p>At the time when I wrote the email replying to Judy, I was perturbed but could not put my finger on why. Her email upset me because she seemed to be suggesting that our briefs are wholesale &#8220;re-posts,&#8221; when in fact Emma and I have thoroughly discussed attribution policies and, as mentioned in my reply, settled on a number of practices including a length limit, automated back linking (yes, with real links, go <a href="http://kinkontap.com/?cat=11">see some Kink On Tap briefs for yourself</a>), and clearly demarcating quotes from the source article in our editorializing to ensure we play fair. Clearly, my somewhat snarky reply betrays my annoyance.</p>
<p>In any event, this exchange prompted me to take a closer look at the Kink On Tap brief I wrote, at the original article, and at the cross-post on AlterNet.org. I never would have imagined that EdenFantasys&#8217;s technical subterfuge would be as pervasive as it has proven to be. It&#8217;s so deeply embedded in the EdenFantasys publishing platform that I&#8217;m willing to give Judy the benefit of the doubt regarding this hypocrisy because she doesn&#8217;t seem to understand the difference between a search query and a permalink (something any laymen blogger would grok). This is apparent from her reply to my response:</p>
<blockquote><p>From: Judy Cole &lt;luxuryholmes@gmail.com&gt;<br />
Subject: Re: Repost mis-attributed<br />
Date: May 18, 2010 4:57:59 AM PDT<br />
[&hellip;redundant email headers clipped&hellip;]</p>
<p>Funny, the <acronym title="Uniform Resource Locator">URL</acronym> in my email opens the same link as the one you sent me when I click on it. </p>
<p>Maybe if you pick up one of our stories in future, you could just say something like &#8220;so and so wrote for SexIs.&#8221; ?</p>
<p>As it stands, it looks as if Lorna wrote the piece for Alternet. Thanks.</p>
<p>Judy</p></blockquote>
<p>That is the end of our email exchange, and will be for good, unless and until EdenFantasys changes its ways. I will from this point forward endeavor never to publish links to any web property that I know to be owned by Web Merchants, Inc., including EdenFantasys.com. I will also do my best to avoid citing any and all SexIs Magazine articles from here on out, and I encourage <em>everyone</em> who has an interest in seeing honesty on the Internet to follow my lead here.</p>
<p>As some of my friends are currently contributors to SexIs Magazine, I would like all of you to know that <strong>I sincerely hope you immediately sever all ties with any and all Web Merchants, Inc. properties, suppliers, and business partners</strong>, especially because you are friends and I think your work is too important to be sullied by such a disreputable company. Similarly, I hope you encourage your friends to do the same. I understand that the economy is rough and that some of you may have business contracts bearing legal penalties for breaking them, but I urge you to nevertheless consider looking at this as a cost-benefit analysis: the sooner you break up with EdenFantasys, the happier everyone on the Internet, including you, will be (and besides, you can loose just as much of your reputation, money, and pagerank while being happy as you can being sad).</p>
<h4 id="what-you-can-do">What you can do</h4>
<ul>
<li>If you are an EdenFantasys reviewer, a SexIs Magazine contributor, or have any other arrangement with Web Merchants, Inc., <strong><a href="mailto: luxuryholmes@gmail.com?subject=EdenFantasys%20and%20SexIs%20Magazine%20must%20conduct%20themselves%20ethically%20or%20I%20quit%20now">write to Judy Cole</a></strong> and demand that content you produce for SexIs Magazine adheres to ethical Internet publication standards. Sever business ties with this company immediately upon receipt of any non-response, or any response that does not adequately address every concern raised in this blog post. (Feel free to leave comments on this post with technical questions, and I&#8217;ll do my best to help you sort out any l33t answers.)</li>
<li>EdenFantasys wants to stack the deck in Google. They do this by misusing your content and harvesting your links. To combat this effort, <strong>immediately remove any and all links to EdenFantasys websites and web presences</strong> from your websites. Furthermore, do not&mdash;I repeat&mdash;do not publish new links to EdenFantasys websites, not even in direct reference to this post. Instead, provide enough information, as I have done, so visitors to your blog posts can find their website themselves. In lieu of links to EdenFantasys, link to other bloggers&#8217; posts about this issue. (Such posts will probably be mentioned in <a href="#comments">the comments section of this post</a>.)</li>
<li><strong>Boycott EdenFantasys</strong>: the technical prowess their website displays does provide a useful shopping experience for some people. However, that in no way obligates you to purchase from their website. If you enjoy using their interface, use it to get information about products you&#8217;re interested in, but then go buy those products elsewhere, perhaps from the manufacturers directly.
<ul>
<li>On the recommendation of my friend <a href="http://charlieglickman.com/">Dr. Charlie Glickman</a>, I suggest <a href="http://www.goodvibes.com/">Good Vibrations</a>.</p>
<li>On the recommendation of <a href="http://maybemaimed.com/2010/04/26/femquaker-shanna-katz-sex-positive-sexuality-educator/">my friend Shanna Katz</a>, I also recommend <a href="http://funlove.com/">Fascinations</a>.</li>
</ul>
</li>
<li><strong>Watch for &#8220;improved&#8221; technical subterfuge from Web Merchants, Inc.</strong> As a professional web developer, I can identify several things EdenFantasys could do to make their unethical practices even harder to spot, and harder to stop. If you have any technical knowledge at all, even if you&#8217;re &#8220;just&#8221; a savvy blogger, you can keep a close watch on EdenFantasys and, if you notice <em>anything</em> that doesn&#8217;t sit well with you, speak up about it like I did. Get a professional programmer to look into things for you if you need help; yes, you can make a difference just by remaining vigilant as long as you share what you know and act honestly, and transparently.</li>
</ul>
<p>If you have additional ideas or recommendations regarding how more people can help keep sex toy retailers honest, please suggest them in the comments.</p>
<p><ins datetime="2010-05-19T20:32:44+00:00"><strong>Update:</strong> To report website spamming or any kind of fraud to Google, use the <a href="https://www.google.com/webmasters/tools/spamreport?pli=1">authenticated Spam Report tool</a>.</ins></p>
<p><ins datetime="2010-05-20T00:07:22+00:00">Update: Google provides much more information about why the kinds of practices EdenFantasys is engaged in degrade the overall web experience for you and me. Read <a href="http://www.google.com/support/webmasters/bin/answer.py?answer=66355">Cloaking, sneaky Javascript redirects, and doorway pages</a> at the Google Webmaster Tools help site for additional <acronym title="Search Engine Optimization">SEO</acronym> information. Using Google&#8217;s terminology, EdenFantasys&#8217;s unethical technology is a very skilled mix of social engineering and &#8220;sneaky JavaScript redirects.&#8221;</ins></p>
]]></content:encoded>
			<wfw:commentRss>http://maymay.net/blog/2010/05/19/web-merchants-inc-edenfantasys-unethical-technology/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>How to work around &#8220;sorry, you must have a tty to run sudo&#8221; without sacrificing security</title>
		<link>http://maymay.net/blog/2010/03/17/how-to-work-around-sorry-you-must-have-a-tty-to-run-sudo-without-sacrificing-security/</link>
		<comments>http://maymay.net/blog/2010/03/17/how-to-work-around-sorry-you-must-have-a-tty-to-run-sudo-without-sacrificing-security/#comments</comments>
		<pubDate>Thu, 18 Mar 2010 01:21:50 +0000</pubDate>
		<dc:creator>Meitar</dc:creator>
				<category><![CDATA[Bash/Shell Scripting]]></category>
		<category><![CDATA[Security & Privacy]]></category>
		<category><![CDATA[Tech/Computing]]></category>
		<category><![CDATA[Unix/Linux]]></category>

		<guid isPermaLink="false">http://maymay.net/blog/?p=1208</guid>
		<description><![CDATA[While working on $client&#8216;s Linux server last week, I found myself installing a cron job that ran as root. The cron job called a custom bash script that, in turn, called out to various custom maintenance tasks client had already written. One task in particular had to run as a different user. During testing, I [...]]]></description>
			<content:encoded><![CDATA[<p>While working on $<var>client</var>&#8216;s Linux server last week, I found myself installing a cron job that ran as <code>root</code>. The cron job called a custom bash script that, in turn, called out to various custom maintenance tasks <var>client</var> had already written. One task in particular had to run as a different user.</p>
<p>During testing, I discovered that the odd-ball task failed to run, and found the following error in the system log:</p>
<pre>sudo: sorry, you must have a tty to run sudo</pre>
<p>I traced this error to a line trying to invoke a <code>perl</code> command as a user called <code>dynamic</code>:</p>
<pre>sudo -u dynamic /usr/bin/perl run-periodic-tasks --load 5 --randomly</pre>
<p>A simple Google search turned up an obvious solution to the error: <a href="http://www.adminmyserver.com/articles/sorry-you-must-have-a-tty-to-run-sudo">use <code>visudo</code> to disable sudo&#8217;s tty requirement</a>, allowing <code>sudo</code> to be invoked from any shell lacking a tty (including <code>cron</code>). This would have solved my problem, but it just felt wrong, dirty, and most troublingly <em>insecure</em>.</p>
<p>One reason why <code>sudo</code> ships with the <code>requiretty</code> option enabled by default is, among other reasons, to <a href="http://www.cyberciti.biz/faq/linux-unix-bsd-sudo-sorry-you-must-haveattytorun/">prevent remote users from exposing the root password over <acronym title="Secure SHell">SSH</acronym></a>. Disabling this security precaution for a simple maintenance task <em>already running as root</em> seemed totally unnecessary, not to mention irresponsible. Moreover, <var>client</var>&#8216;s script didn&#8217;t even need a tty.</p>
<p>Thankfully, there&#8217;s a better way: use <code>su --session-command</code> and send the whole job to the background.</p>
<pre>su --session-command="/usr/bin/perl run-periodic-tasks --load 5 --randomly" dynamic &#038;</pre>
<p>This line launches a new, non-login shell (typically <code>bash</code>) <em>as the other user</em> in a separate, background process and runs the command you passed using the shell&#8217;s <code>-c</code> option. Sending the command to the background (using <code>&#038;</code>) continues execution of the rest of the cron job.</p>
<p>A process listing would look like this:</p>
<pre>root     28109     1  0 17:10 ?        00:00:00 su --session-command=/usr/bin/perl run-periodic-tasks --load 5 --randomly dynamic
dynamic  28110 28109  0 17:10 ?        00:00:00 bash -c /usr/bin/perl run-periodic-tasks --load 5 --randomly</pre>
<p>Note the parent process (PID 28109) is owned by root but the actual <code>perl</code> process (PID 28110) is being run as <code>dynamic</code>.</p>
<p>This in-script solution that replaces <code>sudo -u <var>user</var> <var>cmd</var></code> with <code>su --session-command=<var>cmd</var> <var>user</var></code> seems much better than relying on a change in <code>sudo</code>&#8216;s default (and more secure) configuration to me.</p>
]]></content:encoded>
			<wfw:commentRss>http://maymay.net/blog/2010/03/17/how-to-work-around-sorry-you-must-have-a-tty-to-run-sudo-without-sacrificing-security/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>How To Use Git-SVN as the Only Subversion Client You&#8217;ll Need</title>
		<link>http://maymay.net/blog/2009/02/24/how-to-use-git-svn-as-the-only-subversion-client-youll-need/</link>
		<comments>http://maymay.net/blog/2009/02/24/how-to-use-git-svn-as-the-only-subversion-client-youll-need/#comments</comments>
		<pubDate>Tue, 24 Feb 2009 18:17:37 +0000</pubDate>
		<dc:creator>Meitar</dc:creator>
				<category><![CDATA[HOWTO]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Tech/Computing]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[Subversion]]></category>

		<guid isPermaLink="false">http://maymay.net/blog/?p=891</guid>
		<description><![CDATA[I&#8217;ve been using git as my favorite version control tool for quite a while now. One of its numerous distinguishing features is an optional component called git-svn, which serves as a bi-directional &#8220;bridge&#8221; that enables native git repositories to interact with a Subversion repository, performing all the normal operations you would need to use svn [...]]]></description>
			<content:encoded><![CDATA[<p><a href="/blog/tag/git/">I&#8217;ve been using git</a> as my favorite version control tool for quite a while now. One of its  numerous distinguishing features is an optional component called git-svn, which serves as a bi-directional &#8220;bridge&#8221; that enables native git repositories to interact with a Subversion repository, performing all the normal operations you would need to use <code>svn</code> for. In other words, since you can checkout, commit to, and query the logs of Subversion repositories (among other things) using git-svn, <strong>git can serve as your all-in-one Subversion client</strong>.</p>
<p>One reason why you might use git-svn because your project <em>actually</em> resides in a Subversion repository and other people need to access it using Subversion-only tools. Another might be because you have multiple projects, some that use git and others that use Subversion, and you&#8217;re tired of switching between <code>svn</code> and <code>git</code> commands—like me. For us, it&#8217;s far easier to simply use git as a Subversion client and never have to call <code>svn</code> directly.</p>
<p>As an important aside, please note that I would strongly discourage people who are new to git from learning about it by using git-svn. Although you may think that moving to git from Subversion would be eased by using the git-svn bridge, I really don&#8217;t think that&#8217;s the case. You&#8217;re much, much better off simply using git by itself right off the bat, and you can <a href="/blog/2008/03/26/howto-use-git-for-personal-development-when-everyone-else-is-using-subversion-part-1/">do this even if your fellow committers are using subversion</a>.</p>
<p>Also, I&#8217;m going to assume you&#8217;ve already got a Subversion repository set up somewhere.</p>
<p>First, checkout the subversion repository. In Subversion you would do this:</p>
<pre><code class="shell">svn checkout <var>http://example.com/path/to/svn/repo</var></code></pre>
<p>With git-svn, you do this:</p>
<pre><code class="shell">git svn clone <var>http://example.com/path/to/svn/repo</var></code></pre>
<p>This will cause git-svn to create a new directory called <code>repo</code>, switch to it, initialize a new git repository, configure the Subversion repository at <var>http://example.com/path/to/svn/repo</var> as a remote git branch (confusingly called <code>git-svn</code> by default, although you can specify your name by passing a <code>-R<var>remote_name</var></code> or <code>--svn-remote=<var>remote_name</var></code> option), and then does a checkout.</p>
<p>The output of this command will be a little awkward. Here&#8217;s a sample from one my repositories:</p>
<pre><samp>r14 = dbd7266f328ef2ad061ea4532f39ce7cebaba0c5 (git-svn)
	M	trunk/Chapter 6/Chapter 6.doc
	M	trunk/Chapter 6/code examples/6.1.html
	A	trunk/Chapter 6/code examples/6.2.html
r15 = 4cca08341ab0600069cece77ce67afc449caca68 (git-svn)
	M	trunk/Chapter 6/Chapter 6.doc
	A	trunk/Chapter 6/code examples/print.css
	A	trunk/Chapter 6/code examples/screen.css
	M	trunk/Chapter 6/code examples/6.1.html
	M	trunk/Chapter 6/code examples/6.2.html
r16 = 7b2f3e0ccfd79be61b527b6ba325f8689475dc01 (git-svn)
	M	trunk/Chapter 5/Chapter 5.doc
r17 = a319764855361d92bb6e006cfd18a51319046cae (git-svn)
	M	trunk/Chapter 5/Chapter 5.doc
r18 = 4cd5cb43d33b2dd45bd39b9a2b7ea9416f9e3d8f (git-svn)
	M	trunk/Chapter 6/Chapter 6.doc
	M	trunk/Chapter 6/code examples/screen.css
	M	trunk/Chapter 6/code examples/6.1.html
</samp></pre>
<p>As you can see, git-svn is associating specific Subversion revisions with particular git commit objects. Due to this required mapping, the initial cloning process of a Subversion repository may take some time. This is a good opportunity for your morning coffee break.</p>
<p>When this process is done, you&#8217;ll have a typical git repository with a local master branch and one remote branch for the Subversion repository:</p>
<pre><code class="shell">Perseus:repo maymay$ git branch
* master
Perseus:repo maymay$ git branch -r
  git-svn
</code></pre>
<p>You can now treat the Subversion repository as though it were a remote branch of sorts. Say you&#8217;ve done a bunch of work and, as you typically do with git, you commit this work to your topic branch.</p>
<pre><code class="shell">Perseus:repo maymay$ git checkout -b awesome-feature
<samp>Switched to a new branch "awesome-feature"</samp>
Perseus:repo maymay$ vim awesome-feature-stylesheet.css
Perseus:repo maymay$ git add awesome-feature-stylesheet.css
Perseus:repo maymay$ git commit -m "Now I'm perty."
<samp>Created commit 07ee832: Now I'm perty.
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 awesome-feature-stylesheet.css</samp>
</code></pre>
<p>Right now your changes are still in the topic branch (called <code>awesome-feature</code> in the above example). To get them to Subversion, you merely need to say <code>git svn dcommit</code>:</p>
<pre><code class="shell">Perseus:repo maymay$ git svn dcommit
<samp>Committing to <var>http://example.com/path/to/svn/repo</var> ...</samp>
</code></pre>
<p>Note that pesky extra &#8220;d&#8221; in the command. This is the equivalent of Subversion&#8217;s <code>svn commit</code>, but the commit message used is the one from the previous command, which in this case was <code>git commit -m "Now I'm perty."</code>. Also interesting to note here is that because Subversion doesn&#8217;t understand git branches, any change on any branch can be &#8220;pushed&#8221; to Subversion at any time using <code>git svn dcommit</code>—the git commits don&#8217;t have to be on any specific branch, since all git-svn does is map a git commit object to a Subversion revision and vice versa.</p>
<p>Similarly, you can at any time run the equivalent of <code>svn update</code> to get the latest changes from the Subversion repository into your Subversion branch.</p>
<ul>
<li>To do this, <em>without</em> affecting your working tree—that is, to only fetch the latest changes but not write them to the filesystem, just to the git-svn metadata area and the remote git branch—use <code>git svn fetch</code>. To apply these changes to your local branch, you simply merge: <code>git checkout master; git merge git-svn</code>.</li>
<li>If you do want to write out the changes to the filesystem (as <code>svn update</code> would do), use <code>git svn rebase</code>, which automatically linearizes your local git commit history after the commit history of the incoming Subversion changesets. Very slick.</li>
</ul>
<p>If your fetching/rebasing causes a conflict, you&#8217;ll be notified and will have to resolve it as per usual. If your &#8220;pushes&#8221; to the svn repo causes a Subversion conflict, you&#8217;ll be notified and you should again edit the appropriate files to resolve it, but this time make sure you run a <code>git svn rebase</code> before you try <code>dcommit</code>-ing again (since, remember, Subversion can only handle linear commit history).</p>
<p>As always, saying <code>man git-svn</code> or <code>git help svn</code> to your shell will give you all the other details. Among these, the most likely you&#8217;ll probably want to learn about is <a href="http://www.jukie.net/~bart/blog/svn-branches-in-git">how to track multiple Subversion branches as normal git branches</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://maymay.net/blog/2009/02/24/how-to-use-git-svn-as-the-only-subversion-client-youll-need/feed/</wfw:commentRss>
		<slash:comments>16</slash:comments>
		</item>
		<item>
		<title>clickjane.css: A CSS User Style Sheet to Help Detect and Avoid Clickjacking Attacks</title>
		<link>http://maymay.net/blog/2008/12/29/clickjanecss-a-css-user-style-sheet-to-help-detect-and-avoid-clickjacking-attacks/</link>
		<comments>http://maymay.net/blog/2008/12/29/clickjanecss-a-css-user-style-sheet-to-help-detect-and-avoid-clickjacking-attacks/#comments</comments>
		<pubDate>Mon, 29 Dec 2008 10:31:07 +0000</pubDate>
		<dc:creator>Meitar</dc:creator>
				<category><![CDATA[Crosspost]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[Maymay Media]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Security & Privacy]]></category>
		<category><![CDATA[Tech/Computing]]></category>
		<category><![CDATA[Web Design]]></category>

		<guid isPermaLink="false">http://maymay.net/blog/?p=847</guid>
		<description><![CDATA[Clickjacking or, more formally, user interface redressing, is a class of security vulnerabilities similar to phishing scams. The technique uses web standards to trick unsuspecting victims into performing actions they were not intending to. Clickjacking does not rely on bugs in any software. Instead, the technique is simply an abuse of the growing graphical capabilities [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://en.wikipedia.org/wiki/Clickjacking">Clickjacking</a> or, more formally, <dfn>user interface redressing</dfn>, is a class of security vulnerabilities similar to phishing scams. The technique uses web standards to trick unsuspecting victims into performing actions they were not intending to.</p>
<p>Clickjacking does not rely on bugs in any software. Instead, the technique is simply an abuse of the growing graphical capabilities that advanced web standards like <acronym title="Cascading Style Sheets">CSS</acronym> provide to web browsers. A good <a href="http://www.grc.com/sn/sn-168.htm">introduction to clickjacking</a> is provided by <a href="//grc.com/">Steve Gibson</a> and <a href="http://leoville.com/">Leo Laporte</a> on their <a href="//twit.tv/sn">Security Now! podcast</a>.</p>
<p>As far as I&#8217;m aware, only <a href="//mozilla.com/firefox/">Firefox</a> when combined with the <a href="//noscript.net/">NoScript</a> <a href="https://addons.mozilla.org/firefox/addon/722">add-on</a> and Internet Explorer when combined with the <a href="//guardedid.com/">GuardedID product</a> provide any measure of protection against clickjacking attacks. To date no other browser can detect, alert, or otherwise help you to avoid or mitigate the risks of clickjacking attacks.</p>
<p>That said, there&#8217;s gotta be <em>something</em> users of other browsers can do. Well, it may not be as much as what NoScript can do, but there is something: use a user style sheet to help expose common clickjacking attack attempts.</p>
<h2><code>clickjane.css</code> helps detect clickjacking attacks for all browsers</h2>
<p>Until browser manufacturers provide built-in protections against clickjacking attacks in their software (which is arguably the best place for such logic in the first place), I&#8217;ve started putting together <a href="http://github.com/meitar/clickjane-css/">a user style sheet I&#8217;m calling <code>clickjane.css</code></a> that attempts to instantly reveal common clickjacking attempts. Since it&#8217;s a <acronym title="Cascading Style Sheets">CSS</acronym> user style sheet, this approach should be cross-browser compatible so that users of any browser including Safari, Opera, and other browsers that don&#8217;t have other means of protecting against clickjacking attacks can use it.</p>
<p>I&#8217;ve only recently learned about this class of exploits and so I&#8217;m not supremely well-informed on the topic. As a result, the <code>clickjane.css</code> file is relatively sparse and currently only reveals what I&#8217;m sure is a small set of clickjacking attmpts. However, as I research the topic further and learn more about the actual underlying <acronym title="HyperText Markup Language">HTML</acronym> and <acronym title="Cascading Style Sheets">CSS</acronym> that clickjacking uses, I&#8217;ll be updating the <code>clickjane.css</code> code to reveal those attempts as well.</p>
<p>Naturally, contributions and assistance in any form are most welcome! Learn more about <code>clickjane.css</code> as well as how to use it at the <a href="http://github.com/meitar/clickjane-css/wikis">Clickjane <acronym title="Cascading Style Sheets">CSS</acronym> Github wiki</a>.</p>
<h2>Before and after <code>clickjane.css</code></h2>
<p>Here are two example screenshots of <a href="http://www.planb-security.net/notclickjacking/iframetrick.html">a benign clickjacking demo</a>.</p>
<ol>
<li>Before:
<div><div id="attachment_858" class="wp-caption alignnone" style="width: 310px"><a href="http://maymay.net/blog/wp-content/uploads/2008/12/before-clickjane.png"><img src="http://maymay.net/blog/wp-content/uploads/2008/12/before-clickjane-300x283.png" alt="Screenshot of Safari before clickjane.css is used to expose clickjacking attempts." title="before-clickjane" width="300" height="283" class="size-medium wp-image-858" /></a><p class="wp-caption-text">Screenshot of Safari before clickjane.css is used to expose clickjacking attempts.</p></div></div>
</li>
<li>After:
<div><div id="attachment_859" class="wp-caption alignnone" style="width: 310px"><a href="http://maymay.net/blog/wp-content/uploads/2008/12/after-clickjane.png"><img src="http://maymay.net/blog/wp-content/uploads/2008/12/after-clickjane-300x283.png" alt="Screenshot of Safari after clickjane.css is used to expose clickjacking attempts." title="after-clickjane" width="300" height="283" class="size-medium wp-image-859" /></a><p class="wp-caption-text">Screenshot of Safari after clickjane.css is used to expose clickjacking attempts.</p></div></div>
</li>
</ol>
<h2>Good habits you should get into to mitigate clickjacking risks</h2>
<p>Here is a list of behaviors that you should make habitual while you browse the web. Engaging in these behaviors can dramatically reduce the likelihood that you will be victimized by a clickjacking attack.</p>
<ul>
<li>Explicitly log out of any service you have logged in to when you are done. That log-out button is there for a reason: use it!</li>
<li>Avoid providing your browser with &#8220;Auto-Complete&#8221; information for critical sites, such as your bank.</li>
<li>Make sure you are <a href="http://www.adobe.com/support/security/advisories/apsa08-08.html">running Flash Player 10 or greater, which mitigates this vulnerability</a> for Adobe Flash content.</li>
</ul>
<h2>More resources to learn about clickjacking</h2>
<ul>
<li><a href="http://hackademix.net/2008/10/26/more-clickjacking/">Hackademix.net &#8211; More clickjacking</a> links to the <a href="http://video.google.com/videoplay?docid=-5747622209791380934">OWASP presentation</a>, the <a href="http://www.sectheory.com/clickjacking.htm">white paper</a>, and a blog post showing <a href="http://sirdarckcat.blogspot.com/2008/10/about-css-attacks.html">several <acronym title="Cascading Style Sheets">CSS</acronym>-based exploits</a>.</lI>
</ul>
<h2>Translations of this article:</h2>
<ul>
<li><ins datetime="2011-04-20T07:06:13+00:00"><a href="http://www.designcontest.com/show/everything-in-between-be">Belorussian (thanks, Bohdan Zograf)</a></ins></li>
<li><ins datetime="2011-08-24T20:14:39+00:00"><a href="http://webhostingrating.com/libs/clickjane-css-a-css-ua">Ukrainian (thanks, Alyona Lompar)</a></ins></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://maymay.net/blog/2008/12/29/clickjanecss-a-css-user-style-sheet-to-help-detect-and-avoid-clickjacking-attacks/feed/</wfw:commentRss>
		<slash:comments>21</slash:comments>
		</item>
		<item>
		<title>Why CSS needs delegation capabilities and not &#8220;variables&#8221;</title>
		<link>http://maymay.net/blog/2008/12/14/why-css-needs-delegation-capabilities-and-not-variables/</link>
		<comments>http://maymay.net/blog/2008/12/14/why-css-needs-delegation-capabilities-and-not-variables/#comments</comments>
		<pubDate>Sun, 14 Dec 2008 07:55:04 +0000</pubDate>
		<dc:creator>Meitar</dc:creator>
				<category><![CDATA[Crosspost]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Tech/Computing]]></category>
		<category><![CDATA[Web Design]]></category>
		<category><![CDATA[Web Standards]]></category>

		<guid isPermaLink="false">http://maymay.net/blog/?p=815</guid>
		<description><![CDATA[It&#8217;s been too long since I joined the fun, if amazingly heated, debates over the direction that Web standards are moving in. Recently, given the &#8220;free&#8221; time to do so, I decided to dive head first into what is (sadly) an almost 14 year old debate. The result is this blog post, which is mostly [...]]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s been too long since I joined the fun, if amazingly heated, debates over the direction that Web standards are moving in. Recently, given the &#8220;free&#8221; time to do so, I decided to dive head first into what is (sadly) an almost 14 year old debate. The result is this blog post, which is mostly a response to Bert Bos&#8217;s essay <cite><a href="http://www.w3.org/People/Bos/CSS-variables">Why &#8220;variables&#8221; in <acronym title="Cascading Style Sheets">CSS</acronym> are harmful</a></cite> and Matt Wilcox&#8217;s opposing response to that essay, <cite><a href="http://mattwilcox.net/archive/entry/id/991/">Why <acronym title="Cascading Style Sheets">CSS</acronym> needs to borrow from programming languages</a></cite>. Their articles are each worthy of a read, possibly before this one.</p>
<p>Here&#8217;s <strong>the summary</strong> of my argument.</p>
<p class="summary">Adding many &#8220;programmatic&#8221; features to the <acronym title="Cascading Style Sheets">CSS</acronym> language such as variables, macros, or flow control <em>is</em> a mistake. However, <acronym title="Cascading Style Sheets">CSS</acronym>&#8216;s failure to simply encode visual <em>relationships</em> (instead of merely typographic properties)&mdash;a severe deficiency in the core language itself&mdash;requires the addition of delegation features. With the additional capability to reference an arbitrary element&#8217;s computed value regardless of its hierarchical context, <acronym title="Cascading Style Sheets">CSS</acronym> will be more accessible to both amateur and professional web designers, more capable, and will more forcefully promote the semantic Web and its ideals.</p>
<h3>In this corner: <acronym title="Cascading Style Sheets">CSS</acronym> variables are harmful</h3>
<p>Bert does a great job of summarizing the conclusion of his argument himself. In his essay, Bert says:</p>
<blockquote cite="http://www.w3.org/People/Bos/CSS-variables"><p>Adding any form of macros or additional scopes and indirections, including symbolic constants, is not just redundant, but changes <acronym title="Cascading Style Sheets">CSS</acronym> in ways that make it unsuitable for its intended audience. Given that there is currently no alternative to <acronym title="Cascading Style Sheets">CSS</acronym>, these things must not be added.</p></blockquote>
<p>As we all know, one of the wonderful things about <acronym title="Cascading Style Sheets">CSS</acronym> is that the core language itself is remarkably simple. (What&#8217;s <em>not</em> simple is the spectacular way browser manufacturers have destroyed everyone&#8217;s hope that implementing <acronym title="Cascading Style Sheets">CSS</acronym>-based designs in the real world will ever be easy, but that&#8217;s a whole different can of worms.) Fundamentally, <acronym title="Cascading Style Sheets">CSS</acronym>&#8216;s syntax can be explained with a mere three major components: property/value pairs, declaration blocks, and rule sets.</p>
<p>What this means is that <acronym title="Cascading Style Sheets">CSS</acronym> <em>as a language</em> is stupidly easy to learn. I think everyone would agree that it&#8217;s certainly easier to learn than, say, JavaScript or <a href="//en.wikipedia.org/wiki/Extensible_Stylesheet_Language"><acronym title="eXtensible Stylesheet Language">XSL</acronym></a>. Now, that&#8217;s important because, without putting too fine a point on it, Bert mentions multiple times that <acronym title="Cascading Style Sheets">CSS</acronym>&#8216;s &#8220;intended audience&#8221; are the diverse and likely relatively technically ignorant content authors that are responsible for the overwhelming majority of web pages on the public Internet today.</p>
<p>He makes the very good point that <q cite="http://www.w3.org/People/Bos/CSS-variables">The value of the semantic Web isn&#8217;t defined by how well structured the best documents are, but by how well structured the vast majority of documents</q> are. In other words, <acronym title="Cascading Style Sheets">CSS</acronym> needs to remain instantly useable <em>and reusable</em> to these untrained, amateur web content publishers for the benefits of self-describing documents (i.e., the semantic Web) to see mass adoption.</p>
<p>To wit:</p>
<blockquote cite="http://www.w3.org/People/Bos/CSS-variables"><p>reusing other people&#8217;s style sheets is more difficult if those style sheets contain user-defined names. Class names are an example. Their names may suggest why the author created them (assuming they are in a language you understand), but typically you will have to look at the document to see where they occur and why. Symbolic constants make that problem worse.</p></blockquote>
<p>And, later:</p>
<blockquote cite="http://www.w3.org/People/Bos/CSS-variables"><p>For many people, style sheets with constants will thus simply not be usable. It is too difficult to look in two places at once, the place where a value is used and the place where it is defined, if you don&#8217;t know why the rule is split in this way. Many people are confused by indirection anyway and adding an extra one, in addition to the element and class names, has the same effect as obfuscating the style sheet.</p></blockquote>
<p>Whether or not you believe Bert Bos is underestimating the average web designer, it&#8217;s pretty clear that these are really good points. Nobody wants <acronym title="Cascading Style Sheets">CSS</acronym> to be obfuscated, hard to learn, or hard to reuse. That&#8217;d just be crazy talk.</p>
<h3>In the other corner: <acronym title="Cascading Style Sheets">CSS</acronym> variables are a real-world requirement</h3>
<p>The more features you add to an application, a programming language, or indeed any software, the more difficult it becomes to grok it. As the Python people would say, the larger a language gets the more difficult it is to hold all of it in your head. Nevertheless, adding &#8220;features&#8221; is sometimes the only way to add <em>capabilities</em>, and I don&#8217;t think anyone in their right mind would argue that, once written, software should never change. (That&#8217;d just be crazy talk, too.)</p>
<p>In his opposing arguments, Matt Wilcox recognizes this when he says, <q cite="http://mattwilcox.net/archive/entry/id/991/">Yes, the syntax should be simple, but the capabilities of <acronym title="Cascading Style Sheets">CSS</acronym> should not.</q> What he&#8217;s alluding to without verbalizing it is the balance between adding necessary capabilities without unnecessarily growing the &#8220;size of the language.&#8221;</p>
<p>However, Matt says that modern web design <em>methodologies</em> (e.g., separation of concerns between structure, presentation, and behavior) dictate that <acronym title="Cascading Style Sheets">CSS</acronym> needs more capabilities than it currently has:</p>
<blockquote cite="http://mattwilcox.net/archive/entry/id/991/"><p><acronym title="Cascading Style Sheets">CSS</acronym> lacks capabilities to allow truly flexible design, requiring layer upon layer of ‘tricks’ to accomplish certain objectives, requiring content to be structured ‘just so’ to achieve a display objective, or in the case of some designs proving instead to be completely incapable.</p>
<p>[…]</p>
<p><acronym title="Cascading Style Sheets">CSS</acronym>’s positioning is a cludge. It’s a cludge because you can only position relative to the last positioned parent container. Well, that limitation in itself dictates that all positioning relies upon how the content is structured. And that means the presentation and the content are not truly separable.</p></blockquote>
<p>To align <acronym title="Cascading Style Sheets">CSS</acronym>&#8216;s capabilities with the requirements of real-world web design objectives, he says, <acronym title="Cascading Style Sheets">CSS</acronym> needs to be capable of describing relationships between semantically and structurally arbitrary but visually related elements.</p>
<blockquote cite="http://mattwilcox.net/archive/entry/id/991/"><p>Visual design is fundamentally about relationships between elements. For all of the artistic flourishes and creativity, it’s about relationships. ‘That yellow’ only grabs your attention because of its contrasting relationship with ‘that blue’. ‘This heading’ only works as a heading because of it’s exaggerated relationship to the size of the body text. […] <acronym title="Cascading Style Sheets">CSS</acronym> has no clue about relationships, period. And that’s why <acronym title="Cascading Style Sheets">CSS</acronym> as it stands right now, is not good enough. That’s why <acronym title="Cascading Style Sheets">CSS</acronym> without variables (true variables), without basic logic, without maths, can never be as flexible as we need it to be.</p></blockquote>
<p>This is what web designers have been complaining about for (what feels like hundreds of) years. The fact that <acronym title="Cascading Style Sheets">CSS</acronym> has no capability to describe <em>presentational relationships</em> between elements in addition to directly describing an individual element&#8217;s presentational properties is a gaping hole that sorely degrades its ability to be a media-agnostic styling language. Every single web designer I&#8217;ve worked with has gasped at this omission, and though at first I didn&#8217;t understand why, the more I understood the principles behind graphic design the more I came to realize how fundamentally problematic this omission really is.</p>
<h3>Adding delegation makes <acronym title="Cascading Style Sheets">CSS</acronym> <em>easier</em> for designers</h3>
<p>As Matt eloquently stated, design is all about relationships. Good web designers create designs by constructing visual elements that have strong, often exacting relationships with other visual elements. There are many names and examples for this: visual language, visual hierarchy, the golden ratio, the grid, visual balance, the typographer&#8217;s scale, and so on.</p>
<p>What happens when the designer tries to define <em>a relationship</em> between elements? &#8220;How do I say that the whitespace between element A and element B should always be the same? How do I define element A&#8217;s height as half of element B&#8217;s?&#8221; These definitions, which are natural and necessary to the way designers work in both their mind and their mediums, are impossible to encode in <acronym title="Cascading Style Sheets">CSS</acronym>.</p>
<p>The closest you can get is declaring the same values to each element&#8217;s properties, not describing the relationship itself. This suffices only so long as these values are known ahead of time and are the same as one another, which severely limits the design possibilities we are capable of (without resorting to what Matt calls &#8220;tricks&#8221;). <em>That&#8217;s</em> why achieving simple visual effects are actually very complex and so, sadly, <em>that&#8217;s</em> where you&#8217;ll find the majority of indirection and obfuscation in <acronym title="Cascading Style Sheets">CSS</acronym> today. (I&#8217;m looking at you, <a href="http://www.alistapart.com/articles/fauxcolumns/">faux columns</a>.)</p>
<h3>So who wins?</h3>
<p>Both Bert Bos and Matt Wilcox have made some great points. Bert rightfully wishes to keep <acronym title="Cascading Style Sheets">CSS</acronym> lean and simple, even at the expense of some arguably beneficial styling power. Matt, on the other hand, argues that our needs as web designers have evolved faster than the technology to the point where <acronym title="Cascading Style Sheets">CSS</acronym> is <em>too</em> limited, fundamentally so.</p>
<p>The truth is, they&#8217;re both right. And they&#8217;re both wrong. Or rather, they are each taking a position that is too extreme. Bert&#8217;s absolutely correct when says that many of these proposed extensions are redundant and harmful, and yet Matt&#8217;s also correct that <acronym title="Cascading Style Sheets">CSS</acronym> lacks some fundamental capabilities that designers <em>expect</em> to be present.</p>
<p>Bert says that the <acronym title="Cascading Style Sheets">CSS</acronym> capabilities everyone&#8217;s asking for can be implemented using techniques that don&#8217;t rely on <acronym title="Cascading Style Sheets">CSS</acronym> whatsoever. These techniques, he says, make things like true <acronym title="Cascading Style Sheets">CSS</acronym> variables &#8220;redundant.&#8221;</p>
<blockquote cite="http://www.w3.org/People/Bos/CSS-variables"><p>There are examples of <acronym title="Cascading Style Sheets">CSS</acronym> with constants to satisfy all styles of programming, e.g.: <a href="http://davidwalsh.name/css-variables-php-dynamic">David Walsh</a> (in <acronym title="PHP Hypertext Preprocessor; an HTML-embedded scripting language">PHP</acronym>), <a href="http://sperling.com/examples/pcss/">Tedd Sperling</a> (in <acronym title="PHP Hypertext Preprocessor; an HTML-embedded scripting language">PHP</acronym>), <a href="http://www.digital-web.com/articles/generating_dynamic_css_with_php/">Digital Web Magazine</a> (in <acronym title="PHP Hypertext Preprocessor; an HTML-embedded scripting language">PHP</acronym>), <a href="http://ecoconsulting.co.uk/training/css_includes.shtml">Eco Consulting</a> (in <acronym title="Server Side Include">SSI</acronym>), and <a href="http://icant.co.uk/articles/cssconstants/">Christian Heilmann</a> (<acronym title="Server Side Include">SSI</acronym> and <acronym title="PHP Hypertext Preprocessor; an HTML-embedded scripting language">PHP</acronym>).</p></blockquote>
<p>Quite simply, he&#8217;s correct in stating that programmatic features need not be added to <acronym title="Cascading Style Sheets">CSS</acronym> proper to achieve desired results, but he&#8217;s incorrect in his apparent thinking that designers will be able to use these other tools to leverage <acronym title="Cascading Style Sheets">CSS</acronym>. Take, for instance, the probably more familiar (though not linked above) notion of using JavaScript to manipulate <acronym title="Cascading Style Sheets">CSS</acronym> values.</p>
<pre><code class="javascript">var x = document.getElementById('SideBar'); // get #SideBar element
var y = document.getElementById('MainColumn'); // get #MainColumn
var z = document.defaultView.getComputedStyle(y, '').getPropertyValue('height'); // get computed height of #MainColumn
x.style.height = ( parseInt(z) / 2 ) + 'px'; // set #SideBar's height 1/2 of #MainColumn's</code></pre>
<p>This is an example of programmatic code that uses variables and expressions. It sets the element with the ID of <code>SideBar</code> to half the pixel height of the element with the ID of <code>MainColumn</code>. It does this by obtaining the <code>MainColumn</code>&#8216;s height (at the time this code runs) and saving it in a variable, then performs some trivial math to half the value and use the result as the pixel height of the <code>SideBar</code>.</p>
<p>Doing this is currently impossible with <acronym title="Cascading Style Sheets">CSS</acronym> alone, yet it&#8217;s something that clearly belongs with whatever other &#8220;presentational&#8221; code exists and not in &#8220;programmatic&#8221; scripts that would otherwise be charged with defining &#8220;functionality.&#8221; As Matt states, using JavaScript to &#8220;script&#8221; solutions to <acronym title="Cascading Style Sheets">CSS</acronym>&#8216;s shortcomings like this is not an acceptable answer.</p>
<blockquote cite="http://mattwilcox.net/archive/entry/id/991/"><p><acronym title="Cascading Style Sheets">CSS</acronym> doesn’t have [basic logic or maths]. Nor is it the job of JavaScript to make up for this lack of abilities. JavaScript is about interaction behaviour, and what we are talking about here is pure display logic. Not interaction logic.</p></blockquote>
<p>Moreover, the <em>place</em> designers expect to put code like this is, of course, into a <acronym title="Cascading Style Sheets">CSS</acronym> style sheet. The <em>way</em> designers expect to put code like this into <acronym title="Cascading Style Sheets">CSS</acronym> is by adding delegation features. Requiring designers to learn JavaScript (or any other programming language) to encode such design relationships is nothing short of ridiculous. In what world is that easier for untrained laymen to understand than <acronym title="Cascading Style Sheets">CSS</acronym>?</p>
<h3>Adding delegation to <acronym title="Cascading Style Sheets">CSS</acronym> is worth the effort</h3>
<p>One of Bert&#8217;s arguments against such additions to <acronym title="Cascading Style Sheets">CSS</acronym> is that implementations would become harder to create, and that we&#8217;ll (almost certainly) see more bugs.</p>
<blockquote cite="http://www.w3.org/People/Bos/CSS-variables"><p>extending <acronym title="Cascading Style Sheets">CSS</acronym> makes implementing more difficult and programs bigger, which leads to fewer implementations and more bugs. That has to be balanced against the usefulness of the extension.</p></blockquote>
<p>Although I do agree with his statement that an extension&#8217;s usefulness has to be balanced against its potential costs, I think something so fundamental to design methodology as delegation greatly overcompensates for the cost of such implementation efforts. Moreover, if I understand Bert correctly and as he also discusses, the majority of implementations that would need to implement such delegation already have relatively complex internal structures to make the implementation effort somewhat easier:</p>
<blockquote cite="http://www.w3.org/People/Bos/CSS-variables"><p>There is no scoping [in proposals that only define <em>global</em> constants]. That means that an implementation needs a symbol table, but no stack. A stack would require a little bit more memory, but mostly it would make implementations more complex. (Although every programmer has, one hopes, learnt to program a symbol table with lexical scope during his training.) Constants in <acronym title="Cascading Style Sheets">CSS</acronym> are thus easier than, e.g., <a href="http://www.w3.org/TR/xml-names/"><acronym title="eXtensible Markup Language">XML</acronym> Namespaces,</a> which <em>are</em> lexically scoped.</p>
<p>It is different for those <acronym title="Cascading Style Sheets">CSS</acronym> implementations that provide a <a href="http://www.w3.org/TR/DOM-Level-2-Style/"><acronym title="Cascading Style Sheets">CSS</acronym> Object Model</a> (an <acronym title="Application Programming Interface">API</acronym> for manipulating a style sheet in memory). Those implementations <em>do</em> need to keep track of scope in some way, because adding or removing a line of the style sheet can make a previously redundant definition become meaningful.</p></blockquote>
<p>In order to use JavaScript to solve many of the shortcomings of <acronym title="Cascading Style Sheets">CSS</acronym>, as <em>huge</em> numbers of professional web developers do routinely, we use the very <acronym title="Cascading Style Sheets">CSS</acronym> Object Model whose prior implementation already exists for us to build upon.</p>
<h3><acronym title="Cascading Style Sheets">CSS</acronym> delegation doesn&#8217;t grow the size of the language</h3>
<p>For the sake of argument, let&#8217;s simplify our requirement somewhat so that our somewhat contrived example of design intent is to <em>create a relationship</em> between the <code>MainColumn</code> and the <code>SideBar</code> elements such that they are of equal height. This is more informally known as &#8220;making columns.&#8221;</p>
<p>Here&#8217;s what a natural, hypothetical snippet of <acronym title="Cascading Style Sheets">CSS</acronym> would look like if the language supported delegation features such that it could encode visual relationships.</p>
<pre><code class="css">#SideBar { height: #MainColumn; }</code></pre>
<p>This code theoretically says almost the exact same thing as the JavaScript shown earlier (save for the division, of course); it takes the computed value of the <code>MainColumn</code> element&#8217;s height property and applies that value to the <code>SideBar</code> element&#8217;s height property. In other words, &#8220;The SideBar&#8217;s (element B&#8217;s) height is always the same as the MainColumn&#8217;s (element A&#8217;s).&#8221; (Of course, this is a parse error in reality today.)</p>
<p>This extremely trivial example has some remarkably far-reaching implications, and yet there is really nothing radical about its syntax. Making this a reality significantly expands the capabilities of <acronym title="Cascading Style Sheets">CSS</acronym> without dramatically increasing the size of the language. This capability would not only <a href="http://mattwilcox.net/archive/entry/id/1030/" title="Why you should not use display:table; for layout.">beat the pants off</a> &#8220;<a href="http://www.sitepoint.com/blogs/2008/10/22/everything-you-know-about-css-is-wrong/" title="SitePoint's featuring articles and books about browser support for this."><acronym title="Cascading Style Sheets">CSS</acronym> tables</a>,&#8221; it also potentially obsoletes the arguably <a href="http://ejohn.org/blog/css3-template-layout/" title="Just because John Resig likes it doesn't mean it's good.">misguided efforts of the <acronym title="Cascading Stlye Sheets level 3">CSS3</acronym> Advanced Layout</a> and Grid Positioning modules, too.</p>
<p>We&#8217;ve long since abandoned <code>table</code> layouts because they force us to use presentational markup. That&#8217;s still what &#8220;<acronym title="Cascading Style Sheets">CSS</acronym> tables&#8221; force us to do, too. In other words, with <code>display: table</code>, the <code>SideBar</code> needs to be a child of the <code>MainColumn</code> element or, maybe worse and more likely, a child of a semantically meaningless wrapper element.</p>
<p><acronym title="Cascading Style Sheets">CSS</acronym> positioning was introduced with the promise of freeing us from source-order-dependent styling, without which there is no hope of efficiently abstracting presentation away from structure. Moreover, <strong>abstracting presentation away from structure is the single most important prerequisite needed to improve document reusability and strengthen the semantic Web</strong>. Absolute positioning works, but limitations elsewhere in <acronym title="Cascading Style Sheets">CSS</acronym> mean its use is problematic for many designs, so in practice it doesn&#8217;t gain widespread adoption.</p>
<p>Here&#8217;s a theoretical solution to a two-column and a footer layout using <acronym title="Cascading Style Sheets">CSS</acronym> delegation with this semantic HTML:</p>
<pre><code class="html">&lt;body&gt;
    &lt;div id="MainColumn"&gt;I'm the main column.&lt;/div&gt;
    &lt;div id="SideBar"&gt;I'm the right-hand sidebar.&lt;/div&gt;
    &lt;div id="Legalese"&gt;No one will read me.&lt;/div&gt;
&lt;/body&gt;
</code></pre>
<p>The <acronym title="Cascading Style Sheets">CSS</acronym> would look extremely familiar, possibly like this:</p>
<pre><code class="css">#MainColumn { margin: 0 25% 1em 0; float: left; }
#SideBar { width: 25%; min-height: #MainColumn; }</code></pre>
<p>Using the same <acronym title="HyperText Markup Language">HTML</acronym>, the same solution using the <acronym title="Cascading Stlye Sheets level 3">CSS3</acronym> Advanced Layout module would look something more like this, although to be frank I&#8217;m not certain I fully understand this syntax even after staring at it for months:</p>
<pre><code class="css">body {
    display: "a  b"
             ".  ." /1em
             "c  c"
             75% 25%
}
#MainColumn { position: a; }
#SideBar { position: b; }
#Legalese { position: c; }
</code></pre>
<p>Not only does there seem to me to be far more indirection in this method than there would be using <acronym title="Cascading Style Sheets">CSS</acronym> delegation, there is also an enormous increase to the size of the <acronym title="Cascading Style Sheets">CSS</acronym> language: a new (ASCII-art?!) value to the display property whose syntax is clunky at best. A similar story can be <a href="http://ajaxian.com/archives/w3c-css-grid-positioning" title="Ajaxian reports on a rumor that Internet Explorer 8 will add support for Grid Positioning and shows what that might look like in code.">said of the <acronym title="Cascading Stlye Sheets level 3">CSS3</acronym> Grid Positioning module</a>, which does lots more than just add a new (already complex) <code>gr</code> <acronym title="Cascading Style Sheets">CSS</acronym> unit.</p>
<p>The upshot is that the Advanced Layout and the Grid Positioning modules are doing <em>some</em> of the right things in <em>many</em> of the wrong ways. Both those modules add unnecessary complexity to <acronym title="Cascading Style Sheets">CSS</acronym> without giving designers a natural way to say what they mean. They do more to introduce obfuscation and indirection than simple delegation would, and they aren&#8217;t as broadly capable. Both of them try to solve a specific problem instead of dealing with fundamental deficiencies in the <em>toolset</em> designer&#8217;s have to work with.</p>
<h3>Designers want relationships via delegation, not variables</h3>
<p>Adding delegation such as that I&#8217;ve just shown is a natural, necessary addition to <acronym title="Cascading Style Sheets">CSS</acronym> because it is how designers create visual components&mdash;such as grids&mdash;in their designs. Variables (and constants, and macros, etc.), which simply reuse and modify pre-defined statements aren&#8217;t what designers care about. Adding them <em>will</em> bloat <acronym title="Cascading Style Sheets">CSS</acronym> without adding useful functionality.</p>
<p>&#8220;Okay,&#8221; you may be saying to yourself, &#8220;but delegation is itself a kind of variable, isn&#8217;t it?&#8221; Technically yes, however adding delegation resolves the core deficiency in the <acronym title="Cascading Style Sheets">CSS</acronym> language that designers need to use every day. Yes, it&#8217;s technically a form of variable, but that&#8217;s not how designers think of it. To say that one element&#8217;s visual properties is like another makes a variable only by creating a logical and visually appropriate mapping from the first element&#8217;s property to the second independent of markup, thereby avoiding indirection in the form of a variable name or other unfamiliar symbol.</p>
<p>Delegation like this doesn&#8217;t require the addition of anything other than what already exists in <acronym title="Cascading Style Sheets">CSS</acronym>. Class names and ID values are identifiers whose indirection people <em>already</em> have to deal with. Using them for delegation (to reference another element&#8217;s <em>style</em>) doesn&#8217;t increase the cognitive load any more than using them to reference <em><acronym title="HyperText Markup Language">HTML</acronym> elements</em> does. Though untested, the cognitive load might actually be even less since the <acronym title="Cascading Style Sheets">CSS</acronym> delegation&#8217;s references could be in the same (style sheet) file.</p>
<p>Moreover, delegation will increase the likelihood of document reusability by enabling style sheets to be more self-describing, more self-referential, in a similar way as good markup is. It satisfies a very fundamental need that designers have to define graphical relationships between elements. At the same time, it does so in a way that is natural to both their way of thinking and beneficial to the separation of concerns principle on which the &#8220;web stack&#8221; (the trifecta of <acronym title="HyperText Markup Language">HTML</acronym>, <acronym title="Cascading Style Sheets">CSS</acronym>, and JavaScript) is based.</p>
]]></content:encoded>
			<wfw:commentRss>http://maymay.net/blog/2008/12/14/why-css-needs-delegation-capabilities-and-not-variables/feed/</wfw:commentRss>
		<slash:comments>18</slash:comments>
		</item>
		<item>
		<title>Extract list of all Apple WikiServer wiki titles into CSV format</title>
		<link>http://maymay.net/blog/2008/09/22/extract-list-of-all-apple-wikiserver-wiki-titles-into-csv-format/</link>
		<comments>http://maymay.net/blog/2008/09/22/extract-list-of-all-apple-wikiserver-wiki-titles-into-csv-format/#comments</comments>
		<pubDate>Mon, 22 Sep 2008 05:35:55 +0000</pubDate>
		<dc:creator>Meitar</dc:creator>
				<category><![CDATA[Bash/Shell Scripting]]></category>
		<category><![CDATA[Crosspost]]></category>
		<category><![CDATA[Mac OS X]]></category>
		<category><![CDATA[Tech/Computing]]></category>

		<guid isPermaLink="false">http://maymay.net/blog/?p=652</guid>
		<description><![CDATA[An interesting request came in today from a coworker. She wanted to create a spreadsheet that contained all of our intranet&#8217;s wiki pages (which uses the Apple WikiServer), presumably because Apple doesn&#8217;t provide an easy way to &#8220;list all pages&#8221; in the wiki itself. Along with the page title, she also wanted to extract its [...]]]></description>
			<content:encoded><![CDATA[<p>An interesting request came in today from a coworker. She wanted to create a spreadsheet that contained all of our intranet&#8217;s wiki pages (which uses the Apple WikiServer), presumably because Apple doesn&#8217;t provide an easy way to &#8220;list all pages&#8221; in the wiki itself. Along with the page title, she also wanted to extract its internal ID, its <acronym title="Uniform Resource Locator">URL</acronym>, and the time the page was created as well as the time it was last modified.</p>
<p>I spent about an hour looking into this this afternoon and it turns out that much of this information is readily available on the filesystem in the Apple WikiServer&#8217;s data store. I whipped up the following shell script to extract this information in CSV format, exactly as requested.</p>
<p>I&#8217;m posting this script here in case someone else wants similar &#8220;export a list of WikiServer pages to a comma-separated values (CSV) file&#8221; functionality but isn&#8217;t sure how to go about getting it. To use this, just edit the line that reads <code>http://my-server.example.com/groups/wiki/</code> so that it refers to the wiki base <acronym title="Uniform Resource Identifier">URI</acronym> of your own server.</p>
<p><ins datetime="2008-09-23T07:15:49+00:00"><strong>Update:</strong> The latest version of this script is now available at <a href="//github.com/meitar/wikipages2csv/">its Github-hosted repository</a>. <strong>You should probably use that instead of the script below.</strong></ins></p>
<pre class="shell">
#!/bin/<var>sh</var> -
#
# Script to extract data from an Apple WikiServer's data store by querying the
# filesystem itself. Creates a 'wikipages.csv' file that's readable by any
# spreadsheeting application, such as Numbers.app or Microsoft Excel.app.
#
# USAGE:   To use this script, change to the WikiServer's pages directory, then
#          just run this script. A file named wikipages.csv will be created in
#          your current directory. For instance:
#
#              cd /Library/Collaboration/Groups/mygroup/wiki  # dir to work in
#              wikipages2csv.sh                               # run the script
#              cp wikipages.csv ~/Desktop                     # save output
#
# WARNING: Since the WikiServer's files are only accessible as root, this script
#          must be run as root to function. Additionally, this is not extremely
#          well tested, so use at your own risk.
#
# Author:  Meitar Moscovitz
# Date:    Mon Sep 22 15:03:54 EST 2008

##### CONFIGURE HERE ########

# The prefix to append to generated links. NO SPACES!
WS_URI_PREFIX=http://my-server.example.com/groups/wiki/

##### END CONFIGURATION #####
# DO NOT EDIT PAST THIS LINE
#############################

WS_CSV_OUTFILE=wikipages.csv
WS_PAGE_IDS_FILE=`mktemp ws-ids.tmp.XXXXXX`

function extractPlistValueByKey () {
    head -n \
      $(expr 1 + `grep -n "&lt;key&gt;$1&lt;/key&gt;" page.plist | cut -d ':' -f 1`) page.plist | \
        tail -n 1 | cut -d '&gt;' -f 2 | cut -d '&lt;' -f 1
}

function linkifyWikiServerTitle () {
    echo $1 | sed -e 's/ /_/g' -e 's/&amp;amp;/_/g' -e 's/&amp;gt;/_/g' -e 's/&amp;lt;/_/g' -e 's/\?//g'
}

function formatISO8601date () {
    echo $1 | sed -e 's/T/ /' -e 's/Z$//'
}

function csvQuote () {
    echo $1 | grep -q ',' &gt;/dev/null
    if [ $? -eq 0 ]; then
        echo '"'$1'"'
    else
        echo $1
    fi
}

ls -d [^w]*.page | \
  sed -e 's/^\([a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9]\)\.page$/\1/' &gt; $WS_PAGE_IDS_FILE

echo "Title,ID,Date Created,Last Modified,<acronym title="Uniform Resource Identifier">URI</acronym>" &gt; $WS_CSV_OUTFILE
while read id; do
    cd $id.page
    title=$(extractPlistValueByKey title)
    created_date="$(formatISO8601date $(extractPlistValueByKey createdDate))"
    modified_date="$(formatISO8601date $(extractPlistValueByKey modifiedDate))"
    link=$WS_URI_PREFIX"$id"/`linkifyWikiServerTitle "$title"`.html
    cd ..
    echo `csvQuote "$title"`,$id,$created_date,$modified_date,`csvQuote "$link"` &gt;&gt; $WS_CSV_OUTFILE
done &lt; $WS_PAGE_IDS_FILE
rm $WS_PAGE_IDS_FILE
</pre>
<p>For those new to the Wiki Server, this <a href="/blog/2008/04/05/a-web-developers-introduction-to-the-apple-wikiserver-part-1/">introduction to the Apple WikiServer for web developers</a> may be of interest.</p>
]]></content:encoded>
			<wfw:commentRss>http://maymay.net/blog/2008/09/22/extract-list-of-all-apple-wikiserver-wiki-titles-into-csv-format/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Add a post limit and output format to the WordPress Category Posts plugin v2.0</title>
		<link>http://maymay.net/blog/2008/09/19/add-a-post-limit-and-output-format-to-the-wordpress-category-posts-plugin-v20/</link>
		<comments>http://maymay.net/blog/2008/09/19/add-a-post-limit-and-output-format-to-the-wordpress-category-posts-plugin-v20/#comments</comments>
		<pubDate>Fri, 19 Sep 2008 15:24:25 +0000</pubDate>
		<dc:creator>Meitar</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Tech/Computing]]></category>
		<category><![CDATA[Web Design]]></category>
		<category><![CDATA[Writing and blogging]]></category>

		<guid isPermaLink="false">http://maymay.net/blog/?p=643</guid>
		<description><![CDATA[Tonight I wrote a quick (and idiotic) patch to the very simple WordPress Category Post plugin v2.0. This backwards-compatible patch features: parameter-based post limit to define how many posts the plugin function will print parameter-based format option to output the posts in real &#60;li&#62; elements The wp-category-posts.php patch file is available for download here. To [...]]]></description>
			<content:encoded><![CDATA[<p>Tonight I wrote a quick (and idiotic) patch to the very simple WordPress Category Post plugin v2.0. This backwards-compatible patch features:</p>
<ul>
<li>parameter-based post limit to define how many posts the plugin function will print</li>
<li>parameter-based format option to output the posts in real <code>&lt;li&gt;</code> elements</li>
</ul>
<p>The <a href='http://maymay.net/blog/wp-content/uploads/2008/09/wp-category-posts.patch'><code>wp-category-posts.php</code> patch file is available for download here</a>. To apply the patch, run the following commands at your shell promp:</p>
<pre class="shell">
cd <var>path/to/wordpress/installation</var>/wp-content/plugins/wordpress-category-posts
patch -p0 < <var>path/to/downloaded</var>/wp-category-posts.patch
</pre>
<p>I&#8217;m hoping this will get integrated as the next version of the plugin, perhaps version 2.1.</p>
]]></content:encoded>
			<wfw:commentRss>http://maymay.net/blog/2008/09/19/add-a-post-limit-and-output-format-to-the-wordpress-category-posts-plugin-v20/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Scrum-style Burn Down Chart in iWork &#8217;08 Numbers.app</title>
		<link>http://maymay.net/blog/2008/09/06/scrum-style-burn-down-chart-in-iwork-08-numbersapp/</link>
		<comments>http://maymay.net/blog/2008/09/06/scrum-style-burn-down-chart-in-iwork-08-numbersapp/#comments</comments>
		<pubDate>Sat, 06 Sep 2008 11:46:26 +0000</pubDate>
		<dc:creator>Meitar</dc:creator>
				<category><![CDATA[Apple/Macintosh]]></category>
		<category><![CDATA[Crosspost]]></category>
		<category><![CDATA[Information & Communication]]></category>
		<category><![CDATA[Productivity]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Tech/Computing]]></category>
		<category><![CDATA[Web Design]]></category>

		<guid isPermaLink="false">http://maymay.net/blog/?p=617</guid>
		<description><![CDATA[Ever since I was introduced to the Scrum methodology of software development, I&#8217;ve enjoyed my work so much more than before. Most of that enjoyment is due to a sense of visibility, of knowing what&#8217;s going on. I find working without an accurate awareness of the situation at large very disorienting, and software and web [...]]]></description>
			<content:encoded><![CDATA[<p>Ever since I was introduced to the <a href="http://en.wikipedia.org/wiki/Scrum_(development)">Scrum methodology of software development</a>, I&#8217;ve enjoyed my work so much more than before. Most of that enjoyment is due to a sense of visibility, of knowing what&#8217;s going on.</p>
<p>I find working without an accurate awareness of the situation at large very disorienting, and software and web development are notorious for being circumstances that change rapidly. That&#8217;s why one of my favorite things about Scrum is the <a href="http://en.wikipedia.org/wiki/Burn_down_chart">burn down chart</a>. This is nothing more complex than a simple graph that depicts how much work you bit off and how far along trying to chew it you actually are. The benefit, of course, is that it&#8217;s pretty obvious pretty quickly if you&#8217;ve bit off more than you can chew. ;)</p>
<p>So up &#8217;til now, my team and I have been doing this all on paper. There&#8217;s a certain tactile appreciation I have for doing this sort of thing on paper, but of course there are disadvantages, too. For instance, you can&#8217;t easily archive the information. You can&#8217;t easily share it with remote contractors. You can&#8217;t automatically mine this valuable data with software tools. You get the picture.</p>
<p>There are a few <a href="http://trac-hacks.org/wiki/ScrumBurndownPlugin">cool plugins to some tools like Trac</a> that do all this, but at first blush most of these tools seem to require that you move <em>all</em> of your Scrum&#8217;s planning into the digital world. That is, you can&#8217;t just do the burn down chart, you have to do all your estimation (MoSCoW desirability, sizing, estimating ideal hours) through some tool. That&#8217;s a big step, and I wanted something simpler.</p>
<p>So naturally, I came up with a spreadsheet in <a href="http://www.apple.com/iwork/numbers/">Numbers.app</a> as my solution. I mean, how much simpler can you get? Sure, it&#8217;s not exactly &#8220;well integrated&#8221; with other tools, but your non-tech-savvy boss will probably love it, and <a href="/blog/category/programming/applescript/">AppleScript</a> can be used to automate data extraction. Here&#8217;s what it looks like:</p>
<div id="attachment_618" class="wp-caption aligncenter" style="width: 310px"><a href="http://maymay.net/blog/wp-content/uploads/2008/09/example-burn-down-chart-in-numbers.png"><img src="http://maymay.net/blog/wp-content/uploads/2008/09/example-burn-down-chart-in-numbers-300x203.png" alt="An example Scrum-style burn down chart in Apple&#039;s iWork &#039;08 Numbers spreadsheeting application, complete with an actual chart." title="example-burn-down-chart-in-numbers" width="300" height="203" class="size-medium wp-image-618" /></a><p class="wp-caption-text">An example Scrum-style burn down chart in Apple's iWork '08 Numbers spreadsheeting application, complete with an actual chart.</p></div>
<p><small>(Click the screenshot to get a full-size view.)</small></p>
<p>As you can see, the Numbers sheet is a simple table and a line chart. I&#8217;ve embedded instructions for how to use the chart into the example itself, which I&#8217;ll quote here:</p>
<blockquote><p>
This is a sample Scrum-style iteration burn down chart for software development created by <a href="http://MeitarMoscovitz.com/">Meitar Moscovitz</a>. It can be used to plot a team’s progress throughout a development cycle (<acronym title="Also Known As">aka</acronym>. “iteration” or “sprint”). This sample chart depicts a 3-week iteration (15 working days) with a 150-point target goal.</p>
<p>The <em>X-axis</em> represents time, and is thus labelled <strong>Time in Days</strong>, while the <em>Y-axis</em> represents the work to be completed, and is labelled <strong>Points</strong>.</p>
<p>The <strong style="color: green;">green line</strong> shows the team’s <strong>ideal velocity</strong> based upon the total number of points—termed the <strong>Remaining Initial Value</strong>—scheduled for completion in the graphed iteration.</p>
<p>The <strong style="color: blue;">blue line</strong> shows the team’s <strong>actual velocity</strong> (or “completed work”), which is entered by the team leader (<acronym title="Also Known As">aka</acronym>. Scrum Master) after each day in the <strong>Done</strong> column.</p>
<p><strong>To use this chart:</strong> duplicate this sheet, enter your iteration’s total points in the <strong>Initial Value</strong> row of the <strong>Remaining</strong> column, and delete the values in the Done column except its initial value of 0. To add more days, copy and paste more rows into the table. Optionally, give the sheet and its contents new titles. ;)
</p></blockquote>
<p>Feel free to <a href='http://maymay.net/blog/wp-content/uploads/2008/09/example-burn-down-chartnumbers.zip'>download the <cite>Example Burn Down Chart.numbers</cite> file</a> and use it yourself. If you do use it, please leave a comment and let me know how you&#8217;re going. Thanks, and enjoy!</p>
<p><ins datetime="2008-09-08T08:25:58+00:00">(<a href="http://www.mountaingoatsoftware.com/alt_releaseburndown">Mike Cohn of Mountain Goat Software has got a similar spreadsheet for Excel</a> you can download.)</ins></p>
]]></content:encoded>
			<wfw:commentRss>http://maymay.net/blog/2008/09/06/scrum-style-burn-down-chart-in-iwork-08-numbersapp/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>How to install git on a shared web host&#8217;s server</title>
		<link>http://maymay.net/blog/2008/08/06/how-to-install-git-on-a-shared-web-hosts-server/</link>
		<comments>http://maymay.net/blog/2008/08/06/how-to-install-git-on-a-shared-web-hosts-server/#comments</comments>
		<pubDate>Wed, 06 Aug 2008 16:59:35 +0000</pubDate>
		<dc:creator>Meitar</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Tech/Computing]]></category>
		<category><![CDATA[Unix/Linux]]></category>
		<category><![CDATA[git]]></category>

		<guid isPermaLink="false">http://maymay.net/blog/?p=556</guid>
		<description><![CDATA[Tonight I found myself with the need to host my own git repository on one of my own servers. This time, for the first time, it was a server I don&#8217;t actually have administrative access to and it was one where git wasn&#8217;t pre-installed. Thankfully, with a bit of help from Blue Static, I built [...]]]></description>
			<content:encoded><![CDATA[<p>Tonight I found myself with the need to host my own git repository on one of my own servers. This time, for the first time, it was a server I don&#8217;t actually have administrative access to <em>and</em> it was one where git wasn&#8217;t pre-installed. Thankfully, with a bit of help from <a href="http://www.bluestatic.org/blog/2007/08/01/git-public-push-ing/">Blue Static</a>, I built and installed git from scratch in literally ten minutes. Here&#8217;s the short version of how I did it, which may even be generic enough that you can copy and paste this into a <code>bash</code> shell prompt on your server to do the same thing:</p>
<pre class="shell">
cd ~/                          # change to home directory
test -d ~/src || mkdir ~/src   # if there isn't already a ~/src directory, create it
cd ~/src                       # then change to that directory
curl -O http://www.kernel.org/pub/software/scm/git/git-1.5.6.4.tar.gz # download
tar -xvzf git-1.5.6.4.tar.gz   # and extract the git source code
cd git-1.5.6.4                 # change to the source code directory
./configure --prefix=$HOME     # configure build to install into $HOME
make                           # do the build
make install                   # move the built binaries to the right places
echo "export PATH=\$PATH:$HOME/bin" >> ~/.bashrc # make sure non-interactive shells can find git
</pre>
<p>Of special note is the last line, which sets up the necessary <code>$PATH</code> specifically for non-interactive <code>bash</code> shells for use with <code>git-push</code> or <code>git-pull</code>. With out that, you&#8217;ll run into the infamous &#8220;bash: git-receive-pack: command not found&#8221; error.</p>
<p><ins datetime="2008-10-14T08:57:11+00:00">Also, of course, lines 4 through 6 are referring to version 1.5.6.4 of the git tarballs, so you may want to change these to point to whatever is now the most recent version.</ins></p>
]]></content:encoded>
			<wfw:commentRss>http://maymay.net/blog/2008/08/06/how-to-install-git-on-a-shared-web-hosts-server/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>One Minute Mac Tip: Remove .DS_Store files from ZIP Archives</title>
		<link>http://maymay.net/blog/2008/08/04/one-minute-mac-tip-remove-ds_store-files-from-zip-archives/</link>
		<comments>http://maymay.net/blog/2008/08/04/one-minute-mac-tip-remove-ds_store-files-from-zip-archives/#comments</comments>
		<pubDate>Mon, 04 Aug 2008 06:07:51 +0000</pubDate>
		<dc:creator>Meitar</dc:creator>
				<category><![CDATA[Bash/Shell Scripting]]></category>
		<category><![CDATA[Crosspost]]></category>
		<category><![CDATA[Mac OS X]]></category>
		<category><![CDATA[Productivity]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Tech/Computing]]></category>
		<category><![CDATA[Windows]]></category>

		<guid isPermaLink="false">http://maymay.net/blog/?p=544</guid>
		<description><![CDATA[The Mac OS X Finder has some nifty features, one of which is an exceptionally useful contextual menu item to create ZIP archives of folders. Unfortunately, the Finder also has some really, really annoying habits, one of which is to create a file named .DS_Store in each folder a user opens (when not in Column [...]]]></description>
			<content:encoded><![CDATA[<p>The Mac <acronym title="Operating System">OS</acronym> X Finder has some nifty features, one of which is an exceptionally useful contextual menu item to create ZIP archives of folders. Unfortunately, the Finder also has some really, really annoying habits, one of which is to create a file named <code>.DS_Store</code> in each folder a user opens (when not in Column view). What this means is that if you create a ZIP archive on your Mac and then send it to someone who unzips it without the Finder (such as a Windows user using the Windows Explorer), the recipient will see a lot of litter in the form of useless and meaningless <code>.DS_Store</code> files.</p>
<p>If you&#8217;re not afraid of the Terminal, this can be avoided. Put the following lines in your <code>~/.profile</code> (or similar):</p>
<pre class="shell">
alias rmds='find . -name ".DS_Store" -type f -print0 | xargs -0 rm'
</pre>
<p>What this does is creates a new command that you can use (<code>rmds</code>) which recursively finds and deletes any regular file named &#8220;.DS_Store&#8221; starting from the current directory. Thus, running this command in the folder you are about to create an archive out of will clean it first, and will prevent unnecessary confusion on the part of your archive file recipient.</p>
<p>Alternatively, another way to do this is to use the command-line <code>zip</code> program and an (admittedly more complicated) pipeline to remove the <code>.DS_Store</code> files <em>after</em> they have been added to the archive. To do that, use this series of commands:</p>
<pre class="shell">
zip -d <var>ZIPfile.zip</var> `unzip -l <var>ZIPfile.zip</var> | grep .DS_Store | awk '{print $4}'`
</pre>
<p>where, naturally, <var>ZIPfile.zip</var> is the ZIP archive you want to remove the <code>.DS_Store</code> files from. Creating an alias out of that command (and making it work for paths that contain spaces) is left as an exercise for the reader. ;)</p>
<p>As an aside, the <code>alias</code>, <code>find</code> and <code>xargs</code> commands are incredibly useful in their own right and can be used to do a lot of pretty amazing things. As always, <code>man <var>command</var></code> will give you the nitty gritty.</p>
<p>Also as an aside, you can stop the Finder from creating <code>.DS_Store</code> files entirely when browsing network volumes (like Windows shares) with another command, <a href="//support.apple.com/kb/HT1629">documented in Apple&#8217;s Knowledge Base</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://maymay.net/blog/2008/08/04/one-minute-mac-tip-remove-ds_store-files-from-zip-archives/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>How web designers can do their own HTML/CSS: Read Foundation Website Creation</title>
		<link>http://maymay.net/blog/2008/07/21/how-web-designers-can-do-their-own-htmlcss/</link>
		<comments>http://maymay.net/blog/2008/07/21/how-web-designers-can-do-their-own-htmlcss/#comments</comments>
		<pubDate>Mon, 21 Jul 2008 14:22:19 +0000</pubDate>
		<dc:creator>Meitar</dc:creator>
				<category><![CDATA[Crosspost]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[Hypertext Copywriting]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Maymay Media]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Tech/Computing]]></category>
		<category><![CDATA[Usability]]></category>
		<category><![CDATA[Web Accessibility]]></category>
		<category><![CDATA[Web Design]]></category>
		<category><![CDATA[Web Standards]]></category>
		<category><![CDATA[Writing and blogging]]></category>

		<guid isPermaLink="false">http://maymay.net/blog/?p=476</guid>
		<description><![CDATA[Last month, 37signals published a short but sweet post about why web designers should do the HTML/CSS implementations for their own designs. The bottom line is, as we&#8217;ve all been saying for a long time now, that the Web is not the same kind of medium as other mediums like print. It is a fundamentally [...]]]></description>
			<content:encoded><![CDATA[<p>Last month, 37signals published a short but sweet post about why <a href="//www.37signals.com/svn/posts/1066-web-designers-should-do-their-own-htmlcss">web designers should do the <acronym title="HyperText Markup Language">HTML</acronym>/CSS implementations for their own designs</a>. The bottom line is, as we&#8217;ve all been saying for a long time now, that the Web is not the same kind of medium as other mediums like print. It is a fundamentally different kind of canvas than most web designers are used to using. As a result, if you as a web designer are not intimately familiar with it, you&#8217;re not going to do great work.</p>
<blockquote cite="//www.37signals.com/svn/posts/1066-web-designers-should-do-their-own-htmlcss"><p>designing for the web is a lot less about making something dazzle and a lot more about making it work. The design decisions that matter pertain directly to the constraints of the materials. What form elements to use. What font sizes. What composition. What flow. Those decisions are poorly made at an arm’s length.</p>
<p>I’ve worked with many web designers in the past who only did abstractions and then handed over pictures to be chopped and implemented by “<acronym title="HyperText Markup Language">HTML</acronym> monkeys”. It never really gelled well. The things that got strong attention were all the things that Photoshop did well. Imagery, curvy lines, and the frame. All the <em>around</em> stuff, never the <em>it</em> stuff.</p></blockquote>
<p>In other words, to do great web design you have to design <em>in</em> the Web, not in some other medium <em>for</em> the Web. I mean, serious magazine firm employs designers who don&#8217;t understand how to work with page layout programs like InDesign. Why, then, do so many web design agencies employ designers who don&#8217;t know how to work with web technologies, or even how to use programs like Dreamweaver? It doesn&#8217;t really make any sense, and it&#8217;s no wonder that the resulting implementation is rarely top-notch work.</p>
<p>But if you&#8217;re a graphic designer who doesn&#8217;t know much about Web technologies, what are you to do? Well, as a first step, I think you should pick up my new book, <a href="http://www.amazon.com/gp/redirect.html?ie=UTF8&#038;location=http%3A%2F%2Fwww.amazon.com%2FFoundation-Website-Creation-XHTML-JavaScript%2Fdp%2F1430209917%2F&#038;tag=maymaydotnet-20&#038;linkCode=ur2&#038;camp=1789&#038;creative=9325">Foundation Website Creation</a><img src="http://www.assoc-amazon.com/e/ir?t=maymaydotnet-20&amp;l=ur2&amp;o=1" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />. It&#8217;s available from all good booksellers (and probably some crappy ones) as of today. The book is targeted towards all manner of web professionals, including graphic designers and website producers, who want to learn more about what it takes to actually implement a site.</p>
<p>If I do say so myself, the chapters on <acronym title="eXtensible HyperText Markup Language; HTML reformulated as XML">XHTML</acronym> and <acronym title="Cascading Style Sheets">CSS</acronym> are exceptionally thorough. The book <em>doesn&#8217;t</em> try to turn you into an exceptional programmer. Instead, it will explain the foundational concepts you need to know to <em>understand how <acronym title="eXtensible HyperText Markup Language; HTML reformulated as XML">XHTML</acronym> and <acronym title="Cascading Style Sheets">CSS</acronym> actually work</em>, and in so doing will enable you to use the tools you already know to solve problems and get things done.</p>
<p>I think this book will be an excellent starting point for lots of designers and other web professionals. However, it is not going to take you from zero to hero—no book can. That&#8217;s why I recommend that, after you read <cite>Foundation Website Creation</cite> and have a solid grasp of what the technology can do for you and how it actually does it, you next take a look at these excellent books:</p>
<ul>
<li><a href="http://domscripting.com/book/"><acronym title="Document Object Model">DOM</acronym> Scripting by Jeremy Keith</a> — if you&#8217;re a designer that needs to add a behavioral layer with JavaScript and Ajax to your pages, you need to read this book next.</li>
<li><a href="http://www.amazon.com/Mastering-Dreamweaver-Voices-That-Matter/dp/0321508971">Mastering <acronym title="Cascading Style Sheets">CSS</acronym> with Dreamweaver CS3</a> &#8211; if you&#8217;re familiar with Dreamweaver and want to keep using it to create standards-based web sites, then I recommend you follow <cite>Foundation Website Creation</cite> with this book by <a href="//w3conversions.com/">Stephanie Sullivan</a> and Greg Rewis to take your Dreamweaver skills to the next level.</li>
</ul>
<p>As always, most of all, have fun. Because if you&#8217;re not having fun, you&#8217;re not going to make good web sites no matter what you know.</p>
<p><strong>Note:</strong> As of this writing, the book listing on Amazon still publishes the wrong author list, which is very frustrating but out of my hands. At least the image of our book&#8217;s front cover lists the correct authors.</p>
]]></content:encoded>
			<wfw:commentRss>http://maymay.net/blog/2008/07/21/how-web-designers-can-do-their-own-htmlcss/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Fix Subversion &#8220;checksum mismatch&#8221; error by editing .svn/entries file</title>
		<link>http://maymay.net/blog/2008/06/17/fix-subversion-checksum-mismatch-error-by-editing-svnentries-file/</link>
		<comments>http://maymay.net/blog/2008/06/17/fix-subversion-checksum-mismatch-error-by-editing-svnentries-file/#comments</comments>
		<pubDate>Tue, 17 Jun 2008 14:07:21 +0000</pubDate>
		<dc:creator>Meitar</dc:creator>
				<category><![CDATA[HOWTO]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Tech/Computing]]></category>
		<category><![CDATA[md5]]></category>
		<category><![CDATA[Subversion]]></category>

		<guid isPermaLink="false">http://maymay.net/blog/?p=471</guid>
		<description><![CDATA[I can&#8217;t explain why this happened because in my several-year-long history with Subversion, I&#8217;ve never experienced this issue once. However, today, I fell into the (arguably) unfortunate circumstance of running into a most disturbing error from SVN. When trying to commit my changes, SVN barfed at me and complained of a &#8220;checksum mismatch&#8221;. It looked [...]]]></description>
			<content:encoded><![CDATA[<p>I can&#8217;t explain why this happened because in my several-year-long history with Subversion, I&#8217;ve never experienced this issue once. However, today, I fell into the (arguably) unfortunate circumstance of running into a most disturbing error from SVN. When trying to commit my changes, SVN barfed at me and complained of a &#8220;checksum mismatch&#8221;. It looked something like this:</p>
<pre><samp>Transmitting file data ..svn: Commit failed (details follow):
svn: Checksum mismatch for '/Users/maymay/Sites/path/to/subversion/working/copy/.svn/text-base/working-file.php.svn-base'; expected 'cde4d8fbd5c623da3a8b1a343aa7b3f4', actual: '270b2f20804a5fcdbd15eec5910f6e3f'
</samp></pre>
<p>Of course, the <code>path/to/subversion/working/copy</code> bit was the path to my working copy file&#8217;s parent directory and the <code>working-file.php</code> was an actual file in my working directory.</p>
<p>I think what Subversion was trying to tell me is that its hashed copy of the <code>working-file.php</code> file and the copy I was asking it to commit weren&#8217;t the same. It would be nice if it would actually tell me why that happened, but it&#8217;s clearly more temperamental than that.</p>
<p>Anyway, to fix this issue (at least for now…?) I simply checked out a new working copy of this directory, examined the <code>.svn/entries</code> file from it and sure enough, found the actual checksums in there, just as Subversion reported expecting. I simply copied those expected checksums into the <code>.svn/entries</code> overwriting the old <em>actual</em> checksums and, voila, Subversion has been fooled. After that, I could commit my changes.</p>
<p>Step by step (because I&#8217;m sure someone, somewhere, somehow, will run into this again—if it&#8217;s not me that is!), this procedure looked like this:</p>
<ol>
<li>Copy the &#8220;expected&#8221; and &#8220;actual&#8221; checksums Subversion reports to you to a new text file so you can refer to them later. Note which one is the expected and which is the actual checksum.</li>
<li>Go to where the problem is (that is, <code>cd <var>path/to/broken-files-parent-dir/.svn</var></code>)</li>
<li>Open the <code>entries</code> for editing (for example, <code>vim entries</code>)</li>
<li>Search the file for the actual checksum.</li>
<li>Replace it with the expected checksum. Be careful not to change any other part of the file.</li>
<li>Save the file.</li>
<li>Try to <code>svn commit</code> again.</li>
<li>Lather, rinse, and repeat for any other files Subversion barfs at you about.</li>
</ol>
<p>I&#8217;m sure this is <em>not</em> an elegant or even the recommended solution to this problem. The truth is I never bothered to look up what the recommended solution is, because it seems to me that any code repository that can&#8217;t guarantee what I get out of it is the same as what I put into it isn&#8217;t a versioning system I really want to trust the &#8220;recommended&#8221; solution of, anyway.</p>
<p>Also known as: this is another reason why <a href="/blog/2008/03/26/howto-use-git-for-personal-development-when-everyone-else-is-using-subversion-part-1/">I like git</a> better now.</p>
]]></content:encoded>
			<wfw:commentRss>http://maymay.net/blog/2008/06/17/fix-subversion-checksum-mismatch-error-by-editing-svnentries-file/feed/</wfw:commentRss>
		<slash:comments>17</slash:comments>
		</item>
		<item>
		<title>Ridiculously simple JavaScript version string to object parser</title>
		<link>http://maymay.net/blog/2008/06/15/ridiculously-simple-javascript-version-string-to-object-parser/</link>
		<comments>http://maymay.net/blog/2008/06/15/ridiculously-simple-javascript-version-string-to-object-parser/#comments</comments>
		<pubDate>Sun, 15 Jun 2008 10:39:52 +0000</pubDate>
		<dc:creator>Meitar</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Tech/Computing]]></category>

		<guid isPermaLink="false">http://maymay.net/blog/?p=468</guid>
		<description><![CDATA[In any kind of development, you often have to deal with version strings. Typically, these version strings are just a dot-separated list of numbers that represent different versions of the software. I recently had a need to compare two version numbers against one another to determine which one was newer. This is useful if, say, [...]]]></description>
			<content:encoded><![CDATA[<p>In any kind of development, you often have to deal with version strings. Typically, these version strings are just a dot-separated list of numbers that represent different versions of the software. I recently had a need to compare two version numbers against one another to determine which one was newer. This is useful if, say, you&#8217;re building an application that wants to check its current version against the &#8220;latest&#8221; available version.</p>
<p>In JavaScript, this is thankfully pretty trivial. My solution is to just parse the version strings with a simple function and return them as objects with appropriate properties whose values are integers. Once in this form, we can compare them with simple math.</p>
<pre class="javascript">function parseVersionString (str) {
    if (typeof(str) != 'string') { return false; }
    var x = str.split('.');
    // parse from string or default to 0 if can't parse
    var maj = parseInt(x[0]) || 0;
    var min = parseInt(x[1]) || 0;
    var pat = parseInt(x[2]) || 0;
    return {
        major: maj,
        minor: min,
        patch: pat
    }
}</pre>
<p>Using this new object, we can now compare two versions really simply:</p>
<pre class="javascript">var running_version = parseVersionString('3.5.2');
var latest_version = parseVersionString('3.4.5');
if (running_version.major < latest_version.major) {
    // A major new update is available!
} else if (running_version.minor < latest_version.minor || running_version.patch < latest_version.patch) {
    // A new minor or patch update is available.
} else {
    // We are running the latest version! No need to update.
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://maymay.net/blog/2008/06/15/ridiculously-simple-javascript-version-string-to-object-parser/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Arbitrarily exclude posts from displaying in WordPress</title>
		<link>http://maymay.net/blog/2008/06/06/arbitrarily-exclude-posts-from-displaying-in-wordpress/</link>
		<comments>http://maymay.net/blog/2008/06/06/arbitrarily-exclude-posts-from-displaying-in-wordpress/#comments</comments>
		<pubDate>Fri, 06 Jun 2008 18:11:08 +0000</pubDate>
		<dc:creator>Meitar</dc:creator>
				<category><![CDATA[HOWTO]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Web Design]]></category>
		<category><![CDATA[Writing and blogging]]></category>

		<guid isPermaLink="false">http://maymay.net/blog/?p=437</guid>
		<description><![CDATA[When hacking away at WordPress sites, often times you&#8217;ll find yourself in a situation where you need to filter out certain posts from displaying on some pages, such as the home page. There are a lot of ways to do this, but few are perfect. Recently, I had the need to do this and went [...]]]></description>
			<content:encoded><![CDATA[<p>When hacking away at WordPress sites, often times you&#8217;ll find yourself in a situation where you need to filter out certain posts from displaying on some pages, such as the home page. There are a lot of ways to do this, but few are perfect. Recently, I had the need to do this and went searching for pre-existing solutions.</p>
<p>I came across <a href="//blog.gadodia.net/excluding-certain-categories-from-your-blog-main-page/">Vaibhav&#8217;s post on the topic</a> and noted that his solution uses the <a href="//codex.wordpress.org/Template_Tags/query_posts" title="WordPress documentation for the query_posts() function.">query_posts()</a> function to alter WordPress&#8217;s query object before <a href="//codex.wordpress.org/The_Loop">The Loop</a> has run. While this is a great solution if your exclusion criteria is simple enough to be supported directly by the WordPress query object, other times the query_posts() function doesn&#8217;t provide you with the hook you need.</p>
<p>In these cases, you can run the original query, note any modifications you need to make, and then create a new, modified query and display the results you get from running <em>that</em> one instead. For instance, you might need to do this if you need to exclude posts based on category and, say, the beginning of their title, or their category and a certain piece of content in the post itself, or all three, or any other combination you can think of.</p>
<p>Another advantage of this technique over simpler ones is that this method maintains the same behavior you&#8217;d expect to see in every other way. Most notably, this means that if you&#8217;ve told WordPress to display the 10 most recent posts on the home page (in the WordPress settings), you&#8217;ll still see ten posts on that page even after you exclude some of them.</p>
<p>To do something like excluding posts if they are in the &#8220;Uncategorized&#8221; category (traditionally the category with an ID of 1 in WordPress) and their title begins with &#8220;Some title&#8221;, you can do this:</p>
<pre class="php">
// original query runs in The (real) Loop first
while ( have_posts() ) : the_post();
    // detect pots matching our exclusion criteria
    if (in_category(1) &#038;&#038; (0 === strpos(the_title('', '', false), 'Some title')) ) {
        $wp_query->post_count++; // increment the post counter
        continue;
    }
    endif;
endwhile;
// now make a new query and show the posts for real, with the adjusted post count and filtering
$my_new_query = new WP_Query($query_string.'&#038;showposts='.$wp_query->post_count);
// do another The Loop (and display the results this time)
while ( $my_new_query->have_posts() ) : $my_new_query->the_post();
    // detect and exclude these same posts
    if (in_category(1) &#038;&#038; (0 === strpos(the_title('', '', false), 'Some title')) ) { continue; }

// ...the rest of the WordPress template goes here...
</pre>
<p>This is neat because it gives you the capability to define arbitrarily complex exclusion patterns and directly modify your new query object however you like before you execute it. Once you know this works, you&#8217;ll probably want to extract the filtering code into a function. Using the above example, your new code might look like this:</p>
<pre class="php">
<strong>// define criteria for filtering
function matches_filtering_criteria () {
    if (in_category(1) &#038;&#038; (0 === strpos(the_title('', '', false), 'Some title')) ) {
        return true;
    } else {
        return false;
    }
}</strong>
// original query runs in The (real) Loop first
while ( have_posts() ) : the_post();
    // detect pots matching our exclusion criteria
    if (<strong>matches_filtering_criteria()</strong>) {
        $wp_query->post_count++; // increment the post counter
        continue;
    }
    endif;
endwhile;
// now make a new query and show the posts for real, with the adjusted post count and filtering
$my_new_query = new WP_Query($query_string.'&#038;showposts='.$wp_query->post_count);
// do another The Loop (and display the results this time)
while ( $my_new_query->have_posts() ) : $my_new_query->the_post();
    //
    // detect and exclude these same posts
    if (<strong>matches_filtering_criteria()</strong>) { continue; }

// ...the rest of the WordPress template goes here...
</pre>
<p>For more information on these functions, see:</p>
<ul>
<li><a href="//codex.wordpress.org/The_Loop">The Loop</a></li>
<li><a href="//codex.wordpress.org/Template_Tags/in_category">in_category()</a></li>
<li><a href="//codex.wordpress.org/Template_Tags/the_title">the_title()</a></li>
<li><a href="//codex.wordpress.org/Function_Reference/WP_Query">WP_Query()</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://maymay.net/blog/2008/06/06/arbitrarily-exclude-posts-from-displaying-in-wordpress/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>I&#8217;m getting a book published and it&#8217;s called Foundation Website Creation</title>
		<link>http://maymay.net/blog/2008/05/19/im-getting-a-book-published-and-its-called-foundation-web-standards/</link>
		<comments>http://maymay.net/blog/2008/05/19/im-getting-a-book-published-and-its-called-foundation-web-standards/#comments</comments>
		<pubDate>Mon, 19 May 2008 13:50:36 +0000</pubDate>
		<dc:creator>Meitar</dc:creator>
				<category><![CDATA[Crosspost]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Maybe Maimed]]></category>
		<category><![CDATA[Maymay Media]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Tech/Computing]]></category>
		<category><![CDATA[Usability]]></category>
		<category><![CDATA[Web Accessibility]]></category>
		<category><![CDATA[Web Design]]></category>
		<category><![CDATA[Web Standards]]></category>
		<category><![CDATA[Writing and blogging]]></category>

		<guid isPermaLink="false">http://maymay.net/blog/?p=436</guid>
		<description><![CDATA[For those who have been wondering what is keeping me so busy these days, the answer is that I&#8217;m working on the final stages of a book that is getting published as one of three co-authors. Not only am contributing three chapters (the technical chapters on (X)HTML and CSS, specifically), but I am also technically [...]]]></description>
			<content:encoded><![CDATA[<p>For those who have been wondering what is keeping me so busy these days, the answer is that I&#8217;m working on the final stages of a book that is getting published as one of three co-authors. Not only am contributing three chapters (the technical chapters on (X)<acronym title="HyperText Markup Language">HTML</acronym> and <acronym title="Cascading Style Sheets">CSS</acronym>, specifically), but I am also technically reviewing the entire book.</p>
<p>My co-authors on the book, called <a href="//www.friendsofed.com/book.html?isbn=9781430209911"><cite><del datetime="2008-06-15T10:43:08+00:00">Foundation Web Standards</del> <ins datetime="2008-06-15T10:43:08+00:00">Foundation Website Creation</ins></cite></a> (you can <a href="http://www.amazon.com/gp/redirect.html?ie=UTF8&#038;location=http%3A%2F%2Fwww.amazon.com%2FFoundation-Website-Creation-XHTML-JavaScript%2Fdp%2F1430209917%2F&#038;tag=maymaydotnet-20&#038;linkCode=ur2&#038;camp=1789&#038;creative=9325" title="Buy on Amazon.com.">pre-order now</a>) and published by <a href="//friendsofed.com/">Friends of ED</a>, an <a href="//apress.com/">Apress</a> company, are <a href="//industryinteractive.net/">Jonathan Lane of Industry Interactive, Inc.</a> and <a href="//www.sanbeiji.com/">Joe Lewis, who blogs at Sanbeiji.com</a>. I&#8217;m not going to say much more until after the book is released in late July.</p>
<p>For the eager, here&#8217;s the description of the book posted on the Friends of ED website:</p>
<blockquote cite="//www.friendsofed.com/book.html?isbn=9781430209911"><p>Foundation Website Creation explores the process of constructing a web site from start to finish. There is more to the process than just knowing <acronym title="HyperText Markup Language">HTML</acronym>! Designers and developers must follow a proper process to flush out goals and objectives and determine requirements both prior to, and during project development.</p>
<p>Large Web projects are rarely completed by a single person. Producers, project managers, designers, developers, writers, and editors all play critical parts in a project&#8217;s evolution. This book provides an overview of the entire process, and also shows project development from the perspective of these different roles. It introduces the key concepts and duties performed by every member of such a team, and gives you the skills necessary to tackle projects like a professional.</p></blockquote>
<p>It&#8217;s quite exciting getting a book out, and it&#8217;s quite a bit more work than I&#8217;d have ever originally thought. That being said, it&#8217;s extremely rewarding. There&#8217;s a lot more work I need to do on it between now and the time it gets released to publishing, so, well…back to work I go.</p>
<p>Now you all know where I&#8217;ve been spending my time writing.</p>
]]></content:encoded>
			<wfw:commentRss>http://maymay.net/blog/2008/05/19/im-getting-a-book-published-and-its-called-foundation-web-standards/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Quick &#8216;N&#8217; Dirty Drupal Module SVN Tagging Script</title>
		<link>http://maymay.net/blog/2008/05/14/quick-n-dirty-drupal-module-svn-tagging-script/</link>
		<comments>http://maymay.net/blog/2008/05/14/quick-n-dirty-drupal-module-svn-tagging-script/#comments</comments>
		<pubDate>Wed, 14 May 2008 09:46:14 +0000</pubDate>
		<dc:creator>Meitar</dc:creator>
				<category><![CDATA[Bash/Shell Scripting]]></category>
		<category><![CDATA[Productivity]]></category>
		<category><![CDATA[Tech/Computing]]></category>
		<category><![CDATA[Unix/Linux]]></category>
		<category><![CDATA[Drupal]]></category>
		<category><![CDATA[Subversion]]></category>

		<guid isPermaLink="false">http://maymay.net/blog/?p=435</guid>
		<description><![CDATA[In a (rather beastly) project at work today, I found myself needing to import a significant number of contributed Drupal modules into Subversion vendor branches to prepare for custom development. To do so manually would have been quite the hassle, so after downloading the appropriate tarballs and creating a module_name/current directory under my vendor/drupal/modules vendor [...]]]></description>
			<content:encoded><![CDATA[<p>In a (rather beastly) project at work today, I found myself needing to import a significant number of contributed Drupal modules into Subversion vendor branches to prepare for custom development. To do so manually would have been quite the hassle, so after downloading the appropriate tarballs and creating a <code><var>module_name</var>/current</code> directory under my <code>vendor/drupal/modules</code> vendor branch directory, I concocted this little (relatively untested) script to handle the mass tagging operations I needed to perform.</p>
<pre>for i in *; do
    v=`grep 'version = "' "$i/current/$i/"*.info |
      cut -d ':' -f 2 |
        sed -e 's/^version = "/v/' -e 's/"$//'`
    svn cp "$i/current" "$i/$v"
done;</pre>
<p>It&#8217;s a bit buggy for some modules that have multiple <code>.info</code> files, but I&#8217;m sure a few more pipeline stages can fix that. (Which, because I&#8217;m done with this at the moment, I will leave as an exercise to the reader.)</p>
<p>Chalk this one up as another testament to the power of shell scripting and how it can help every developer get their job done faster.</p>
]]></content:encoded>
			<wfw:commentRss>http://maymay.net/blog/2008/05/14/quick-n-dirty-drupal-module-svn-tagging-script/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>How to import CVS code repositories into Git using `git cvsimport`</title>
		<link>http://maymay.net/blog/2008/04/15/how-to-import-cvs-code-repositories-into-git-using-git-cvsimport/</link>
		<comments>http://maymay.net/blog/2008/04/15/how-to-import-cvs-code-repositories-into-git-using-git-cvsimport/#comments</comments>
		<pubDate>Tue, 15 Apr 2008 09:06:24 +0000</pubDate>
		<dc:creator>Meitar</dc:creator>
				<category><![CDATA[HOWTO]]></category>
		<category><![CDATA[Mac OS X]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Unix/Linux]]></category>
		<category><![CDATA[git]]></category>

		<guid isPermaLink="false">http://maymay.net/blog/?p=425</guid>
		<description><![CDATA[This should be straightforward, but it&#8217;s not. To import (not track, but just import) code from a remote CVS repository to a local git repository, you need to do the following: Be certain you have the git-core package installed on your system and that this package includes the git-cvsimport command. You can run git help [...]]]></description>
			<content:encoded><![CDATA[<p>This should be straightforward, but it&#8217;s not. To import (not <em>track</em>, but just import) code from a remote <acronym title="Concurrent Versions System">CVS</acronym> repository to a local git repository, you need to do the following:</p>
<ol>
<li>Be certain you have the git-core package installed on your system and that this package includes the <code>git-cvsimport</code> command. You can run <code>git help -a | grep cvsimport</code> to verify this.</li>
<li>Be certain you have the <code>cvsps</code> command-line tool installed. This does <em>not</em> come with the git suite of tools, so you&#8217;ll need to get it separately. If you&#8217;re a lazy Mac <acronym title="Operating System">OS</acronym> X user, like me, you can use MacPorts: <code>sudo port install cvsps</code>. Otherwise, get it from <a href="//www.cobite.com/cvsps/">the source</a>.</li>
<li>Prepare your <acronym title="Concurrent Versions System">CVS</acronym> login information for the remote server <em>before</em> you run <code>git cvsimport</code>. You need to do this so that the git tool will be able to log you in to the <acronym title="Concurrent Versions System">CVS</acronym> server automatically. The command for this looks like:
<pre>CVSROOT=:<var>cvs-login-method</var>:<var>cvs-user-name</var>@<var>cvs.server.name</var>:<var>/path/to/CVS/root</var> cvs login</pre>
<p> For example, if you&#8217;re pulling code from the anonymous <acronym title="Concurrent Versions System">CVS</acronym> server that runs on Drupal.org, you might use this: <code>CVSROOT=:pserver:anonymous@cvs.drupal.org:/cvs/drupal-contrib cvs login</code>. This command will prompt you for the password for the user you specified at the server you specified (for anonymous access, the password is almost always <code>anonymous</code>) and will hash this in the <code>~/.cvspass</code> file for future use by <acronym title="Concurrent Versions System">CVS</acronym></li>
<li>Finally, run the <code>git cvsimport</code> tool, and specify the proper options. Using the Drupal example above, your command might look like this:
<pre>git cvsimport -v -d :pserver:anonymous@cvs.drupal.org:/cvs/drupal-contrib contributions/modules/<var>module-name</var></pre>
<p> This would login to <code>cvs.drupal.org</code> using the <acronym title="Concurrent Versions System">CVS</acronym>&#8216;s <code>pserver</code> login method, provide the username <code>anonymous</code> and the password you specified in the previous step that is hashed in <code>~/.cvspass</code>, set the <acronym title="Concurrent Versions System">CVS</acronym> document root to <code>/cvs/drupal-contrib</code>, and pull the code located at <code>contributions/modules/<var>module-name</var></code> into the current working directory as a git repository.</li>
</ol>
<p>This works pretty nicely, and creates a git repository just as though you&#8217;d created it with <code>git init</code> in the current working directory.</p>
<p>If you get an error that looks like this:</p>
<pre>AuthReply: cvs [pserver aborted]: descramble: unknown scrambling method</pre>
<p>then you&#8217;ve most likely specified the <acronym title="Concurrent Versions System">CVS</acronym> document root incorrectly. Most notably, <code>git cvsimport</code> does not understand a <acronym title="Concurrent Versions System">CVS</acronym> document root wherein the password is specified in the document root <acronym title="Uniform Resource Locator">URL</acronym> itself. So, for example, <code>git cvsimport -d :pserver:<var>password</var>:<var>username</var>@<var>cvs.server.name</var>:<var>/path/to/CVS/root</var> <var>code/to/checkout</var></code> will not work. Omitting the password and the separating colon from the <acronym title="Uniform Resource Locator">URL</acronym> should fix it.</p>
]]></content:encoded>
			<wfw:commentRss>http://maymay.net/blog/2008/04/15/how-to-import-cvs-code-repositories-into-git-using-git-cvsimport/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>HowTo: Use git for personal development when everyone else is using Subversion (part 2)</title>
		<link>http://maymay.net/blog/2008/03/28/howto-use-git-for-personal-development-when-everyone-else-is-using-subversion-part-2/</link>
		<comments>http://maymay.net/blog/2008/03/28/howto-use-git-for-personal-development-when-everyone-else-is-using-subversion-part-2/#comments</comments>
		<pubDate>Fri, 28 Mar 2008 08:20:36 +0000</pubDate>
		<dc:creator>Meitar</dc:creator>
				<category><![CDATA[HOWTO]]></category>
		<category><![CDATA[Mac OS X]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Unix/Linux]]></category>
		<category><![CDATA[git]]></category>

		<guid isPermaLink="false">http://maymay.net/blog/2008/03/28/howto-use-git-for-personal-development-when-everyone-else-is-using-subversion-part-2/</guid>
		<description><![CDATA[When we left off, you had just finished transforming a remote Subversion repository into a git repository and optimizing it to save you some space. Now that you have a git repository, what do you do? First things first. Once you have an idea of what work you want to do, you should give yourself [...]]]></description>
			<content:encoded><![CDATA[<p>When we left off, you had just finished transforming a remote Subversion repository into a git repository and optimizing it to save you some space. Now that you have a git repository, what do you do?</p>
<p>First things first. Once you have an idea of what work you want to do, you should give yourself a space to do this work without disturbing anybody else&#8217;s work. Do this by making a new, personal branch. Unlike Subversion and some other centralized version control systems, git makes it possible to do make all kinds of changes to your repository, including making branches, and even save those changes without having to republish everything back to the central repository server at each step. In other words, if you thought Subversion branches were &#8220;cheap,&#8221; you&#8217;ll love git&#8217;s branches.</p>
<p>Also unlike Subversion, which stores its branches in completely separate pathnames, git keeps all branches in the same filesystem tree separated only with metadata (in <code>.git/refs/remotes</code> for remote branches and in <code>.git/refs/heads</code> for local branches, to be a bit more precise), so you don&#8217;t have to create lots of different directories for all your branches (unless you want to). With no branches defined, you&#8217;re working in the &#8220;master branch,&#8221; or &#8220;the trunk&#8221; by default.</p>
<pre><kbd>git branch</kbd>
<samp>* master</samp></pre>
<p>By saying <code>git branch</code> you ask git to print a list of all (local) branches. The one with the asterisk marks the current branch, the one you&#8217;re using at the moment you run the command. Since there are no branches, you&#8217;re currently in the &#8220;master&#8221; branch. But we don&#8217;t want to make changes here, we want to make changes in our own private branch, so we&#8217;ll make a new one.</p>
<pre>git checkout -b <var>new_branch_name</var></pre>
<p>This will create a new branch with the name <var>new_branch_name</var> and immediately switch to it. Notice that you&#8217;ve made absolutely no changes to the filesystem itself; only the git metadata has been altered. Saying <code>git branch</code> again will show you the change:</p>
<pre><kbd>git branch</kbd>
<samp>  master
* new_branch_name</samp></pre>
<p>Also note that since we haven&#8217;t comitted any changes, we don&#8217;t need a commit message (or &#8220;log message&#8221;) for creating this branch. We&#8217;ll add one later, when we need it. Now go ahead and write some code in your new branch. At any time, you can create a new branch in the same way you added the first. Each new branch is created at the <code>HEAD</code> (&#8220;latest&#8221;) revision of whatever branch you&#8217;re currently working in.</p>
<pre><kbd>git checkout -b <var>another_branch</var></kbd>
<samp>Switched to a new branch "<var>another_branch</var>"</samp>
<kbd>git branch</kbd>
<samp>  master
  new_branch_name
* another_branch</samp></pre>
<p>To switch back to any other branch, simply <code>git checkout</code> that branch again:</p>
<pre><kbd>git checkout master</kbd>
<samp>Switched to branch "master"</samp></pre>
<p>If you want to delete a branch you don&#8217;t like, that&#8217;s easy too:</p>
<pre><kbd>git branch -d <var>another_branch</var></kbd>
<samp>Deleted branch <var>another_branch</var></samp>.</pre>
<p>Keep in mind that throughout all of these branch creation and deletion actions, the only thing that&#8217;s being altered is the git metadata. That&#8217;s why it&#8217;s so cheap to create new branches. If you ever have a new idea you&#8217;re working on, it&#8217;s recommended that you create a branch for it, even if that branch is so short-lived it never gets published.</p>
<p>So you have a new local branch, and you&#8217;ve been working as you normally do for a few minutes, creating files, editting them, and so on. Running <code>git status</code> now will ask git to show you the changes you&#8217;ve made to your filesystem. If you&#8217;ve created any files in new directories that git doesn&#8217;t know about, it will simply report that directory. If you&#8217;ve made new files in directories git does know about, it will list all those files explicitly.</p>
<pre><kbd>git status</kbd>
<samp># On branch cartoon_contests
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#	sites/default/modules/cartoon_contests/
#	sites/default/modules/factiva/.factiva.module.swp
#	sites/default/modules/testfile
#	sites/default/settings.php
nothing added to commit but untracked files present (use "git add" to track)
</samp></pre>
<p>In the above sample output, I&#8217;m working on a new Drupal module for a web site. I&#8217;ve created a new directory, <code>sites/default/modules/cartoon_contests/</code> (note the trailing slash), and I have several untracked files. One is my <a href="//vim.org/">vim</a> swap file for a different module, <code>.factiva.module.swp</code>, one&#8217;s an unimportant <code>testfile</code>, and the last is the Drupal configuration file, <code>settings.php</code>.</p>
<p>The only thing I want to commit is the new <code>cartoon_contests</code> directory, and all the files within it. Like Subversion, I have to tell git that I want to <dfn>track</dfn> this directory, which is done simply by saying <code>git add cartoon_contests</code>. Unlike Subversion, future invocations of <code>git status</code> let me see everything that git is going to go in the next commit.</p>
<pre><kbd>git status</kbd>
<samp># On branch cartoon_contests
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#	new file:   sites/default/modules/cartoon_contests/cartoon_contests.info
#	new file:   sites/default/modules/cartoon_contests/cartoon_contests.module
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#	sites/default/modules/factiva/.factiva.module.swp
#	sites/default/modules/testfile
#	sites/default/settings.php</samp></pre>
<p>The &#8220;Changes to be committed&#8221; section of the output is called the <dfn>staging area</dfn>, or the <dfn>index</dfn>. In this way, I can prepare all the changes I want to commit before I do so, making sure they&#8217;re perfect before I actually commit them to the git repository. At any time before I commit, I can make additional modifications, such as <code>git add</code>ing more files or directories, <code>git reset</code>ing to unstage all (or some) of my changes, etc. <code>git help reset</code> also has a number of handy explanations with examples for different things you might need to do at this point.</p>
<p>If you were using <code>svn:ignore</code>, equivalent functionality exists in git. Simply append file glob patterns, one per line, to the <code>$GIT_DIR/info/exclude</code> file in your git repository. Like so:</p>
<pre><kbd>echo -e "*.swp\nsites/default/settings.php" >> .git/info/exclude ; git status</kbd>
<samp># On branch cartoon_contests
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#	new file:   sites/default/modules/cartoon_contests/cartoon_contests.info
#	new file:   sites/default/modules/cartoon_contests/cartoon_contests.module
#
</samp></pre>
<p>If you&#8217;ve got many of these, you can use <code>git svn show-ignore >> .git/info/exclude</code> to search through your old Subversion repository and look for any and all ignores, automatically adding them to git&#8217;s exclude list. (Checkout <a href="//tsunanet.blogspot.com/2007/07/learning-git-svn-in-5min.html">Tsuna&#8217;s blog entry on learning git</a> for more tips like this.)</p>
<p>Finally, after you&#8217;ve done some of your work and you&#8217;ve finished <dfn>staging</dfn> your changes, you&#8217;re ready to commit them to the repository. On the other hand, if you hate what you&#8217;ve done and want to undo it all, you can say <code>git reset --hard HEAD</code> to throw away all your local changes. To throw away changes to just a single file, just checkout that file again by saying <code>git checkout <var>filename</var></code>. This is the equivalent of Subversions <code>svn revert <var>filename</var></code> command.</p>
<p>Before you actually make your first commit, however, you should properly introduce yourself to git. You don&#8217;t have to do this because git will try to figure out who you are by itself (<a>details explained in detail here</a>), but you probably should at least create global defaults for your user (which git will store in <code>~/.gitconfig</code>). If you want to, you can also create per-repository defaults (which git will store in <code>.git/config</code>), or even system-wide defaults for all users of this computer (which git will store in <code>/etc/gitconfig</code>). To do so, say this:</p>
<pre><kbd>git config --global user.name "Your Full Name"
git config --global user.email "you@example.com"</kbd></pre>
<p>This will create the file <code>~/.gitconfig</code> if it doesn&#8217;t alreay exist and will write your name into it. You can also just edit the file directly yourself instead of using <code>git config</code> commands.</p>
<p>Like Subversion, committing will create a saved, fixed point-in-time that marks the changes you have made to your files. Like branches, commits are also very cheap in git, so go ahead and commit at any time you like. Remember to stage your files (by <code>git add</code>ing them), and then just <code>git commit</code>.</p>
<pre><kbd>git add <var>filepattern</var></kbd>
<kbd>git commit -m "My very frst git commit!"</kbd>
<samp>Created commit ef483c1: My very frst git commit!
 5 files changed, 58 insertions(+), 12 deletions(-)</samp></pre>
<p>If you forget to specify a commit log message (<code>-m "log message"</code>) on the command line, or if you want to enter a multi-line commit log, git will prompt you for it in your favorite <code>$EDITOR</code>. You can view a history, including all the log messages, for your project with <code>git log</code>. You can even the view logs in a number of pretty formats. Check <code>git help log</code> for more information.</p>
<p>If you want to change anything about the commit you just made, such as the author, you can just run <code>git commit</code> again with the <code>--amend</code> flag added to the command. Notice the typo in the commit log? Fixing it is really easy:</p>
<pre><kbd>git commit --amend -m "My very first git commit!"</kbd>
<samp>Created commit 88602f6: My very first git commit!
 5 files changed, 58 insertions(+), 12 deletions(-)</samp></pre>
<p>Finally, with your new code commited to your local git branch, it&#8217;s time to share that code with your colleagues who are (for some inexplicable reason) still using Subversion. This is also extremely simple. Just say <code>git svn dcommit</code>. (That&#8217;s right, <code><strong>d</strong>commit</code>, not just commit. I don&#8217;t know why….)</p>
<p>Not sharing your changes via Subversion, but with a patch instead? <code>git diff -p</code> will generate patches for you. See <code>git help diff-files</code> and look for the &#8220;GENERATING PATCHES WITH -P&#8221; section.</p>
<p>If you found this helpful, you may also enjoy an <a href="//cworth.org/hgbook-git/tour/">alternative tour for beginners from Carl Worth</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://maymay.net/blog/2008/03/28/howto-use-git-for-personal-development-when-everyone-else-is-using-subversion-part-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>HowTo: Use git for personal development when everyone else is using Subversion (part 1)</title>
		<link>http://maymay.net/blog/2008/03/26/howto-use-git-for-personal-development-when-everyone-else-is-using-subversion-part-1/</link>
		<comments>http://maymay.net/blog/2008/03/26/howto-use-git-for-personal-development-when-everyone-else-is-using-subversion-part-1/#comments</comments>
		<pubDate>Wed, 26 Mar 2008 06:54:57 +0000</pubDate>
		<dc:creator>Meitar</dc:creator>
				<category><![CDATA[HOWTO]]></category>
		<category><![CDATA[Mac OS X]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Unix/Linux]]></category>
		<category><![CDATA[git]]></category>

		<guid isPermaLink="false">http://maymay.net/blog/2008/03/26/howto-use-git-for-personal-development-when-everyone-else-is-using-subversion-part-1/</guid>
		<description><![CDATA[Let&#8217;s just jump into it. Using Mac OS X, first make sure you have the git core installed as well as the git-svn components. Unlike other version control systems, git is not one monolithic program but a collection of smaller utilities, and the Subversion conduits are a subset of these utilities. The git-Subversion utilities themselves [...]]]></description>
			<content:encoded><![CDATA[<p>Let&#8217;s just jump into it.</p>
<p>Using Mac <acronym title="Operating System">OS</acronym> X, first make sure you have the git core installed as well as the git-svn components. Unlike other version control systems, git is not one monolithic program but a collection of smaller utilities, and the Subversion conduits are a subset of these utilities. The git-Subversion utilities themselves depend on having the <code>Perl::Subversion</code> bindings installed for Perl 5.</p>
<p>For the lazy MacPorts user, simply run:</p>
<pre>sudo port install git-core +svn</pre>
<p>This will install the git-core, the git-svn packages, as well as all the documentation for git and any required dependencies you don&#8217;t already have. Note that the documentation (man pages) is installed in <code>/opt/local/man</code>, which may not be in your default <code>$MANPATH</code>, so be sure to add that directory if <code>man git</code> returns a &#8220;No manual entry for git&#8221; error.</p>
<p>Alternatively, if you don&#8217;t want to use MacPorts, you can <a href="//git.or.cz/#download">download a pre-compiled Mac <acronym title="Operating System">OS</acronym> X binary</a> that includes git, the git docs, and the git-svn package from the git web site that comes complete with a standard <acronym title="Graphical User Interface">GUI</acronym> installation procedure.</p>
<p>Next, initialize a new git repository that tracks a remote Subversion one. This allows you to work privately using git, but to collaborate with other people who are using Subversion. Running</p>
<pre>git svn init svn://my.svn.server/path/to/svn/repo <var>workingcopy</var></pre>
<p>will give you an empty git working copy named <var>workingcopy</var> configured to use the remote Subversion repository at <var>svn://my.svn.server/path/to/svn/repo</var>. This step is analogous to the need to run <code>svnadmin create <var>repo_db</var></code>, to initialize a new repository database (where all the file versioning information will be stored). Unlike Subversion, git&#8217;s distributed database means that the working copy itself is <em>also</em> the location of the repository database, so there&#8217;s no need to deal with two filesystem paths anymore.</p>
<p>Next, change directory to your working copy, and run</p>
<pre>cd <var>workingcopy</var>
git svn fetch</pre>
<p>to populate your new, empty git working copy (and repository) with all the files from the remote Subversion repository.</p>
<p>Now that you have filled your git repository with a lot of data, <e>if you want to</em>, you can now also save a significant chunk of disk space by repacking that data into a more single, efficient, native packed git archive format (a <code>.pack</code> file). The <code>git-repack -a</code> command is used to do this, and its manual page says:</p>
<blockquote><p>Instead of incrementally packing the unpacked objects, pack everything referenced into a single pack. Especially useful when packing a repository that is used for private development and there is no need to worry about people fetching via dumb protocols from it.</p></blockquote>
<p>According to <a href="//utsl.gen.nz/talks/git-svn/intro.html#howto-fetch-convert" title="An introduction to git-svn for Subversion/SVK users and deserters">some sources</a>, this can turn a <acronym title="353 Megabyte(s)">353MB</acronym> Subversion repository into <acronym title="31 Megabyte(s)">31MB</acronym> of git pack. Say:</p>
<pre><kbd>git repack -a -d -f</kbd>
<samp>Generating pack...
Counting objects: 284
Done counting 4475 objects.
Deltifying 4475 objects...
 100% (4475/4475) done
Writing 4475 objects...
 100% (4475/4475) done
Total 4475 (delta 1876), reused 0 (delta 0)
Pack pack-a115c320ff9c9968248bd250bdfea3110d0f0c1a created.
Removing unused objects 100%...
Done.
</samp></pre>
<p>to perform the compression. For even greater savings, increase the compression by stipulating a high <code>--window</code> option, such as <code>git repack -a -d -f --window=100</code>. (<code>--window</code> defaults to 10—the higher the window, the harder <code>git-repack</code> will try to compress your stuff.) This turned a <acronym title="36 Megabyte(s)">36MB</acronym> Subversion repository in one of my projects to a <acronym title="20 Megabyte(s)">20MB</acronym> git pack. As always, your mileage may vary.</p>
<p>Congratulations, you have transformed your old Subversion repository to a git repository. All that&#8217;s left to do now is to get to the coding. Perhaps start by making a local (cheaper than cheap!) git branch….</p>
<p>That was easy, right? Most things in git are, in fact, that easy. If you&#8217;ve never used other version control systems before, be grateful. If you have, you can breathe a sigh of relief. Still not convinced? Don&#8217;t take my word for it…<a href="//video.google.com/videoplay?docid=-2199332044603874737">ask Linus Torvalds</a> (or <a href="//video.google.com/videoplay?docid=-3999952944619245780">Randall Schwartz</a>).</p>
]]></content:encoded>
			<wfw:commentRss>http://maymay.net/blog/2008/03/26/howto-use-git-for-personal-development-when-everyone-else-is-using-subversion-part-1/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
	</channel>
</rss>

