Add mdi, fake sessions, WIP db models (no migrations yet)
This commit is contained in:
parent
d529552609
commit
4a519bbb85
|
@ -8,3 +8,4 @@ __pycache__
|
||||||
.ycm_extra_conf.py
|
.ycm_extra_conf.py
|
||||||
postgres/
|
postgres/
|
||||||
.envrc
|
.envrc
|
||||||
|
.mypy_cache/
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "material-design-icons"]
|
||||||
|
path = material-design-icons
|
||||||
|
url = https://github.com/google/material-design-icons
|
5
Makefile
5
Makefile
|
@ -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
|
||||||
|
|
|
@ -12,6 +12,7 @@ mistune
|
||||||
mistune_contrib
|
mistune_contrib
|
||||||
flask-sqlalchemy
|
flask-sqlalchemy
|
||||||
flask-migrate
|
flask-migrate
|
||||||
|
pygments
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 224895a86501195e7a7ff3dde18e39f00b8e3d5a
|
3
setup.py
3
setup.py
|
@ -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={
|
||||||
|
|
|
@ -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$"))
|
||||||
|
|
||||||
|
|
|
@ -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)
|
|
@ -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 %}
|
|
@ -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))
|
|
@ -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}>"
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
../../material-design-icons/iconfont/
|
|
@ -0,0 +1,5 @@
|
||||||
|
body {
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block title %}
|
||||||
|
{{ article_title }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>{{ article_title }}</h2>
|
||||||
|
{{ article_content }}
|
||||||
|
{% endblock %}
|
|
@ -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>
|
|
@ -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 %}
|
Loading…
Reference in New Issue