Browse Source

Chapter 11: Rich text server side handling with Markdown and Bleach (11f)

T. Meissner 8 months ago
parent
commit
44b9042b9f
4 changed files with 53 additions and 1 deletions
  1. 15
    0
      app/models.py
  2. 7
    1
      app/templates/_posts.html
  3. 28
    0
      migrations/versions/53a175284ebe_.py
  4. 3
    0
      requirements/common.txt

+ 15
- 0
app/models.py View File

@@ -2,6 +2,8 @@ from datetime import datetime
2 2
 import hashlib
3 3
 from werkzeug.security import generate_password_hash, check_password_hash
4 4
 from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
5
+from markdown import markdown
6
+import bleach
5 7
 from itsdangerous import BadSignature
6 8
 from flask import current_app
7 9
 from flask_login import UserMixin, AnonymousUserMixin
@@ -191,9 +193,22 @@ class Post(db.Model):
191 193
     __tablename__ = 'posts'
192 194
     id = db.Column(db.Integer, primary_key=True)
193 195
     body = db.Column(db.Text)
196
+    body_html = db.Column(db.Text)
194 197
     timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
195 198
     author_id = db.Column(db.Integer, db.ForeignKey('users.id'))
196 199
 
200
+    @staticmethod
201
+    def on_changed_body(target, value, oldvalue, initiator):
202
+        allowed_tags = ['a', 'abbr', 'acronym', 'b', 'blockquote', 'code',
203
+                        'em', 'i', 'li', 'ol', 'pre', 'strong', 'ul',
204
+                        'h1', 'h2', 'h3', 'p']
205
+        md = markdown(value, output_format='html')
206
+        clean_md = bleach.clean(md, tags=allowed_tags, strip=True)
207
+        target.body_html = bleach.linkify(clean_md)
208
+
209
+
210
+db.event.listen(Post.body, 'set', Post.on_changed_body)
211
+
197 212
 
198 213
 class AnonymousUser(AnonymousUserMixin):
199 214
     def can(self, perm):

+ 7
- 1
app/templates/_posts.html View File

@@ -9,7 +9,13 @@
9 9
         <div class="post-content">
10 10
             <div class="post-date">{{ moment(post.timestamp).fromNow() }}</div>
11 11
             <div class="post-author"><a href="{{ url_for('.user', username=post.author.username) }}">{{ post.author.username }}</a></div>
12
-            <div class="post-body">{{ post.body }}</div>
12
+            <div class="post-body">
13
+            {% if post.body_html %}
14
+                {{ post.body_html | safe }}
15
+            {% else %}
16
+                {{ post.body }}
17
+            {% endif %}
18
+            </div>
13 19
         </div>
14 20
     </li>
15 21
     {% endfor %}

+ 28
- 0
migrations/versions/53a175284ebe_.py View File

@@ -0,0 +1,28 @@
1
+"""empty message
2
+
3
+Revision ID: 53a175284ebe
4
+Revises: c16f3d665442
5
+Create Date: 2018-11-18 01:12:11.693237
6
+
7
+"""
8
+from alembic import op
9
+import sqlalchemy as sa
10
+
11
+
12
+# revision identifiers, used by Alembic.
13
+revision = '53a175284ebe'
14
+down_revision = 'c16f3d665442'
15
+branch_labels = None
16
+depends_on = None
17
+
18
+
19
+def upgrade():
20
+    # ### commands auto generated by Alembic - please adjust! ###
21
+    op.add_column('posts', sa.Column('body_html', sa.Text(), nullable=True))
22
+    # ### end Alembic commands ###
23
+
24
+
25
+def downgrade():
26
+    # ### commands auto generated by Alembic - please adjust! ###
27
+    op.drop_column('posts', 'body_html')
28
+    # ### end Alembic commands ###

+ 3
- 0
requirements/common.txt View File

@@ -1,4 +1,5 @@
1 1
 alembic==1.0.1
2
+bleach==3.0.2
2 3
 blinker==1.4
3 4
 Click==7.0
4 5
 dominate==2.3.4
@@ -14,11 +15,13 @@ Flask-WTF==0.14.2
14 15
 itsdangerous==0.24
15 16
 Jinja2==2.10
16 17
 Mako==1.0.7
18
+Markdown==3.0.1
17 19
 MarkupSafe==1.0
18 20
 python-dateutil==2.7.3
19 21
 python-editor==1.0.3
20 22
 six==1.11.0
21 23
 SQLAlchemy==1.2.12
22 24
 visitor==0.1.3
25
+webencodings==0.5.1
23 26
 Werkzeug==0.14.1
24 27
 WTForms==2.2.1