diff --git a/pelican/bloggenerator.py b/pelican/bloggenerator.py
index dcccd86c..96dc1053 100644
--- a/pelican/bloggenerator.py
+++ b/pelican/bloggenerator.py
@@ -13,6 +13,7 @@ from feedgenerator import Atom1Feed
# import the directives to have pygments support
import rstdirectives
+from utils import truncate_html_words
## Constants ##########################################################
@@ -58,6 +59,13 @@ def generate_blog(path=None, theme=None, output_path=None, markup=None,
# get the list of files to parse
if not path:
raise Exception('you need to specify a path to search the docs on !')
+
+ # remove all the existing content from the output folder
+ try:
+ shutil.rmtree(os.path.join(output_path))
+ except:
+ pass
+
files = []
for root, dirs, temp_files in os.walk(path, followlinks=True):
files.extend([os.sep.join((root, f)) for f in temp_files
@@ -119,14 +127,9 @@ def generate_blog(path=None, theme=None, output_path=None, markup=None,
# copy static paths to output
for path in context['STATIC_PATHS']:
try:
- real_output = os.path.join(output_path, path)
- theme_path = os.path.join(theme, path)
-
- print "updating %s" % real_output
- shutil.rmtree(real_output)
- shutil.copytree(theme_path, real_output)
-
- except OSError as e:
+ shutil.copytree(os.path.join(theme, path),
+ os.path.join(output_path, path))
+ except OSError:
pass
@@ -304,4 +307,4 @@ class Article(object):
@property
def summary(self):
- return self.content
+ return truncate_html_words(self.content, 50)
diff --git a/pelican/utils.py b/pelican/utils.py
new file mode 100644
index 00000000..197bacd3
--- /dev/null
+++ b/pelican/utils.py
@@ -0,0 +1,67 @@
+import re
+
+def truncate_html_words(s, num, end_text='...'):
+ """Truncates HTML to a certain number of words (not counting tags and
+ comments). Closes opened tags if they were correctly closed in the given
+ html. Takes an optional argument of what should be used to notify that the
+ string has been truncated, defaulting to ellipsis (...).
+
+ Newlines in the HTML are preserved.
+ From the django framework.
+ """
+ length = int(num)
+ if length <= 0:
+ return u''
+ html4_singlets = ('br', 'col', 'link', 'base', 'img', 'param', 'area', 'hr', 'input')
+ # Set up regular expressions
+ re_words = re.compile(r'&.*?;|<.*?>|(\w[\w-]*)', re.U)
+ re_tag = re.compile(r'<(/)?([^ ]+?)(?: (/)| .*?)?>')
+ # Count non-HTML words and keep note of open tags
+ pos = 0
+ end_text_pos = 0
+ words = 0
+ open_tags = []
+ while words <= length:
+ m = re_words.search(s, pos)
+ if not m:
+ # Checked through whole string
+ break
+ pos = m.end(0)
+ if m.group(1):
+ # It's an actual non-HTML word
+ words += 1
+ if words == length:
+ end_text_pos = pos
+ continue
+ # Check for tag
+ tag = re_tag.match(m.group(0))
+ if not tag or end_text_pos:
+ # Don't worry about non tags or tags after our truncate point
+ continue
+ closing_tag, tagname, self_closing = tag.groups()
+ tagname = tagname.lower() # Element names are always case-insensitive
+ if self_closing or tagname in html4_singlets:
+ pass
+ elif closing_tag:
+ # Check for match in open tags list
+ try:
+ i = open_tags.index(tagname)
+ except ValueError:
+ pass
+ else:
+ # SGML: An end tag closes, back to the matching start tag, all unclosed intervening start tags with omitted end tags
+ open_tags = open_tags[i+1:]
+ else:
+ # Add it to the start of the open tags list
+ open_tags.insert(0, tagname)
+ if words <= length:
+ # Don't try to close tags if we don't need to truncate
+ return s
+ out = s[:end_text_pos]
+ if end_text:
+ out += ' ' + end_text
+ # Close any tags still open
+ for tag in open_tags:
+ out += '%s>' % tag
+ # Return string
+ return out
diff --git a/samples/themes/notmyidea/css/main.css b/samples/themes/notmyidea/css/main.css
index 4431f59d..14ec30b8 100644
--- a/samples/themes/notmyidea/css/main.css
+++ b/samples/themes/notmyidea/css/main.css
@@ -34,9 +34,12 @@ h2, h3, h4, h5, h6 {
font-weight: 400;
line-height: 1.1;
margin-bottom: .8em;
- margin-top: .8em;
}
+
+h3, h4, h5, h6 { margin-top: .8em; }
+hr { border: 2px solid #EEEEEE; }
+
/* Anchors */
a {outline: 0;}
a img {border: 0px; text-decoration: none;}
@@ -54,7 +57,6 @@ a:hover, a:active {
/* Paragraphs */
p {margin-bottom: 1.143em;}
-* p:last-child {margin-bottom: 0;}
strong, b {font-weight: bold;}
em, i {font-style: italic;}
@@ -73,6 +75,14 @@ ol {
margin: 1em 0 1.5em 1.5em;
}
+.post-info {
+ float:right;
+ margin:10px;
+ padding:5px;
+}
+
+.readmore { float: right }
+
dl {margin: 0 0 1.5em 0;}
dt {font-weight: bold;}
dd {margin-left: 1.5em;}
@@ -121,7 +131,7 @@ img.left, figure.left {float: right; margin: 0 0 2em 2em;}
}
/* Banner */
- #banner h1 {font-size: 3.571em; line-height: .6;}
+ #banner h1 {font-size: 3.571em; line-height: 0;}
#banner h1 a:link, #banner h1 a:visited {
color: #000305;
display: block;
@@ -324,7 +334,7 @@ img.left, figure.left {float: right; margin: 0 0 2em 2em;}
li:last-child .hentry, #content > .hentry {border: 0; margin: 0;}
#content > .hentry {padding: 1em 0;}
-.entry-title {font-size: 1.429em; margin-bottom: 5px; margin-top: 0; }
+.entry-title {font-size: 2em; margin-bottom: 5px; margin-top: 0; }
.entry-title a:link, .entry-title a:visited {text-decoration: none;}
.hentry .post-info * {font-style: normal;}
diff --git a/samples/themes/notmyidea/templates/article.html b/samples/themes/notmyidea/templates/article.html
index 3bc28f47..cc2ffb9d 100644
--- a/samples/themes/notmyidea/templates/article.html
+++ b/samples/themes/notmyidea/templates/article.html
@@ -1,19 +1,19 @@
{% extends "base.html" %}
{% block content %}
{{ article.title }}