Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Remove Flask-FlatPages.

Throw out all this generic stuff with a nice API,
make a some custom code with a few hard-coded paths.
  • Loading branch information...
commit d1cedc633c1df4d1f395441a56d01cfef8dcebd6 1 parent df78bf9
Simon Sapin authored September 27, 2012

Showing 30 changed files with 124 additions and 122 deletions. Show diff stats Hide diff stats

  1. 148  exyr/__init__.py
  2. 18  exyr/freezer.py
  3. 1  exyr/pages/2010/Flask-Static.markdown
  4. 1  exyr/pages/2010/Jinja-in-Django.markdown
  5. 1  exyr/pages/2011/Frozen-Flask.markdown
  6. 1  exyr/pages/2011/Poor-man-NTP.markdown
  7. 1  exyr/pages/2011/event-loop.markdown
  8. 1  exyr/pages/2011/gedit-plugins-packaged.markdown
  9. 1  exyr/pages/2011/git-mirrors.markdown
  10. 1  exyr/pages/2011/gnome-terminal-tabs.markdown
  11. 1  exyr/pages/2011/hashing-passwords.markdown
  12. 1  exyr/pages/2011/inotify-run.markdown
  13. 1  exyr/pages/2011/multicorn.markdown
  14. 1  exyr/pages/2011/random-pronounceable-passwords.markdown
  15. 1  exyr/pages/2011/virtualenv-HOWTO.markdown
  16. 1  exyr/pages/2011/weasyprint.markdown
  17. 1  exyr/pages/2012/csswg-invited-expert.markdown
  18. 1  exyr/pages/2012/flask-weasyprint.markdown
  19. 1  exyr/pages/2012/fosdem.markdown
  20. 1  exyr/pages/2012/tinycss-css-parser.markdown
  21. 1  exyr/pages/2012/weasyprint-at-pyconfr.markdown
  22. 1  exyr/pages/2012/weasyprint-is-bsd.markdown
  23. 1  exyr/pages/about.markdown
  24. 27  exyr/public_pages.py
  25. 11  exyr/templates/atom.xml
  26. 4  exyr/templates/flatpage.html
  27. 6  exyr/templates/layout.html
  28. 2  exyr/templates/style.css
  29. 6  exyr/templates/utils.html
  30. 3  setup.py
148  exyr/__init__.py
... ...
@@ -1,37 +1,96 @@
1 1
 import os
  2
+import io
2 3
 import re
3 4
 import math
4 5
 import datetime
  6
+import itertools
5 7
 
6  
-import markdown
  8
+import markdown as markdown_module
  9
+import pygments.formatters
  10
+import yaml
7 11
 import jinja2
8  
-from flask import Flask, render_template, send_from_directory, abort
9  
-from flaskext.flatpages import pygments_style_defs
10  
-
11  
-from .public_pages import PublicPages
  12
+import werkzeug
  13
+from flask import Flask, render_template, send_from_directory, abort, url_for
12 14
 
13 15
 
14 16
 app = Flask(__name__)
15 17
 app.jinja_env.undefined = jinja2.StrictUndefined
  18
+app.jinja_env.globals['today'] = datetime.date.today
16 19
 
17 20
 # The atom.xml template uses url_for(..., _external=True)
18 21
 app.config['FREEZER_BASE_URL'] = 'http://exyr.org/'
19 22
 
20  
-pages = PublicPages(app)
21  
-app.jinja_env.globals['pages'] = pages
22  
-app.jinja_env.globals['build_date'] = datetime.date.today()
23  
-
24  
-
25  
-@app.template_filter(name='markdown')
26  
-def my_markdown(text):
27  
-    return markdown.markdown(text, ['codehilite'] + 2 * ['downheader'])
  23
+PYGMENTS_CSS = (pygments.formatters.HtmlFormatter(style='tango')
  24
+                .get_style_defs('.codehilite'))
  25
