Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 197 lines (163 sloc) 6.657 kb
eeb84df Initial commit
Orf authored
1 from functools import wraps
d68b733 Support added for posts with the same title but different slugs. Require...
Orf authored
2 from flask import render_template, request, Flask, flash, redirect, url_for, abort, jsonify, Response, make_response
eeb84df Initial commit
Orf authored
3 import re
4 from unicodedata import normalize
5 from flaskext.sqlalchemy import SQLAlchemy
6 import datetime
34c6982 Removed models.py and moved the model into simple.py.
Orf authored
7 import markdown
101410f @hellerbarde replace auth storage (prev: unsalted md5) with built in Werkzeug functio...
hellerbarde authored
8 from werkzeug.security import check_password_hash
eeb84df Initial commit
Orf authored
9
10 app = Flask(__name__)
11 app.config.from_object('settings')
12 db = SQLAlchemy(app)
13
14 _punct_re = re.compile(r'[\t !"#$%&\'()*\-/<=>?@\[\\\]^_`{|},.]+')
15
34c6982 Removed models.py and moved the model into simple.py.
Orf authored
16 class Post(db.Model):
17 __tablename__ = "posts"
18 id = db.Column(db.Integer, primary_key=True)
d68b733 Support added for posts with the same title but different slugs. Require...
Orf authored
19 title = db.Column(db.String())
34c6982 Removed models.py and moved the model into simple.py.
Orf authored
20 slug = db.Column(db.String(), unique=True)
21 text = db.Column(db.String(), default="")
22 draft = db.Column(db.Boolean(), index=True, default=True)
808529c Post views are now tracked, and when writing a post it is auto saved eve...
Orf authored
23 views = db.Column(db.Integer(), default=0)
34c6982 Removed models.py and moved the model into simple.py.
Orf authored
24 created_at = db.Column(db.DateTime, index=True)
25 updated_at = db.Column(db.DateTime)
26
27 def render_content(self):
28 return markdown.Markdown(extensions=['fenced_code'], output_format="html5", safe_mode=True).convert(self.text)
29
3797614 Now handles if the db has been created before
Orf authored
30 try:
31 db.create_all()
32 except Exception:
33 pass
b1a4d52 Database is auto created now. Also config fixes
Orf authored
34
eeb84df Initial commit
Orf authored
35 def requires_authentication(f):
36 @wraps(f)
37 def _auth_decorator(*args, **kwargs):
38 auth = request.authorization
39 if not auth or not (auth.username == app.config["ADMIN_USERNAME"]
101410f @hellerbarde replace auth storage (prev: unsalted md5) with built in Werkzeug functio...
hellerbarde authored
40 and check_password_hash(app.config["ADMIN_PASSWORD"], auth.password)):
eeb84df Initial commit
Orf authored
41 return Response("Could not authenticate you", 401, {"WWW-Authenticate":'Basic realm="Login Required"'})
42 return f(*args, **kwargs)
43
44 return _auth_decorator
45
46 @app.route("/")
47 def index():
48 page = request.args.get("page", 0, type=int)
0148396 Posts are now displayed descending.
Orf authored
49 posts_master = db.session.query(Post).filter_by(draft=False).order_by(Post.created_at.desc())
eeb84df Initial commit
Orf authored
50 posts_count = posts_master.count()
51
52 posts = posts_master.limit(app.config["POSTS_PER_PAGE"]).offset(page*app.config["POSTS_PER_PAGE"]).all()
53 is_more = posts_count > ((page*app.config["POSTS_PER_PAGE"]) + app.config["POSTS_PER_PAGE"])
54
55 return render_template("index.html", posts=posts, now=datetime.datetime.now(),
56 is_more=is_more, current_page=page)
57
58 @app.route("/<int:post_id>")
59 def view_post(post_id):
60 try:
d49de45 And removed the import models. Oops.
Orf authored
61 post = db.session.query(Post).filter_by(id=post_id, draft=False).one()
eeb84df Initial commit
Orf authored
62 except Exception:
63 return abort(404)
64
808529c Post views are now tracked, and when writing a post it is auto saved eve...
Orf authored
65 db.session.query(Post).filter_by(id=post_id).update({Post.views:Post.views+1})
66 db.session.commit()
67
eeb84df Initial commit
Orf authored
68 return render_template("view.html", post=post)
69
70 @app.route("/<slug>")
71 def view_post_slug(slug):
72 try:
d49de45 And removed the import models. Oops.
Orf authored
73 post = db.session.query(Post).filter_by(slug=slug,draft=False).one()
eeb84df Initial commit
Orf authored
74 except Exception:
75 return abort(404)
76
808529c Post views are now tracked, and when writing a post it is auto saved eve...
Orf authored
77 db.session.query(Post).filter_by(slug=slug).update({Post.views:Post.views+1})
78 db.session.commit()
eeb84df Initial commit
Orf authored
79
808529c Post views are now tracked, and when writing a post it is auto saved eve...
Orf authored
80 pid = request.args.get("pid", "0")
eeb84df Initial commit
Orf authored
81 return render_template("view.html", post=post, pid=pid)
82
83 @app.route("/new", methods=["POST", "GET"])
84 @requires_authentication
85 def new_post():
d49de45 And removed the import models. Oops.
Orf authored
86 post = Post()
eeb84df Initial commit
Orf authored
87 post.title = request.form.get("title","untitled")
88 post.slug = slugify(post.title)
89 post.created_at = datetime.datetime.now()
90 post.updated_at = datetime.datetime.now()
91
92 db.session.add(post)
93 db.session.commit()
94
95 return redirect(url_for("edit", id=post.id))
96
97 @app.route("/edit/<int:id>", methods=["GET","POST"])
98 @requires_authentication
99 def edit(id):
100 try:
d49de45 And removed the import models. Oops.
Orf authored
101 post = db.session.query(Post).filter_by(id=id).one()
eeb84df Initial commit
Orf authored
102 except Exception:
103 return abort(404)
104
105 if request.method == "GET":
106 return render_template("edit.html", post=post)
107 else:
d68b733 Support added for posts with the same title but different slugs. Require...
Orf authored
108 if post.title != request.form.get("post_title", ""):
109 post.title = request.form.get("post_title","")
110 post.slug = slugify(post.title)
111 post.text = request.form.get("post_content","")
eeb84df Initial commit
Orf authored
112 post.updated_at = datetime.datetime.now()
113
114 if any(request.form.getlist("post_draft", type=int)):
115 post.draft = True
116 else:
117 post.draft = False
118
119 db.session.add(post)
120 db.session.commit()
121 return redirect(url_for("edit", id=id))
122
123 @app.route("/delete/<int:id>", methods=["GET","POST"])
124 @requires_authentication
125 def delete(id):
126 try:
d49de45 And removed the import models. Oops.
Orf authored
127 post = db.session.query(Post).filter_by(id=id).one()
eeb84df Initial commit
Orf authored
128 except Exception:
129 flash("Error deleting post ID %s"%id, category="error")
130 else:
131 db.session.delete(post)
132 db.session.commit()
133
134 return redirect(request.args.get("next","") or request.referrer or url_for('index'))
135
136 @app.route("/admin", methods=["GET", "POST"])
137 @requires_authentication
138 def admin():
d49de45 And removed the import models. Oops.
Orf authored
139 drafts = db.session.query(Post).filter_by(draft=True)\
140 .order_by(Post.created_at.desc()).all()
141 posts = db.session.query(Post).filter_by(draft=False)\
142 .order_by(Post.created_at.desc()).all()
eeb84df Initial commit
Orf authored
143 return render_template("admin.html", drafts=drafts, posts=posts)
144
145 @app.route("/admin/save/<int:id>", methods=["POST"])
146 @requires_authentication
147 def save_post(id):
148 try:
d49de45 And removed the import models. Oops.
Orf authored
149 post = db.session.query(Post).filter_by(id=id).one()
eeb84df Initial commit
Orf authored
150 except Exception:
151 return abort(404)
d68b733 Support added for posts with the same title but different slugs. Require...
Orf authored
152 if post.title != request.form.get("title", ""):
153 post.title = request.form.get("title","")
154 post.slug = slugify(post.title)
eeb84df Initial commit
Orf authored
155 post.text = request.form.get("content", "")
156 post.updated_at = datetime.datetime.now()
157 db.session.add(post)
158 db.session.commit()
159 return jsonify(success=True)
160
161 @app.route("/preview/<int:id>")
162 @requires_authentication
163 def preview(id):
164 try:
d49de45 And removed the import models. Oops.
Orf authored
165 post = db.session.query(Post).filter_by(id=id).one()
eeb84df Initial commit
Orf authored
166 except Exception:
167 return abort(404)
168
169 return render_template("post_preview.html", post=post)
170
3c6bd1d Added RSS feed generation
Orf authored
171 @app.route("/posts.rss")
172 def feed():
b579a80 Feed generation now works. Was a bit too hasty to push.
Orf authored
173 posts = db.session.query(Post).filter_by(draft=False).order_by(Post.created_at.desc()).limit(10).all()
6ba8e1c @rmanocha Moving the feed generation to a template
rmanocha authored
174
d68b733 Support added for posts with the same title but different slugs. Require...
Orf authored
175 r = make_response(render_template('index.xml', posts=posts))
176 r.mimetype = "application/xml"
177 return r
3c6bd1d Added RSS feed generation
Orf authored
178
eeb84df Initial commit
Orf authored
179 def slugify(text, delim=u'-'):
180 """Generates an slightly worse ASCII-only slug."""
181 result = []
182 for word in _punct_re.split(text.lower()):
183 word = normalize('NFKD', unicode(word)).encode('ascii', 'ignore')
184 if word:
185 result.append(word)
d68b733 Support added for posts with the same title but different slugs. Require...
Orf authored
186 slug = unicode(delim.join(result))
187 # This could have issues if a post is marked as draft, then live, then draft, then live and there are > 1 posts
188 # with the same slug. Oh well.
189 _c = db.session.query(Post).filter_by(slug=slug).count()
190 if _c > 0:
b22b7d2 Added twitter and google plus profile options.
Orf authored
191 return "%s%s%s"%(slug, delim, _c)
d68b733 Support added for posts with the same title but different slugs. Require...
Orf authored
192 else:
193 return slug
eeb84df Initial commit
Orf authored
194
195 if __name__ == "__main__":
101410f @hellerbarde replace auth storage (prev: unsalted md5) with built in Werkzeug functio...
hellerbarde authored
196 app.run()
Something went wrong with that request. Please try again.