changeset 36:485682a6de50

New site layout support.
author Ludovic Chabant <ludovic@chabant.com>
date Wed, 20 Aug 2014 23:16:51 -0700
parents e4c345dcf33c
children afcfecd3bf92
files piecrust/__init__.py piecrust/app.py piecrust/baking/baker.py piecrust/pathutil.py piecrust/processing/base.py piecrust/resources/theme/_content/pages/_category.html piecrust/resources/theme/_content/pages/_index.html piecrust/resources/theme/_content/pages/_tag.html piecrust/resources/theme/_content/templates/default.html piecrust/resources/theme/_content/templates/partial_post.html piecrust/resources/theme/_content/templates/post.html piecrust/resources/theme/_content/theme_config.yml piecrust/resources/theme/pages/_category.html piecrust/resources/theme/pages/_index.html piecrust/resources/theme/pages/_tag.html piecrust/resources/theme/templates/default.html piecrust/resources/theme/templates/partial_post.html piecrust/resources/theme/templates/post.html piecrust/resources/theme/theme_config.yml piecrust/sources/base.py piecrust/sources/posts.py tests/mockutil.py tests/test_data_assetor.py tests/test_processing_base.py tests/test_sources_base.py
diffstat 23 files changed, 262 insertions(+), 252 deletions(-) [+]
line wrap: on
line diff
--- a/piecrust/__init__.py	Wed Aug 20 21:46:27 2014 -0700
+++ b/piecrust/__init__.py	Wed Aug 20 23:16:51 2014 -0700
@@ -2,13 +2,13 @@
 APP_VERSION = '2.0.0alpha'
 
 CACHE_DIR = '_cache'
-CONTENT_DIR = '_content'
-TEMPLATES_DIR = '_content/templates'
-PLUGINS_DIR = '_content/plugins'
-THEME_DIR = '_content/theme'
+ASSETS_DIR = 'assets'
+TEMPLATES_DIR = 'templates'
+PLUGINS_DIR = 'plugins'
+THEME_DIR = 'theme'
 
-CONFIG_PATH = '_content/config.yml'
-THEME_CONFIG_PATH = '_content/theme_config.yml'
+CONFIG_PATH = 'config.yml'
+THEME_CONFIG_PATH = 'theme_config.yml'
 
 DEFAULT_FORMAT = 'markdown'
 DEFAULT_TEMPLATE_ENGINE = 'jinja2'