+
  26
+
  27
+@app.template_filter()
  28
+def markdown(text):
  29
+    return markdown_module.markdown(text, ['codehilite'] + 2 * ['downheader'])
  30
+
  31
+
  32
+class Page(object):
  33
+    root = os.path.join(app.root_path, u'pages')
  34
+    suffix = '.markdown'
  35
+    _cache = {}
  36
+
  37
+    @classmethod
  38
+    def load(cls, year, name):
  39
+        filename = os.path.join(cls.root, year, name) + cls.suffix
  40
+        if not os.path.isfile(filename):
  41
+            abort(404)
  42
+        mtime = os.path.getmtime(filename)
  43
+        page, old_mtime = cls._cache.get(filename, (None, None))
  44
+        if not page or mtime != old_mtime:
  45
+            with io.open(filename, encoding='utf8') as fd:
  46
+                head = ''.join(itertools.takewhile(unicode.strip, fd))
  47
+                body = fd.read()
  48
+            page = cls(year, name, head, body)
  49
+            cls._cache[filename] = (page, mtime)
  50
+        return page
  51
+
  52
+    @classmethod
  53
+    def years(cls):
  54
+        for year in os.listdir(cls.root):
  55
+            if year.isdigit():
  56
+                yield year
  57
+
  58
+    @classmethod
  59
+    def articles_by_year(cls, year):
  60
+        directory = os.path.join(cls.root, year)
  61
+        if not os.path.isdir(directory):
  62
+            abort(404)
  63
+        for name in os.listdir(directory):
  64
+            if name.endswith(cls.suffix):
  65
+                yield cls.load(year, name[:-len(cls.suffix)])
  66
+
  67
+    @classmethod
  68
+    def all_articles(cls):
  69
+        for year in cls.years():
  70
+            for article in cls.articles_by_year(year):
  71
+                yield article
  72
+
  73
+    def __init__(self, year, name, head, body):
  74
+        self.year = year
  75
+        self.name = name
  76
+        self.head = head
  77
+        self.body = body
  78
+
  79
+    @werkzeug.cached_property
  80
+    def meta(self):
  81
+        return yaml.safe_load(self.head) or {}
  82
+
  83
+    def __getitem__(self, name):
  84
+        return self.meta[name]
  85
+
  86
+    @werkzeug.cached_property
  87
+    def html(self):
  88
+        return markdown(self.body)
  89
+
  90
+    def url(self, **kwargs):
  91
+        return url_for(
  92
+            'article', year=int(self.year), name=self.name, **kwargs)
28 93
 
29  
-app.config['FLATPAGES_HTML_RENDERER'] = my_markdown
30  
-app.config['FLATPAGES_EXTENSION'] = '.markdown'
31  
-
32  
-
33  
-def all_articles():
34  
-    return (p for p in pages if 'published' in p.meta)
35 94
 
36 95
 def by_date(articles):
37 96
     return sorted(articles, reverse=True, key=lambda p: p['published'])
@@ -39,13 +98,19 @@ def by_date(articles):
39 98
 
40 99
 @app.route('/')
41 100
 def home():
42  
-    return render_template('all_posts.html', posts=by_date(all_articles()))
  101
+    return render_template(
  102
+        'all_posts.html', posts=by_date(Page.all_articles()))
  103
+
  104
+
  105
+@app.route('/about/')
  106
+def about():
  107
+    return render_template('flatpage.html', page=Page.load('', 'about'))
43 108
 
44 109
 
45 110
 @app.route('/tags/')
46 111
 def tags():
47 112
     counts = {}
48  
-    for article in all_articles():
  113
+    for article in Page.all_articles():
49 114
         for tag in article.meta.get('tags', []):
50 115
             counts[tag] = counts.get(tag, 0) + 1
51 116
 
@@ -59,7 +124,7 @@ def tags():
59 124
 @app.route('/tags/<name>/')
