Add the ability to create and view articles

This commit is contained in:
xenia 2019-06-01 19:23:07 -04:00
parent 549e15add0
commit 55514664f0
9 changed files with 128 additions and 28 deletions

View File

@ -1,9 +1,9 @@
from flask import Flask, render_template, g
from .models import *
from .markdown import *
from .models import db, migrate
from .blueprints.session_simple import session_provider
from .blueprints.user_default import user_provider
from .blueprints.article_default import article_provider
app = Flask(__name__)
app.config["SECRET_KEY"] = "changeme"
@ -14,18 +14,11 @@ db.init_app(app)
migrate.init_app(app, db)
app.register_blueprint(session_provider, url_prefix="/session")
app.register_blueprint(user_provider, url_prefix="/users")
app.register_blueprint(user_provider, url_prefix="/user")
app.register_blueprint(article_provider, url_prefix="/article")
@app.route("/")
def main_page():
print(g.wl_user)
return render_template('index.html')
@app.route("/test")
def test_article():
return render_template(
'article.html', article_title="Sample Text",
article_content=md_render("# sample\n\nmeme and also\n\n\n---\nmeme $2+2$"))

View File

@ -0,0 +1,77 @@
from flask import Blueprint, render_template, g, session, redirect, request, url_for, flash
import secrets
from datetime import datetime
import re
from ..models import *
from ..markdown import *
article_provider = Blueprint("article", __name__, template_folder='templates')
blacklisted_titles = ["new", "by-id"]
allowed_titles = re.compile(r"^[a-zA-Z0-9 \(\)\.\,:;]+$")
def urlify(title):
return title[0].upper() + title[1:].replace(" ", "_")
def unurlify(title):
return title.replace("_", " ").lower()
@article_provider.route("/<title>")
def show(title):
real_title = unurlify(title)
print(real_title)
article = Article.query.filter_by(title=real_title).first_or_404()
revision = ArticleRevision.query.filter_by(article_id=article.id).order_by(
ArticleRevision.date.desc()).first()
toc, content = md_render(revision.content)
return render_template(
'article.html', article_title=article.title,
article_content=content, article_toc=toc)
@article_provider.route("/new", methods=["GET", "POST"])
def new():
if g.wl_user is None:
return redirect(url_for("session.login"), code=302)
old_csrf = session.get("csrf", None)
csrf = secrets.token_urlsafe(16)
session["csrf"] = csrf
if request.method == "GET":
return render_template("article_new.html", csrf=csrf, article_title="", article_content="")
else:
article_title = request.form["article_title"].strip().lower()
article_content = request.form["article_content"]
if request.form["csrf"] != old_csrf:
flash("Error creating article", "error");
return render_template("article_new.html",
csrf=csrf, article_title=article_title,
article_content=article_content)
if (len(article_title) == 0
or article_title in blacklisted_titles
or not allowed_titles.match(article_title)):
flash("Invalid title", "error")
return render_template("article_new.html",
csrf=csrf, article_title=article_title,
article_content=article_content)
if Article.query.filter_by(title=article_title).first() is not None:
flash("Article already exists", "error")
return render_template("article_new.html",
csrf=csrf, article_title=article_title,
article_content=article_content)
article = Article(title=article_title)
article.revisions.append(ArticleRevision(
date=datetime.now(),
user=g.wl_user,
changelog="Created article " + article_title,
content=article_content))
db.session.add(article)
db.session.commit()
session.pop("csrf", None)
return redirect(url_for(".show", title=urlify(article_title)), code=302)

View File

