1. David Chambers
  2. Mango

Commits

David Chambers  committed e932f5d

Replaced tabs with spaces throughout the codebase.

  • Participants
  • Parent commits fc80e6b
  • Branches default

Comments (0)

Files changed (9)

File feeds.py

View file
 from mango.settings import *
 
 def atom(request):
-	content_type = 'application/atom+xml; charset=utf-8'
-	cache_key = feed_url = BASE_URL.rstrip('/') + request.META['PATH_INFO']
-	xml = cache.get(cache_key)
-	if xml:
-		return HttpResponse(xml, content_type=content_type)
+    content_type = 'application/atom+xml; charset=utf-8'
+    cache_key = feed_url = BASE_URL.rstrip('/') + request.META['PATH_INFO']
+    xml = cache.get(cache_key)
+    if xml:
+        return HttpResponse(xml, content_type=content_type)
 
-	def required_kwargs(required, post=None):
-		try:
-			return dict([key, function()] for key, function in required.items())
-		except KeyError:
-			return False
+    def required_kwargs(required, post=None):
+        try:
+            return dict([key, function()] for key, function in required.items())
+        except KeyError:
+            return False
 
-	def all_kwargs(required, optional, post=None):
-		kwargs = required_kwargs(required, post)
-		if not kwargs:
-			return False
-		for key, function in optional.items():
-			try:
-				value = function()
-				if value: # don't include pair if its value is empty
-					kwargs[key] = value
-			except KeyError:
-				pass
-		return kwargs
+    def all_kwargs(required, optional, post=None):
+        kwargs = required_kwargs(required, post)
+        if not kwargs:
+            return False
+        for key, function in optional.items():
+            try:
+                value = function()
+                if value: # don't include pair if its value is empty
+                    kwargs[key] = value
+            except KeyError:
+                pass
+        return kwargs
 
-	required = {
-		'title':        lambda: unicode(SITE_TITLE, 'utf-8'),
-		'link':         lambda: unicode(BASE_URL, 'utf-8'),
-		'description':  lambda: u'', # required by constructor, but does not affect output
-	}
-	optional = {
-		'author_name':  lambda: unicode(PRIMARY_AUTHOR_NAME, 'utf-8'),
-		'author_email': lambda: unicode(PRIMARY_AUTHOR_EMAIL, 'utf-8'),
-		'author_link':  lambda: unicode(PRIMARY_AUTHOR_URL, 'utf-8'),
-		'feed_url':     lambda: feed_url,
-		'feed_guid':    lambda: unicode(BASE_URL, 'utf-8'),
-	}
+    required = {
+        'title':        lambda: unicode(SITE_TITLE, 'utf-8'),
+        'link':         lambda: unicode(BASE_URL, 'utf-8'),
+        'description':  lambda: u'', # required by constructor, but does not affect output
+    }
+    optional = {
+        'author_name':  lambda: unicode(PRIMARY_AUTHOR_NAME, 'utf-8'),
+        'author_email': lambda: unicode(PRIMARY_AUTHOR_EMAIL, 'utf-8'),
+        'author_link':  lambda: unicode(PRIMARY_AUTHOR_URL, 'utf-8'),
+        'feed_url':     lambda: feed_url,
+        'feed_guid':    lambda: unicode(BASE_URL, 'utf-8'),
+    }
 
-	kwargs = all_kwargs(required, optional)
+    kwargs = all_kwargs(required, optional)
 
-	if not kwargs:
-		return HttpResponseServerError
+    if not kwargs:
+        return HttpResponseServerError
 
-	feed = feedgenerator.Atom1Feed(**kwargs)
-	todo = FEED_MAX_POSTS
-	all = todo == 0
+    feed = feedgenerator.Atom1Feed(**kwargs)
+    todo = FEED_MAX_POSTS
+    all = todo == 0
 
-	for year, month, posts in utils.posts():
-		for post in posts:
-			if todo or all:
-				required = {
-					'title':       lambda: post['meta']['title'],
-					'link':        lambda: post['meta']['url'],
-					'description': lambda: post['html'],
-				}
-				optional = {
-					'author_name': lambda: post['meta']['author'],
-					'pubdate':     lambda: post['meta']['datetime'],
-					'unique_id':   lambda: post['meta']['url'],
-				}
-				kwargs = all_kwargs(required, optional, post)
-				if kwargs:
-					feed.add_item(**kwargs)
-					todo -= 1
+    for year, month, posts in utils.posts():
+        for post in posts:
+            if todo or all:
+                required = {
+                    'title':       lambda: post['meta']['title'],
+                    'link':        lambda: post['meta']['url'],
+                    'description': lambda: post['html'],
+                }
+                optional = {
+                    'author_name': lambda: post['meta']['author'],
+                    'pubdate':     lambda: post['meta']['datetime'],
+                    'unique_id':   lambda: post['meta']['url'],
+                }
+                kwargs = all_kwargs(required, optional, post)
+                if kwargs:
+                    feed.add_item(**kwargs)
+                    todo -= 1
 
-	xml = feed.writeString('utf-8')
-	cache.set(cache_key, xml, CACHE_SECONDS)
-	return HttpResponse(xml, content_type=content_type)
+    xml = feed.writeString('utf-8')
+    cache.set(cache_key, xml, CACHE_SECONDS)
+    return HttpResponse(xml, content_type=content_type)

File forms.py

View file
 from django import forms
 
 class CommentForm(forms.Form):
-	author_name = forms.CharField(label='Name', max_length=100)
-	author_email = forms.EmailField(label='E-mail', max_length=100)
-	author_url = forms.URLField(label='Website', max_length=100, required=False)
-	message = forms.CharField(label='Comment')
-	subscribe = forms.BooleanField(label='Notify me of follow-up comments via e-mail', required=False)
+    author_name = forms.CharField(label='Name', max_length=100)
+    author_email = forms.EmailField(label='E-mail', max_length=100)
+    author_url = forms.URLField(label='Website', max_length=100, required=False)
+    message = forms.CharField(label='Comment')
+    subscribe = forms.BooleanField(label='Notify me of follow-up comments via e-mail', required=False)
 
-	def clean_author_name(self):
-		return self.cleaned_data.get('author_name', '').strip()
+    def clean_author_name(self):
+        return self.cleaned_data.get('author_name', '').strip()
 
-	def clean_author_email(self):
-		return self.cleaned_data.get('author_email', '').strip().lower()
+    def clean_author_email(self):
+        return self.cleaned_data.get('author_email', '').strip().lower()
 
-	def clean_author_url(self):
-		return self.cleaned_data.get('author_url', '').strip()
+    def clean_author_url(self):
+        return self.cleaned_data.get('author_url', '').strip()
 
-	def clean_message(self):
-		return self.cleaned_data.get('message', '').strip()
+    def clean_message(self):
+        return self.cleaned_data.get('message', '').strip()
 
 class ContactForm(forms.Form):
-	sender_name = forms.CharField(label='Name', initial='your name', max_length=100)
-	sender_email = forms.EmailField(label='E-mail', initial='your e-mail address', max_length=100)
-	subject = forms.CharField(label='Subject', max_length=100, required=False)
-	message = forms.CharField(label='Message')
-	cc_sender = forms.BooleanField(label='Send me a copy of this message', required=False)
+    sender_name = forms.CharField(label='Name', initial='your name', max_length=100)
+    sender_email = forms.EmailField(label='E-mail', initial='your e-mail address', max_length=100)
+    subject = forms.CharField(label='Subject', max_length=100, required=False)
+    message = forms.CharField(label='Message')
+    cc_sender = forms.BooleanField(label='Send me a copy of this message', required=False)
 
