From 924913ce6c3556267c25e61c2a14e1b6f528febc Mon Sep 17 00:00:00 2001 From: Miguel Grinberg Date: Tue, 18 Jul 2017 07:55:41 -0700 Subject: [PATCH 01/20] Chapter 12: Show followed blog posts in home page (12d) --- app/main/views.py | 29 ++++++++++++++++++++++++++--- app/static/styles.css | 7 +++++++ app/templates/index.html | 10 +++++++++- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/app/main/views.py b/app/main/views.py index edbed206b..c88374b4b 100644 --- a/app/main/views.py +++ b/app/main/views.py @@ -1,5 +1,5 @@ from flask import render_template, redirect, url_for, abort, flash, request,\ - current_app + current_app, make_response from flask_login import login_required, current_user from . import main from .forms import EditProfileForm, EditProfileAdminForm, PostForm @@ -18,12 +18,19 @@ def index(): db.session.commit() return redirect(url_for('.index')) page = request.args.get('page', 1, type=int) - pagination = Post.query.order_by(Post.timestamp.desc()).paginate( + show_followed = False + if current_user.is_authenticated: + show_followed = bool(request.cookies.get('show_followed', '')) + if show_followed: + query = current_user.followed_posts + else: + query = Post.query + pagination = query.order_by(Post.timestamp.desc()).paginate( page, per_page=current_app.config['FLASKY_POSTS_PER_PAGE'], error_out=False) posts = pagination.items return render_template('index.html', form=form, posts=posts, - pagination=pagination) + show_followed=show_followed, pagination=pagination) @main.route('/user/') @@ -174,3 +181,19 @@ def followed_by(username): return render_template('followers.html', user=user, title="Followed by", endpoint='.followed_by', pagination=pagination, follows=follows) + + +@main.route('/all') +@login_required +def show_all(): + resp = make_response(redirect(url_for('.index'))) + resp.set_cookie('show_followed', '', max_age=30*24*60*60) + return resp + + +@main.route('/followed') +@login_required +def show_followed(): + resp = make_response(redirect(url_for('.index'))) + resp.set_cookie('show_followed', '1', max_age=30*24*60*60) + return resp diff --git a/app/static/styles.css b/app/static/styles.css index 2b305a424..666d1ff11 100644 --- a/app/static/styles.css +++ b/app/static/styles.css @@ -5,12 +5,19 @@ min-height: 260px; margin-left: 280px; } +div.post-tabs { + margin-top: 16px; +} ul.posts { list-style-type: none; padding: 0px; margin: 16px 0px 0px 0px; border-top: 1px solid #e0e0e0; } +div.post-tabs ul.posts { + margin: 0px; + border-top: none; +} ul.posts li.post { padding: 8px; border-bottom: 1px solid #e0e0e0; diff --git a/app/templates/index.html b/app/templates/index.html index c1334588e..32c76e0f7 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -13,7 +13,15 @@

Hello, {% if current_user.is_authenticated %}{{ current_user.username }}{% e {{ wtf.quick_form(form) }} {% endif %} -{% include '_posts.html' %} +
+ + {% include '_posts.html' %} +
{% if pagination %} diff --git a/app/templates/post.html b/app/templates/post.html index adbe2f30b..c52c053c3 100644 --- a/app/templates/post.html +++ b/app/templates/post.html @@ -1,8 +1,21 @@ {% extends "base.html" %} +{% import "bootstrap/wtf.html" as wtf %} {% import "_macros.html" as macros %} {% block title %}Flasky - Post{% endblock %} {% block page_content %} {% include '_posts.html' %} +

Comments

+{% if current_user.can(Permission.COMMENT) %} +
+ {{ wtf.quick_form(form) }} +
+{% endif %} +{% include '_comments.html' %} +{% if pagination %} + +{% endif %} {% endblock %} diff --git a/app/templates/user.html b/app/templates/user.html index abd28709f..80e865e4b 100644 --- a/app/templates/user.html +++ b/app/templates/user.html @@ -21,7 +21,7 @@

{{ user.username }}

{% endif %} {% if user.about_me %}

{{ user.about_me }}

{% endif %}

Member since {{ moment(user.member_since).format('L') }}. Last seen {{ moment(user.last_seen).fromNow() }}.

-

{{ user.posts.count() }} blog posts.

+

{{ user.posts.count() }} blog posts. {{ user.comments.count() }} comments.

{% if current_user.can(Permission.FOLLOW) and user != current_user %} {% if not current_user.is_following(user) %} diff --git a/config.py b/config.py index 1ac398f5f..ddce6275c 100644 --- a/config.py +++ b/config.py @@ -16,6 +16,7 @@ class Config: SQLALCHEMY_TRACK_MODIFICATIONS = False FLASKY_POSTS_PER_PAGE = 20 FLASKY_FOLLOWERS_PER_PAGE = 50 + FLASKY_COMMENTS_PER_PAGE = 30 @staticmethod def init_app(app): diff --git a/flasky.py b/flasky.py index f2c7411e7..e0d08657c 100644 --- a/flasky.py +++ b/flasky.py @@ -2,7 +2,7 @@ import click from flask_migrate import Migrate from app import create_app, db -from app.models import User, Follow, Role, Permission, Post +from app.models import User, Follow, Role, Permission, Post, Comment app = create_app(os.getenv('FLASK_CONFIG') or 'default') migrate = Migrate(app, db) @@ -11,7 +11,7 @@ @app.shell_context_processor def make_shell_context(): return dict(db=db, User=User, Follow=Follow, Role=Role, - Permission=Permission, Post=Post) + Permission=Permission, Post=Post, Comment=Comment) @app.cli.command() diff --git a/migrations/versions/51f5ccfba190_comments.py b/migrations/versions/51f5ccfba190_comments.py new file mode 100644 index 000000000..a4b8586b4 --- /dev/null +++ b/migrations/versions/51f5ccfba190_comments.py @@ -0,0 +1,39 @@ +"""comments + +Revision ID: 51f5ccfba190 +Revises: 2356a38169ea +Create Date: 2014-01-01 12:08:43.287523 + +""" + +# revision identifiers, used by Alembic. +revision = '51f5ccfba190' +down_revision = '2356a38169ea' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + ### commands auto generated by Alembic - please adjust! ### + op.create_table('comments', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('body', sa.Text(), nullable=True), + sa.Column('body_html', sa.Text(), nullable=True), + sa.Column('timestamp', sa.DateTime(), nullable=True), + sa.Column('disabled', sa.Boolean(), nullable=True), + sa.Column('author_id', sa.Integer(), nullable=True), + sa.Column('post_id', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['author_id'], ['users.id'], ), + sa.ForeignKeyConstraint(['post_id'], ['posts.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_index('ix_comments_timestamp', 'comments', ['timestamp'], unique=False) + ### end Alembic commands ### + + +def downgrade(): + ### commands auto generated by Alembic - please adjust! ### + op.drop_index('ix_comments_timestamp', 'comments') + op.drop_table('comments') + ### end Alembic commands ### From b12d867ab6f0ae958fd40c4a6a6426c29ba18853 Mon Sep 17 00:00:00 2001 From: Miguel Grinberg Date: Tue, 18 Jul 2017 07:55:44 -0700 Subject: [PATCH 04/20] Chapter 13: Comment moderation (13b) --- app/main/views.py | 37 ++++++++++++++++++++++++++++++++++++ app/templates/_comments.html | 21 ++++++++++++++++---- app/templates/base.html | 3 +++ app/templates/moderate.html | 17 +++++++++++++++++ 4 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 app/templates/moderate.html diff --git a/app/main/views.py b/app/main/views.py index d598ff04a..a79b85a52 100644 --- a/app/main/views.py +++ b/app/main/views.py @@ -216,3 +216,40 @@ def show_followed(): resp = make_response(redirect(url_for('.index'))) resp.set_cookie('show_followed', '1', max_age=30*24*60*60) return resp + + +@main.route('/moderate') +@login_required +@permission_required(Permission.MODERATE) +def moderate(): + page = request.args.get('page', 1, type=int) + pagination = Comment.query.order_by(Comment.timestamp.desc()).paginate( + page, per_page=current_app.config['FLASKY_COMMENTS_PER_PAGE'], + error_out=False) + comments = pagination.items + return render_template('moderate.html', comments=comments, + pagination=pagination, page=page) + + +@main.route('/moderate/enable/') +@login_required +@permission_required(Permission.MODERATE) +def moderate_enable(id): + comment = Comment.query.get_or_404(id) + comment.disabled = False + db.session.add(comment) + db.session.commit() + return redirect(url_for('.moderate', + page=request.args.get('page', 1, type=int))) + + +@main.route('/moderate/disable/') +@login_required +@permission_required(Permission.MODERATE) +def moderate_disable(id): + comment = Comment.query.get_or_404(id) + comment.disabled = True + db.session.add(comment) + db.session.commit() + return redirect(url_for('.moderate', + page=request.args.get('page', 1, type=int))) diff --git a/app/templates/_comments.html b/app/templates/_comments.html index 4f5cad575..aa278f724 100644 --- a/app/templates/_comments.html +++ b/app/templates/_comments.html @@ -10,12 +10,25 @@

{{ moment(comment.timestamp).fromNow() }}
- {% if comment.body_html %} - {{ comment.body_html | safe }} - {% else %} - {{ comment.body }} + {% if comment.disabled %} +

This comment has been disabled by a moderator.

+ {% endif %} + {% if moderate or not comment.disabled %} + {% if comment.body_html %} + {{ comment.body_html | safe }} + {% else %} + {{ comment.body }} + {% endif %} {% endif %}
+ {% if moderate %} +
+ {% if comment.disabled %} + Enable + {% else %} + Disable + {% endif %} + {% endif %} {% endfor %} diff --git a/app/templates/base.html b/app/templates/base.html index edd5640f2..fb0139cb1 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -29,6 +29,9 @@ {% endif %}