Commits

Chris Ridgway committed 0bf0d31

Updated to Cactus 2.0.

Comments (0)

Files changed (34)

extras/__init__.py

Empty file removed.

extras/hooks.py

-import os
-import re
-
-def preBuild(path, config):
-    import re
-    import render
-    import shutil
-    import codecs
-    from datetime import datetime, date
-    import markdown
-
-    from cactus import Config
-    site_cfg = Config(os.path.join(path, 'site.json'))
-    print site_cfg._data
-
-    src_dir = os.path.join(path, 'posts')
-    dst_dir = os.path.join(path, 'pages/posts')
-
-    # Make the ouput directory
-    shutil.rmtree(path=dst_dir, ignore_errors=True)
-    os.mkdir(dst_dir)
-
-    # Convert all the markdown files in the src directory to HTML and put
-    # the new pages in the dst directory.
-    md = markdown.Markdown(extensions = ['meta', 'codehilite'])
-
-    def toPostFilename(m):
-        return m.group(2) + '.html'
-
-    test = re.compile(r'^(\d{4}-\d{2}-\d{2})-([a-z\-]+)\.markdown')
-    files = [(os.path.join(src_dir, f), os.path.join(dst_dir, test.sub(toPostFilename, f))) for f in os.listdir(src_dir) if test.search(f)]
-
-    posts = []
-    for (infile, outfile) in files:
-
-        print "  * Converting %s" % infile
-
-        if not os.path.exists(outfile):
-
-            # Read in markdown and convert it to HTML
-            f = codecs.open(infile)
-            source = f.read()
-            f.close()
-
-            html = md.convert(source)
-
-            # Write out new article as HTML
-            f = codecs.open(outfile, 'w', 'utf8')
-
-            title = md.Meta['title'][0].encode('utf-8')
-            post_date = datetime.strptime(md.Meta['date'][0], "%Y/%m/%d %H:%M:%S")
-
-            f.write('{% extends "article.html" %}\n')
-            f.write('{%% block title %%}%s{%% endblock %%}\n' % title)
-            f.write('{%% block content %%}\n<h1>%s</h1>\n<p class="postdate">Posted %s</p><br/>%s\n{%% endblock %%}\n' % (title, post_date.strftime("%B %d, %Y"), html))
-            f.close()
-
-            # Add the post to our list of posts to be used later for building
-            # the site archive or latest pages listing.
-            page = os.path.relpath(outfile, os.path.join(path, 'pages'))
-
-            posts.append((post_date,       # For sorting
-                                {
-                                    "datetime": post_date,
-                                    "title": title,
-                                    "author": md.Meta['authors'][0].encode('utf-8'),
-                                    "page": page,
-                                    "permalink": '/'.join([site_cfg.get('SITE_URL'), page]),
-                                    "content": html
-                                 }))
-        else:
-            print "ERROR: Multiple files with same title: %s" % title
-            print "       Continuing on anyway."
-
-    # Now build a chronological list of posts and their information for a template
-    # to use.  The order is newest to oldest.
-    render.global_context["POST_LISTING"] = [post[1] for post in sorted(posts, reverse=True)]
-    render.global_context.update(site_cfg._data)
-
-def postBuild(path, config):
-    import shutil
-    shutil.rmtree(path=os.path.join(path, 'pages/posts'), ignore_errors=True)
-
-def preDeploy(path, config):
-
-    # Add a deploy log at /versions.txt
-
-    import urllib2
-    import datetime
-    import platform
-    import codecs
-    import getpass
-
-    url = config.get('aws-bucket-website')
-    data = u''
-
-    try:
-        data = urllib2.urlopen('http://%s/versions.txt' % url).read() + u'\n'
-    except:
-        pass
-
-    data += u'	'.join([datetime.datetime.now().isoformat(), platform.node(), getpass.getuser()])
-    codecs.open(os.path.join(path, 'build', 'versions.txt'), 'w', 'utf8').write(data)
-
-def postDeploy(path, config):
-    pass

extras/render.py