--- a/piecrust/app.py	Wed Aug 20 21:46:27 2014 -0700
+++ b/piecrust/app.py	Wed Aug 20 23:16:51 2014 -0700
@@ -7,7 +7,7 @@
 import yaml
 from werkzeug.utils import cached_property
 from piecrust import (APP_VERSION,
-        CACHE_DIR, TEMPLATES_DIR,
+        CACHE_DIR, TEMPLATES_DIR, ASSETS_DIR,
         PLUGINS_DIR, THEME_DIR,
         CONFIG_PATH, THEME_CONFIG_PATH,
         DEFAULT_FORMAT, DEFAULT_TEMPLATE_ENGINE, DEFAULT_POSTS_FS,
@@ -24,7 +24,7 @@
 logger = logging.getLogger(__name__)
 
 
-CACHE_VERSION = 11
+CACHE_VERSION = 12
 
 
 class VariantNotFoundError(Exception):
@@ -392,11 +392,24 @@
         return config
 
     @cached_property
+    def assets_dirs(self):
+        assets_dirs = self._get_configurable_dirs(ASSETS_DIR,
+                'site/assets_dirs')
+
+        # Also add the theme directory, if any.
+        if self.theme_dir:
+            default_theme_dir = os.path.join(self.theme_dir, ASSETS_DIR)
+            if os.path.isdir(default_theme_dir):
+                assets_dirs.append(default_theme_dir)
+
+        return assets_dirs
+
+    @cached_property
     def templates_dirs(self):
         templates_dirs = self._get_configurable_dirs(TEMPLATES_DIR,
                 'site/templates_dirs')
 
-        # Also, add the theme directory, if nay.
+        # Also, add the theme directory, if any.
         if self.theme_dir:
             default_theme_dir = os.path.join(self.theme_dir, TEMPLATES_DIR)
             if os.path.isdir(default_theme_dir):
--- a/piecrust/baking/baker.py	Wed Aug 20 21:46:27 2014 -0700
+++ b/piecrust/baking/baker.py	Wed Aug 20 23:16:51 2014 -0700
@@ -409,11 +409,12 @@
         self._waitOnWorkerPool(pool, abort)
 
     def _bakeAssets(self, record):
+        mounts = self.app.assets_dirs
         baker_params = self.app.config.get('baker') or {}
         skip_patterns = baker_params.get('skip_patterns')
         force_patterns = baker_params.get('force_patterns')
         proc = ProcessorPipeline(
-                self.app, self.out_dir, force=self.force,
+                self.app, mounts, self.out_dir, force=self.force,
                 skip_patterns=skip_patterns, force_patterns=force_patterns,
                 num_workers=self.num_workers)
         proc.run()
--- a/piecrust/pathutil.py	Wed Aug 20 21:46:27 2014 -0700
+++ b/piecrust/pathutil.py	Wed Aug 20 23:16:51 2014 -0700
@@ -8,14 +8,14 @@
             root = os.getcwd()
         Exception.__init__(self,
                 "No PieCrust website in '%s' "
-                "('_content/config.yml' not found!)." % root)
+                "('config.yml' not found!)." % root)
 
 
 def find_app_root(cwd=None):
     if cwd is None:
         cwd = os.getcwd()
 
-    while not os.path.isfile(os.path.join(cwd, '_content', 'config.yml')):
+    while not os.path.isfile(os.path.join(cwd, 'config.yml')):
         cwd = os.path.dirname(cwd)
         if not cwd or cwd == '/':
             return None
--- a/piecrust/processing/base.py	Wed Aug 20 21:46:27 2014 -0700
+++ b/piecrust/processing/base.py	Wed Aug 20 23:16:51 2014 -0700
@@ -98,14 +98,11 @@
 
     def __init__(self):
         super(ProcessorPipelineRecord, self).__init__()
-        self.is_multi_mount = False
 
     def addEntry(self, item):
         self.entries.append(item)
 
     def hasOverrideEntry(self, rel_path):
-        if not self.is_multi_mount:
-            return False
         return self.findEntry(rel_path) is not None
 
     def findEntry(self, rel_path):
@@ -133,9 +130,10 @@
 
 
 class ProcessorPipeline(object):
