USA: +1 (888) 390-0649 toll-free | UK: +44 (1273) 399-431

How Subtraction.com was converted to EE

Adam Khan, Friday, August 29th, 2008
Last updated: Sat 1 Nov ’08 @ 10:20pm GMT

Being a blog, Khoi Vinh’s renowned Subtraction.com isn’t as complex a web site as one for an entire organization. But being a standard-bearer of design excellence, everything has to be just so. As Khoi switched to EllisLab’s ExpressionEngine web publishing system some of the details he wanted fell through the cracks of the software’s out-the-box functionality. This article explains how those details were implemented and how the site was architected in EE.

For the conversion of the renowned Subtraction.com web site to the renowned ExpressionEngine web publishing system the key architectural insight was that all except one of the major screens are either single-entry or multi-entry. The various multi-entry screens are therefore powered by just one multi-entry template and the single-entry screens by just one single-entry template, reducing the number of templates required, making the site easier to maintain.

The Index Template

In turn, both the single- and the various multi-entry screens spark from a single template, /site/index:

{embed="site/header"}

{if segment_1
=="" }
{
!-- Display multi-entry as homepage --}
{embed
="site/multi-entry" pagetype="homepage"}

{if
:elseif
 
segment_1 != "" && 
 
segment_1 != "archives" && 
 
segment_1 != "categories" && 
 
segment_1 != "about" && 
 
segment_4==""
}
{
!-- Display multi-entry as monthly --}
{if segment_1 
2000 && segment_2 && segment_2 13}
{embed
="site/multi-entry" pagetype="monthly"}
{
/if}

{if
:elseif segment_1 == "categories"}
{
!-- Display multi-entry as category --}
{embed
="site/multi-entry" pagetype="category"}

{if
:elseif segment_4}
{
!-- Display single-entry --}
{embed
="site/single-entry"}
{
/if}