-	def clean_sender_name(self):
-		return self.cleaned_data.get('sender_name', '').strip()
+    def clean_sender_name(self):
+        return self.cleaned_data.get('sender_name', '').strip()
 
-	def clean_sender_email(self):
-		return self.cleaned_data.get('sender_email', '').strip().lower()
+    def clean_sender_email(self):
+        return self.cleaned_data.get('sender_email', '').strip().lower()
 
-	def clean_subject(self):
-		return self.cleaned_data.get('subject', '').strip()
+    def clean_subject(self):
+        return self.cleaned_data.get('subject', '').strip()
 
-	def clean_message(self):
-		return self.cleaned_data.get('message', '').strip()
+    def clean_message(self):
+        return self.cleaned_data.get('message', '').strip()

File settings/__init__.py

View file
 execfile(os.path.join(dirname, 'defaults.py'))
 
 try:
-	execfile(os.path.join(dirname, 'custom.py'))
+    execfile(os.path.join(dirname, 'custom.py'))
 except IOError:
-	pass
+    pass

File settings/defaults.py

View file
 # `tags: accessibility, best practice, CSS, UX` becomes ['accessibility',
 # 'best practice', 'CSS', 'UX'] and `tags: iPhone` becomes ['iPhone'].
 META_LISTS = (
-	'tags',
+    'tags',
 )
 
 # When True, a handful of replacements are made on the title and body of each
 
 # http://www.freewisdom.org/projects/python-markdown/Available_Extensions
 MARKDOWN_EXTENSIONS = (
-	# Markdown Extra
-#	'abbr', # abbreviations
-	'def_list', # definition lists
-	'fenced_code', # fenced code blocks
-#	'footnotes',
-#	'tables',
+    # Markdown Extra
+#   'abbr', # abbreviations
+    'def_list', # definition lists
+    'fenced_code', # fenced code blocks
+#   'footnotes',
+#   'tables',
 
-	# additional
-#	'codehilite'
-#	'rss',
-#	'wikilink',
+    # additional
+#   'codehilite'
+#   'rss',
+#   'wikilink',
 )
 
 # http://docs.python.org/library/datetime.html#strftime-and-strptime-behavior

File templatetags/mango_extras.py

View file
 @register.filter
 @stringfilter
 def slugify(string):
-	return string.replace(' ', '-').lower()
+    return string.replace(' ', '-').lower()
 
 @register.filter
 def datetime(dt):
-	return '%d-%02d-%02dT%02d:%02d-00:00' % (dt.year, dt.month, dt.day, dt.hour, dt.minute)
+    return '%d-%02d-%02dT%02d:%02d-00:00' % (dt.year, dt.month, dt.day, dt.hour, dt.minute)
 
 @register.filter
 def date_and_time(dt):
-	try:
-		return '%02d:%02d:%02d on %d/%d/%d' % (dt.hour, dt.minute, dt.second, dt.year, dt.month, dt.day)
-	except AttributeError:
-		return '??'
+    try:
+        return '%02d:%02d:%02d on %d/%d/%d' % (dt.hour, dt.minute, dt.second, dt.year, dt.month, dt.day)
+    except AttributeError:
+        return '??'
 
 @register.filter
 @stringfilter
 def isoformat(string):
-	return dt.datetime.strptime(string, '%Y-%m-%d %H:%M:%S').replace(tzinfo=pytz.utc).isoformat()
+    return dt.datetime.strptime(string, '%Y-%m-%d %H:%M:%S').replace(tzinfo=pytz.utc).isoformat()
 
 def display_date_or_time(string, format_as):
-	'''
-	Python does not support 'am' and 'pm' in time format strings (only their uppercase equivalents).
-	Nor does Python accommodate hours in the form '1', '2', '3', etc. (only '01', '02', '03', etc.).
-	The display_date and display_time filters support these though additional directives:
+    '''
+    Python does not support 'am' and 'pm' in time format strings (only their uppercase equivalents).
+    Nor does Python accommodate hours in the form '1', '2', '3', etc. (only '01', '02', '03', etc.).
+    The display_date and display_time filters support these though additional directives:
 
-	- %p indicates that 'am' and 'pm' are preferred
-	- %P indicates that 'AM' and 'PM' are preferred
+    - %p indicates that 'am' and 'pm' are preferred
+    - %P indicates that 'AM' and 'PM' are preferred
 
-	- %i indicates that hours should not be zero-padded
-	- %I indicates that hours should be zero-padded
+    - %i indicates that hours should not be zero-padded
+    - %I indicates that hours should be zero-padded
 
-	- %d indicates that date should not be zero-padded
-	- %D indicates that date should be zero-padded
-	'''
-	string = string[:-6] if string.endswith('+00:00') else string
+    - %d indicates that date should not be zero-padded
+    - %D indicates that date should be zero-padded
+    '''
+    string = string[:-6] if string.endswith('+00:00') else string
 
-	try:
-		struct_time = time.strptime(string, '%Y-%m-%d %H:%M:%S')
-	except ValueError:
-		return string
+    try:
+        struct_time = time.strptime(string, '%Y-%m-%d %H:%M:%S')
+    except ValueError:
+        return string
 
-	if format_as == 'date':
-		format = DISPLAY_DATE_FORMAT
-	elif format_as == 'time':
-		format = DISPLAY_TIME_FORMAT
-	else:
-		raise ValueError
+    if format_as == 'date':
+        format = DISPLAY_DATE_FORMAT
+    elif format_as == 'time':
+        format = DISPLAY_TIME_FORMAT
+    else:
+        raise ValueError
 
-	format = re.sub(r'(?<!%)%P', '{{%p|upper}}', format)
-	format = re.sub(r'(?<!%)%i', '{{%I|unpad}}', format)
-	format = re.sub(r'(?<!%)%d', '{{%d|unpad}}', format)
-	format = re.sub(r'(?<!%)%D', '%d', format) # default
+    format = re.sub(r'(?<!%)%P', '{{%p|upper}}', format)
+    format = re.sub(r'(?<!%)%i', '{{%I|unpad}}', format)
+    format = re.sub(r'(?<!%)%d', '{{%d|unpad}}', format)
+    format = re.sub(r'(?<!%)%D', '%d', format) # default
 
-	string = time.strftime(format, struct_time)
-	string = string.lower() if format_as == 'time' else string
+    string = time.strftime(format, struct_time)
+    string = string.lower() if format_as == 'time' else string
 
-	funcs = {
-		'unpad': lambda string: string[1:] if string.startswith('0') else string,
-		'upper': lambda string: string.upper(),
-	}
+    funcs = {
+        'unpad': lambda string: string[1:] if string.startswith('0') else string,
+        'upper': lambda string: string.upper(),
+    }
 
-	return re.sub(r'{{(\w\w)\|(lower|unpad)}}', lambda m: funcs[m.group(2)](m.group(1)), string)
+    return re.sub(r'{{(\w\w)\|(lower|unpad)}}', lambda m: funcs[m.group(2)](m.group(1)), string)
 
 @register.filter
 @stringfilter
 def display_date(string):
-	return display_date_or_time(string, 'date')
+    return display_date_or_time(string, 'date')
 
 @register.filter
 @stringfilter
 def display_time(string):
-	return display_date_or_time(string, 'time')
+    return display_date_or_time(string, 'time')
 
 @register.filter
 def month(number):