-global_context = {}
-
-def process(path, data):
-    import re
-    import os
-
-    # Convert Markdown tags
-    pattern = re.compile(r'\{%\s+markdown\s+%\}\s+(.+?)\{%\s+endmarkdown\s+%\}', re.S)
-    def convertToMarkdown(m):
-        import markdown
-        return markdown.markdown(m.group(1), extensions = ['meta', 'codehilite'])
-
-    data = pattern.sub(convertToMarkdown, data)
-
-    return global_context, data

extras/templatetags.py

Empty file removed.
-{% extends "article.html" %}
+{% extends "page.html" %}
 {% block title %}About Chris Ridgway{% endblock %}
 {% block content %}
-{% markdown %}
+
+{% load markup %}
+{% filter markdown %}
 
 About
 =====
 I'm an embedded real-time Software Engineer living in Iowa.  I've been developing
-software for over fifteen years.  I attended the [University of Iowa][UofI] 
-for [Computer Science][CompSci].  [Go Hawks!][GoHawks]  In my free time I have a 
-myriad of interests, most revolving around computing.  Right now my hot topic interests 
+software for over fifteen years.  I attended the [University of Iowa][UofI]
+for [Computer Science][CompSci].  [Go Hawks!][GoHawks]  In my free time I have a
+myriad of interests, most revolving around computing.  Right now my hot topic interests
 are:
 
  * Web programming
  * Large scale computing
  * Networking
  * [DNA analysis][23andMe] in Python
- * [Genetic Algorithms][pyEvolve] 
+ * [Genetic Algorithms][pyEvolve]
  * All things [Python][]
- * All things [Apple][].  
+ * All things [Apple][].
 
 I primarily work in C++, but I prefer to work in [Python][]
 when possible and it makes sense.  I've worked with a number of other languages,
 but these are my main languages right now.
 
-This site is my way of participating in the greater internet community by sharing 
-my thoughts and ideas, and documenting things I've learned so others don't have 
+This site is my way of participating in the greater internet community by sharing
+my thoughts and ideas, and documenting things I've learned so others don't have
 to struggle with the same things.  We all stand on the shoulders of those before
 us, maybe this site can provide a tidbit of information for someone to stand on.
 
 ### Disclaimer ##
-This site is my personal site.  The posts on this site reflect my own personal 
+This site is my personal site.  The posts on this site reflect my own personal
 opinion, not those of my employers (past, present or future).
 
-Additionally, the information provided on this website is AS-IS and I make no 
+Additionally, the information provided on this website is AS-IS and I make no
 warranty of any kind about it.  See [Legal](legal.html) for more details.
 
 
 [Apple]: http://www.apple.com/ "Apple"
 [pyEvolve]: http://pyevolve.sourceforge.net/ "Pyevolve"
 
-{% endmarkdown %}
+{% endfilter %}
 {% endblock %}
 

pages/archives.html

-{% extends "article.html" %}
+{% extends "page.html" %}
 {% block title %}Archives{% endblock %}
 {% block content %}
 <h1>Archives</h1>
 	<ul class="archive-list">
-	{% for post in POST_LISTING %}
-		<li class="archive-list-item">{{ post.datetime|date:"Y-d-m" }}: <a href="{{ ROOT_URL }}/{{ post.page }}">{{ post.title }} </a></li>
+	{% for post in posts %}
+		<li class="archive-list-item">{{ post.date|date:"Y-d-m" }}: <a href="{{ ROOT_URL }}/{{ post.path }}">{{ post.title }} </a></li>
 	{% empty %}
 		<li>There are currently no postings.</li>
 	{% endfor %}

pages/colophon.html

-{% extends "article.html" %}
-{% block title %}About Chris Ridgway{% endblock %}
+{% extends "page.html" %}
+{% block title %}About This Site{% endblock %}
 {% block content %}
-{% markdown %}
+
+{% load markup %}
+{% filter markdown %}
 
 Colophon
 ========
 This site is:
 
  * Edited in [Sublime Text 2][]