{embed
="site/footer"

Here the first and last lines show that the header and footer are shunted out of the way from the get-go; any header variations among the screens are handled within the /site/header template. Into these site-wide standards is shoveled as much as possible in order to reduce the main templates.

The heart of /site/index is a conditional to test various URL structures. If for instance our URL has four segments then we’re on a single-entry page, as the site uses the blog-standard /year/month/date/dirified-title URL format for entries and no other screens have so many URL segments.

The Single-Entry Template

EE is not, despite its blogging software pedigree, designed for blog-standard URLs. Instead, its URLs use a /template-group/template/dirified-title format. Khoi was adamant however that his blog-standard URLs must stay. So this is handled in /site/single-entry:

{exp:weblog:entries weblog="subtraction|lens|elsewhere" disable="category_fields|pagination|trackbacks" year="{segment_1}" month="{segment_2}"  url_title="{segment_4}" dynamic="off" limit="1" status="not closed"}
{if member_group
=="1" || status=="open"}

<div id="posts">

<
div class="day">

 <
class="date">{entry_date format='%D %d %M'}<br /> {entry_date format='%Y'}</p>
 <
h2{if status=="Hold"} style="color: red"{/if}>{title}</h2>
 <
div class="post-info">
  <
span class="info-label">Posted</span>
  <
span class="info-data">{entry_date format='%h&#58;%i %a'}</span>
 </
div>
 <
div class="post-info">
  <
span class="info-label">Author</span>
  <
span class="info-data"><a href="{url}">{author}</a></span>
 </
div>

 
{if weblog_short_name == "subtraction"}
 
<div class="post-info">
  <
span class="info-label">Categories</span>
  <
span class="info-data">{categories backspace="1" style="linear"}<a href="{path=/}">{category_name}</a>, {/categories}</span>
 </
div>
 <
div class="body-lead">
  <
div class="info-label">Body</div>
  <
div class="first">
   
{body}
  
</div>
  
{extended}
 
</div><!-- /.body-lead -->
 <
div id="terminator">+</div>
 <
div class="clear"></div>
 
{/if}

 {if weblog_short_name 
== "elsewhere"}
 
<div class="post-info">
  <
span class="info-label">Rating</span>
  <
span class="info-data">
   <
div class="stars">
    <
a href="" class="stars-{elsewhere-rating}"><img src="{site_url}assets/1-star.gif" alt="Stars" width="14" height="14" /></a>
   </
div>
   <
div class="clear"></div>
  </
span>
 </
div>
 <
div class="body-lead">
  <
div class="info-label">Body</div>
  <
div class="first">
   
{elsewhere-body}
  
</div>
 </
div><!-- /.body-lead -->
 <
div id="terminator">+</div>
 <
div class="clear"></div>
 <
div class="post-info post-info-elsewhere">
  <
span class="info-label">Link</span>
  <
span class="info-data">
   <
div class="elsewhere-link"><a href="{elsewhere-url}">Jump to This Link</a></div>
  </
span>
 </
div>
 
{/if}

 {if weblog_short_name 
== "lens"}
 
<div class="post-info">
  <
span class="info-label">Categories</span>
  <
span class="info-data">&nbsp;
  
{categories backspace="2" style="linear"}<a href="{path=weblog/index}">{category_name}</a>, {/categories}
  
</span>
 </
div>
 <
div class="body-lead {lens-orientation}">
  
{lens-image} 
  {if lens
-caption != "" }
  {lens
-caption}
  {
/if}
  {if lens
-caption-extended != "" }
  {lens
-caption-extended}
  {
/if}
 
</div><!-- /.body-lead -->
 <
div id="terminator">+</div>
 <
div class="clear"></div>
 
{/if}

</div><!-- /.day -->

<?php
$still_allow_comments
="no";
$post_time=strval("{entry_date format='%U'}");
$current_time="{current_time}";
$comments_allowed_from $current_time 5184000;
if ( 
$post_time >= $comments_allowed_from 
{
 $still_allow_comments
="yes";
}
?>

{if allow_comments 
&& comment_total == 0}
{if
:elseif allow_comments && comment_total >= 1}
<a name="remarks"></a>
<
div id="remarks">
 <
h3>
  <
span class="notation">Remarks</span>
  
<?php if ( $still_allow_comments == "yes" ) : ?>
  {comment_total} total remarks on this post
. <a href="#add-form">Add your own remarks below.</a>
  
<?php else: ?>
  {comment_total} total remarks were added before the post was closed
.
  
<?php endif; ?>
 
</h3>
{/if}

{embed
="site/display-comments" thisentry_id="{entry_id}"}

{if member_group
=="1" || status=="open"}
</div><!-- /.remarks -->
{/if}

<?php
if ( $still_allow_comments == "yes" ) :
?>

<div class="add-remarks">

 
{exp:comment:form preview="site/comment_preview" url_title="{segment_4}"}
 
<h3>Add Remarks<a name="add-form"></a></h3>
 <
div class="guidelines">Please be nice.</div>
 
{if logged_in}
 
<div class="add-lead">
  <
label for="name">Name</label>
  <
div><input type="text" size="3" id="name" name="name" value="{name}" /><span>Your name is required.</span></div>
 </
div><!-- /.add-lead -->
 <
div class="clear"></div>

 <
div class="add-lead">
  <
label for="email">Email</label>
  <
div><input type="text" size="3" id="email" name="email" value="{email}" /><span>An email address is required.</span></div>
 </
div><!-- /.add-lead -->
 <
div class="clear"></div>

 <
div class="add-lead">
  <
label for="url">Web Site</label>
  <
input type="text" size="3" id="url" name="url" value="{url}" />
 </
div><!-- /.add-lead -->
 <
div class="clear"></div>
 
{/if}

 {if logged_out}
 
<div class="add-lead">
  <
label for="remember">Your Info</label>
  <
input type="checkbox" name="save_info" value="yes" {save_info} />
<
span>Save my personal information for next time</span>
 </
div><!-- /.add-lead -->
 <
div class="clear"></div>
 
{/if}

 
<div class="add-lead">
  <
label for="remember">Follow-up</label>
  <
input type="checkbox" name="notify_me" value="yes" {notify_me} />
<
span>Notify me of further remarks on this post</span>
  <
div class="clear"></div>
 </
div><!-- /.add-lead -->

 <
div class="add-lead">
  <
label class="your-remarks">Your Remarks</label>
  <
textarea name="comment" rows="10" cols="40" class="your-remarks">{comment}</textarea>
 </
div><!-- /.add-lead -->

 <
div class="add-last-lead">
  <
label>Submit</label>
  <
span><input type="submit" name="preview" value="Preview"  /></span>
  <
span><input type="submit" name="submit" value="Submit" /></span>
 </
div><!-- /.add-lead -->
 
{/exp:comment:form}
 
</div><!-- /.add-remarks -->

<?php endif; ?>

</div><!-- /#posts -->

{embed="site/sidebar" pagetype="article" articletype="{lens-orientation}" }

{
/if}
{
/exp:weblog:entries} 

In the template’s sole weblog:entries tag a few extra parameters have been added:

year="{segment_1}" month="{segment_2}" url_title="{segment_4}" dynamic="off" limit="1" 

The tag’s “dynamic” parameter switches off EE’s default method of retrieving the entry from the URL. Instead the year is tested against the contents of segment #1, the month against segment #2 and the dirified URL against segment #4. (The date isn’t tested against segment #3 because this seems to filter out entries posted between 12:00am and 1:00am.)

The “weblog” parameter contains all three weblogs—subtraction, lens and elsewhere. Initially the template handles entries similarly regardless of their weblog, displaying the title, date and author. Then entries fork depending on their weblog.

After the “day” div are a few lines of PHP beginning with

still_allow_comments="no"

Khoi wants each post’s comments to close after 60 days. While EE does have a comment expiration field in the Publish/Edit Form, it’s a pity to set each post’s expiration date manually when a single 60-day rule covers them all. So the PHP here tests whether the entry is less than 60 days old, and if it is, the variable $still_allow_comments is set to “yes”, displaying the comment area’s header as an invitation to comment rather than a notice that comments are closed, followed later by the comment form.

Near the end of the template is the call to the sidebar. This call could have been made from the footer except for the sidebar needing to know if our page is a Lens entry with a landscape-shaped image, and if so to not display certain sidebar modules since they conflict with the image. (The sidebar template could also find out what it needs by running the weblog:entries tag itself.)

The comments are displayed in a sub-template /site/display-comments because we need them to be outside of the weblog:entries tag in order to display Khoi’s comments differently:

{exp:comment:entries weblog="subtraction|lens|elsewhere" dynamic="off" entry_id="{embed:thisentry_id}"}
<div class="remarks-lead{if author_id=="1"} owner{/if}">
    <
div class="remarks-date">
        <
b>{comment_date format='%D %d %M %Y'}</bat {comment_date format='%h&#58;%i %a'}<br />
        
{url_as_author}
    
</div>
    
{comment}
    
<div class="clear"></div>
</
div><!-- /.remarks-lead -->
{/exp:comment:entries} 

The Sidebar Template

/site/sidebar:

<div id="sidebar">

 
{!-- QUICK ACCESSDisplay on HomepageMonthlyCategoryArticle --}

 {if 
  
"{embed:pagetype}" == "homepage"
  
|| "{embed:pagetype}" == "monthly"
  
|| "{embed:pagetype}" == "category" 
  
|| "{embed:pagetype}" == "article"
  
|| "{embed:pagetype}" == "about"
 
}
 
<div class="sidebar-module">
  <
h3>Quick Access</h3>
  
{exp:stats weblog="subtraction|elsewhere|lens"}
  <?php $total_entries 
number_format("{total_entries}"); ?>
  {
/exp:stats}
  
<class="first"><a href="http://www.subtraction.com/archives/"><?php echo $total_entries?></aposts since July 2000.</p>
  <
div class="full-archives">
   <
label>Dates</label>
   <
div class="pulldown">
    <
select name="date-archive" onchange="jumpMenu('parent',this,0)">
     <
option value="" selected="selected">Select&#8230;</option>
     
{exp:weblog:month_links limit="12"}
     
<option value="{path=/}">{month} {year}</option>
     
{/exp:weblog:month_links}
     
<option value="http://www.subtraction.com/archives/">All Older Posts</option>
    </
select>
   </
div><!-- /.pulldown -->
  </
div><!-- /.full-archives -->
  <
div class="full-archives">
   <
label>Categories</label>
   <
div class="pulldown">
    <
select name="date-archive" onchange="jumpMenu('parent',this,0)">
     <
option value="" selected="selected">Select&#8230;</option>
     
{exp:weblog:categories weblog="subtraction" category_group="1" 
  
style="linear" orderby="title" disable="category_fields" }
     
<option value="{path=/}">{category_name}</option>
     
{/exp:weblog:categories}
    
</select>
   </
div><!-- /.pulldown -->
  </
div><!-- /.full-archives -->
 </
div><!-- /.sidebar-module -->
 
{/if}

 {
!-- RECENT POSTSDisplay on MonthlyCategoryArticle providing not Landscape --}

 {if 
  
"{embed:pagetype}" == "monthly" 
  
|| "{embed:pagetype}" == "category" 
  
|| "{embed:pagetype}" == "article" 
  
&& "{embed:articletype}" != "landscape" 
 
}
 
<div class="sidebar-module">
  <
h3>Recent Posts</h3
  
{exp:weblog:entries orderby="date" sort="desc" limit="5" weblog="subtraction" dynamic="off"
  
disable="pagination|categories|member_data|trackbacks"}
  
<class="date">{entry_date format='%d %M %Y'}</p>
  <
h4><a href="{path={entry_date format="%Y/%m/%d"}}{url_title}">{title}</a></h4>
  
{summary}{lens-caption}
  {
/exp:weblog:entries}
 
</div><!-- /.sidebar-module -->
 
{/if}

 {
!-- ABOUT MEDisplay on Homepage --}

 {if 
"{embed:pagetype}" == "homepage"}
 {
!-- Display on homepage --}
 
<div class="sidebar-module">
  <
h3>About Me</h3>
  <
class="first">I work as the Design Director at <a href="http://www.nytimes.com">NYTimes.com</a>, 
 and 
I have a dog named <a href="http://www.misterpresident.org/">Mister President</a>. 
 <
a href="http://www.subtraction.com/about/">Read more</a>.</p>
 </
div><!-- /.sidebar-module -->
 
{/if}

 {
!-- PLEASE NOTEDisplay on Homepage --}

 {if 
"{embed:pagetype}" == "homepage"}
 
<div class="sidebar-module">
  <
h3>Please Note</h3
  <
class="first">Some things that may interest you.</p>
  <
a href="http://www.abriefmessage.com/"><img src="http://www.subtraction.com/images/tout_abm.gif" 
 
alt="A Brief Message" width="190" height="170" /></a>
  <
a href="http://www.misterpresident.org"><img src="http://www.subtraction.com/images/tout_mr_president.gif" 
 
alt="MisterPresident.org" width="190" height="50" /></a>
  <
a href="http://www.subtraction.com/archives/2007/0827_a_subway_sys.php">
<
img src="http://www.subtraction.com/images/tout_subway_maps.gif" alt="Subway Maps for iPhone" width="190" height="50" />
</
a>
 </
div><!-- /.sidebar-module -->
 
{/if}

 {
!-- GET YOUR XML ONDisplay on Homepage --}

 {if 
"{embed:pagetype}" == "homepage"}
 {
!-- Display on homepage --}
 
<div class="sidebar-module">
  <
h3>Get Your XML On</h3>
  <
ul class="xml">
   <
li><a href="http://feeds.feedburner.com/subtraction" class="atom">Subscribe to the Feed</a></li>
  </
ul>
 </
div><!-- /.sidebar-module -->
 
{/if}

 {
!-- COLOPHONDisplay on Homepage --}

 {if 
"{embed:pagetype}" == "homepage"}
 {
!-- Display on homepage --}
 
<div class="sidebar-module">
  <
h3>Colophon</h3>
  <
ul class="colophon">
   <
li><a href="http://www.apple.com" class="mac">Made with a Macintosh</a></li>
   <
li><a href="http://www.expressionengine.com" class="ee">Published with Expression Engine</a></li>
   <
li><a href="http://creativecommons.org/licenses/by-nc-sa/1.0" class="cc">Creative Commons License</a></li>
  </
ul>
 </
div><!-- /.sidebar-module -->
 
{/if}

 {
!-- RECENT REMARKSDisplay on Homepage --}

 {if 
"{embed:pagetype}" == "homepage"}
 
<div class="sidebar-module">
  <
h3>Recent Remarks</h3
  
{exp:comment:entries sort="desc" orderby="date" limit="5" dynamic="off"}
  
<class="date">{comment_date format="%d %M %Y"}</p>
  <
h4>{name} on <a href="{comment_url_title_auto_path}">{title}</a></h4>
  
{/exp:comment:entries}
 
</div><!-- /.sidebar-module -->
 
{/if}
 
</div><!-- /sidebar --> 

The sidebar is comprised of a series of modules—Quick Access, Recent Posts, About Me, Please Note, Get Your XML On, Colophon and Recent Remarks—some static, some dynamic, each displayed or not depending on the value of the pagetype variable, which is always passed to the sidebar template by its parent.

The Multi-Entry Template

That’s it for a single entry, now for the multi-purpose /site/multi-entry template:

<div class="clear"></div>

{if embed:pagetype == "homepage"}
<div id="cover">&nbsp;</div>
{if:elseif embed:pagetype == "monthly" }
{embed
="embeds/top-monthly"}
{if
:elseif embed:pagetype == "category"}
{embed
="embeds/top-categories"}
{
/if}

<div id="posts">

 
{if segment_1 == ""}
 {embed
="site/multi-entry-inner" entrieslimit="60"}
 {if
:else}
 {embed
="site/multi-entry-inner" entrieslimit="999"}
 {
/if}

 {
!-- if "{embed:pagetype}" != "category"}
 {embed
="embeds/posts-more"}
 {
/if --}

</div><!-- /#posts -->

{embed="site/sidebar" pagetype="{embed:pagetype}" articletype=""

The multi-entry template contains only three parts: its top, the posts, and the call to the sidebar. The top varies depending on whether the template has been called to display the homepage, a monthly or a category page.

The Multi-Entry Inner Template

If we’re on the homepage, the /site/multi-entry-inner template is called with a limit of 60 entries:

<?php 
$daylimit
=""
$daycount=""
?>

{exp
:weblog:entries weblog="subtraction|elsewhere|lens" orderby="date" sort="desc" 
 
limit="{embed:entrieslimit}" disable="member_data|trackbacks" relaxed_categories="yes"}
{date_heading display
="daily"}
<?php $daylimit
++; if ($daylimit=="1"{ $daylimit="2"} ?>
{
/date_heading}
{
/exp:weblog:entries}
 
<?php 
if ("{embed:entrieslimit}" != "60"{ $daylimit="999"} ?>

{exp
:weblog:entries weblog="subtraction|elsewhere|lens" orderby="date" sort="desc" 
 
limit="{embed:entrieslimit}" disable="member_data|trackbacks" relaxed_categories="yes"}

{date_heading display
="daily"}
<?php $daycount
++; if ($daycount $daylimit) : ?>
<div class="day">
 <
class="date">
  
{entry_date format='%D %d %M'}<br />
  
{entry_date format='%Y'}
 
</p>
 
<?php endif; ?>
 {
/date_heading}

 <?php 
if ($daycount $daylimit) : ?>

 <?php
 $still_allow_comments
="no";
 
$post_time=strval("{entry_date format='%U'}");
 
$current_time="{current_time}";
 
$comments_allowed_from $current_time 5184000;
 if ( 
$post_time >= $comments_allowed_from 
 
{
  $still_allow_comments
="yes";
 
}
 ?>

 {if weblog_short_name 
== "elsewhere"}
 
<h2 id="{entry_id}" class="elsewhere"><a href="{elsewhere-url}">{title}</a></h2>
 <
div class="post-lead">
  <
div class="time-remarks">
   <
div class="stars">
    <
a href="{path={entry_date format="%Y/%m/%d"}}{url_title}" class="stars-{elsewhere-rating}">
<
img src="{site_url}assets/1-star.gif" alt="Stars" width="14" height="14" />
</
a>
   </
div><!-- /.stars -->
  </
div><!-- /.time-remarks -->
  
{if elsewhere-body != "" }
  {elsewhere
-body}
  {if
:elseif elsewhere-body == ""}
  
<p>&nbsp;</p>
  
{/if}
 
</div><!-- /.post-lead -->
 <
div class="clear"></div>
 
{/if}

 {if weblog_short_name 
== "lens"}
 
<h2 id="{entry_id}">
 <
a href="{path={entry_date format="%Y"}/{entry_date format="%m"}/{entry_date format="%d"}}{url_title}">{title}</a>
</
h2>
 <
div class="post-lead lens">
  <
div class="time-remarks">
   
{entry_date format="%g:%i %A"}<br />
   
{if allow_comments && comment_total == 0}
   <?php 
if ( $still_allow_comments == "yes" ) : ?>
   
<a href="{path={entry_date format="%Y/%m/%d"}}{url_title}#add-form">Add Remarks</a>
   
<?php endif; ?>
   {if
:elseif allow_comments && comment_total >= 1}
   
<a href="{path={entry_date format="%Y/%m/%d"}}{url_title}#remarks">Remarks ({comment_total})</a>
   
{/if}
  
</div><!-- /.time-remarks -->
  <
a href="{path={entry_date format="%Y/%m/%d"}}{url_title}">{lens-preview}</a
  
{if lens-caption != "" }
  {lens
-caption}
  {
/if}
  
<div class="full">
   <
a href="{path={entry_date format="%Y/%m/%d"}}{url_title}">View Full Image</a>
  </
div>

  
{if allow_comments && comment_total == 0}
  <?php 
if ( $still_allow_comments == "yes" ) : ?>
  
<div class="add"><a href="{path={entry_date format="%Y/%m/%d"}}{url_title}#add-form">Add First Remarks</a></div>
  
<?php endif; ?>
  {if
:elseif allow_comments && comment_total >= 1}
  <?php 
if ( $still_allow_comments == "yes" ) : ?>
  
<div class="add"><a href="{path={entry_date format="%Y/%m/%d"}}{url_title}#add-form">Add Remarks ({comment_total} so far)</a></div>
  
<?php endif; ?>
  {
/if}
 
</div><!-- /.post-lead -->
 <
div class="clear"></div>
 
{/if}

 {if weblog_short_name 
== "subtraction"}
 
<h2 id="{entry_id}"><a href="{path={entry_date format="%Y"}/{entry_date format="%m"}/{entry_date format="%d"}}{url_title}">{title}</a></h2>
 <