-	return {
-		1: 'January',
-		2: 'February',
-		3: 'March',
-		4: 'April',
-		5: 'May',
-		6: 'June',
-		7: 'July',
-		8: 'August',
-		9: 'September',
-		10: 'October',
-		11: 'November',
-		12: 'December',
-	}[number]
+    return {
+        1: 'January',
+        2: 'February',
+        3: 'March',
+        4: 'April',
+        5: 'May',
+        6: 'June',
+        7: 'July',
+        8: 'August',
+        9: 'September',
+        10: 'October',
+        11: 'November',
+        12: 'December',
+    }[number]
 
 @register.filter
 @stringfilter
 def gravatar(email_hash):
-	args = [(key, value) for key, value in GRAVATAR.items() if value]
-	return 'http://www.gravatar.com/avatar/%s?%s' % (email_hash, urllib.urlencode(args))
+    args = [(key, value) for key, value in GRAVATAR.items() if value]
+    return 'http://www.gravatar.com/avatar/%s?%s' % (email_hash, urllib.urlencode(args))
 
 @register.filter
 @stringfilter
 def html(markdown):
-	return utils.parse_markdown(markdown)['html']
+    return utils.parse_markdown(markdown)['html']

File tests.py

View file
 ALREADY_RUN = ['tests.py', 'models.py']
 
 def find_untested_modules(package):
-	""" Gets all modules not already included in Django's test suite """
-	files = []
-	for f in os.listdir(os.path.dirname(package.__file__)):
-		if f.endswith('.py') and os.path.basename(f) not in ALREADY_RUN:
-			files.append(re.sub(r'\.py$', '', f))
-	return [imp.load_module(f, *imp.find_module(f, package.__path__)) for f in files]
+    """ Gets all modules not already included in Django's test suite """
+    files = []
+    for f in os.listdir(os.path.dirname(package.__file__)):
+        if f.endswith('.py') and os.path.basename(f) not in ALREADY_RUN:
+            files.append(re.sub(r'\.py$', '', f))
+    return [imp.load_module(f, *imp.find_module(f, package.__path__)) for f in files]
 
 def modules_callables(module):
-	return [m for m in dir(module) if callable(getattr(module, m))]
+    return [m for m in dir(module) if callable(getattr(module, m))]
 
 def has_doctest(docstring):
-	return '>>>' in docstring
+    return '>>>' in docstring
 
 __test__ = {}
 for module in find_untested_modules(mango):
-	for method in modules_callables(module):
-		docstring = str(getattr(module, method).__doc__)
-		if has_doctest(docstring):
-			print 'Mango found doctest: ' + module.__name__ + '.' + method
+    for method in modules_callables(module):
+        docstring = str(getattr(module, method).__doc__)
+        if has_doctest(docstring):
+            print 'Mango found doctest: ' + module.__name__ + '.' + method
 
-			# import the method itself, so doctest can find it
-			_temp = __import__(module.__name__, globals(), locals(), [method])
-			locals()[method] = getattr(_temp, method)
+            # import the method itself, so doctest can find it
+            _temp = __import__(module.__name__, globals(), locals(), [method])
+            locals()[method] = getattr(_temp, method)
 
-			# Django looks in __test__ for doctests to run
-			__test__[method] = getattr(module, method)
+            # Django looks in __test__ for doctests to run
+            __test__[method] = getattr(module, method)

File urls.py

View file
 urlpatterns = patterns('')
 
 if settings.DEBUG:
-	urlpatterns += patterns('',
-		(r'^static/(?P<path>.*)$', 'django.views.static.serve', {'document_root': PATH_TO_STATIC}),
-	)
+    urlpatterns += patterns('',
+        (r'^static/(?P<path>.*)$', 'django.views.static.serve', {'document_root': PATH_TO_STATIC}),
+    )
 
 if CONTACT_FORM:
-	urlpatterns += patterns('',
-		(r'^contact/(thanks/)?$', contact),
-	)
+    urlpatterns += patterns('',
+        (r'^contact/(thanks/)?$', contact),
+    )
 
 if TAGS_PAGE:
-	urlpatterns += patterns('',
-		(r'^tags/$', tags),
-		(r'^tag/([^/]+)/$', tagged_as),
-	)
+    urlpatterns += patterns('',
+        (r'^tags/$', tags),
+        (r'^tag/([^/]+)/$', tagged_as),
+    )
 
 urlpatterns += patterns('',
-	(r'^$', index),
-	(r'^feed/$', atom),
-	(r'^archive/$', lambda request: HttpResponsePermanentRedirect('/archives/')),
-	(r'^archives/$', archives),
-	(r'^(.+?)/redirect/$', redirect),
-	(r'^(.+?)/comment/$', post),
-	(r'^(.+?)/(m/)?$', post),
+    (r'^$', index),
+    (r'^feed/$', atom),
+    (r'^archive/$', lambda request: HttpResponsePermanentRedirect('/archives/')),
+    (r'^archives/$', archives),
+    (r'^(.+?)/redirect/$', redirect),
+    (r'^(.+?)/comment/$', post),
+    (r'^(.+?)/(m/)?$', post),
 )

File utils.py

View file
 match = [r'(?m)^', r'(?=[ \n\r]) ?']
 
 RE = {
-	'replacements': (
-		(re.compile(r'(?<!\\)\.\.\.(?!\.)'), u'\u2026'),     # ... -> ellipsis
-		(re.compile(r' -- '), u'\u2009\u2014\u2009'),        # [space][hyphen][hyphen][space] -> [thin space][em dash][thin space]
-		(re.compile(r'(?<!\\)&lt;&lt;(?!&lt;)'), u'\u00AB'), # << -> «
-		(re.compile(r'(?<!\\)&gt;&gt;(?!&gt;)'), u'\u00BB'), # >> -> »
-	),
-	'heading':       re.compile(r'(?m)\s*<(h[1-6])[^>]*>(?P<title>.+?)</\1>$(?P<html>[\s\S]*)'),
+    'replacements': (
+        (re.compile(r'(?<!\\)\.\.\.(?!\.)'), u'\u2026'),     # ... -> ellipsis
+        (re.compile(r' -- '), u'\u2009\u2014\u2009'),        # [space][hyphen][hyphen][space] -> [thin space][em dash][thin space]
+        (re.compile(r'(?<!\\)&lt;&lt;(?!&lt;)'), u'\u00AB'), # << -> «
+        (re.compile(r'(?<!\\)&gt;&gt;(?!&gt;)'), u'\u00BB'), # >> -> »
+    ),
+    'heading':       re.compile(r'(?m)\s*<(h[1-6])[^>]*>(?P<title>.+?)</\1>$(?P<html>[\s\S]*)'),
 
-	# excerpts
-	'hand-crafted':  re.compile(r' {,3}\S+:.*(\r?\n[ \t]*\S+:.*)*\r?\n{2,}(?P<excerpt>(\|(?=[ \n\r])[^\n\r]*\r?\n)+)'),
-	'excerpt':       re.compile(r'\|'.join(block)),
-	'excerpt_pipes': re.compile(r'\|'.join(match)),
+    # excerpts
+    'hand-crafted':  re.compile(r' {,3}\S+:.*(\r?\n[ \t]*\S+:.*)*\r?\n{2,}(?P<excerpt>(\|(?=[ \n\r])[^\n\r]*\r?\n)+)'),
+    'excerpt':       re.compile(r'\|'.join(block)),
+    'excerpt_pipes': re.compile(r'\|'.join(match)),
 
-	# updates
-	'update':        re.compile(r'\|\|'.join(block)),
-	'update_pipes':  re.compile(r'\|\|'.join(match)),
+    # updates
+    'update':        re.compile(r'\|\|'.join(block)),
+    'update_pipes':  re.compile(r'\|\|'.join(match)),
 
-	# {{ filesize }} following internal links
-	'ref-style':     re.compile(r'(\[(?P<id>[^\]]+)\]\s*){{\s*filesize\s*}}'),
-	'inline':        re.compile(r'(\[[^\]]+\]\(/(?P<path>\S+?)\)\s*){{\s*filesize\s*}}'),
+    # {{ filesize }} following internal links
+    'ref-style':     re.compile(r'(\[(?P<id>[^\]]+)\]\s*){{\s*filesize\s*}}'),
+    'inline':        re.compile(r'(\[[^\]]+\]\(/(?P<path>\S+?)\)\s*){{\s*filesize\s*}}'),
 
-	'alias=>canon':  re.compile(r'^(0*(?P<alias>.*?)=>)?(?P<canon>.+)$'),
+    'alias=>canon':  re.compile(r'^(0*(?P<alias>.*?)=>)?(?P<canon>.+)$'),
 }
 
 def id_to_path(identifier, text):