- * Statically generated and deployed by [Cactus][]  
- * Previewed and debuged in [Chrome][] 
- * Hosted and served from [Amazon S3][] 
+ * Statically generated and deployed by [Cactus][]
+ * Previewed and debuged in [Chrome][] and [Safari][]
+ * Hosted and served from [Amazon S3][]
  * Styled using [Bootstrap][] CSS
  * Revision controlled at [Bitbucket][] using [Mercurial][]
 
 Shout outs to:
- 
+
  * The [Django Project][Django] which Cactus relies on for its template engine
  * [Python][] which Django, Cactus, and I rely on every day
  * [Subtle Patterns][] for the background
 [Bootstrap]: http://twitter.github.com/bootstrap/ "Twitter Bootstrap"
 [Sublime Text 2]: http://www.sublimetext.com/2 "Sublime Text 2"
 [Chrome]: http://www.google.com/chrome "Google Chrome"
+[Safari]: http://www.apple.com/safari/ "Apple Safari"
 [Python]: http://python.org/ "Python"
 [Bitbucket]: https://bitbucket.org "Bitbucket"
 [Mercurial]: http://mercurial.selenic.com/ "Mercurial"
 [Werner Vogels]: http://www.allthingsdistributed.com/ "All Things Distributed"
 [NoServer]: http://www.allthingsdistributed.com/2011/08/Jekyll-amazon-s3.html "No Server Required"
 
-{% endmarkdown %}
+{% endfilter %}
 {% endblock %}
 {% extends "base.html" %}
-{% block title %}{{ SITE_NAME }} | {{ SITE_LEAD }}{% endblock %}
 {% block page %}
 
 	<!-- Masthead
 						<h2>Latest Posts</h2>
 
 						<ul>
-						{% for post in POST_LISTING|slice:"10" %}
-							<li><a href="{{ ROOT_URL}}/{{ post.page }}">{{ post.title }} </a></li>
+						{% for post in posts|slice:"10" %}
+							<li><a href="{{ ROOT_URL}}/{{ post.path }}">{{ post.title }} </a></li>
 						{% empty %}
 							<li>There are currently no postings.</li>
 						{% endfor %}
 								<div class="row">
 									<div class="span2 columns">
 										<a href="#">
-											<img class="thumbnail" src="{{ STATIC_URL }}/images/blog.png" alt="Blog">
+											<img class="thumbnail" src="{{ STATIC_URL }}/img/blog.png" alt="Blog">
 										</a>
 										</div>
 										<div class="span10 columns">
                         <div class="row">
                            <div class="span2 columns">
                               <a href="#">
-                                 <img class="thumbnail" src="{{ STATIC_URL }}/images/bravenewsurf.png" alt="Brave New Surf">
+                                 <img class="thumbnail" src="{{ STATIC_URL }}/img/bravenewsurf.png" alt="Brave New Surf">
                               </a>
                               </div>
                               <div class="span10 columns">
-{% extends "article.html" %}
+{% extends "page.html" %}
 {% block title %}Legal Terms{% endblock %}
 {% block content %}
-{% markdown %}
+
+{% load markup %}
+{% filter markdown %}
 
 Legal
 =====
 ### Privacy ##
-I do not share personal information with third-parties except as needed to 
-support Disqus comments and Google Analytics.  I am not responsible for the 
-republishing of the content found on this blog on other Web sites or media 
+I do not share personal information with third-parties except as needed to
+support Disqus comments and Google Analytics.  I am not responsible for the
+republishing of the content found on this blog on other Web sites or media
 without permission.
 
 ### Blog Comments ##
-I reserve the right to edit or delete any comments submitted to this blog for 
-any reason and without notice.  Don't spam, troll, attack, swear, or post 
+I reserve the right to edit or delete any comments submitted to this blog for
+any reason and without notice.  Don't spam, troll, attack, swear, or post
 illegal or offensive material.
 
 ### Terms and Conditions ##
 All content provided on this blog is for informational purposes only. I make no
-representations as to the accuracy or completeness of any information on this 
-site or found by following any link on this site.  I will not be liable for any 
-errors or omissions in this information nor for the availability of this 
-information. I will not be liable for any losses, injuries, or damages from the 
+representations as to the accuracy or completeness of any information on this
+site or found by following any link on this site.  I will not be liable for any
+errors or omissions in this information nor for the availability of this
+information. I will not be liable for any losses, injuries, or damages from the
 display or use of this information.
 
 This policy is subject to change at anytime.
 
-{% endmarkdown %}
+{% endfilter %}
 {% endblock %}
 

pages/posts/lorem-ipsum.html

+title: Lorem Ipsum
+author: Chris Ridgway
+date: 16-09-2011
+
+{% extends "blog.html" %}
+{% block blog %}
+
+{% load markup %}
+{% filter markdown:"codehilite,footnotes" %}
+
+Second Heading
+--------------
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+Footnotes[^1] have a label[^label] and a definition[^!DEF].
+
+## Second Heading ##
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+	:::python
+	class Foo:
+		def __init__(self):
+			pass
+
+	class Bar:
+		def __init__(self):
+			self.a = 1
+			self.b = 2
+			self.c = 3
+			pass
+
+
+### Third Heading ###
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+
+#### Fourth Heading ####
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+
+##### Fifth Heading #####
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+[^1]: This is a footnote
+[^label]: A footnote on "label"
+[^!DEF]: The definition of a footnote.
+
+{% endfilter %}
+{% endblock %}
-{% for path in CACTUS.pages %}{{ path }}
+{% for page in CACTUS.pages %}{% if page.path != 'error.html' %}{{ page.path }}{% endif %}
 {% endfor %}

pages/sitemap.xml

 <?xml version="1.0" encoding="UTF-8"?> 
-<urlset xmlns="http://www.google.com/schemas/sitemap/0.84"> 
-{% for path in CACTUS.pages %}
+<urlset xmlns="http://www.google.com/schemas/sitemap/0.84">
+{% for page in CACTUS.pages %}
+	{% if page.path != 'error.html' %}
 	<url>
-		<loc>{{ path }}</loc>
-		<changefreq>daily</changefreq> 
-		<priority>1.0</priority> 
+		<loc>{{ page.path }}</loc>
+		<changefreq>daily</changefreq>
+		<priority>1.0</priority>
 	</url>
+	{% endif %}
 {% endfor %}
 </urlset>
+import datetime
+
+POSTS_PATH = 'posts/'
+POSTS = []
+
+def preBuild(site):
+
+    global POSTS
+
+    # Build all the posts
+    for page in site.pages():
+        if page.path.startswith(POSTS_PATH):
+
+            if not page.path.endswith('.html'):
+                continue
+
+            c = page.context()
+
+            postContext = {}
+            postContext['title'] = c.get('title', '')
+            postContext['author'] = c.get('author', '')
+            postContext['date'] = c.get('date', '')
+            postContext['path'] = page.path
+
+            # Parse the date into a date object
+            postContext['date'] = datetime.datetime.strptime(postContext['date'], '%d-%m-%Y')
+
+            POSTS.append(postContext)
+
+    # Sort the posts by date
+    POSTS = sorted(POSTS, key=lambda x: x['date'])
+    POSTS.reverse()
+
+    indexes = xrange(0, len(POSTS))
+
+    for i in indexes:
+        if i+1 in indexes: POSTS[i]['prevPost'] = POSTS[i+1]
+        if i-1 in indexes: POSTS[i]['nextPost'] = POSTS[i-1]
+
+
+def preBuildPage(site, page, context, data):
+
+    context['posts'] = POSTS
+
+    for post in POSTS:
+        if post['path'] == page.path:
+            context.update(post)
+
+    return context, data

plugins/coffeescript.disabled.py

+import os
+import pipes
+
+def postBuild(site):
+	os.system('coffee -c %s/static/js/*.coffee' % pipes.quote(site.paths['build']))

plugins/markdown.disabled.py

+import os
+import sys
+import markdown
+
+from cactus.utils import fileList
+
+
+template = """
+%s
+
+{%% extends "%s" %%}
+{%% block %s %%}
+
+%s
+
+{%% endblock %%}
+"""
+
+CLEANUP = []
+
+def preBuild(site):
+	for path in fileList(site.paths['pages']):
+	
+		if not path.endswith('.md'):
+			continue
+	
+		md = markdown.Markdown(extensions=['meta'])
+	
+		with open(path, 'r') as f:
+			html = md.convert(f.read())
+	
+		metadata = []
+	
+		for k, v in md.Meta.iteritems():
+			metadata.append('%s: %s' % (k, v[0]))
+	
+		outPath = path.replace('.md', '.html')
+		
+		with open(outPath, 'w') as f:
+		
+			data = template % (
+				'\n'.join(metadata),
+				md.Meta['extends'][0],
+				md.Meta['block'][0],
+				html
+			)
+		
+			f.write(data)
+		
+		CLEANUP.append(outPath)
+
+def postBuild(site):
+	global CLEANUP
+	for path in CLEANUP:
+		print path
+		os.remove(path)
+	CLEANUP = []

plugins/scss.disabled..py

+import os
+import sys
+import pipes
+import shutil
+import subprocess
+
+from cactus.utils import fileList
+
+"""
+This plugin uses pyScss to translate sass files to css
+
+Install:
+
+sudo easy_install pyScss
+
+"""
+
+try:
+	from scss import Scss
+except:
+	sys.exit("Could not find pyScss, please install: sudo easy_install pyScss")
+
+
+CSS_PATH = 'static/css'
+
+for path in fileList(CSS_PATH):
+	
+	if not path.endswith('.scss'):
+		continue
+	
+	with open(path, 'r') as f:
+		data = f.read()
+	
+	css = Scss().compile(data)
+
+	with open(path.replace('.scss', '.css'), 'w') as f:
+		f.write(css)

plugins/site_cfg.py

+import os
+import json
+
+def preBuild(site):
+    """
+    Get site specific dictionary.
+    """
+    global site_cfg
+
+    site_cfg = json.load(open(os.path.join(site.path, 'site.json'), 'r'))
+
+
+def preBuildPage(site, page, context, data):
+    context.update(site_cfg)
+    return context, data

plugins/sprites.disabled.py

+import os
+import sys
+import pipes
+import shutil
+import subprocess
+
+"""
+This plugin uses glue to sprite images:
+http://glue.readthedocs.org/en/latest/quickstart.html
+
+Install:
+
+(Only if you want to sprite jpg too)
+brew install libjpeg
+
+(Only if you want to optimize pngs with optipng)
+brew install optipng
+
+sudo easy_install pip
+sudo pip uninstall pil
+sudo pip install pil
+sudo pip install glue
+"""
+
+try:
+	import glue
+except Exception, e:
+	sys.exit('Could not use glue: %s\nMaybe install: sudo easy_install glue' % e)
+
+
+IMG_PATH = 'static/img/sprites'
+CSS_PATH = 'static/css/sprites'
+
+KEY = '_PREV_CHECKSUM'
+
+def checksum(path):
+	command = 'md5 `find %s -type f`' % pipes.quote(IMG_PATH)
+	return subprocess.check_output(command, shell=True)
+
+def preBuild(site):
+	
+	currChecksum = checksum(IMG_PATH)
+	prevChecksum = getattr(site, KEY, None)
+	
+	# Don't run if none of the images has changed
+	if currChecksum == prevChecksum:
+		return
+	
+	if os.path.isdir(CSS_PATH):
+		shutil.rmtree(CSS_PATH)
+	
+	os.mkdir(CSS_PATH)
+	os.system('glue --cachebuster --crop --optipng "%s" "%s" --project' % (IMG_PATH, CSS_PATH))
+	
+	setattr(site, KEY, currChecksum)

plugins/version.py