60 125
 def tag(name):
61 126
     articles = by_date(
62  
-        a for a in all_articles() if name in a.meta.get('tags', [])
  127
+        a for a in Page.all_articles() if name in a.meta.get('tags', [])
63 128
     )
64 129
     if not articles:
65 130
         abort(404)
@@ -68,22 +133,27 @@ def tag(name):
68 133
 
69 134
 @app.route('/<int:year>/')
70 135
 def archives(year):
71  
-    articles = by_date(
72  
-        article for article in all_articles()
73  
-        if article.path.startswith(str(year) + '/')
74  
-    )
75  
-    if not articles:
76  
-        abort(404)
  136
+    articles = by_date(Page.articles_by_year(str(year)))
77 137
     return render_template('archives.html', **locals())
78 138
 
79 139
 
  140
+@app.route('/<int:year>/<name>/')
  141
+def article(year, name):
  142
+    return render_template('flatpage.html', page=Page.load(str(year), name))
  143
+
  144
+
  145
+@app.route('/<int:year>/<name>/<path:path>')
  146
+def static_in_pages(year, name, path):
  147
+    return send_from_directory(Page.root, '%i/%s/%s' % (year, name, path))
  148
+
  149
+
80 150
 @app.route('/feed.atom')
81 151
 def feed():
82 152
     articles = sorted(
83 153
         (
84 154
             # with `modified`, but defaults to `published`
85 155
             (article.meta.get('modified', article['published']), article)
86  
-            for article in all_articles()
  156
+            for article in Page.all_articles()
87 157
         ),
88 158
         reverse=True
89 159
     )[:10]
@@ -103,7 +173,7 @@ def minify_css(css):
103 173
 
104 174
 @app.route('/style.css')
105 175
 def stylesheet():
106  
-    css = render_template('style.css', pygments_style_defs=pygments_style_defs)
  176
+    css = render_template('style.css', pygments_css=PYGMENTS_CSS)
107 177
     css = minify_css(css)
108 178
     # Add this after minification, would be removed otherwise.