-	"""
-	Finds the path after the [X] identifier in the text (used for filesize in our Markdown files)
-	
-	>>> path = id_to_path('1', '[1]: /static/downloads/package.zip')
-	>>> path.endswith('/downloads/package.zip')
-	True
-	"""
-	m = re.search(r'(?m)^ {,3}\[' + identifier + r'\]:\s+/(\S+)', text)
-	try:
-		path = m.group(1)
-	except AttributeError:
-		return False
+    """
+    Finds the path after the [X] identifier in the text (used for filesize in our Markdown files)
+    
+    >>> path = id_to_path('1', '[1]: /static/downloads/package.zip')
+    >>> path.endswith('/downloads/package.zip')
+    True
+    """
+    m = re.search(r'(?m)^ {,3}\[' + identifier + r'\]:\s+/(\S+)', text)
+    try:
+        path = m.group(1)
+    except AttributeError:
+        return False
 
-	return os.path.join(PATH_TO_STATIC, *path.split('/')[1:])
+    return os.path.join(PATH_TO_STATIC, *path.split('/')[1:])
 
 def print_filesize(path_to_file, plaintext=False):
-	"""
-	Prints the filesize of the file specified by `path_to_file`
-	(wrapped in an HTML span unless `plaintext` is True)
-	
-	>>> head, tail = os.path.split(__file__)
-	>>> path_to_file = os.path.join(head, 'examples', '1=>my-first-post.text')
-	>>> print_filesize(path_to_file)
-	u'<span class="filesize">(258\u2009bytes)</span>'
-	>>> print_filesize(path_to_file, True)
-	u'(258\u2009bytes)'
-	>>> print_filesize('DOES_NOT_EXIST')
-	u''
-	"""
-	kb_size = KILOBYTE_SIZE
-	try:
-		filesize = os.path.getsize(path_to_file)
-		bytes = (
-			('bytes', 1),
-			('kB', kb_size**1),
-			('MB', kb_size**2),
-			('GB', kb_size**3),
-			('TB', kb_size**4),
-		)
-		for t in bytes:
-			if filesize <= t[1] * kb_size:
-				if t[0] == 'bytes':
-					text = u'%s\u2009bytes' % filesize
-				else:
-					text = u'≈%s\u2009%s' % (round(float(filesize)/t[1], 1), t[0])
+    """
+    Prints the filesize of the file specified by `path_to_file`
+    (wrapped in an HTML span unless `plaintext` is True)
+    
+    >>> head, tail = os.path.split(__file__)
+    >>> path_to_file = os.path.join(head, 'examples', '1=>my-first-post.text')
+    >>> print_filesize(path_to_file)
+    u'<span class="filesize">(258\u2009bytes)</span>'
+    >>> print_filesize(path_to_file, True)
+    u'(258\u2009bytes)'
+    >>> print_filesize('DOES_NOT_EXIST')
+    u''
+    """
+    kb_size = KILOBYTE_SIZE
+    try:
+        filesize = os.path.getsize(path_to_file)
+        bytes = (
+            ('bytes', 1),
+            ('kB', kb_size**1),
+            ('MB', kb_size**2),
+            ('GB', kb_size**3),
+            ('TB', kb_size**4),
+        )
+        for t in bytes:
+            if filesize <= t[1] * kb_size:
+                if t[0] == 'bytes':
+                    text = u'%s\u2009bytes' % filesize
+                else:
+                    text = u'≈%s\u2009%s' % (round(float(filesize)/t[1], 1), t[0])
 
-				before, after = '(', ')'
-				if not plaintext:
-					before = '<span class="filesize">' + before
-					after += '</span>'
+                before, after = '(', ')'
+                if not plaintext:
+                    before = '<span class="filesize">' + before
+                    after += '</span>'
 
-				return u''.join([before, text, after])
-	except:
-		return u'' # fail silently
+                return u''.join([before, text, after])
+    except:
+        return u'' # fail silently
 
 def parse_markdown(text, plaintext=False):
-	"""
-	Returns the parsed text of the given string as a string. If plaintext only the mango replacements are done
-	
-	>>> text = get_contents('mango/examples/1=>my-first-post.text')
-	>>> parsed = parse_markdown(text)
-	>>> parsed['html']
-	u"\\n<p>Welcome to Mango. ... <strong>Congratulations!</strong></p>"
-	>>> parsed['meta']['title']
-	u'My First Post'
-	>>> parsed['excerpt']
-	u"\\n<p>Welcome to Mango. ... <strong>Congratulations!</strong></p>"
-	>>> parse_markdown(text, True)
-	u"date:\\t13 April ... **Congratulations!**\\n\\n"
-	"""
-	text += '\n' # helps to keep regular expressions as simple as possible
-	text = re.sub(RE['ref-style'],
-					lambda m: m.group(1) + print_filesize(id_to_path(m.group('id'), text), plaintext=plaintext),
-					text) # e.g. Download the [tiny calendar icon set][1] {{ filesize }}.
-	text = re.sub(RE['inline'],
-					lambda m: m.group(1) + print_filesize(m.group('path'), plaintext=plaintext),
-					text) # e.g. Download the [tiny calendar icon set](/downloads/tiny-calendar-icon-set.zip) {{ filesize }}.
+    """
+    Returns the parsed text of the given string as a string. If plaintext only the mango replacements are done
+    
+    >>> text = get_contents('mango/examples/1=>my-first-post.text')
+    >>> parsed = parse_markdown(text)
+    >>> parsed['html']
+    u"\\n<p>Welcome to Mango. ... <strong>Congratulations!</strong></p>"
+    >>> parsed['meta']['title']
+    u'My First Post'
+    >>> parsed['excerpt']
+    u"\\n<p>Welcome to Mango. ... <strong>Congratulations!</strong></p>"
+    >>> parse_markdown(text, True)
+    u"date:\\t13 April ... **Congratulations!**\\n\\n"
+    """
+    text += '\n' # helps to keep regular expressions as simple as possible
+    text = re.sub(RE['ref-style'],
+                    lambda m: m.group(1) + print_filesize(id_to_path(m.group('id'), text), plaintext=plaintext),
+                    text) # e.g. Download the [tiny calendar icon set][1] {{ filesize }}.
+    text = re.sub(RE['inline'],
+                    lambda m: m.group(1) + print_filesize(m.group('path'), plaintext=plaintext),
+                    text) # e.g. Download the [tiny calendar icon set](/downloads/tiny-calendar-icon-set.zip) {{ filesize }}.
 
-	if plaintext:
-		return text
+    if plaintext:
+        return text
 
-	excerpt = ''
-	match = re.match(RE['hand-crafted'], text)
-	if match:
-		capture = match.group('excerpt')
-		excerpt = parse_markdown(re.sub(RE['excerpt_pipes'], '', capture))['html'] + '\n'
-		text = text.replace(capture, '')
-	else:
-		for match in re.finditer(RE['excerpt'], text):
-			capture = match.group(0)
-			snippet = parse_markdown(re.sub(RE['excerpt_pipes'], '', capture))['html']
-			excerpt += snippet + '\n'
-			text = text.replace(capture, snippet)
+    excerpt = ''
+    match = re.match(RE['hand-crafted'], text)
+    if match:
+        capture = match.group('excerpt')
+        excerpt = parse_markdown(re.sub(RE['excerpt_pipes'], '', capture))['html'] + '\n'
+        text = text.replace(capture, '')
+    else:
+        for match in re.finditer(RE['excerpt'], text):
+            capture = match.group(0)
+            snippet = parse_markdown(re.sub(RE['excerpt_pipes'], '', capture))['html']
+            excerpt += snippet + '\n'
+            text = text.replace(capture, snippet)
 
-	t = loader.get_template('update.dhtml')
+    t = loader.get_template('update.dhtml')
 
-	for match in re.finditer(RE['update'], text):
-		capture = match.group(0)
-		c = Context(parse_markdown(re.sub(RE['update_pipes'], '', capture)))
-		text = text.replace(capture, t.render(c))
+    for match in re.finditer(RE['update'], text):
+        capture = match.group(0)
+        c = Context(parse_markdown(re.sub(RE['update_pipes'], '', capture)))
+        text = text.replace(capture, t.render(c))
 
-	md = markdown.Markdown(extensions = ('meta',) + MARKDOWN_EXTENSIONS)
-	html = md.convert(text)
+    md = markdown.Markdown(extensions = ('meta',) + MARKDOWN_EXTENSIONS)
+    html = md.convert(text)
 
-	if REPLACEMENTS: # perform replacements on HTML so that code snippets are not affected
-		fragments = re.split(r'(?s)(<code>.*?</code>|<pre>.*?</pre>)', html)
-		html = ''
-		for fragment in fragments:
-			if not re.match(r'(?s)^<(code|pre)>.*?</\1>$', fragment):
-				for key, value in dict(RE['replacements']).items():
-					fragment = re.sub(key, value, fragment)
-			html += fragment
+    if REPLACEMENTS: # perform replacements on HTML so that code snippets are not affected
+        fragments = re.split(r'(?s)(<code>.*?</code>|<pre>.*?</pre>)', html)
+        html = ''
+        for fragment in fragments:
+            if not re.match(r'(?s)^<(code|pre)>.*?</\1>$', fragment):
+                for key, value in dict(RE['replacements']).items():
+                    fragment = re.sub(key, value, fragment)
+            html += fragment
 
-	meta = {}
-	if hasattr(md, 'Meta'): # this needs to be checked as this doesn't exist if the file was empty
-		for key, value in md.Meta.items(): # note: every item in md.Meta.items() is a list
-			meta[key] = value
-			if len(value) == 1:
-				if key in META_LISTS:
-					meta[key] = value[0].split(', ')
-				else:
-					meta[key] = value[0]
+    meta = {}
+    if hasattr(md, 'Meta'): # this needs to be checked as this doesn't exist if the file was empty
+        for key, value in md.Meta.items(): # note: every item in md.Meta.items() is a list
+            meta[key] = value
+            if len(value) == 1:
+                if key in META_LISTS:
+                    meta[key] = value[0].split(', ')
+                else:
+                    meta[key] = value[0]
 
-	if meta.has_key('date') and meta.has_key('time'):
-		tz = pytz.timezone(settings.TIME_ZONE)
-		dt_format = ' '.join([MARKDOWN_DATE_FORMAT, MARKDOWN_TIME_FORMAT])
-		try:
-			meta['datetime'] = tz.localize(datetime.datetime.strptime(' '.join([meta['date'], meta['time']]), dt_format)).astimezone(pytz.utc)
-		except ValueError: # date and/or time incorrectly formatted
-			meta['datetime'] = None
+    if meta.has_key('date') and meta.has_key('time'):
+        tz = pytz.timezone(settings.TIME_ZONE)
+        dt_format = ' '.join([MARKDOWN_DATE_FORMAT, MARKDOWN_TIME_FORMAT])
+        try:
+            meta['datetime'] = tz.localize(datetime.datetime.strptime(' '.join([meta['date'], meta['time']]), dt_format)).astimezone(pytz.utc)
+        except ValueError: # date and/or time incorrectly formatted
+            meta['datetime'] = None
 
-	# Changed to process the HTML as this will also detect if the Markdown has an HTML header tag (in legal Markdown format).
-	# It also allows the setting of the title in meta in which case nothing is altered.
-	if not meta.has_key('title'):
-		m = re.match(RE['heading'], html)
-		if m:
-			meta['title'] = m.group('title')
-			html = m.group('html')
+    # Changed to process the HTML as this will also detect if the Markdown has an HTML header tag (in legal Markdown format).
+    # It also allows the setting of the title in meta in which case nothing is altered.
+    if not meta.has_key('title'):
+        m = re.match(RE['heading'], html)
+        if m:
+            meta['title'] = m.group('title')
+            html = m.group('html')
 
-	return {'meta': meta, 'excerpt': excerpt if excerpt else html, 'html': html}
+    return {'meta': meta, 'excerpt': excerpt if excerpt else html, 'html': html}
 
 def get_contents(filepath):
-	"""
-	Returns the contents of the file as a UTF-8 encoded string
+    """
+    Returns the contents of the file as a UTF-8 encoded string
 
-	>>> get_contents('mango/examples/1=>my-first-post.text')
-	u"date:\\t13 April ... **Congratulations!**\\n"
-	"""
-	f = open(filepath)
-	u = f.read().decode('utf-8')
-	f.close()
-	return u
+    >>> get_contents('mango/examples/1=>my-first-post.text')
+    u"date:\\t13 April ... **Congratulations!**\\n"
+    """
+    f = open(filepath)
+    u = f.read().decode('utf-8')
+    f.close()
+    return u
 
 def post_urls(filepath):
