<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -15,7 +15,9 @@ class Entries_Model /* extends Model */
         'txt'      =&gt; 'formatMarkdown',
         'md'       =&gt; 'formatMarkdown',
         'markdown' =&gt; 'formatMarkdown',
+        'tx'       =&gt; 'formatTextile',
         'textile'  =&gt; 'formatTextile',
+        'opml'     =&gt; 'formatOPML',
         'html'     =&gt; 'formatPassthrough'
     );
 
@@ -25,6 +27,7 @@ class Entries_Model /* extends Model */
     public function __construct()
     {
         $this-&gt;path = Kohana::config('model.entries_path');
+        $this-&gt;exts = &quot;{&quot; . join(&quot;,&quot;, array_keys($this-&gt;formatter_map)) . &quot;}&quot;;
     }
 
     /**
@@ -56,6 +59,20 @@ class Entries_Model /* extends Model */
     }
 
     /**
+     * For a directory to a month in a year, remove the path and year from the 
+     * front.
+     *
+     * @param string absolute path to month directory
+     * @return string month directory
+     */
+    private function removePathAndYear($fn)
+    {
+        $fn = str_replace($this-&gt;path.'/', '', $fn);
+        list($undef, $mo) = explode('/', $fn, 2);
+        return $mo;
+    }
+
+    /**
      * Get a list of days for a month in a year.
      *
      * @param string Year in which to look for months, defaults to current year.
@@ -66,8 +83,8 @@ class Entries_Model /* extends Model */
     {
         if (!is_numeric($yr)) $yr = date('Y');
         if (!is_numeric($mo)) $yr = date('m');
-        $files = glob(&quot;{$this-&gt;path}/{$yr}/{$mo}/*&quot;, GLOB_ONLYDIR);
-        $das = array_map(array($this, 'extractDayFromPath'), $files);
+        $files = glob(&quot;{$this-&gt;path}/{$yr}/{$mo}/*&quot;);
+        $das = array_map(array($this, 'convertPathIntoDate'), $files);
         rsort($das);
         return $das;
     }
@@ -79,8 +96,8 @@ class Entries_Model /* extends Model */
      */
     public function getDates()
     {
-        $dirs = glob($this-&gt;path . '/*/*/*', GLOB_ONLYDIR);
-        $dates = array_map(array($this, 'removePath'), $dirs);
+        $dirs = glob($this-&gt;path . '/*/*/*');
+        $dates = array_map(array($this, 'convertPathIntoDate'), $dirs);
         return $dates;
     }
 
@@ -131,8 +148,13 @@ class Entries_Model /* extends Model */
         if (!is_numeric($da)) $da = '*';
 
         $dates = array_map(
-            array($this, 'removePath'),
-            glob(&quot;{$this-&gt;path}/{$yr}/{$mo}/{$da}&quot;)
+            array($this, 'convertPathIntoDate'),
+            array_merge(
+                // HACK: Need to get both directories of entries, as well as 
+                // single-day entry files.
+                glob(&quot;{$this-&gt;path}/{$yr}/{$mo}/{$da}&quot;),
+                glob(&quot;{$this-&gt;path}/{$yr}/{$mo}/{$da}.{$this-&gt;exts}&quot;)
+            )
         );
         rsort($dates);
 
@@ -147,14 +169,42 @@ class Entries_Model /* extends Model */
     }
 
     /**
+     * Take an absolute path, either to a day's directory or a day's entry file 
+     * and convert into a yyyy/mm/dd date.
+     *
+     * @param string absolute path
+     * @return string a date in yyyy/mm/dd format
+     */
+    private function convertPathIntoDate($fn)
+    {
+        $fn = $this-&gt;removePath($fn);
+        return substr($fn, 0, 10);
+    }
+
+    /**
+     * Snip the current entries path from the front of an absolute path.
+     *
+     * @param string an absolute path
+     * @return string the relative path
+     */
+    private function removePath($fn)
+    {
+        return str_replace($this-&gt;path.'/', '', $fn);
+    }
+
+    /**
      * Get a count of available entries
      *
      * @param numeric count of entries
      */
     public function getEntryCount()
     {
-        $exts = &quot;{&quot; . join(&quot;,&quot;, array_keys($this-&gt;formatter_map)) . &quot;}&quot;;
-        $files = glob(&quot;{$this-&gt;path}/*/*/*/*.{$exts}&quot;, GLOB_BRACE);
+        $files = array_merge(
+            // HACK: Need to get both directories of entries, as well as 
+            // single-day entry files.
+            glob(&quot;{$this-&gt;path}/*/*/*/*.{$this-&gt;exts}&quot;, GLOB_BRACE),
+            glob(&quot;{$this-&gt;path}/*/*/*.{$this-&gt;exts}&quot;, GLOB_BRACE)
+        );
         return count( $files );
     }
 
@@ -178,8 +228,12 @@ class Entries_Model /* extends Model */
         if (isset($this-&gt;_entry_cache[$date]))
             return $this-&gt;_entry_cache[$date];
 
-        $exts = &quot;{&quot; . join(&quot;,&quot;, array_keys($this-&gt;formatter_map)) . &quot;}&quot;;
-        $files = glob(&quot;{$this-&gt;path}/{$date}/*.{$exts}&quot;, GLOB_BRACE);
+        $files = array_merge(
+            // HACK: Need to get both directories of entries, as well as 
+            // single-day entry files.
+            glob(&quot;{$this-&gt;path}/{$date}/*.{$this-&gt;exts}&quot;, GLOB_BRACE),
+            glob(&quot;{$this-&gt;path}/{$date}.{$this-&gt;exts}&quot;, GLOB_BRACE)
+        );
         rsort($files);
         
         $raw = array();
@@ -191,13 +245,10 @@ class Entries_Model /* extends Model */
             $ext = pathinfo($fn, PATHINFO_EXTENSION);
             if (isset($this-&gt;formatter_map[$ext])) {
                 
-                $ct = $this-&gt;filterContent(file_get_contents($fn));
-                $raw[$fn] = $ct;
-
-                $ct_html = call_user_func(
+                $raw[$fn] = $ct = file_get_contents($fn);
+                $html[] = call_user_func(
                     array($this, $this-&gt;formatter_map[$ext]), $ct
                 ); 
-                $html[] = $ct_html;
             }
         }
 
@@ -213,33 +264,103 @@ class Entries_Model /* extends Model */
             );
     }
 
+    /**
+     * Format raw text as HTML using Markdown
+     *
+     * @param string raw text
+     * @param string formatted HTML
+     */
     public function formatMarkdown($raw)
     {
         require_once 'Markdown.php';
-        return Markdown($raw);
+        return Markdown($this-&gt;filterComments($raw));
     }
 
+    /**
+     * Format raw text as HTML using Textile
+     *
+     * @param string raw text
+     * @param string formatted HTML
+     */
     public function formatTextile($raw)
     {
         $t = new Textile();
-        return $t-&gt;TextileThis($raw);
+        return $t-&gt;TextileThis($this-&gt;filterComments($raw));
     }
 
+    /**
+     * Format raw text as HTML using nothing
+     *
+     * @param string raw text
+     * @param string formatted HTML
+     */
     public function formatPassthrough($raw)
     {
         return $raw;
     }
 
-    public function filterContent($raw)
+    /**
+     * Format raw OPML as HTML using an XSL stylesheet
+     *
+     * @param string raw OPML
+     * @param string formatted HTML
+     */
+    public function formatOPML($raw)
+    {
+        $doc = simplexml_load_string($raw);
+        return $this-&gt;_formatOutlineNode($doc-&gt;body);
+    }
+
+    /**
+     * Format the outline children of a parsed OPML node.
+     * Recursively called to format an entire outline.
+     *
+     * @param SimpleXMLElement parent outline node
+     * @param text formatted HTML.
+     */
+    public function _formatOutlineNode($node)
+    {
+        if (empty($node)) return '';
+
+        $out = array();
+        $out[] = '&lt;ul&gt;';
+        foreach ($node-&gt;outline as $outline) {
+            $out[] = '&lt;li&gt;';
+            if (!empty($outline['text'])) {
+                $out[] = '&lt;p&gt;' . $outline['text'] . '&lt;/p&gt;';
+                // TODO: inject permalinks based on date/time or derived title.
+            }
+            if (count($outline-&gt;outline)) {
+                $out[] = $this-&gt;_formatOutlineNode($outline);
+            }
+            $out[] = '&lt;/li&gt;';
+        }
+        $out[] = '&lt;/ul&gt;';
+        return join(&quot;\n&quot;, $out);
+    }
+
+    /**
+     * Filter comments from raw text, eg. lines starting with // and /*
+     *
+     * @param string raw text
+     * @return string text filtered for comments
+     */
+    public function filterComments($raw)
     {
         $lines = array_filter( 
             explode(&quot;\n&quot;, $raw),
-            array($this, 'filterContentLine')
+            array($this, 'filterCommentLine')
         );
         return join(&quot;\n&quot;, $lines);
     }
 
-    public function filterContentLine($line)
+    /**
+     * Determine whether a line contains a comment.
+     *
+     * @param string raw text line
+     * @return boolean whether or not the line is a comment
+     */    
+    public function filterCommentLine($line)
     {
         return (
             strpos($line, '/* ') !== 0 &amp;&amp;
@@ -247,24 +368,4 @@ class Entries_Model /* extends Model */
         );
     }
 
-    private function extractDayFromPath($fn)
-    {
-        $fn = str_replace($this-&gt;path.'/', '', $fn);
-        $parts = explode('/', $fn);
-        $name = array_pop($parts);
-        return $name;
-    }
-
-    private function removePathAndYear($fn)
-    {
-        $fn = str_replace($this-&gt;path.'/', '', $fn);
-        list($undef, $mo) = explode('/', $fn, 2);
-        return $mo;
-    }
-
-    private function removePath($fn)
-    {
-        return str_replace($this-&gt;path.'/', '', $fn);
-    }
-
 }</diff>
      <filename>application/models/entries.php</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>2e714bde99fbfeb373288c8f2a211f07977d3ae9</id>
    </parent>
  </parents>
  <author>
    <name>Leslie Michael Orchard</name>
    <login>lmorchard</login>
    <email>l.m.orchard@pobox.com</email>
  </author>
  <url>http://github.com/lmorchard/decafbucket/commit/dbacbd6a8252720679afac925eb656081b030738</url>
  <id>dbacbd6a8252720679afac925eb656081b030738</id>
  <committed-date>2009-02-23T19:04:59-08:00</committed-date>
  <authored-date>2009-02-23T19:04:59-08:00</authored-date>
  <message>Added support for both single files and directories per day</message>
  <tree>42d5066e4eee40b152c32e1ca502f64416babc69</tree>
  <committer>
    <name>Leslie Michael Orchard</name>
    <login>lmorchard</login>
    <email>l.m.orchard@pobox.com</email>
  </committer>
</commit>
