Add mdi, fake sessions, WIP db models (no migrations yet)

This commit is contained in:
xenia 2019-05-30 20:43:53 -04:00
parent d529552609
commit 4a519bbb85
17 changed files with 226 additions and 11 deletions

1
.gitignore vendored
View File

@ -8,3 +8,4 @@ __pycache__
.ycm_extra_conf.py .ycm_extra_conf.py
postgres/ postgres/
.envrc .envrc
.mypy_cache/

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "material-design-icons"]
path = material-design-icons
url = https://github.com/google/material-design-icons

View File

@ -1,4 +1,7 @@
.PHONY: check install .PHONY: setup check install
setup:
! [ -d wikilain/static/mdi ] && ln -s ../../material-design-icons/iconfont/ wikilain/static/mdi || true
check: check:
@mypy . || true @mypy . || true

View File

@ -12,6 +12,7 @@ mistune
mistune_contrib mistune_contrib
flask-sqlalchemy flask-sqlalchemy
flask-migrate flask-migrate
pygments
``` ```
``` ```

1
material-design-icons Submodule

@ -0,0 +1 @@
Subproject commit 224895a86501195e7a7ff3dde18e39f00b8e3d5a

View File

@ -15,7 +15,8 @@ setup(name='wikilain',
"mistune", "mistune",
"mistune_contrib", "mistune_contrib",
"flask-sqlalchemy", "flask-sqlalchemy",
"flask-migrate" "flask-migrate",
"pygments"
], ],
include_package_data=True, include_package_data=True,
entry_points={ entry_points={

View File

@ -1,15 +1,29 @@
from flask import Flask from flask import Flask, render_template, g
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate from .models import *
from .markdown import *
from .adapters.session_simple import session_provider
app = Flask(__name__) app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = 'sqlite:////tmp/test.db' app.config["SECRET_KEY"] = "changeme"
app.config["SQLALCHEMY_DATABASE_URI"] = 'postgresql://@/wikilain_dev'
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
from .models import db, migrate, User
db.init_app(app) db.init_app(app)
migrate.init_app(app, db) migrate.init_app(app, db)
app.register_blueprint(session_provider, url_prefix="/session")
@app.route("/") @app.route("/")
def main_page(): def main_page():
return "Hello and also world" 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

View File

@ -0,0 +1,49 @@
from flask import Blueprint, session, request, render_template, redirect, g, flash
import secrets
from ..models import User, db
__all__ = ["session_provider"]
session_provider = Blueprint("session_simple", __name__, template_folder='templates')
@session_provider.before_app_request
def get_session_user():
if session.get("username", None) is not None:
g.wl_user = User.query.filter_by(username=session["username"]).first()
else:
g.wl_user = None
@session_provider.route("/login", methods=["GET", "POST"])
def login():
old_csrf = session.get("_csrf", None)
csrf = secrets.token_urlsafe(32)
session["_csrf"] = csrf
if g.wl_user is not None:
return redirect("/", code=302)
if request.method == 'GET':
return render_template("login_form.html", csrf=csrf)
else:
if not request.form["username"]:
flash("Must provide username", "error")
return render_template("login_form.html", csrf=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.commit()
session["username"] = request.form["username"]
del session["_csrf"]
return redirect("/", code=302)
@session_provider.route("/logout", methods=["POST"])
def logout():
del session["username"]
del session["_csrf"]
return redirect("/", code=302)

View File

@ -0,0 +1,14 @@
{% extends 'base.html' %}
{% block title %}
Log In
{% endblock %}
{% block content %}
<h2>Log In</h2>
<form action="{{ url_for(request.endpoint) }}" method="POST">
<input type="hidden" style="display:none" name="_csrf" value="{{ csrf }}" />
<input type="text" name="username" placeholder="Username" />
<input type="submit" />
</form>
{% endblock %}

32
wikilain/markdown.py Normal file
View File

@ -0,0 +1,32 @@
from mistune import Markdown, Renderer, InlineLexer, BlockLexer, BlockGrammar
from mistune_contrib.highlight import HighlightMixin
from mistune_contrib.math import MathBlockMixin, MathInlineMixin, MathRendererMixin
from flask import Markup
class WlInlineLexer(InlineLexer, MathInlineMixin):
def __init__(self, *args, **kwargs):
super(WlInlineLexer, self).__init__(*args, **kwargs)
self.enable_math()
class WlBlockLexer(BlockLexer, MathBlockMixin):
def __init__(self, *args, **kwargs):
super(WlBlockLexer, self).__init__(*args, **kwargs)
self.enable_math()
class WlRenderer(Renderer, MathRendererMixin, HighlightMixin):
def __init__(self):
super(WlRenderer, self).__init__(escape=True, use_xhtml=True)
renderer = WlRenderer()
inline = WlInlineLexer(renderer)
block = WlBlockLexer(BlockGrammar())
markdown = Markdown(renderer=renderer, inline=inline, block=block)
__all__ = ["md_render"]
def md_render(*args, **kwargs):
return Markup(markdown(*args, **kwargs))

View File

@ -5,9 +5,44 @@ db = SQLAlchemy()
migrate = Migrate(None, db) migrate = Migrate(None, db)
class User(db.Model): class User(db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False) username = db.Column(db.String, unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False) email = db.Column(db.String, unique=True, nullable=False)
avatar = db.Column(db.String, nullable=True)
def __repr__(self): def __repr__(self):
return '<User %r>' % self.username return f"<User {self.username}>"
class Section(db.Model):
__tablename__ = "sections"
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String, unique=True, nullable=False)
articles = db.relationship("Article", backref="section", lazy=True)
def __repr__(self):
return f"<Section \"{self.title}\">"
DEFAULT_SECTION = 1
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)
revisions = db.relationship("ArticleRevision", backref="article", lazy=True)
def __repr__(self):
return f"<Article \"{self.title}\">"
class ArticleRevision(db.Model):
__tablename__ = "articlerevisions"
id = db.Column(db.Integer, primary_key=True)
article_id = db.Column(db.Integer, db.ForeignKey("articles.id"), nullable=False)
date = db.Column(db.DateTime, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False)
user = db.relationship("User", lazy=True)
content = db.Column(db.Text, nullable=False)
def __repr__(self):
return f"<ArticleRevision at {self.date} by {self.user}>"

1
wikilain/static/mdi Symbolic link
View File

@ -0,0 +1 @@
../../material-design-icons/iconfont/

View File

@ -0,0 +1,5 @@
body {
height: 100%;
margin: 0;
padding: 0;
}

View File

@ -0,0 +1,10 @@
{% extends 'base.html' %}
{% block title %}
{{ article_title }}
{% endblock %}
{% block content %}
<h2>{{ article_title }}</h2>
{{ article_content }}
{% endblock %}

View File

@ -0,0 +1,35 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>{% block title %}{% endblock %} - WikiLain</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}" />
<link rel="stylesheet" href="{{ url_for('static', filename='mdi/material-icons.css') }}" />
</head>
<body>
<nav>
<h1><a href="/">WikiLain</a></h1>
<ul>
<li>
<form action="/search" action="POST">
<input type="text" name="q" placeholder="Search" />
</form>
</li>
<li><a href="/sections">Sections</a></li>
<li><a href="/user/login">Log In</a></li>
</ul>
</nav>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<section class="flashed-messages">
{% for category, message in messages %}
<div class="flash flash-{{ category }}">{{ message }}</div>
{% endfor %}
</section>
{% endif %}
{% endwith %}
<main>
{% block content %}{% endblock %}
</main>
</body>
</html>

View File

@ -0,0 +1,10 @@
{% extends 'base.html' %}
{% block title %}
Main Page
{% endblock %}
{% block content %}
<h2>Welcome to WikiLain</h2>
<p>Sample and also text</p>
{% endblock %}