-	"""
-	Returns a post's short and canonical URLs
-	
-	>>> path_to_posts = os.path.join(*PATH_TO_POSTS.split('/'))
-	>>> setattr(mango.settings, 'SHORT_URL_BASE', 'http://✪df.ws/')
-	>>> post_urls(os.path.join(path_to_posts, '01=>my-first-post.text'))
-	(u'http://\u272adf.ws/1/', u'/my-first-post/')
-	>>> setattr(mango.settings, 'SHORT_URL_BASE', '')
-	>>> post_urls(os.path.join(path_to_posts, '01=>my-first-post.text'))
-	(u'/1/', u'/my-first-post/')
-	>>> post_urls(os.path.join(path_to_posts,
-	...         'js=>javascript', 'libs=>libraries', 'prototype.js', '$.text'))
-	(u'/js/libs/prototype.js/$/', u'/javascript/libraries/prototype.js/$/')
-	"""
-	canon_fragments = [u'']
-	alias_fragments = [u'']
+    """
+    Returns a post's short and canonical URLs
+    
+    >>> path_to_posts = os.path.join(*PATH_TO_POSTS.split('/'))
+    >>> setattr(mango.settings, 'SHORT_URL_BASE', 'http://✪df.ws/')
+    >>> post_urls(os.path.join(path_to_posts, '01=>my-first-post.text'))
+    (u'http://\u272adf.ws/1/', u'/my-first-post/')
+    >>> setattr(mango.settings, 'SHORT_URL_BASE', '')
+    >>> post_urls(os.path.join(path_to_posts, '01=>my-first-post.text'))
+    (u'/1/', u'/my-first-post/')
+    >>> post_urls(os.path.join(path_to_posts,
+    ...         'js=>javascript', 'libs=>libraries', 'prototype.js', '$.text'))
+    (u'/js/libs/prototype.js/$/', u'/javascript/libraries/prototype.js/$/')
+    """
+    canon_fragments = [u'']
+    alias_fragments = [u'']
 
-	head, tail = os.path.split(os.path.abspath(filepath))
-	match = re.match(RE['alias=>canon'], tail)
-	if match:
-		canon = os.path.splitext(match.group('canon'))[0] # strip extension
-		canon_fragments.insert(0, canon)
-		alias_fragments.insert(0, match.group('alias') or canon)
+    head, tail = os.path.split(os.path.abspath(filepath))
+    match = re.match(RE['alias=>canon'], tail)
+    if match:
+        canon = os.path.splitext(match.group('canon'))[0] # strip extension
+        canon_fragments.insert(0, canon)
+        alias_fragments.insert(0, match.group('alias') or canon)
 
-	while tail:
-		head, tail = os.path.split(head)
-		match = re.match(RE['alias=>canon'], tail)
-		if match:
-			canon = match.group('canon')
-			canon_fragments.insert(0, canon)
-			alias_fragments.insert(0, match.group('alias') or canon)
+    while tail:
+        head, tail = os.path.split(head)
+        match = re.match(RE['alias=>canon'], tail)
+        if match:
+            canon = match.group('canon')
+            canon_fragments.insert(0, canon)
+            alias_fragments.insert(0, match.group('alias') or canon)
 
-	fragments = []
-	absolute_path = os.path.abspath(os.path.join(*PATH_TO_POSTS.split(u'/')))
-	head, tail = os.path.split(absolute_path)
-	while tail:
-		fragments.insert(0, tail)
-		head, tail = os.path.split(head)
-	absolute_path = u'/'.join(fragments)
-	short_url_base = mango.settings.SHORT_URL_BASE.decode('utf-8').rstrip(u'/')
-	short_url = u'/'.join(alias_fragments).replace(absolute_path, short_url_base, 1)
-	canon_url = u'/'.join(canon_fragments).replace(absolute_path, u'', 1)
+    fragments = []
+    absolute_path = os.path.abspath(os.path.join(*PATH_TO_POSTS.split(u'/')))
+    head, tail = os.path.split(absolute_path)
+    while tail:
+        fragments.insert(0, tail)
+        head, tail = os.path.split(head)
+    absolute_path = u'/'.join(fragments)
+    short_url_base = mango.settings.SHORT_URL_BASE.decode('utf-8').rstrip(u'/')
+    short_url = u'/'.join(alias_fragments).replace(absolute_path, short_url_base, 1)
+    canon_url = u'/'.join(canon_fragments).replace(absolute_path, u'', 1)
 
-	return (short_url, canon_url)
+    return (short_url, canon_url)
 
 def posts(path_to_posts=PATH_TO_POSTS):
-	"""
-	Returns all of the posts in the directory and all directories below it. This is used for the index pages
-	as well as the index pages of any of the subfolders
-	
-	The posts are returned in list of 3-tuples sorted by their year and month in the form: (year, month, [posts])
-	
-	>>> p = posts('mango/examples/')
-	>>> p[0][0] == 2010
-	True
-	>>> p[0][1] == 4
-	True
-	>>> p[0][2][0]['html']
-	u"\\n<p>Welcome to Mango. ... <strong>Congratulations!</strong></p>"
-	"""
-	posts = []
-	for dirpath, dirnames, filenames in os.walk(path_to_posts):
-		filenames[:] = [f for f in filenames if not f.startswith('.')]
-		for filename in filenames:
-			joined_path = os.path.join(dirpath, filename)
-			absolute_path = os.path.abspath(joined_path)
-			# ignore symlink if it points to a file (post) in same directory
-			if absolute_path == os.path.realpath(joined_path):
-				this = cache.get(absolute_path)
-				if not this:
-					this = parse_markdown(get_contents(absolute_path))
-					this['meta']['url'] = post_urls(absolute_path)[1]
-					cache.set(absolute_path, this, CACHE_SECONDS)
-				posts.append(this)
+    """
+    Returns all of the posts in the directory and all directories below it. This is used for the index pages
+    as well as the index pages of any of the subfolders
+    
+    The posts are returned in list of 3-tuples sorted by their year and month in the form: (year, month, [posts])
+    
+    >>> p = posts('mango/examples/')
+    >>> p[0][0] == 2010
+    True
+    >>> p[0][1] == 4
+    True
+    >>> p[0][2][0]['html']
+    u"\\n<p>Welcome to Mango. ... <strong>Congratulations!</strong></p>"
+    """
+    posts = []
+    for dirpath, dirnames, filenames in os.walk(path_to_posts):
+        filenames[:] = [f for f in filenames if not f.startswith('.')]
+        for filename in filenames:
+            joined_path = os.path.join(dirpath, filename)
+            absolute_path = os.path.abspath(joined_path)
+            # ignore symlink if it points to a file (post) in same directory
+            if absolute_path == os.path.realpath(joined_path):
+                this = cache.get(absolute_path)
+                if not this:
+                    this = parse_markdown(get_contents(absolute_path))
+                    this['meta']['url'] = post_urls(absolute_path)[1]
+                    cache.set(absolute_path, this, CACHE_SECONDS)
+                posts.append(this)
 
-	dated_posts = [post for post in posts if post['meta'].get('datetime')] # display a post only if it has both a date and a time
-	dated_posts.sort(key=lambda post: post['meta']['datetime'], reverse=True) # most recent first
+    dated_posts = [post for post in posts if post['meta'].get('datetime')] # display a post only if it has both a date and a time
+    dated_posts.sort(key=lambda post: post['meta']['datetime'], reverse=True) # most recent first
 
-	last_index = len(dated_posts) - 1
-	posts = []
-	for index, post in enumerate(dated_posts):
-		dt = post['meta']['datetime']
-		this_year, this_month = dt.year, dt.month
+    last_index = len(dated_posts) - 1
+    posts = []
+    for index, post in enumerate(dated_posts):
+        dt = post['meta']['datetime']
+        this_year, this_month = dt.year, dt.month
 
-		if index == 0:
-			year, month = this_year, this_month
-			these_posts = []
+        if index == 0:
+            year, month = this_year, this_month
+            these_posts = []
 
-		if this_year == year and this_month == month:
-			these_posts.append(post)
-		else:
-			posts.append((year, month, these_posts))
-			year, month = this_year, this_month
-			these_posts = [post]
+        if this_year == year and this_month == month:
+            these_posts.append(post)
+        else:
+            posts.append((year, month, these_posts))
+            year, month = this_year, this_month
+            these_posts = [post]
 
-		if index == last_index:
-			posts.append((year, month, these_posts))
+        if index == last_index:
+            posts.append((year, month, these_posts))
 