@ -17,12 +17,16 @@ def get_session_user():
@session_provider.route("/login", methods=["GET", "POST"])
def login():
old_csrf = session.get("_csrf", None)
old_csrf = session.get("csrf", None)
csrf = secrets.token_urlsafe(32)
session["_csrf"] = csrf
session["csrf"] = csrf
redir = request.args.get("redirect", session.get("redirect", "/"))
if g.wl_user is not None:
return redirect("/", code=302)
session.pop("redirect", None)
return redirect(redir, code=302)
session["redirect"] = redir
if request.method == 'GET':
return render_template("login_form.html", csrf=csrf)
@ -30,20 +34,22 @@ def login():
if not request.form["username"]:
flash("Must provide username", "error")
return render_template("login_form.html", csrf=csrf)
if old_csrf != request.form["_csrf"]:
if old_csrf != request.form["csrf"]:
flash("Invalid request", "error")
return render_template("login_form.html", csrf=csrf)
user = User.query.filter_by(username=request.form["username"]).first()
if not user:
db.session.add(User(username=request.form["username"], email=secrets.token_hex(8) + "@example.com"))
db.session.add(User(username=request.form["username"],
email=secrets.token_hex(8) + "@example.com"))
db.session.commit()
session["username"] = request.form["username"]
session.pop("_csrf", None)
return redirect("/", code=302)
session.pop("csrf", None)
session.pop("redirect", None)
return redirect(redir, code=302)
@session_provider.route("/logout", methods=["POST"])
def logout():
session.pop("username", None)
session.pop("_csrf", None)
return redirect("/", code=302)
session.pop("csrf", None)
return redirect(request.form.get("redirect", "/"), code=302)

View File

@ -6,5 +6,6 @@
{% block content %}
<h1>{{ article_title }}</h1>
{{ article_toc }}
{{ article_content }}
{% endblock %}

View File

@ -0,0 +1,15 @@
{% extends 'base.html' %}
{% block title %}
New Article
{% endblock %}
{% block content %}
<h1>New Article</h1>
<form action="{{ url_for('article.new') }}" method="POST">
<input type="hidden" style="display:none" name="csrf" value="{{ csrf }}" />
<input type="text" name="article_title" placeholder="Title" value="{{ article_title }}" />
<textarea name="article_content" placeholder="Content">{{article_content}}</textarea>
<button type="submit">Create</button>
</form>
{% endblock %}

View File

@ -7,8 +7,9 @@ Log In
{% block content %}
<h2>Log In</h2>
<form action="{{ url_for(".login") }}" method="POST">
<input type="hidden" style="display:none" name="_csrf" value="{{ csrf }}" />
<input type="text" name="username" placeholder="Username" />
<input type="submit" />
<input type="hidden" style="display:none" name="csrf" value="{{ csrf }}" />
<input type="text" name="username" placeholder="Username" autocomplete="off" />
<input type="password" name="password" placeholder="Password" />
<button type="submit">Log In</button>
</form>
{% endblock %}

View File

@ -32,5 +32,5 @@ __all__ = ["md_render"]
def md_render(*args, **kwargs):
renderer.reset_toc()
results = markdown(*args, **kwargs)
print(renderer.toc_tree)
return Markup(renderer.render_toc(level=3) + results)
toc = renderer.render_toc(level=3) if len(renderer.toc_tree) > 0 else ""
return Markup(toc), Markup(results)

View File

@ -29,7 +29,7 @@ class Article(db.Model):
__tablename__ = "articles"
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String, unique=True, nullable=False)
section_id = db.Column(db.Integer, db.ForeignKey("sections.id"), nullable=False)
section_id = db.Column(db.Integer, db.ForeignKey("sections.id"), nullable=True)
revisions = db.relationship("ArticleRevision", backref="article", lazy=True)
def __repr__(self):

View File

@ -11,16 +11,23 @@
<h1><a href="/">WikiLain</a></h1>
<ul>
<li>
<form action="/search" action="POST">
<form action="/search" method="POST">
<button type="submit"></button>
<input type="text" name="q" placeholder="Search" />
</form>
</li>
<li><a href="/sections">Sections</a></li>
{% if not g.wl_user %}
<li><a href="{{ url_for("session.login") }}">Log In</a></li>
<li>
<a href="{{ url_for("session.login") }}?redirect={{ request.path }}">
Log In
</a>
</li>
{% else %}
<li>
<form action="{{ url_for("session.logout") }}" method="POST">
<input type="hidden" style="display:none" name="redirect"
value="{{ request.path }}" />
<button type="submit">Log Out</button>
</form>
</li>