+import os
+
+INFO = {
+	'name': 'Version Updater',
+	'description': 'Add a version to /versions.txt after each deploy'
+}
+
+# Set up extra django template tags
+
+def templateTags():
+	pass
+
+
+# Build actions
+
+# def preBuild(site):
+# 	print 'preBuild'
+#
+# def postBuild(site):
+# 	print 'postBuild'
+
+# Build page actions
+
+# def preBuildPage(site, path, context, data):
+# 	print 'preBuildPage', path
+# 	return context, data
+#
+# def postBuildPage(site, path):
+# 	print 'postBuildPage', path
+# 	pass
+
+
+# Deploy actions
+
+def preDeploy(site):
+
+	# Add a deploy log at /versions.txt
+
+	import urllib2
+	import datetime
+	import platform
+	import codecs
+	import getpass
+
+	url = site.config.get('aws-bucket-website')
+	data = u''
+
+	# Get the current content of versions.txt
+	try: data = urllib2.urlopen('http://%s/versions.txt' % url).read() + u'\n'
+	except: pass
+
+	data += u'\t'.join([datetime.datetime.now().isoformat(), platform.node(), getpass.getuser()])
+	codecs.open(os.path.join(site.paths['build'], 'versions.txt'), 'w', 'utf8').write(data)
+
+def postDeploy(site):
+	pass

posts/2011-09-16-lorem-ipsum.markdown

-Title: Lorem Ipsum
-Authors: Chris Ridgway
-Date: 2011/09/16 22:20:00
-
-
-Second Heading
---------------
-Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
-tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
-quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
-consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
-cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
-proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
-
-
-## Second Heading ##
-Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
-tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
-quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
-consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
-cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
-proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
-
-	:::python
-	class Foo:
-		def __init__(self):
-			pass
-
-	class Bar:
-		def __init__(self):
-			self.a = 1
-			self.b = 2
-			self.c = 3
-			pass
-
-
-### Third Heading ###
-Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
-tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
-quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
-consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
-cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
-proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
-
-
-#### Fourth Heading ####
-Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
-tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
-quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
-consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
-cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
-proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
-
-
-##### Fifth Heading #####
-Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
-tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
-quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
-consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
-cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
-proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