109 179
     css = (
@@ -115,24 +185,6 @@ def stylesheet():
115 185
     return app.response_class(css, mimetype='text/css')
116 186
 
117 187
 
118  
-STATIC_EXTENSIONS = ('.jpg', '.png', '.svg', '.html', '.css', '.pdf')
119  
-
120  
-# the repr() of a tuple matches the micro-syntax used by `any`
121  
-# http://werkzeug.pocoo.org/documentation/dev/routing.html#werkzeug.routing.AnyConverter
122  
-@app.route('/<path:path><any%r:type>' % (STATIC_EXTENSIONS,))
123  
-def static_in_pages(path, type):
124  
-    return send_from_directory(pages.root, path + type)
125  
-
126  
-
127  
-@app.route('/<path:path>/')
128  
-def page(path):
129  
-    return render_template('flatpage.html',
130  
-        page=pages.get_or_404(path),
131  
-#        sub_pages=by_date(p for p in all_articles()
132  
-#                          if p.path.startswith(path + '/')),
133  
-    )
134  
-
135  
-
136 188
 @app.errorhandler(404)
137 189
 def not_found(e):
138 190
     return render_template('404.html')
18  exyr/freezer.py
... ...
@@ -1,8 +1,7 @@
1 1
 import os
2  
-import posixpath
3 2
 import mimetypes
4 3
 
5  
-from . import app, pages, STATIC_EXTENSIONS, all_articles
  4
+from . import app, Page
6 5
 from flask.ext.frozen import Freezer, walk_directory
7 6
 
8 7
 
@@ -15,14 +14,15 @@
15 14
 
16 15
 @freezer.register_generator
17 16
 def archives():
18  
-    for name in os.listdir(pages.root):
19  
-        if name.isdigit():
20  
-            yield {'year': name}
  17
+    for year in Page.years():
  18
+        yield {'year': int(year)}
21 19
 
22 20
 
23 21
 @freezer.register_generator
24 22
 def static_in_pages():
25  
-    for filename in walk_directory(pages.root):
26  
-        path, extension = posixpath.splitext(filename)
27  
-        if extension in STATIC_EXTENSIONS:
28  
-            yield {'path': path, 'type': extension}
  23
+    for year in Page.years():
  24
+        for name in os.listdir(os.path.join(Page.root, year)):
  25
+            directory = os.path.join(Page.root, year, name)
  26
+            if os.path.isdir(directory):
  27
+                for path in walk_directory(directory):
  28
+                    yield {'year': int(year), 'name': name, 'path': path}
1  exyr/pages/2010/Flask-Static.markdown
Source Rendered
... ...
@@ -1,5 +1,4 @@
1 1
 title: "Flask-Static: yet another static website generator"
2  
-public: true
3 2
 published: 2010-12-28
4 3
 modified: 2011-02-21
5 4
 tags:
1  exyr/pages/2010/Jinja-in-Django.markdown
Source Rendered
... ...
@@ -1,5 +1,4 @@
1 1
 title: Using Jinja2 in Django
2  
-public: true
3 2
 published: 2010-12-29
4 3
 tags:
5 4
     - web-development
1  exyr/pages/2011/Frozen-Flask.markdown
Source Rendered
... ...
@@ -1,5 +1,4 @@
1 1
 title: Flask-Static is dead, long live Frozen-Flask!
2  
-public: true
3 2
 published: 2011-02-21
4 3
 tags: [web-development, flask]
5 4
 summary: Just because the new name is so much cooler.
1  exyr/pages/2011/Poor-man-NTP.markdown
Source Rendered
... ...
@@ -1,5 +1,4 @@
1 1
 title: Poor man’s NTP
2  
-public: true
3 2
 published: 2011-05-28
4 3
 tags: [ntp]
5 4
 summary: How to synchronize a Linux computer’s clock when NTP doesn’t work.
1  exyr/pages/2011/event-loop.markdown
Source Rendered
... ...
@@ -1,7 +1,6 @@
1 1
 title: Single-threaded event loop for file input and timers
2 2
 published: 2011-02-27
3 3
 tags: [snippets]
4  
-public: true
5 4
 summary: |
6 5
     To wait for various events and without polling, a blocking threads for each
7 6
     event is the obvious solution. However, multi-threading comes with its
1  exyr/pages/2011/gedit-plugins-packaged.markdown
Source Rendered
... ...
@@ -1,5 +1,4 @@
1 1
 title: Two Gedit 3 plugins packaged for Arch Linux
2  
-public: true
3 2
 published: 2011-07-03
4 3
 tags: [gedit, packaging, archlinux]
5 4
 summary:
1  exyr/pages/2011/git-mirrors.markdown
Source Rendered
... ...
@@ -1,7 +1,6 @@
1 1
 title: Mirroring a Gitorious repository to GitHub
2 2
 published: 2011-04-30
3 3
 tags: [git, backups]
4  
-public: yes
5 4
 summary: |
6 5
     You can not make Gitorious or GitHub push to somewhere else, so mirroring
7 6
     them requires a few more steps.
1  exyr/pages/2011/gnome-terminal-tabs.markdown
Source Rendered
... ...
@@ -1,6 +1,5 @@
1 1
 title: Programatically open multiple GNOME Terminal tabs
2 2
 published: 2011-02-20
3  
-public: yes
4 3
 tags: [snippets]
5 4
 summary: |
6 5
     To run several tasks in parallel and keep their output separated, run each
1  exyr/pages/2011/hashing-passwords.markdown
Source Rendered
... ...
@@ -1,5 +1,4 @@
1 1
 title: Hashing passwords the Right Way
2  
-public: true
3 2
 published: 2011-12-05
4 3
 tags: [passwords, web-development, snippets]
5 4
 summary:
1  exyr/pages/2011/inotify-run.markdown
Source Rendered
... ...
@@ -1,5 +1,4 @@
1 1
 title: Run a script on file changes with inotify
2  
-public: true
3 2
 published: 2011-10-13
4 3
 tags: [inotify, snippets]
5 4
 summary:
1  exyr/pages/2011/multicorn.markdown
Source Rendered
... ...
@@ -1,5 +1,4 @@
1 1
 title: "Multicorn: Access foreign data in PostgreSQL with Python"
2  
-public: true
3 2
 published: 2011-11-07
4 3
 tags: [kozea]
5 4
 summary:
1  exyr/pages/2011/random-pronounceable-passwords.markdown
Source Rendered
... ...
@@ -1,6 +1,5 @@
1 1
 title: Random pronounceable passwords
2 2
 published: 2011-02-11
3  
-public: yes
4 3
 tags: [passwords, snippets]
5 4
 summary: |
6 5
     Using a sample text for statistics and Markov chains, we can generate
1  exyr/pages/2011/virtualenv-HOWTO.markdown
Source Rendered
... ...
@@ -1,5 +1,4 @@
1 1
 title: virtualenv HOWTO
2  
-public: true
3 2
 published: 2011-06-10
4 3
 tags: [packaging]
5 4
 summary: Slides for an introduction to virtualenv and pip I gave recently.
1  exyr/pages/2011/weasyprint.markdown
Source Rendered
... ...
@@ -1,5 +1,4 @@
1 1
 title: WeasyPrint renders HTML+CSS to PDF
2  
-public: true
3 2
 published: 2011-11-02
4 3
 tags: [kozea, weasyprint]
5 4
 summary:
1  exyr/pages/2012/csswg-invited-expert.markdown
Source Rendered
... ...
@@ -1,7 +1,6 @@
1 1
 title: Invited Expert at W3C’s CSSWG
2 2
 published: 2012-09-17
3 3
 tags: [weasyprint, W3C]
4  
-public: true
5 4
 summary: |
6 5
     I just joined [W3C’s *CSS Working Group*](http://www.w3.org/Style/CSS/)
7 6
     as an *Invited Expert*.
1  exyr/pages/2012/flask-weasyprint.markdown
Source Rendered
... ...
@@ -1,7 +1,6 @@
1 1
 title: Flask-WeasyPrint
2 2
 published: 2012-07-19
3 3
 tags: [kozea, weasyprint]
4  
-public: true
5 4
 summary: |
6 5
     I just released [Flask-WeasyPrint](http://packages.python.org/Flask-WeasyPrint/)
7 6
 
1  exyr/pages/2012/fosdem.markdown
Source Rendered
... ...
@@ -1,7 +1,6 @@
1 1
 title: I’m going to FOSDEM
2 2
 published: 2012-02-01
3 3
 tags: [fosdem, kozea]
4  
-public: true
5 4
 summary: |
6 5
     I’m going to [FOSDEM](http://fosdem.org/2012/) this week-end.
7 6
     See you there!
1  exyr/pages/2012/tinycss-css-parser.markdown
Source Rendered
... ...
@@ -1,7 +1,6 @@
1 1
 title: "tinycss: a new CSS parser for Python"
2 2
 published: 2012-04-05
3 3
 tags: [kozea, weasyprint]
4  
-public: true
5 4
 summary: |
6 5
     I made [tinycss](http://packages.python.org/tinycss/), a new free software
7 6
     CSS parser for Python. It is extensible, well-documented, well-tested
1  exyr/pages/2012/weasyprint-at-pyconfr.markdown
Source Rendered
... ...
@@ -1,7 +1,6 @@
1 1
 title: WeasyPrint at PyConFR 2012
2 2
 published: 2012-09-10
3 3
 tags: [kozea, weasyprint]
4  
-public: true
5 4
 summary: |
6 5
     I’ll be [giving a talk](http://www.pycon.fr/2012/schedule/presentation/16/)
7 6
     about WeasyPrint at PyConFR on Sunday.
1  exyr/pages/2012/weasyprint-is-bsd.markdown
Source Rendered
... ...
@@ -1,7 +1,6 @@
1 1
 title: WeasyPrint is now BSD-licensed
2 2
 published: 2012-03-22
3 3
 tags: [kozea, weasyprint]
4  
-public: true
5 4
 summary: |
6 5
     We just changed [WeasyPrint](http://weasyprint.org/)’s license from
7 6
     AGPL to BSD.
1  exyr/pages/about.markdown
Source Rendered
... ...
@@ -1,5 +1,4 @@
1 1
 title: About
2  
-public: true
3 2
 
4 3
 Hi, I’m Simon Sapin and this is my personal website. I’m a software engineer at
5 4
 [Kozea](http://kozea.fr), a member of [W3C’s CSS Working Group](
27  exyr/public_pages.py
... ...
@@ -1,27 +0,0 @@
1  
-from werkzeug import cached_property
2  
-from flaskext.flatpages import FlatPages
3  
-
4  
-
5  
-class PublicPages(FlatPages):
6  
-    """
7  
-    In debug mode, prefix the title of pages that do not have a true-ish 
8  
-    `public` metadata with 'DRAFT: '. Otherwise hide these pages.
9  
-    """
10  
-    
11  
-    @cached_property
12  
-    def _pages(self):
13  
-        # cached_property(f).func is f
14  
-        pages = FlatPages._pages.func(self)
15  
-        if self.app.debug:
16  
-            return pages
17  
-        else:
18  
-            return dict((path, p) for path, p in pages.iteritems() if p.public)
19  
-
20  
-    def _parse(self, *args, **kwargs):
21  
-        page = super(PublicPages, self)._parse(*args, **kwargs)
22  
-        page.public = page.meta.get('public', False)
23  
-        if not page.public:
24  
-            # Pages are required to have a title
25  
-            page.meta['title'] = 'DRAFT: ' + page.meta['title']
26  
-        return page
27  
-
11  exyr/templates/atom.xml
... ...
@@ -1,4 +1,4 @@
1  
-<?xml version="1.0" encoding="utf-8"?> 
  1
+<?xml version="1.0" encoding="utf-8"?>
2 2
 <feed xmlns="http://www.w3.org/2005/Atom">
3 3
 	<title>exyr.org</title>
4 4
 	<id>{{ url_for('home', _external=True) }}</id>
@@ -10,24 +10,23 @@
10 10
 		<name>Simon Sapin</name>
11 11
 	</author>
12 12
 	<rights type="html">
13  
-		&lt;p&gt;© Copyright {{ build_date.year }} by Simon Sapin.&lt;/p&gt;
  13
+		&lt;p&gt;© Copyright {{ today().year }} by Simon Sapin.&lt;/p&gt;
14 14
         &lt;p&gt;Content licensed under a Creative Commons
15 15
           Attribution-NonCommercial-ShareAlike License.&lt;/p&gt;
16 16
 	</rights>
17 17
 	{% for updated, article in articles %}
18  
-	<entry>
19  
-		<id>{{ url_for('page', path=article.path, _external=True) }}</id>
  18
+	<entry>{% set url = article.url(_external=True) %}
  19
+		<id>{{ url }}</id>
20 20
 		<title>{{ article.title }}</title>
21 21
 		<updated>{{ updated.strftime('%Y-%m-%dT%H:%M:%SZ') }}</updated>
22 22
 		<published>{{ article.published.strftime('%Y-%m-%dT%H:%M:%SZ') }}</published>
23 23
 		<author>
24 24
 			<name>Simon Sapin</name>
25 25
 		</author>
26  
-		<link href="{{ url_for('page', path=article.path, _external=True) }}" />
  26
+		<link href="{{ url }}" />
27 27
 		<content type="html">
28 28
 			{{ article.html }}
29 29
 		</content>
30 30
 	</entry>
31 31
 	{% endfor %}
32 32
 </feed>
33  
-
4  exyr/templates/flatpage.html
@@ -15,7 +15,7 @@
15 15
         {%- endfor %}
16 16
       </p>
17 17
     </header>
18  
-    {{ page }}
  18
+    {{ page.html|safe }}
19 19
   </article>
20 20
 {% else %}
21 21
   <header>
@@ -23,7 +23,7 @@
23 23
   </header>
24 24
   {% if page.html %}
25 25
   <section>
26  
-    {{ page }}
  26
+    {{ page.html|safe }}
27 27
   </section>
28 28
   {% endif %}
29 29
 {% endif %}
6  exyr/templates/layout.html
@@ -41,7 +41,7 @@
41 41
       <ul>
42 42
         <li><a href="{{ url_for('home') }}">Posts</a></li>
43 43
         <li><a href="{{ url_for('tags') }}">Tags</a></li>
44  
-        <li><a href="{{ url_for('page', path='about') }}">About</a></li>
  44
+        <li><a href="{{ url_for('about') }}">About</a></li>
45 45
         <li><a href="{{ url_for('feed') }}">Subscribe <img alt=""
46 46
           src="{{ url_for('static', filename='feed-icon.png') }}"></a></li>
47 47
       </ul>
@@ -54,8 +54,8 @@
54 54
 
55 55
   <footer>
56 56
     {#- See <rights/> in atom.xml too #}
57  
-    <p>© Copyright {{ build_date.year }} by
58  
-      <a href="{{ url_for('page', path='about') }}">Simon Sapin</a>.</p>
  57
+    <p>© Copyright {{ today().year }} by
  58
+      <a href="{{ url_for('about') }}">Simon Sapin</a>.</p>
59 59
     <p>Content licensed under <a rel=license
60 60
         href="http://creativecommons.org/licenses/by-nc-sa/3.0/">
61 61
           Creative Commons BY-NC-SA</a>.</p>
2  exyr/templates/style.css
@@ -164,7 +164,7 @@ hr:before {
164 164
     margin: 0 .5em;
165 165
 }
166 166
 
167  
-{{ pygments_style_defs('tango') }}
  167
+{{ pygments_css }}
168 168
 
169 169
 .codehilite, blockquote {
170 170
     background-color: #eee;
6  exyr/templates/utils.html
@@ -9,15 +9,13 @@
9 9
   <section class=article_list>
10 10
   {% for page in articles %}
11 11
     <article>
12  
-      <h3><a href="{{ url_for('page', path=page.path) }}">
  12
+      <h3><a href="{{ page.url() }}">
13 13
           {{ page.title }}</a></h3>
14 14
       <p>{{ html5_date(page.published) }}</p>
15 15
       {{ page.summary|markdown|safe }}
16  
-      <p><a href="{{ url_for('page', path=page.path) }}">Read more…</a></p>
  16
+      <p><a href="{{ page.url() }}">Read more…</a></p>
17 17
     </article>
18 18
   {% endfor %}
19 19
   </section>
20 20
 {% endif %}
21 21
 {% endmacro %}
22  
-
23  
-
3  setup.py
@@ -11,9 +11,10 @@
11 11
     license="BSD License",
12 12
     install_requires=[
13 13
         'Flask',
14  
-        'Flask-FlatPages',
15 14
         'Frozen-Flask>=0.9',
16 15
         'Flask-Script',
17 16
         'Pygments',
  17
+        'PyYAML',
  18
+        'Markdown',
18 19
     ],
19 20
 )

0 notes on commit d1cedc6

Please sign in to comment.
Something went wrong with that request. Please try again.