-	return posts
+    return posts
 
 def primary_author_email():
-	"""
-	Returns the email address of the primary author as set in the settings file
-	
-	>>> primary_author_email()
-	'... <...@...>'
-	"""
-	name = PRIMARY_AUTHOR_NAME
-	email = PRIMARY_AUTHOR_EMAIL
-	if name and email:
-		email = '%s <%s>' % (name, email)
-	return email
+    """
+    Returns the email address of the primary author as set in the settings file
+    
+    >>> primary_author_email()
+    '... <...@...>'
+    """
+    name = PRIMARY_AUTHOR_NAME
+    email = PRIMARY_AUTHOR_EMAIL
+    if name and email:
+        email = '%s <%s>' % (name, email)
+    return email

File views.py

View file
 from mango.utils import RE
 
 def redirect(request, path):
-	return HttpResponseRedirect('/%s/' % path)
+    return HttpResponseRedirect('/%s/' % path)
 
 def context_defaults(request):
-	path_to_css, css = settings.__dict__.get('CSS', (None, []))
-	path_to_js, js = settings.__dict__.get('JS', (None, []))
-	return {
-		'request': request,
-		'settings': settings,
-		'stylesheets': [dict(media=media, href=path_to_css.lstrip('.')+filename) for media, stylesheets in css for filename in stylesheets],
-		'scripts': [dict(src=path_to_js.lstrip('.')+filename) for filename in js],
-		'now': datetime.datetime.now(),
-	}
+    path_to_css, css = settings.__dict__.get('CSS', (None, []))
+    path_to_js, js = settings.__dict__.get('JS', (None, []))
+    return {
+        'request': request,
+        'settings': settings,
+        'stylesheets': [dict(media=media, href=path_to_css.lstrip('.')+filename) for media, stylesheets in css for filename in stylesheets],
+        'scripts': [dict(src=path_to_js.lstrip('.')+filename) for filename in js],
+        'now': datetime.datetime.now(),
+    }
 
 def index(request):
-	return render_to_response('index.dhtml', dict({
-				'posts': [post for year, month, posts in utils.posts() for post in posts],
-			}, **context_defaults(request)))
+    return render_to_response('index.dhtml', dict({
+                'posts': [post for year, month, posts in utils.posts() for post in posts],
+            }, **context_defaults(request)))
 
 def archives(request):
-	return render_to_response('archives.dhtml', dict({'posts': utils.posts()}, **context_defaults(request)))
+    return render_to_response('archives.dhtml', dict({'posts': utils.posts()}, **context_defaults(request)))
 
 def tags(request):
-	tags = {}
-	for year, month, these_posts in utils.posts():
-		for post in these_posts:
-			for tag in post['meta'].get('tags', []):
-				if tag in tags:
-					tags[tag] += 1
-				else:
-					tags[tag] = 1
-	tags = [(key, value) for key, value in sorted(tags.items(), key=lambda pair: pair[0].lower())]
-	return render_to_response('tags.dhtml', dict({'tags': tags}, **context_defaults(request)))
+    tags = {}
+    for year, month, these_posts in utils.posts():
+        for post in these_posts:
+            for tag in post['meta'].get('tags', []):
+                if tag in tags:
+                    tags[tag] += 1
+                else:
+                    tags[tag] = 1
+    tags = [(key, value) for key, value in sorted(tags.items(), key=lambda pair: pair[0].lower())]
+    return render_to_response('tags.dhtml', dict({'tags': tags}, **context_defaults(request)))
 
 def tagged_as(request, tag):
-	tag = slugify(tag)
-	posts = []
-	for year, month, these_posts in utils.posts():
-		for post in these_posts:
-			if tag in [slugify(t) for t in post['meta'].get('tags', [])]:
-				posts.append(post)
-	return render_to_response('index.dhtml', dict({'posts': posts}, **context_defaults(request)))
+    tag = slugify(tag)
+    posts = []
+    for year, month, these_posts in utils.posts():
+        for post in these_posts:
+            if tag in [slugify(t) for t in post['meta'].get('tags', [])]:
+                posts.append(post)
+    return render_to_response('index.dhtml', dict({'posts': posts}, **context_defaults(request)))
 
 #TODO - this name should be changed as it now is also an index page, should this be combined with the index post?
 def post(request, path, view_source=False):
-	def render_post(filepath):
-		context = utils.parse_markdown(utils.get_contents(filepath))
-		form = CommentForm()
-		thread = None
-		num_total_posts = 0
-		num_visible_posts = 0
+    def render_post(filepath):
+        context = utils.parse_markdown(utils.get_contents(filepath))
+        form = CommentForm()
+        thread = None
+        num_total_posts = 0
+        num_visible_posts = 0
 
-		def get_forum(forums):
-			shortname = DISQUS['shortname']
-			for forum in forums:
-				if forum.shortname == shortname:
-					return forum
-			raise disqus.APIError
+        def get_forum(forums):
+            shortname = DISQUS['shortname']
+            for forum in forums:
+                if forum.shortname == shortname:
+                    return forum
+            raise disqus.APIError
 
-		#TODO - This will need to be updated, I don't currently have a DISQUS account so can't test, but I've changed the identifier
-		if settings.__dict__.get('DISQUS'):
-			try:
-				dsq = disqus.DisqusService()
-				dsq.login(DISQUS['api_key'])
-				forum = get_forum(dsq.get_forum_list())
-				thread = dsq.get_thread_by_url(forum, BASE_URL.rstrip('/') + canon_url)
-				if not thread:
-					thread = dsq.thread_by_identifier(forum, context['meta']['title'], canon_url)['thread']
-				# don't trust the documentation!
-				# http://wiki.disqus.net/API#getnumposts.3A
-				num_posts = dsq.get_num_posts(forum, [thread])[thread.id]
-				num_total_posts = max(num_posts)
-				num_visible_posts = min(num_posts)
+        #TODO - This will need to be updated, I don't currently have a DISQUS account so can't test, but I've changed the identifier
+        if settings.__dict__.get('DISQUS'):
+            try:
+                dsq = disqus.DisqusService()
+                dsq.login(DISQUS['api_key'])
+                forum = get_forum(dsq.get_forum_list())
+                thread = dsq.get_thread_by_url(forum, BASE_URL.rstrip('/') + canon_url)
+                if not thread:
+                    thread = dsq.thread_by_identifier(forum, context['meta']['title'], canon_url)['thread']
+                # don't trust the documentation!
+                # http://wiki.disqus.net/API#getnumposts.3A
+                num_posts = dsq.get_num_posts(forum, [thread])[thread.id]
+                num_total_posts = max(num_posts)
+                num_visible_posts = min(num_posts)
 
-				if request.method == 'POST':
-					form = CommentForm(request.POST)
-					if form.is_valid():
-						author_name = form.cleaned_data['author_name']
-						author_email = form.cleaned_data['author_email']
-						author_url = form.cleaned_data['author_url']
-						message = form.cleaned_data['message']
-						subscribe = form.cleaned_data['subscribe']
+                if request.method == 'POST':
+                    form = CommentForm(request.POST)
+                    if form.is_valid():
+                        author_name = form.cleaned_data['author_name']
+                        author_email = form.cleaned_data['author_email']
+                        author_url = form.cleaned_data['author_url']
+                        message = form.cleaned_data['message']
+                        subscribe = form.cleaned_data['subscribe']
 