-    def __init__(self, app, out_dir, force=False, mounts=None,
+    def __init__(self, app, mounts, out_dir, force=False,
             skip_patterns=None, force_patterns=None, num_workers=4):
         self.app = app
+        self.mounts = mounts
         tmp_dir = app.cache_dir
         if not tmp_dir:
             import tempfile
@@ -143,16 +141,12 @@
         self.tmp_dir = os.path.join(tmp_dir, 'proc')
         self.out_dir = out_dir
         self.force = force
-        self.mounts = mounts or {}
         self.skip_patterns = skip_patterns or []
         self.force_patterns = force_patterns or []
         self.processors = app.plugin_loader.getProcessors()
         self.num_workers = num_workers
 
-        if app.theme_dir is not None:
-            self.mounts['theme'] = app.theme_dir
-
-        self.skip_patterns += ['_cache', '_content', '_counter',
+        self.skip_patterns += ['_cache', '_counter',
                 'theme_info.yml',
                 '.DS_Store', 'Thumbs.db',
                 '.git*', '.hg*', '.svn']
@@ -186,11 +180,15 @@
 
         if src_dir_or_file is not None:
             # Process only the given path.
-            # Find out if this source directory is in a mount point.
-            base_dir = self.app.root_dir
-            for name, path in self.mounts.items():
+            # Find out what mount point this is in.
+            for path in self.mounts:
                 if src_dir_or_file[:len(path)] == path:
                     base_dir = path
+                    break
+            else:
+                raise Exception("Input path '%s' is not part of any known "
+                                "mount point: %s" %
+                                (src_dir_or_file, self.mounts))
 
             ctx = ProcessingContext(base_dir, queue, record)
             logger.debug("Initiating processing pipeline on: %s" % src_dir_or_file)
@@ -201,14 +199,10 @@
 
         else:
             # Process everything.
-            ctx = ProcessingContext(self.app.root_dir, queue, record)
-            logger.debug("Initiating processing pipeline on: %s" % self.app.root_dir)
-            self.processDirectory(ctx, self.app.root_dir)
-            ctx.is_multi_mount = True
-            for name, path in self.mounts.items():
-                mount_ctx = ProcessingContext(path, queue, record)
+            for path in self.mounts:
+                ctx = ProcessingContext(path, queue, record)
                 logger.debug("Initiating processing pipeline on: %s" % path)
-                self.processDirectory(mount_ctx, path)
+                self.processDirectory(ctx, path)
 
         # Wait on all workers.
         for w in pool:
--- a/piecrust/resources/theme/_content/pages/_category.html	Wed Aug 20 21:46:27 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
----
-title:
-format: none
----
-<h2>Posts in {{ category }}</h2>
-
-<section>
-    {% for post in pagination.posts %}
-    {% include 'partial_post.html' %}
-    {% endfor %}
-</section>
-<section>
-    {% if pagination.prev_page %}<div class="prev"><a href="{{ pagination.prev_page }}">Next Posts</a></div>{% endif %}
-    {% if pagination.next_page %}<div class="next"><a href="{{ pagination.next_page }}">Previous Posts</a></div>{% endif %}
-</section>
-
--- a/piecrust/resources/theme/_content/pages/_index.html	Wed Aug 20 21:46:27 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
----
-title: 
-format: none
----
-
-{% if pagination.has_posts %}
-<section>
-    {% for post in pagination.posts %}
-    {% include 'partial_post.html' %}
-    {% endfor %}
-</section>
-<section>
-    {% if pagination.prev_page %}<div class="prev"><a href="{{ pagination.prev_page }}">Next Posts</a></div>{% endif %}
-    {% if pagination.next_page %}<div class="next"><a href="{{ pagination.next_page }}">Previous Posts</a></div>{% endif %}
-</section>
-{% endif %}
-
-
-<--markdown-->
-
-{% if not pagination.has_posts or not site.hide_quickstart %}
-
-## Quick Start
-
-Welcome to your new [PieCrust][] website!
-
-Since you don't seem to have any blog post or home page created yet, here's a
-quick reference of things you probably want to do next. All `chef` commands need
-to be run from inside your website's directory.
-
-For more information, refer to the [documentation][doc].
-
-
-### Create a new blog post
-
-Run `chef prepare post my-new-post-slug`, where `my-new-post-slug` is the [URL slug][slug] you want for your new post.
-
-
-### Change this page
-
-To override this default home page, you can do one of the following:
-
-* Just hide this "quick start" section by setting the `hide_quickstart` property
-  to `true` in your site configuration. To do this, edit `_content/config.yml`
-  and `hide_quickstart: true` under the `site` section.
-
-* Manually rewrite this page by creating `_content/pages/_index.md` or running
-  `chef prepare page _index`.
-
-* Install a theme with `chef themes install theme-name`, where `theme-name` is
-  the name of a theme. You can list existing official themes with `chef themes
-  find`.
-
-
-[piecrust]: {{ piecrust.url }}
-[doc]: http://bolt80.com/piecrust/doc/
-[slug]: http://en.wikipedia.org/wiki/Clean_URL#Slug
-{% endif %}
-
--- a/piecrust/resources/theme/_content/pages/_tag.html	Wed Aug 20 21:46:27 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
----
-title:
-format: none
----
-<h2>Posts tagged with {{ tag }}</h2>
-
-<section>
-    {% for post in pagination.posts %}
-    {% include 'partial_post.html' %}
-    {% endfor %}
-</section>
-<section>
-    {% if pagination.prev_page %}<div class="prev"><a href="{{ pagination.prev_page }}">Next Posts</a></div>{% endif %}
-    {% if pagination.next_page %}<div class="next"><a href="{{ pagination.next_page }}">Previous Posts</a></div>{% endif %}
-</section>
-
--- a/piecrust/resources/theme/_content/templates/default.html	Wed Aug 20 21:46:27 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,89 +0,0 @@
-<!doctype html>
-<head>
-  <meta charset="utf-8">
-  <meta name="description" content="{{ site.description }}">
-  <meta name="author" content="{{ site.author }}">
-  <meta name="viewport" content="width=device-width,initial-scale=1">
-  <title>{{ site.title }}{% if page.title %} &mdash; {{ page.title }}{% endif %}</title>
-  {% if site.css %}
-  <link rel="stylesheet" type="text/css" href="{{ site.root }}{{ site.css }}">
-  {% else %}
-  <style type="text/css">
-body {
-    font-family: 'Baskerville', Georgia, Times, serif;
-    font-size: 1.3em;
-    line-height: 1.5em;
-    padding: 0 1.5em;
-    margin: 0;
-}
-h1, h2, h3, h4, h5, h6 { font-weight: bold; }
-h1 { font-size: 2em; line-height: 0.75em; margin: 0.75em 0; }
-h2 { font-size: 1.5em; line-height: 1em; margin: 1em 0; }
-h3 { font-size: 1.2em; line-height: 1.25em; margin: 1.25em 0; }
-h4 { font-size: 1em; line-height: 1.5em; margin: 1.5em 0; }
-h5 { font-size: 0.8em; line-height: 1.875em; margin: 1.875em 0; }
-h6 { font-size: 0.75em; line-height: 2em; margin: 2em 0; }
-p {
-    font-size: 1em;
-    line-height: 1.5em;
-    margin: 1.5em 0;
-}
-code { background: #fee; padding: 0 0.3em; }
-#container > header {
-    text-align: center;
-}
-#container > footer {
-    text-align: center;
-    font-style: italic;
-    font-size: 0.8em;
-    color: #888;
-}
-
-.site-title {
-    font-size: 2em;
-    font-weight: bold;
-    line-height: 0.75em; 
-    margin: 0.75em 0; 
-}
-.post h2 { margin-bottom: 0.33em; }
-.date {
-    font-style: italic;
-    font-size: 0.75em;
-    line-height: 0.5em;
-    margin-bottom: 1.5em;
-}
-blockquote {
-    font-style: italic;
-}
-  </style>
-  {% endif %}
-</head>
-<body>
-  <div id="container">
-    <header>
-        {% block header %}
-        {% if page.title %}
-        <p class="site-title"><a href="{{ site.root }}">{{ site.title }}</a></p>
-        {% else %}
-        <h1><a href="{{ site.root }}">{{ site.title }}</a></h1>
-        {% endif %}
-        {% endblock %}
-    </header>
-    <section id="main" role="main">
-        {% block main %}
-        {% if page.title %}
-        <h1 class="page-title">{{ page.title }}</h1>
-        {% endif %}
-
-        {{ content|raw }}
-        {% endblock %}
-    </section>
-    <footer>
-        {% block footer %}
-        {% endblock %}
-        {{ piecrust.debug_info|raw }}
-        <p>{{ piecrust.branding|raw }}</p>
-    </footer>
-  </div>
-</body>
-</html>
--- a/piecrust/resources/theme/_content/templates/partial_post.html	Wed Aug 20 21:46:27 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +0,0 @@
-<article>
-    <header>
-        <h2><a href="{{ post.url }}">{{ post.title }}</a></h2>
-        <small>{{ post.date }}
-            {% if post.tags %}
-            in 
-            {% for t in post.tags %}
-            <a href="{{ pctagurl(t) }}">{{ t }}</a>{% if not loop.last %}, {% endif %}
-            {% endfor %}
-            {% endif %}
-            {% if post.category %}
-            | <a href="{{ pccaturl(post.category) }}">{{ post.category }}</a>
-            {% endif %}
-        </small>
-    </header>
-    <section>
-        {{ post.content|raw }}
-    </section>
-</article>
--- a/piecrust/resources/theme/_content/templates/post.html	Wed Aug 20 21:46:27 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
-{% extends "default.html" %}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/piecrust/resources/theme/pages/_category.html	Wed Aug 20 23:16:51 2014 -0700
@@ -0,0 +1,16 @@
+---
+title:
+format: none
+---
+<h2>Posts in {{ category }}</h2>
+
+<section>
+    {% for post in pagination.posts %}
+    {% include 'partial_post.html' %}
+    {% endfor %}
+</section>
+<section>
+    {% if pagination.prev_page %}<div class="prev"><a href="{{ pagination.prev_page }}">Next Posts</a></div>{% endif %}
+    {% if pagination.next_page %}<div class="next"><a href="{{ pagination.next_page }}">Previous Posts</a></div>{% endif %}
+</section>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/piecrust/resources/theme/pages/_index.html	Wed Aug 20 23:16:51 2014 -0700
@@ -0,0 +1,59 @@
+---
+title: 
+format: none
+---
+
+{% if pagination.has_posts %}
+<section>
+    {% for post in pagination.posts %}
+    {% include 'partial_post.html' %}
+    {% endfor %}
+</section>
+<section>
+    {% if pagination.prev_page %}<div class="prev"><a href="{{ pagination.prev_page }}">Next Posts</a></div>{% endif %}
+    {% if pagination.next_page %}<div class="next"><a href="{{ pagination.next_page }}">Previous Posts</a></div>{% endif %}
+</section>
+{% endif %}
+
+
+<--markdown-->
+
+{% if not pagination.has_posts or not site.hide_quickstart %}
+
+## Quick Start
+
+Welcome to your new [PieCrust][] website!
+
+Since you don't seem to have any blog post or home page created yet, here's a
+quick reference of things you probably want to do next. All `chef` commands need
+to be run from inside your website's directory.
+
+For more information, refer to the [documentation][doc].
+
+
+### Create a new blog post
+
+Run `chef prepare post my-new-post-slug`, where `my-new-post-slug` is the [URL slug][slug] you want for your new post.
+
+
+### Change this page
+
+To override this default home page, you can do one of the following:
+
+* Just hide this "quick start" section by setting the `hide_quickstart` property
+  to `true` in your site configuration. To do this, edit `_content/config.yml`
+  and `hide_quickstart: true` under the `site` section.
+
+* Manually rewrite this page by creating `_content/pages/_index.md` or running
+  `chef prepare page _index`.
+
+* Install a theme with `chef themes install theme-name`, where `theme-name` is
+  the name of a theme. You can list existing official themes with `chef themes
+  find`.
+
+
+[piecrust]: {{ piecrust.url }}
+[doc]: http://bolt80.com/piecrust/doc/
+[slug]: http://en.wikipedia.org/wiki/Clean_URL#Slug
+{% endif %}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/piecrust/resources/theme/pages/_tag.html	Wed Aug 20 23:16:51 2014 -0700
@@ -0,0 +1,16 @@
+---
+title:
+format: none
+---
+<h2>Posts tagged with {{ tag }}</h2>
+
+<section>
+    {% for post in pagination.posts %}
+    {% include 'partial_post.html' %}
+    {% endfor %}
+</section>
+<section>
+    {% if pagination.prev_page %}<div class="prev"><a href="{{ pagination.prev_page }}">Next Posts</a></div>{% endif %}
+    {% if pagination.next_page %}<div class="next"><a href="{{ pagination.next_page }}">Previous Posts</a></div>{% endif %}
+</section>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/piecrust/resources/theme/templates/default.html	Wed Aug 20 23:16:51 2014 -0700
@@ -0,0 +1,89 @@
+<!doctype html>
+<head>
+  <meta charset="utf-8">
+  <meta name="description" content="{{ site.description }}">
+  <meta name="author" content="{{ site.author }}">
+  <meta name="viewport" content="width=device-width,initial-scale=1">
+  <title>{{ site.title }}{% if page.title %} &mdash; {{ page.title }}{% endif %}</title>
+  {% if site.css %}
+  <link rel="stylesheet" type="text/css" href="{{ site.root }}{{ site.css }}">
+  {% else %}
+  <style type="text/css">
+body {
+    font-family: 'Baskerville', Georgia, Times, serif;
+    font-size: 1.3em;
+    line-height: 1.5em;
+    padding: 0 1.5em;
+    margin: 0;
+}
+h1, h2, h3, h4, h5, h6 { font-weight: bold; }
+h1 { font-size: 2em; line-height: 0.75em; margin: 0.75em 0; }
+h2 { font-size: 1.5em; line-height: 1em; margin: 1em 0; }
+h3 { font-size: 1.2em; line-height: 1.25em; margin: 1.25em 0; }
+h4 { font-size: 1em; line-height: 1.5em; margin: 1.5em 0; }
+h5 { font-size: 0.8em; line-height: 1.875em; margin: 1.875em 0; }
+h6 { font-size: 0.75em; line-height: 2em; margin: 2em 0; }
+p {
+    font-size: 1em;
+    line-height: 1.5em;
+    margin: 1.5em 0;
+}
+code { background: #fee; padding: 0 0.3em; }
+#container > header {
+    text-align: center;
+}
+#container > footer {
+    text-align: center;
+    font-style: italic;
+    font-size: 0.8em;
+    color: #888;
+}
+
+.site-title {
+    font-size: 2em;
+    font-weight: bold;
+    line-height: 0.75em; 
+    margin: 0.75em 0; 
+}
+.post h2 { margin-bottom: 0.33em; }
+.date {
+    font-style: italic;
+    font-size: 0.75em;
+    line-height: 0.5em;
+    margin-bottom: 1.5em;
+}
+blockquote {
+    font-style: italic;
+}
+  </style>
+  {% endif %}
+</head>
+<body>
+  <div id="container">
+    <header>
+        {% block header %}
+        {% if page.title %}
+        <p class="site-title"><a href="{{ site.root }}">{{ site.title }}</a></p>
+        {% else %}
+        <h1><a href="{{ site.root }}">{{ site.title }}</a></h1>
+        {% endif %}
+        {% endblock %}
+    </header>
+    <section id="main" role="main">
+        {% block main %}
+        {% if page.title %}
+        <h1 class="page-title">{{ page.title }}</h1>
+        {% endif %}
+
+        {{ content|raw }}
+        {% endblock %}
+    </section>
+    <footer>
+        {% block footer %}
+        {% endblock %}
+        {{ piecrust.debug_info|raw }}
+        <p>{{ piecrust.branding|raw }}</p>
+    </footer>
+  </div>
+</body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/piecrust/resources/theme/templates/partial_post.html	Wed Aug 20 23:16:51 2014 -0700
@@ -0,0 +1,19 @@
+<article>
+    <header>
+        <h2><a href="{{ post.url }}">{{ post.title }}</a></h2>
+        <small>{{ post.date }}
+            {% if post.tags %}
+            in 
+            {% for t in post.tags %}
+            <a href="{{ pctagurl(t) }}">{{ t }}</a>{% if not loop.last %}, {% endif %}
+            {% endfor %}
+            {% endif %}
+            {% if post.category %}
+            | <a href="{{ pccaturl(post.category) }}">{{ post.category }}</a>
+            {% endif %}
+        </small>
+    </header>
+    <section>
+        {{ post.content|raw }}
+    </section>
+</article>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/piecrust/resources/theme/templates/post.html	Wed Aug 20 23:16:51 2014 -0700
@@ -0,0 +1,2 @@
+{% extends "default.html" %}
+
--- a/piecrust/sources/base.py	Wed Aug 20 21:46:27 2014 -0700
+++ b/piecrust/sources/base.py	Wed Aug 20 23:16:51 2014 -0700
@@ -3,7 +3,6 @@
 import os.path
 import logging
 from werkzeug.utils import cached_property