static/css/styles.css

 -----------------------------------------------
  */
 html, body {
-	background: url("/static/images/whitey.png");
+	background: url("/static/img/whitey.png");
 }
 
 html, body, li, ul, a, p {
 -----------------------------------------------
  */
 div.codehilite pre {
-	background-color: transparent; 
+	background-color: transparent;
 }
 
 /* Pygments - manni */

static/images/blog.png

Removed
Old image

static/images/bravenewsurf.png

Removed
Old image

static/images/favicon.ico

Removed
Old image

static/images/whitey.png

Removed
Old image

static/img/blog.png

Added
New image

static/img/bravenewsurf.png

Added
New image

static/img/favicon.ico

Added
New image

static/img/whitey.png

Added
New image

templates/article.html

-{% extends "base.html" %}
-{% block page %}
-
-	<div class="container">
-		<div class="row">
-
-			<!-- Article Sidebar
-			=========================================== -->
-			<div class="span4 columns">
-				<div id="article-left">
-					<div id="article-masthead">
-						<div style="align:center">
-							<div style="width:100px; height:100px; border:4px solid #bbb; border-radius:200px; 
-										background: url('http://www.gravatar.com/avatar/b5fba2f517d483b266ccd6d7a37d3a2c');
-										background-size:100% 100%; 
-										background-repeat: no-repeat;
-										margin-bottom: 10px; margin-left: auto; margin-right: auto;">
-							</div>
-						<a href="{{ ROOT_URL }}/index.html"><h1>Chris Ridgway</h1></a>
-						</div>
-					</div>
-					<p>
-						I'm Chris.  A real-time embedded software engineer,
-						interested in web and mobile app development, 
-						large scale computing, networking, and all things Python.
-						Read <a class="tinylink" href="{{ ROOT_URL }}/about.html">more about me.</a>
-					</p>
-					<h3>Contact</h3>
-					<a href="mailto:ckridgway@gmail.com">ckridgway@gmail.com</a><br/>
-
-					<h3>Social</h3>
-					<a href="http://twitter.com/cridgway">Twitter</a><br/>
-					<a href="https://plus.google.com/114382913609569476532">Google+</a><br/>
-					<a href="http://www.linkedin.com/in/ckridgway">LinkedIn</a><br/>
-					<a href="http://www.delicious.com/cridgway">Delicious</a><br/>
-					
-					<h3>Sharing</h3>
-					<a href="https://bitbucket.org/ckridgway">Bitbucket</a><br/>
-					<a href="https://github.com/ckridgway">GitHub</a><br/>
-				</div>
-			</div>
-
-			<!-- Article Content 
-			=========================================== -->
-			<div class="span10 columns">
-				<div id="article">
-					{% block content %}
-					{% endblock %}
-				</div>
-				&nbsp;<br/>
-				&nbsp;<br/>
-				<a href="{{ ROOT_URL }}/index.html">← Back to the homepage</a>
-			</div>
-
-			<div class="span2 columns">
-			&nbsp;
-			</div>
-		</div>
-	</div>
-
-{% endblock %}

templates/base.html

 <html lang="en">
 	<head>
 		<title>{% block title %}{{ SITE_NAME }}{% endblock %}</title>
-		
+
 		<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 
-		<link rel="shortcut icon" href="{{ STATIC_URL }}/images/favicon.ico"> 
+		<link rel="shortcut icon" href="{{ STATIC_URL }}/img/favicon.ico">
 		<link rel="stylesheet" href="http://twitter.github.com/bootstrap/1.4.0/bootstrap.min.css">
 		<link rel="stylesheet" href="{{ STATIC_URL }}/css/styles.css">
 		<link href='http://fonts.googleapis.com/css?family=Voltaire' rel='stylesheet' type='text/css'>

templates/blog.html

+{% extends "page.html" %}
+{% block title %}{{ title }}{% endblock %}
+
+{% block content %}
+
+<h1>{{ title }}</h1>
+
+<p class="postdate">Posted {{ date|date:"l, F dS, Y" }}</p><br/>
+
+{% block blog %}
+
+{% endblock %}
+{% endblock %}

templates/page.html

+{% extends "base.html" %}
+{% block page %}
+
+	<div class="container">
+		<div class="row">
+
+			<!-- Article Sidebar
+			=========================================== -->
+			<div class="span4 columns">
+				<div id="article-left">
+					<div id="article-masthead">
+						<div style="align:center">
+							<div style="width:100px; height:100px; border:4px solid #bbb; border-radius:200px;
+										background: url('http://www.gravatar.com/avatar/b5fba2f517d483b266ccd6d7a37d3a2c');
+										background-size:100% 100%;
+										background-repeat: no-repeat;
+										margin-bottom: 10px; margin-left: auto; margin-right: auto;">
+							</div>
+						<a href="{{ ROOT_URL }}/index.html"><h1>Chris Ridgway</h1></a>
+						</div>
+					</div>
+					<p>
+						I'm Chris.  A real-time embedded software engineer,
+						interested in web and mobile app development,
+						large scale computing, networking, and all things Python.
+						Read <a class="tinylink" href="{{ ROOT_URL }}/about.html">more about me.</a>
+					</p>
+					<h3>Contact</h3>
+					<a href="mailto:ckridgway@gmail.com">ckridgway@gmail.com</a><br/>
+
+					<h3>Social</h3>
+					<a href="http://twitter.com/cridgway">Twitter</a><br/>
+					<a href="https://plus.google.com/114382913609569476532">Google+</a><br/>
+					<a href="http://www.linkedin.com/in/ckridgway">LinkedIn</a><br/>
+					<a href="http://www.delicious.com/cridgway">Delicious</a><br/>
+
+					<h3>Sharing</h3>
+					<a href="https://bitbucket.org/ckridgway">Bitbucket</a><br/>
+					<a href="https://github.com/ckridgway">GitHub</a><br/>
+				</div>
+			</div>
+
+			<!-- Article Content
+			=========================================== -->
+			<div class="span10 columns">
+				<div id="article">
+					{% block content %}
+					{% endblock %}
+				</div>
+				&nbsp;<br/>
+				&nbsp;<br/>
+				<a href="{{ ROOT_URL }}/index.html">← Back to the homepage</a>
+			</div>
+
+			<div class="span2 columns">
+			&nbsp;
+			</div>
+		</div>
+	</div>
+
+{% endblock %}
 TODO
 ====
+ - Fix RSS.
  - Add RSS feed in Subscribe sidebar.
  - Write first blog post
+ - Update to self-hosted Bootstrap