-						# send request to Disqus
-						dsq.create_post(forum=forum, thread=thread, message=message,
-								author_name=author_name, author_email=author_email, author_url=author_url)
+                        # send request to Disqus
+                        dsq.create_post(forum=forum, thread=thread, message=message,
+                                author_name=author_name, author_email=author_email, author_url=author_url)
 
-						# send e-mail notification
-						author = u'%s <%s>' % (author_name, author_email)
-						subject = u'[%s] Comment: "%s"' % (SITE_TITLE.decode('utf-8'), context['meta']['title'])
-						message = u'%s wrote:\n\n%s\n\nhttp://disqus.com/comments/moderate/%s/' % (
-								author, message, DISQUS['shortname'])
-						send_mail(subject, message, author, [utils.primary_author_email()], fail_silently=False)
+                        # send e-mail notification
+                        author = u'%s <%s>' % (author_name, author_email)
+                        subject = u'[%s] Comment: "%s"' % (SITE_TITLE.decode('utf-8'), context['meta']['title'])
+                        message = u'%s wrote:\n\n%s\n\nhttp://disqus.com/comments/moderate/%s/' % (
+                                author, message, DISQUS['shortname'])
+                        send_mail(subject, message, author, [utils.primary_author_email()], fail_silently=False)
 
-						return HttpResponseRedirect('redirect/')
-			except (disqus.APIError, KeyError):
-				pass
+                        return HttpResponseRedirect('redirect/')
+            except (disqus.APIError, KeyError):
+                pass
 
-			if DISQUS['sort'] == 'oldest_first':
-				try:
-					thread.posts.sort(key=lambda post: post.created_at)
-				except AttributeError:
-					pass 
+            if DISQUS['sort'] == 'oldest_first':
+                try:
+                    thread.posts.sort(key=lambda post: post.created_at)
+                except AttributeError:
+                    pass 
 
-		return render_to_string('post.dhtml', dict(context, **dict({
-					'form': form,
-					'num_visible_posts': num_visible_posts,
-					'short_url': short_url,
-					'thread': thread,
-					'type': 'post' if 'datetime' in context['meta'] else 'page',
-				}, **context_defaults(request))))
+        return render_to_string('post.dhtml', dict(context, **dict({
+                    'form': form,
+                    'num_visible_posts': num_visible_posts,
+                    'short_url': short_url,
+                    'thread': thread,
+                    'type': 'post' if 'datetime' in context['meta'] else 'page',
+                }, **context_defaults(request))))
 
-	filepath = os.path.join(os.sep, *PATH_TO_POSTS.split('/'))
-	is_short = False
-	fragments = path.split('/')
-	last_index = len(fragments) - 1
-	for index, fragment in enumerate(fragments):
-		found = False
-		for entry in os.listdir(filepath):
-			name = entry
-			if index == last_index:
-				name, ext = os.path.splitext(entry)
-			match = re.match(RE['alias=>canon'], name)
-			if fragment in match.groupdict().values():
-				found = True
-				if fragment == match.group('alias'):
-					is_short = True
-				filepath = os.path.join(filepath, entry)
-				break
-		if not found:
-			raise Http404
+    filepath = os.path.join(os.sep, *PATH_TO_POSTS.split('/'))
+    is_short = False
+    fragments = path.split('/')
+    last_index = len(fragments) - 1
+    for index, fragment in enumerate(fragments):
+        found = False
+        for entry in os.listdir(filepath):
+            name = entry
+            if index == last_index:
+                name, ext = os.path.splitext(entry)
+            match = re.match(RE['alias=>canon'], name)
+            if fragment in match.groupdict().values():
+                found = True
+                if fragment == match.group('alias'):
+                    is_short = True
+                filepath = os.path.join(filepath, entry)
+                break
+        if not found:
+            raise Http404
 
-	short_url, canon_url = utils.post_urls(filepath)
+    short_url, canon_url = utils.post_urls(filepath)
 
-	if is_short:
-		if view_source:
-			canon_url += 'm/'
-		return HttpResponseRedirect(canon_url)
+    if is_short:
+        if view_source:
+            canon_url += 'm/'
+        return HttpResponseRedirect(canon_url)
 
-	if os.path.isdir(filepath):
-		#TODO - if view_source is true what do we do here?
-		#TODO - caching
-		html = render_to_response('index.dhtml', dict({
-			'posts': [post for year, month, posts in utils.posts(filepath) for post in posts],
-		}, **context_defaults(request)))
-		return HttpResponse(html)
+    if os.path.isdir(filepath):
+        #TODO - if view_source is true what do we do here?
+        #TODO - caching
+        html = render_to_response('index.dhtml', dict({
+            'posts': [post for year, month, posts in utils.posts(filepath) for post in posts],
+        }, **context_defaults(request)))
+        return HttpResponse(html)
 
-	if view_source: # Return the plain text of the Markdown page.
-		text = utils.parse_markdown(text=utils.get_contents(filepath), plaintext=True)
-		return HttpResponse(text, content_type='text/plain; charset=utf-8')
+    if view_source: # Return the plain text of the Markdown page.
+        text = utils.parse_markdown(text=utils.get_contents(filepath), plaintext=True)
+        return HttpResponse(text, content_type='text/plain; charset=utf-8')
 
-	if os.path.islink(filepath): # symbolic link
-		short_url, canon_url = utils.post_urls(os.path.realpath(filepath))
-		return HttpResponseRedirect(canon_url)
+    if os.path.islink(filepath): # symbolic link
+        short_url, canon_url = utils.post_urls(os.path.realpath(filepath))
+        return HttpResponseRedirect(canon_url)
 
-	# Parse the Markdown and return it through the Django template.
-	value = render_post(filepath)
-	if isinstance(value, HttpResponseRedirect):
-		return value
+    # Parse the Markdown and return it through the Django template.
+    value = render_post(filepath)
+    if isinstance(value, HttpResponseRedirect):
+        return value
 
-	return HttpResponse(value)
+    return HttpResponse(value)
 
 def contact(request, message_sent=False):
-	if message_sent:
-		return render_to_response('contact.dhtml', context_defaults(request))
+    if message_sent:
+        return render_to_response('contact.dhtml', context_defaults(request))
 
-	if request.method == 'POST':
-		form = ContactForm(request.POST)
-		if form.is_valid():
-			subject = form.cleaned_data['subject']
-			message = form.cleaned_data['message']
-			sender = u'%s <%s>' % (form.cleaned_data['sender_name'], form.cleaned_data['sender_email'])
+    if request.method == 'POST':
+        form = ContactForm(request.POST)
+        if form.is_valid():
+            subject = form.cleaned_data['subject']
+            message = form.cleaned_data['message']
+            sender = u'%s <%s>' % (form.cleaned_data['sender_name'], form.cleaned_data['sender_email'])
 
-			recipients = [utils.primary_author_email()]
-			if form.cleaned_data['cc_sender']:
-				recipients.append(sender)
+            recipients = [utils.primary_author_email()]
+            if form.cleaned_data['cc_sender']:
+                recipients.append(sender)
 
-			send_mail(subject, message, sender, recipients, fail_silently=False)
-			return HttpResponseRedirect('thanks/')
-	else:
-		form = ContactForm()
+            send_mail(subject, message, sender, recipients, fail_silently=False)
+            return HttpResponseRedirect('thanks/')
+    else:
+        form = ContactForm()
 
-	return render_to_response('contact.dhtml', dict({'form': form}, **context_defaults(request)))
+    return render_to_response('contact.dhtml', dict({'form': form}, **context_defaults(request)))