-from piecrust import CONTENT_DIR
 from piecrust.configuration import ConfigurationError
 from piecrust.data.base import IPaginationSource, PaginationData
 from piecrust.data.filters import PaginationFilter
@@ -292,7 +291,7 @@
     def __init__(self, app, name, config):
         super(SimplePageSource, self).__init__(app, name, config)
         self.fs_endpoint = config.get('fs_endpoint', name)
-        self.fs_endpoint_path = os.path.join(self.root_dir, CONTENT_DIR, self.fs_endpoint)
+        self.fs_endpoint_path = os.path.join(self.root_dir, self.fs_endpoint)
         self.supported_extensions = list(app.config.get('site/auto_formats').keys())
         self.default_auto_format = app.config.get('site/default_auto_format')
 
--- a/piecrust/sources/posts.py	Wed Aug 20 21:46:27 2014 -0700
+++ b/piecrust/sources/posts.py	Wed Aug 20 23:16:51 2014 -0700
@@ -4,7 +4,6 @@
 import glob
 import logging
 import datetime
-from piecrust import CONTENT_DIR
 from piecrust.sources.base import (PageSource, IPreparingSource,
         SimplePaginationSourceMixin,
         PageNotFoundError, InvalidFileSystemEndpointError,
@@ -20,7 +19,7 @@
     def __init__(self, app, name, config):
         super(PostsSource, self).__init__(app, name, config)
         self.fs_endpoint = config.get('fs_endpoint', name)
-        self.fs_endpoint_path = os.path.join(self.root_dir, CONTENT_DIR, self.fs_endpoint)
+        self.fs_endpoint_path = os.path.join(self.root_dir, self.fs_endpoint)
         self.supported_extensions = list(app.config.get('site/auto_formats').keys())
         self.default_auto_format = app.config.get('site/default_auto_format')
 
--- a/tests/mockutil.py	Wed Aug 20 21:46:27 2014 -0700
+++ b/tests/mockutil.py	Wed Aug 20 23:16:51 2014 -0700
@@ -37,7 +37,7 @@
         self._fs = {self._root: {}}
         if default_spec:
             self.withDir('counter')
-            self.withFile('kitchen/_content/config.yml',
+            self.withFile('kitchen/config.yml',
                     "site:\n  title: Mock Website\n")
 
     def path(self, p):
@@ -72,12 +72,12 @@
 
     def withConfig(self, config):
         return self.withFile(
-                'kitchen/_content/config.yml',
+                'kitchen/config.yml',
                 yaml.dump(config))
 
     def withThemeConfig(self, config):
         return self.withFile(
-                'kitchen/_content/theme/_content/theme_config.yml',
+                'kitchen/theme/theme_config.yml',
                 yaml.dump(config))
 
     def withPage(self, url, config=None, contents=None):
@@ -92,13 +92,13 @@
         if not ext:
             url += '.md'
         url = url.lstrip('/')
-        return self.withAsset('_content/' + url, text)
+        return self.withAsset(url, text)
 
     def withPageAsset(self, page_url, name, contents=None):
         contents = contents or "A test asset."
         url_base, ext = os.path.splitext(page_url)
         dirname = url_base + '-assets'
-        return self.withAsset('_content/%s/%s' % (dirname, name),
+        return self.withAsset('%s/%s' % (dirname, name),
                 contents)
 
     def getStructure(self, path=None):
--- a/tests/test_data_assetor.py	Wed Aug 20 21:46:27 2014 -0700
+++ b/tests/test_data_assetor.py	Wed Aug 20 23:16:51 2014 -0700
@@ -22,7 +22,7 @@
         page = MagicMock()
         page.app = fs.getApp()
         page.app.env.base_asset_url_format = '%uri%'
-        page.path = fs.path('/kitchen/_content/pages/foo/bar.md')
+        page.path = fs.path('/kitchen/pages/foo/bar.md')
         assetor = Assetor(page, '/foo/bar')
         for en in expected.keys():
             assert hasattr(assetor, en)
@@ -37,7 +37,7 @@
         with mock_fs_scope(fs):
             page = MagicMock()
             page.app = fs.getApp()
-            page.path = fs.path('/kitchen/_content/pages/foo/bar.md')
+            page.path = fs.path('/kitchen/pages/foo/bar.md')
             assetor = Assetor(page, '/foo/bar')
             assetor['this_doesnt_exist']
 
@@ -51,7 +51,7 @@
         with mock_fs_scope(fs):
             page = MagicMock()
             page.app = fs.getApp()
-            page.path = fs.path('/kitchen/_content/pages/foo/bar.md')
+            page.path = fs.path('/kitchen/pages/foo/bar.md')
             assetor = Assetor(page, '/foo/bar')
             assetor['one']
 
--- a/tests/test_processing_base.py	Wed Aug 20 21:46:27 2014 -0700
+++ b/tests/test_processing_base.py	Wed Aug 20 23:16:51 2014 -0700
@@ -1,12 +1,16 @@
+import os.path
 import pytest
 from piecrust.processing.base import ProcessorPipeline
 from .mockutil import mock_fs, mock_fs_scope
 
 
 def _get_pipeline(fs, **kwargs):
-    return ProcessorPipeline(fs.getApp(), fs.path('counter'),
+    app = fs.getApp()
+    mounts = [os.path.join(app.root_dir, 'assets')]
+    return ProcessorPipeline(app, mounts, fs.path('counter'),
             num_workers=1, **kwargs)
 
+
 def test_empty():
     fs = mock_fs()
     with mock_fs_scope(fs):
@@ -21,7 +25,7 @@
 
 def test_one_file():
     fs = (mock_fs()
-            .withFile('kitchen/something.html', 'A test file.'))
+            .withFile('kitchen/assets/something.html', 'A test file.'))
     with mock_fs_scope(fs):
         pp = _get_pipeline(fs)
         pp.filterProcessors(['copy'])
@@ -43,9 +47,9 @@
         ])
 def test_skip_pattern(patterns, expected):
     fs = (mock_fs()
-            .withFile('kitchen/something.html', 'A test file.')
-            .withFile('kitchen/_hidden.html', 'Shhh')
-            .withFile('kitchen/foo/_important.html', 'Important!'))
+            .withFile('kitchen/assets/something.html', 'A test file.')
+            .withFile('kitchen/assets/_hidden.html', 'Shhh')
+            .withFile('kitchen/assets/foo/_important.html', 'Important!'))
     with mock_fs_scope(fs):
         pp = _get_pipeline(fs, skip_patterns=['/^_/'])
         pp.filterProcessors(['copy'])
--- a/tests/test_sources_base.py	Wed Aug 20 21:46:27 2014 -0700
+++ b/tests/test_sources_base.py	Wed Aug 20 23:16:51 2014 -0700
@@ -29,7 +29,7 @@
                 {'url': '/%path%', 'source': 'test'}]
             }
         })
-    fs.withDir('kitchen/_content/test')
+    fs.withDir('kitchen/test')
     with mock_fs_scope(fs):
         app = PieCrust(fs.path('kitchen'), cache=False)
         s = app.getSource('test')
@@ -42,8 +42,8 @@
 
 
 @pytest.mark.parametrize('ref_path, expected', [
-        ('foo.html', '/kitchen/_content/test/foo.html'),
-        ('foo/bar.html', '/kitchen/_content/test/foo/bar.html'),
+        ('foo.html', '/kitchen/test/foo.html'),
+        ('foo/bar.html', '/kitchen/test/foo/bar.html'),
         ])
 def test_default_source_resolve_ref(ref_path, expected):
     fs = mock_fs()