203 lines
7.2 KiB
Python
203 lines
7.2 KiB
Python
|
|
|
|
import datetime
|
|
import re
|
|
import sys
|
|
from typing import Mapping
|
|
from flask import Blueprint, abort, flash, g, jsonify, redirect, render_template, request
|
|
from flask_login import current_user, login_required
|
|
from sqlalchemy import select, insert
|
|
from suou import want_isodate
|
|
|
|
from salvi.i18n import get_string
|
|
|
|
from ..utils import parse_tag_list
|
|
from ..renderer import md_and_toc
|
|
|
|
from ..models import PERM_CREATE, Page, PageLink, PageRevision, PageText, User, db, is_url_available, is_valid_url, perms_required
|
|
|
|
current_user: User
|
|
|
|
bp = Blueprint('edit', __name__)
|
|
|
|
def savepoint(form: Mapping, is_preview: bool = False, pageobj: Page | None = None):
|
|
'''Middle point during page editing.'''
|
|
|
|
if is_preview:
|
|
preview = md_and_toc(form['text'])[0]
|
|
else:
|
|
preview = None
|
|
pl_js_info = dict()
|
|
pl_js_info['editing'] = dict(
|
|
original_text = pageobj.current_text() if pageobj else None,
|
|
preview_text = form['text'],
|
|
page_id = pageobj.id if pageobj else None
|
|
)
|
|
return render_template(
|
|
'edit.html',
|
|
pl_url=form['url'],
|
|
pl_title=form['title'],
|
|
pl_text=form['text'],
|
|
pl_tags=form['tags'],
|
|
pl_comment=form['comment'],
|
|
pl_is_locked='lockpage' in form,
|
|
pl_owner_is_current_user=current_user.owns(pageobj) if pageobj else True,
|
|
preview=preview,
|
|
pl_js_info=pl_js_info,
|
|
pl_calendar=('usecalendar' in form) and form['calendar'],
|
|
pl_readonly=not pageobj.is_editable_by(current_user) if pageobj else False,
|
|
pl_cw=('cw' in form) and form['cw']
|
|
)
|
|
|
|
@bp.route('/create/', methods=['GET', 'POST'])
|
|
@perms_required(PERM_CREATE, message='You are not authorized to create pages.')
|
|
def create():
|
|
if request.method == 'POST':
|
|
if request.form.get('preview'):
|
|
return savepoint(request.form, is_preview=True)
|
|
p_url = request.form['url'] or None
|
|
p_title = request.form['title']
|
|
if p_url:
|
|
if not is_valid_url(p_url):
|
|
flash('Invalid URL. Valid URLs contain only letters, numbers and hyphens.')
|
|
return savepoint(request.form)
|
|
elif not is_url_available(p_url):
|
|
flash('This URL is not available.')
|
|
return savepoint(request.form)
|
|
|
|
try:
|
|
p_tags = parse_tag_list(request.form.get('tags', ''))
|
|
except ValueError:
|
|
flash('Invalid tags text. Tags contain only letters, numbers and hyphens, and are separated by comma.')
|
|
return savepoint(request.form)
|
|
|
|
p_calendar = datetime.date.fromisoformat(request.form["calendar"]) if 'usecalendar' in request.form else None
|
|
|
|
try:
|
|
p = db.session.execute(insert(Page).values(
|
|
url = p_url,
|
|
title = p_title,
|
|
is_redirect = False,
|
|
touched = datetime.datetime.now(),
|
|
owner_id = current_user.id,
|
|
calendar = p_calendar,
|
|
is_locked = 'lockpage' in request.form,
|
|
is_cw = 'cw' in request.form
|
|
).returning(Page)).scalar()
|
|
|
|
p.change_tags(p_tags)
|
|
|
|
pt = PageText.create_content(request.form['text'])
|
|
|
|
db.session.execute(insert(PageRevision).values(
|
|
page_id = p.id,
|
|
user_id = p.owner_id,
|
|
comment = request.form.get('comment', ''),
|
|
textref_id = pt.id,
|
|
pub_date = datetime.datetime.now(),
|
|
length = len(request.form['text'])
|
|
))
|
|
|
|
PageLink.parse_links(p, request.form['text'])
|
|
|
|
db.session.commit()
|
|
except Exception as e:
|
|
flash(f'An error occurred while saving this revision. Please be patient.')
|
|
sys.excepthook(*sys.exc_info())
|
|
return savepoint(request.form)
|
|
|
|
return redirect(p.get_url())
|
|
return savepoint(dict(url=request.args.get('url'), title='', text='', tags='', comment=get_string(g.lang, 'page_created')))
|
|
|
|
@bp.route('/edit/<int:id>', methods=['GET', 'POST'])
|
|
@login_required
|
|
def edit(id: int):
|
|
p = db.session.execute(select(Page).where(Page.id == id)).scalar()
|
|
if p is None:
|
|
abort(404)
|
|
|
|
p_latest = p.latest()
|
|
|
|
if request.method == 'POST':
|
|
if not p.can_edit(current_user):
|
|
flash('You are trying to edit a locked page!')
|
|
abort(403)
|
|
if request.form.get('preview'):
|
|
return savepoint(request.form, is_preview=True, pageobj=p)
|
|
p_url = request.form['url'] or None
|
|
if p_url:
|
|
if not is_valid_url(p_url):
|
|
flash('Invalid URL. Valid URLs contain only letters, numbers and hyphens.')
|
|
return savepoint(request.form, pageobj=p)
|
|
elif not is_url_available(p_url) and p_url != p.url:
|
|
flash('This URL is not available.')
|
|
return savepoint(request.form, pageobj=p)
|
|
p_tags = [x.strip().lower().replace(' ', '-').replace('_', '-').lstrip('#')
|
|
for x in request.form.get('tags', '').split(',')]
|
|
p_tags = [x for x in p_tags if x]
|
|
if any(not re.fullmatch(SLUG_RE, x) for x in p_tags):
|
|
flash('Invalid tags text. Tags contain only letters, numbers and hyphens, and are separated by comma.')
|
|
return savepoint(request.form, pageobj=p)
|
|
p.url = p_url
|
|
p.title = request.form['title']
|
|
p.touched = datetime.datetime.now()
|
|
p.is_locked = 'lockpage' in request.form
|
|
p.is_cw = 'cw' in request.form
|
|
p.calendar = datetime.date.fromisoformat(request.form["calendar"]) if 'usecalendar' in request.form else None
|
|
p.change_tags(p_tags)
|
|
if request.form['text'] != p_latest.text():
|
|
pt = PageText.create_content(request.form['text'])
|
|
|
|
db.session.execute(insert(PageRevision).values(
|
|
page_id=p.id,
|
|
user_id=current_user.id,
|
|
comment=request.form["comment"],
|
|
textref_id=pt.id,
|
|
pub_date=datetime.datetime.now(),
|
|
length=len(request.form['text'])
|
|
))
|
|
db.session.add(p)
|
|
db.session.commit()
|
|
return redirect(p.get_url())
|
|
|
|
form = dict(
|
|
url=p.url,
|
|
title=p.title,
|
|
text=p_latest.text(),
|
|
tags=','.join(x.name for x in p.tags),
|
|
comment=''
|
|
)
|
|
if p.is_locked:
|
|
form['lockpage'] = '1'
|
|
if p.calendar:
|
|
form['usecalendar'] = '1'
|
|
form['calendar'] = want_isodate(p.calendar).split('T')[0]
|
|
return savepoint(form, pageobj=p)
|
|
|
|
|
|
@bp.route('/_jsoninfo/<int:id>', methods=['GET', 'POST'])
|
|
@bp.route('/p/<int:id>.json', methods=['GET'])
|
|
def page_jsoninfo(id):
|
|
p = db.session.execute(select(Page).where(Page.id == id)).scalar()
|
|
if p is None:
|
|
return jsonify({'status':'fail'}), 404
|
|
j = p.js_info()
|
|
j["status"] = "ok"
|
|
if request.method == "POST":
|
|
j["text"] = p.latest().text()
|
|
return jsonify(j)
|
|
|
|
@bp.route("/_jsoninfo/changed/<float:ts>")
|
|
def jsoninfo_changed(ts):
|
|
tse = str(datetime.datetime.fromtimestamp(ts).isoformat(" "))
|
|
ps = db.session.execute(select(Page).where(Page.touched >= tse)).scalars()
|
|
return jsonify({
|
|
"ids": [i.id for i in ps],
|
|
"status": "ok"
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|