Improved users, now /create and /edit require user login, changed savepoint() helper
This commit is contained in:
parent
83e2c892b3
commit
f4d536dc47
9 changed files with 102 additions and 42 deletions
86
app.py
86
app.py
|
|
@ -211,6 +211,13 @@ class Page(BaseModel):
|
||||||
def is_editable(self):
|
def is_editable(self):
|
||||||
return not self.is_locked
|
return not self.is_locked
|
||||||
|
|
||||||
|
def can_edit(self, user):
|
||||||
|
if self.is_locked:
|
||||||
|
return user.id == self.owner.id
|
||||||
|
return True
|
||||||
|
def is_owned_by(self, user):
|
||||||
|
return user.id == self.owner.id
|
||||||
|
|
||||||
|
|
||||||
class PageText(BaseModel):
|
class PageText(BaseModel):
|
||||||
content = BlobField()
|
content = BlobField()
|
||||||
|
|
@ -508,12 +515,16 @@ def error_404(body):
|
||||||
def error_403(body):
|
def error_403(body):
|
||||||
return render_template('forbidden.html'), 403
|
return render_template('forbidden.html'), 403
|
||||||
|
|
||||||
@app.errorhandler(500)
|
@app.errorhandler(400)
|
||||||
def error_400(body):
|
def error_400(body):
|
||||||
return render_template('badrequest.html'), 400
|
return render_template('badrequest.html'), 400
|
||||||
|
|
||||||
|
@app.errorhandler(500)
|
||||||
|
def error_500(body):
|
||||||
|
return render_template('internalservererror.html'), 500
|
||||||
|
|
||||||
# Middle point during page editing.
|
# Middle point during page editing.
|
||||||
def savepoint(form, is_preview=False, pageid=None):
|
def savepoint(form, is_preview=False, pageobj=None):
|
||||||
if is_preview:
|
if is_preview:
|
||||||
preview = md(form['text'], math='enablemath' in form)
|
preview = md(form['text'], math='enablemath' in form)
|
||||||
else:
|
else:
|
||||||
|
|
@ -522,7 +533,7 @@ def savepoint(form, is_preview=False, pageid=None):
|
||||||
pl_js_info['editing'] = dict(
|
pl_js_info['editing'] = dict(
|
||||||
original_text = None, # TODO
|
original_text = None, # TODO
|
||||||
preview_text = form['text'],
|
preview_text = form['text'],
|
||||||
page_id = pageid
|
page_id = pageobj.id if pageobj else None
|
||||||
)
|
)
|
||||||
return render_template(
|
return render_template(
|
||||||
'edit.html',
|
'edit.html',
|
||||||
|
|
@ -531,11 +542,15 @@ def savepoint(form, is_preview=False, pageid=None):
|
||||||
pl_text=form['text'],
|
pl_text=form['text'],
|
||||||
pl_tags=form['tags'],
|
pl_tags=form['tags'],
|
||||||
pl_enablemath='enablemath' in form,
|
pl_enablemath='enablemath' in form,
|
||||||
|
pl_is_locked='lockpage' in form,
|
||||||
|
pl_owner_is_current_user=pageobj.is_owned_by(current_user) if pageobj else True,
|
||||||
preview=preview,
|
preview=preview,
|
||||||
pl_js_info=pl_js_info
|
pl_js_info=pl_js_info,
|
||||||
|
pl_readonly=not pageobj.can_edit(current_user) if pageobj else False
|
||||||
)
|
)
|
||||||
|
|
||||||
@app.route('/create/', methods=['GET', 'POST'])
|
@app.route('/create/', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
def create():
|
def create():
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
if request.form.get('preview'):
|
if request.form.get('preview'):
|
||||||
|
|
@ -559,7 +574,9 @@ def create():
|
||||||
title=request.form['title'],
|
title=request.form['title'],
|
||||||
is_redirect=False,
|
is_redirect=False,
|
||||||
touched=datetime.datetime.now(),
|
touched=datetime.datetime.now(),
|
||||||
is_math_enabled='enablemath' in request.form
|
owner=current_user,
|
||||||
|
is_math_enabled='enablemath' in request.form,
|
||||||
|
is_locked = 'lockpage' in request.form
|
||||||
)
|
)
|
||||||
p.change_tags(p_tags)
|
p.change_tags(p_tags)
|
||||||
except IntegrityError as e:
|
except IntegrityError as e:
|
||||||
|
|
@ -567,7 +584,7 @@ def create():
|
||||||
return savepoint(request.form)
|
return savepoint(request.form)
|
||||||
pr = PageRevision.create(
|
pr = PageRevision.create(
|
||||||
page=p,
|
page=p,
|
||||||
user_id=0,
|
user_id=p.owner.id,
|
||||||
comment='',
|
comment='',
|
||||||
textref=PageText.create_content(request.form['text']),
|
textref=PageText.create_content(request.form['text']),
|
||||||
pub_date=datetime.datetime.now(),
|
pub_date=datetime.datetime.now(),
|
||||||
|
|
@ -575,37 +592,49 @@ def create():
|
||||||
)
|
)
|
||||||
PageLink.parse_links(p, request.form['text'])
|
PageLink.parse_links(p, request.form['text'])
|
||||||
return redirect(p.get_url())
|
return redirect(p.get_url())
|
||||||
return render_template('edit.html', pl_url=request.args.get('url'))
|
return savepoint({
|
||||||
|
"url": request.args.get("url"),
|
||||||
|
"title": "",
|
||||||
|
"text": "",
|
||||||
|
"tags": ""
|
||||||
|
})
|
||||||
|
|
||||||
@app.route('/edit/<int:id>/', methods=['GET', 'POST'])
|
@app.route('/edit/<int:id>/', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
def edit(id):
|
def edit(id):
|
||||||
p = Page[id]
|
p = Page[id]
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
|
# check if page is locked first!
|
||||||
|
if not p.can_edit(current_user):
|
||||||
|
flash('You are trying to edit a locked page!')
|
||||||
|
abort(403)
|
||||||
|
|
||||||
if request.form.get('preview'):
|
if request.form.get('preview'):
|
||||||
return savepoint(request.form, is_preview=True, pageid=id)
|
return savepoint(request.form, is_preview=True, pageobj=p)
|
||||||
p_url = request.form['url'] or None
|
p_url = request.form['url'] or None
|
||||||
if p_url:
|
if p_url:
|
||||||
if not is_valid_url(p_url):
|
if not is_valid_url(p_url):
|
||||||
flash('Invalid URL. Valid URLs contain only letters, numbers and hyphens.')
|
flash('Invalid URL. Valid URLs contain only letters, numbers and hyphens.')
|
||||||
return savepoint(request.form, pageid=id)
|
return savepoint(request.form, pageobj=p)
|
||||||
elif not is_url_available(p_url) and p_url != p.url:
|
elif not is_url_available(p_url) and p_url != p.url:
|
||||||
flash('This URL is not available.')
|
flash('This URL is not available.')
|
||||||
return savepoint(request.form, pageid=id)
|
return savepoint(request.form, pageobj=p)
|
||||||
p_tags = [x.strip().lower().replace(' ', '-').replace('_', '-').lstrip('#')
|
p_tags = [x.strip().lower().replace(' ', '-').replace('_', '-').lstrip('#')
|
||||||
for x in request.form.get('tags', '').split(',')]
|
for x in request.form.get('tags', '').split(',')]
|
||||||
p_tags = [x for x in p_tags if x]
|
p_tags = [x for x in p_tags if x]
|
||||||
if any(not re.fullmatch(SLUG_RE, x) for x in p_tags):
|
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.')
|
flash('Invalid tags text. Tags contain only letters, numbers and hyphens, and are separated by comma.')
|
||||||
return savepoint(request.form, pageid=id)
|
return savepoint(request.form, pageobj=p)
|
||||||
p.url = p_url
|
p.url = p_url
|
||||||
p.title = request.form['title']
|
p.title = request.form['title']
|
||||||
p.touched = datetime.datetime.now()
|
p.touched = datetime.datetime.now()
|
||||||
p.is_math_enabled = 'enablemath' in request.form
|
p.is_math_enabled = 'enablemath' in request.form
|
||||||
|
p.is_locked = 'lockpage' in request.form
|
||||||
p.save()
|
p.save()
|
||||||
p.change_tags(p_tags)
|
p.change_tags(p_tags)
|
||||||
pr = PageRevision.create(
|
pr = PageRevision.create(
|
||||||
page=p,
|
page=p,
|
||||||
user_id=0,
|
user_id=current_user.id,
|
||||||
comment='',
|
comment='',
|
||||||
textref=PageText.create_content(request.form['text']),
|
textref=PageText.create_content(request.form['text']),
|
||||||
pub_date=datetime.datetime.now(),
|
pub_date=datetime.datetime.now(),
|
||||||
|
|
@ -613,22 +642,19 @@ def edit(id):
|
||||||
)
|
)
|
||||||
PageLink.parse_links(p, request.form['text'])
|
PageLink.parse_links(p, request.form['text'])
|
||||||
return redirect(p.get_url())
|
return redirect(p.get_url())
|
||||||
pl_js_info = dict()
|
|
||||||
pl_js_info['editing'] = dict(
|
|
||||||
original_text = None, # TODO
|
|
||||||
preview_text = None,
|
|
||||||
page_id = id
|
|
||||||
)
|
|
||||||
|
|
||||||
return render_template(
|
form = {
|
||||||
'edit.html',
|
"url": p.url,
|
||||||
pl_url=p.url,
|
"title": p.title,
|
||||||
pl_title=p.title,
|
"text": p.latest.text,
|
||||||
pl_text=p.latest.text,
|
"tags": ','.join(x.name for x in p.tags)
|
||||||
pl_tags=','.join(x.name for x in p.tags),
|
}
|
||||||
pl_js_info=pl_js_info,
|
if p.is_math_enabled:
|
||||||
pl_enablemath = p.is_math_enabled
|
form["enablemath"] = "1"
|
||||||
)
|
if p.is_locked:
|
||||||
|
form["lockpage"] = "1"
|
||||||
|
|
||||||
|
return savepoint(form, pageobj=p)
|
||||||
|
|
||||||
@app.route("/__sync_start")
|
@app.route("/__sync_start")
|
||||||
def __sync_start():
|
def __sync_start():
|
||||||
|
|
@ -805,6 +831,8 @@ def theme_switch():
|
||||||
|
|
||||||
@app.route('/accounts/login/', methods=['GET','POST'])
|
@app.route('/accounts/login/', methods=['GET','POST'])
|
||||||
def accounts_login():
|
def accounts_login():
|
||||||
|
if current_user.is_authenticated:
|
||||||
|
return redirect(request.args.get('next', '/'))
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
try:
|
try:
|
||||||
username = request.form['username']
|
username = request.form['username']
|
||||||
|
|
@ -826,8 +854,6 @@ def accounts_login():
|
||||||
|
|
||||||
@app.route('/accounts/register/', methods=['GET','POST'])
|
@app.route('/accounts/register/', methods=['GET','POST'])
|
||||||
def accounts_register():
|
def accounts_register():
|
||||||
if current_user.is_authenticated:
|
|
||||||
return redirect(request.args.get('next', '/'))
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
username = request.form['username']
|
username = request.form['username']
|
||||||
password = request.form['password']
|
password = request.form['password']
|
||||||
|
|
@ -980,7 +1006,7 @@ class Importer(object):
|
||||||
|
|
||||||
rev = PageRevision.create(
|
rev = PageRevision.create(
|
||||||
page = p,
|
page = p,
|
||||||
user = self.owner.id,
|
user_id = self.owner.id,
|
||||||
textref = textref,
|
textref = textref,
|
||||||
comment = revobj.get('comment'),
|
comment = revobj.get('comment'),
|
||||||
pub_date = datetime.datetime.fromtimestamp(revobj['timestamp']),
|
pub_date = datetime.datetime.fromtimestamp(revobj['timestamp']),
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@
|
||||||
"privacy-policy": "Privacy Policy",
|
"privacy-policy": "Privacy Policy",
|
||||||
"already-have-account": "Already have an account?",
|
"already-have-account": "Already have an account?",
|
||||||
"logged-in-as": "Logged in as",
|
"logged-in-as": "Logged in as",
|
||||||
"not-logged-in": "Not logged in"
|
"not-logged-in": "Not logged in",
|
||||||
|
"owner": "Owner"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -45,6 +45,7 @@
|
||||||
"notes-count-with-url": "Numero di note con URL impostato",
|
"notes-count-with-url": "Numero di note con URL impostato",
|
||||||
"revision-count": "Numero di revisioni",
|
"revision-count": "Numero di revisioni",
|
||||||
"revision-count-per-page": "Media di revisioni per pagina",
|
"revision-count-per-page": "Media di revisioni per pagina",
|
||||||
"remember-me-for": "Ricordami per"
|
"remember-me-for": "Ricordami per",
|
||||||
|
"owner": "Proprietario"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -45,7 +45,13 @@
|
||||||
</ul>
|
</ul>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<div class="footer-copyright">© 2020–2023 Sakuragasaki46.</div>
|
<div class="footer-copyright">© 2020–2023 Sakuragasaki46.</div>
|
||||||
<div class="footer-loggedinas">{% if current_user %}{{ T('logged-in-as') }}: <strong>{{ current_user.username }}</strong>{% else %}{{ T('not-logged-in') }}{% endif %}</div>
|
<div class="footer-loggedinas">
|
||||||
|
{% if current_user.is_authenticated %}
|
||||||
|
{{ T('logged-in-as') }}: <strong>{{ current_user.username }}</strong>
|
||||||
|
{% else %}
|
||||||
|
{{ T('not-logged-in') }}. <a href="/accounts/login">{{ T("login") }}</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
<div class="footer-actions" id="page-actions">{% block actions %}{% endblock %}</div>
|
<div class="footer-actions" id="page-actions">{% block actions %}{% endblock %}</div>
|
||||||
<div class="footer-versions">{{app_name}} version {{app_version}}</div>
|
<div class="footer-versions">{{app_name}} version {{app_version}}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
<meta name="robots" content="noindex,nofollow" />
|
<meta name="robots" content="noindex,nofollow" />
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block json_info %}<script>window.page_info={{ ( pl_js_info or {} )|tojson|safe }};</script>{% endblock %}
|
{% block json_info %}<script>/*<![CDATA[*/window.page_info={{ ( pl_js_info or {} )|tojson|safe }};/*]]>*/</script>{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
|
|
@ -35,25 +35,32 @@
|
||||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||||
<div>
|
<div>
|
||||||
<label for="title"><span class="material-icons">link</span> /</label>
|
<label for="title"><span class="material-icons">link</span> /</label>
|
||||||
<input type="text" name="url" class="url-input" placeholder="(No URL)" maxlength="64" value="{{ pl_url or '' }}">
|
<input type="text" name="url" class="url-input" placeholder="(No URL)" maxlength="64" value="{{ pl_url or '' }}" {% if pl_readonly %}disabled=""{% endif %}>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<input type="text" required name="title" placeholder="Title (required)" class="title-input" maxlength="256" value="{{ pl_title }}">
|
<input type="text" required name="title" placeholder="Title (required)" class="title-input" maxlength="256" value="{{ pl_title }}" {% if pl_readonly %}disabled=""{% endif %}>
|
||||||
</div>
|
</div>
|
||||||
<div id="editing-area">
|
<div id="editing-area">
|
||||||
|
{% if not pl_readonly %}
|
||||||
<div class="pre-text-input">
|
<div class="pre-text-input">
|
||||||
<p>This editor is using Markdown for text formatting (e.g. bold, italic, headers and tables). <a href="https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet" rel="nofollow">More info on Markdown</a>.</p>
|
<p>This editor is using Markdown for text formatting (e.g. bold, italic, headers and tables). <a href="https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet" rel="nofollow">More info on Markdown</a>.</p>
|
||||||
{% if math_version %}
|
{% if math_version and pl_enablemath %}
|
||||||
<p>Math with KaTeX is <mark>enabled</mark>. <a href="https://katex.org/docs/supported.html">KaTeX guide</a></p>
|
<p>Math with KaTeX is <mark>enabled</mark>. <a href="https://katex.org/docs/supported.html">KaTeX guide</a></p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="over-text-input"></div>
|
<div class="over-text-input"></div>
|
||||||
<textarea name="text" class="text-input">{{ pl_text }}</textarea>
|
{% else %}
|
||||||
|
<div class="pre-text-input">
|
||||||
|
<p>This page was locked by the owner, and is therefore not editable. You can still copy the source text.</p>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<textarea name="text" class="text-input" {% if pl_readonly %}disabled=""{% endif %}>{{ pl_text }}</textarea>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="tags">Tags (comma separated):</label>
|
<label for="tags">Tags (comma separated):</label>
|
||||||
<input type="text" name="tags" class="tags-input" placeholder="No tags" value="{{ pl_tags }}">
|
<input type="text" name="tags" class="tags-input" placeholder="No tags" value="{{ pl_tags }}">
|
||||||
</div>
|
</div>
|
||||||
|
{% if not pl_readonly %}
|
||||||
<div>
|
<div>
|
||||||
<input type="submit" value="Save" id="save-button" class="submit-primary">
|
<input type="submit" value="Save" id="save-button" class="submit-primary">
|
||||||
<input type="submit" name="preview" value="Preview" id="preview-button" class="submit-secondary">
|
<input type="submit" name="preview" value="Preview" id="preview-button" class="submit-secondary">
|
||||||
|
|
@ -63,6 +70,13 @@
|
||||||
<input type="checkbox" id="CB__enablemath" name="enablemath" {% if math_version and pl_enablemath %}checked=""{% elif not math_version %}disabled=""{% endif %}>
|
<input type="checkbox" id="CB__enablemath" name="enablemath" {% if math_version and pl_enablemath %}checked=""{% elif not math_version %}disabled=""{% endif %}>
|
||||||
<label for="CB__enablemath">Enable math expressions parsing {% if not math_version %}(disabled for this instance){% endif %}</label>
|
<label for="CB__enablemath">Enable math expressions parsing {% if not math_version %}(disabled for this instance){% endif %}</label>
|
||||||
</div>
|
</div>
|
||||||
|
{% if pl_owner_is_current_user %}
|
||||||
|
<div>
|
||||||
|
<input type="checkbox" id="CB__lockpage" name="lockpage" {% if pl_is_locked %}checked=""{% endif %}>
|
||||||
|
<label for="CB__lockpage">Lock page for editing by other users</label>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@
|
||||||
#{{ rev.id }}
|
#{{ rev.id }}
|
||||||
·
|
·
|
||||||
{{ rev.pub_date.strftime("%B %-d, %Y %H:%M:%S") }}
|
{{ rev.pub_date.strftime("%B %-d, %Y %H:%M:%S") }}
|
||||||
|
by
|
||||||
|
{{ rev.user.username }}
|
||||||
</a></li>
|
</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
||||||
9
templates/internalservererror.html
Normal file
9
templates/internalservererror.html
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}Internal Server Error - {{ app_name }}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>Internal Server Error</h1>
|
||||||
|
|
||||||
|
<p>We’re sorry, an unexpected error occurred. Try refreshing the page.</p>
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -17,18 +17,18 @@
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label>{{ T('confirm-password') }}:</label>
|
<label>{{ T('confirm-password') }}:</label>
|
||||||
<input type="password" name="confirmpassword" />
|
<input type="password" name="confirm_password" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label>{{ T('email') }} ({{ T('optional') }}):</label>
|
<label>{{ T('email') }} ({{ T('optional') }}):</label>
|
||||||
<input type="email" name="email" />
|
<input type="text" name="email" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<input type="checkbox" name="legal" value="1" />
|
<input type="checkbox" name="legal" value="1" />
|
||||||
<label>{{ T('have-read-terms').format(T('terms-of-service'), T('privacy-policy')) }}</label>
|
<label>{{ T('have-read-terms').format(T('terms-of-service'), T('privacy-policy')) }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<input type="submit" value="{{ T('login') }}" />
|
<input type="submit" value="{{ T('sign-up') }}" />
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,8 @@
|
||||||
<a href="/history/{{ p.id }}/">{{ T('action-history') }}</a> -
|
<a href="/history/{{ p.id }}/">{{ T('action-history') }}</a> -
|
||||||
<a href="/backlinks/{{ p.id }}/">Backlinks</a> -
|
<a href="/backlinks/{{ p.id }}/">Backlinks</a> -
|
||||||
{{ T('last-changed') }} <time datetime="{{ rev.pub_date.isoformat() }}">{{ rev.pub_date.strftime('%B %-d, %Y at %H:%M:%S') }}</time> -
|
{{ T('last-changed') }} <time datetime="{{ rev.pub_date.isoformat() }}">{{ rev.pub_date.strftime('%B %-d, %Y at %H:%M:%S') }}</time> -
|
||||||
{{ T('page-id') }}: {{ p.id }}
|
{{ T('page-id') }}: {{ p.id }} -
|
||||||
|
{{ T('owner') }}: {{ p.owner.username }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue