1
Fork 0
mirror of https://github.com/RYGhub/royalnet.git synced 2024-11-23 11:34:18 +00:00

Readd bios to profiles

This commit is contained in:
Steffo 2019-08-03 14:51:05 +02:00
parent c0fca28c9a
commit 04d360c20f
16 changed files with 328 additions and 93 deletions

View file

@ -2,7 +2,7 @@ from sqlalchemy import Column, \
Integer, \
Text, \
ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.orm import relationship, backref
from sqlalchemy.ext.declarative import declared_attr
from .royals import Royal
@ -16,11 +16,11 @@ class Bio:
@declared_attr
def royal(self):
return relationship("Royal")
return relationship("Royal", backref=backref("bio", uselist=False))
@declared_attr
def contents(self):
return Column(Text, unique=True, nullable=False)
return Column(Text, nullable=False, default="")
def __repr__(self):
return f"<Bio of {self.royal}>"

View file

@ -22,7 +22,7 @@ class WikiPage:
return Column(String, nullable=False)
@declared_attr
def content(self):
def contents(self):
return Column(Text)
@declared_attr

View file

@ -0,0 +1,29 @@
import re
import markdown2
class RenderError(Exception):
"""An error occurred while trying to render the page."""
def prepare_page_markdown(markdown):
if list(markdown).count(">") > 99:
raise RenderError("Too many nested quotes")
converted_md = markdown2.markdown(markdown.replace("<", "&lt;"),
extras=["spoiler", "tables", "smarty-pants", "fenced-code-blocks"])
converted_md = re.sub(r"{https?://(?:www\.)?(?:youtube\.com/watch\?.*?&?v=|youtu.be/)([0-9A-Za-z-]+).*?}",
r'<div class="youtube-embed">'
r' <iframe src="https://www.youtube-nocookie.com/embed/\1?rel=0&amp;showinfo=0"'
r' frameborder="0"'
r' allow="autoplay; encrypted-media"'
r' allowfullscreen'
r' width="640px"'
r' height="320px">'
r' </iframe>'
r'</div>', converted_md)
converted_md = re.sub(r"{https?://clyp.it/([a-z0-9]+)}",
r'<div class="clyp-embed">'
r' <iframe width="100%" height="160" src="https://clyp.it/\1/widget" frameborder="0">'
r' </iframe>'
r'</div>', converted_md)
return converted_md

View file

@ -23,7 +23,7 @@
<div class="diario-quote">
{% if entry.quoted_account %}
<a href="{{ url_for("profile.profile_by_username", username=entry.quoted_account.username) }}" class="diario-quoted">{{ entry.quoted }}</a>
<a href="{{ url_for("profile.profile_page", username=entry.quoted_account.username) }}" class="diario-quoted">{{ entry.quoted }}</a>
{% else %}
<span class="diario-quoted">{{ entry.quoted }}</span>
{% endif %}
@ -33,7 +33,7 @@
</div>
{% endif %}
{% if entry.creator %}
<div class="diario-created">Salvato da <a href="{{ url_for("profile.profile_by_username", username=entry.creator.username) }}" class="diario-creator">{{ entry.creator.username }}</a></div>
<div class="diario-created">Salvato da <a href="{{ url_for("profile.profile_page", username=entry.creator.username) }}" class="diario-creator">{{ entry.creator.username }}</a></div>
{% endif %}
<time class="diario-timestamp" datetime="{{ entry.timestamp.isoformat() }}">{{ entry.timestamp.strftime("%d %b %Y %H:%M:%S") }}</time>
<span class="diario-id">#{{ entry.diario_id }}</span>

View file

@ -5,13 +5,14 @@ import os
from ...royalprint import Royalprint
from ...shortcuts import error
from ....database.tables import *
from ....utils.wikirender import prepare_page_markdown, RenderError
# Maybe some of these tables are optional...
tmpl_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates')
rp = Royalprint("profile", __name__, url_prefix="/profile", template_folder=tmpl_dir,
required_tables={Royal, ActiveKvGroup, Alias, Diario, Discord, Keygroup, Keyvalue, Telegram, WikiPage,
WikiRevision})
WikiRevision, Bio})
@rp.route("/")
@ -22,9 +23,39 @@ def profile_index():
@rp.route("/<username>")
def profile_by_username(username):
def profile_page(username):
alchemy, alchemy_session = f.current_app.config["ALCHEMY"], f.current_app.config["ALCHEMY_SESSION"]
royal = alchemy_session.query(alchemy.Royal).filter_by(username=username).one_or_none()
if royal is None:
return error(404, "Non esiste nessun utente con l'username richiesto.")
return f.render_template("profile_page.html", royal=royal)
if royal.bio is not None and royal.bio.contents != "":
try:
parsed_bio = f.Markup(prepare_page_markdown(royal.bio.contents))
except RenderError as e:
return error(500, f"Il profilo non può essere visualizzato a causa di un errore nella bio: {str(e)}")
else:
parsed_bio = None
return f.render_template("profile_page.html", royal=royal, parsed_bio=parsed_bio)
@rp.route("/<username>/editbio", methods=["GET", "POST"])
def profile_editbio(username):
if "royal" not in f.session:
return error(403, "Devi aver effettuato il login per modificare una bio.")
alchemy, alchemy_session = f.current_app.config["ALCHEMY"], f.current_app.config["ALCHEMY_SESSION"]
royal = alchemy_session.query(alchemy.Royal).filter_by(username=username).one_or_none()
if not (f.session["royal"]["uid"] == royal.uid or f.session["royal"]["role"] == "Admin"):
return error(403, "Non sei autorizzato a modificare questa pagina bio.")
if f.request.method == "GET":
return f.render_template("profile_editbio.html", royal=royal)
elif f.request.method == "POST":
fd = f.request.form
if royal.bio is None:
bio = alchemy.Bio(royal=royal, contents=fd.get("contents", ""))
alchemy_session.add(bio)
else:
royal.bio.contents = fd.get("contents", "")
alchemy_session.commit()
return f.redirect(f.url_for(".profile_page", username=username))

View file

@ -0,0 +1,126 @@
{% extends "base.html" %}
{% block title %}
{{ royal.username }} - RYGbioeditor
{% endblock %}
{% block head %}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.css">
<script src="https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.js"></script>
{% endblock %}
{% block content %}
<div class="dbox">
<div class="dbox-top">
<span class="left">
Editor Bio
</span>
</div>
<div class="dbox-bot">
<form method="POST" class="full">
<!--suppress HtmlFormInputWithoutLabel -->
<textarea id="profile-editbio-contents" name="contents">{{ royal.bio.contents }}</textarea>
<label for="profile-editbio-submit">
<input id="profile-editbio-submit" type="submit" value="Salva">
</label>
</form>
</div>
</div>
{% endblock %}
{% block footscripts %}
<script>
//TODO: maybe enable autosave?
let simplemde = new SimpleMDE({
element: document.getElementById("profile-editbio-contents"),
blockStyles: {
italic: "_"
},
tabSize: 4,
spellChecker: false,
toolbar: [
{
name: "bold",
action: SimpleMDE.toggleBold,
className: "fas fa-bold no-icon",
title: "Grassetto",
},
{
name: "italic",
action: SimpleMDE.toggleItalic,
className: "fas fa-italic no-icon",
title: "Corsivo",
},
{
name: "strikethrough",
action: SimpleMDE.toggleStrikethrough,
className: "fas fa-strikethrough no-icon",
title: "Barrato",
},
{
name: "link",
action: SimpleMDE.drawLink,
className: "fas fa-link no-icon",
title: "Link",
},
"|",
{
name: "heading-smaller",
action: SimpleMDE.toggleHeadingSmaller,
className: "fas fa-header no-icon",
title: "(Rimpicciolisci) Titolo",
},
{
name: "heading",
action: SimpleMDE.toggleHeadingBigger,
className: "fas fa-header fa-lg no-icon",
title: "(Ingrandisci) Titolo",
},
"|",
{
name: "code",
action: SimpleMDE.toggleCodeBlock,
className: "fas fa-code no-icon",
title: "Codice",
},
{
name: "quote",
action: SimpleMDE.toggleBlockquote,
className: "fas fa-quote-left no-icon",
title: "Citazione",
},
{
name: "unordered-list",
action: SimpleMDE.toggleUnorderedList,
className: "fas fa-list-ul no-icon",
title: "Lista puntata",
},
{
name: "ordered-list",
action: SimpleMDE.toggleOrderedList,
className: "fas fa-list-ol no-icon",
title: "Lista ordinata",
},
{
name: "horizontal-rule",
action: SimpleMDE.drawHorizontalRule,
className: "fas fa-minus no-icon",
title: "Separatore",
},
"|",
{
name: "image",
action: SimpleMDE.drawImage,
className: "fas fa-picture-o no-icon",
title: "Immagine",
},
{
name: "table",
action: SimpleMDE.drawTable,
className: "fas fa-table no-icon",
title: "Tabella",
}
],
});
</script>
{% endblock %}

View file

@ -14,7 +14,7 @@
<div class="dbox-bot">
<ul>
{% for royal in royals %}
<li><a href="{{ url_for("profile.profile_by_username", username=royal.username) }}">{{ royal.username }}</a></li>
<li><a href="{{ url_for("profile.profile_page", username=royal.username) }}">{{ royal.username }}</a></li>
{% endfor %}
</ul>
</div>

View file

@ -9,6 +9,29 @@
<h1>
{{ royal.username }}
</h1>
<div class="profile-bio">
<div class="dbox">
<div class="dbox-top">
<span class="left">
Bio
</span>
<span class="right">
{% if session["royal"]["uid"] == royal.uid or session["royal"]["role"] == "Admin" %}
<a class="edit no-icon" href="{{ url_for("profile.profile_editbio", username=royal.username) }}">Modifica</a>
{% else %}
<a class="edit no-icon disabled" title="Non sei autorizzato a modificare questa bio.">Modifica</a>
{% endif %}
</span>
</div>
<div class="dbox-bot">
{% if parsed_bio %}
{{ parsed_bio }}
{% else %}
<span class="disabled">Questo utente non ha nessuna bio.</span>
{% endif %}
</div>
</div>
</div>
<div class="columns-3">
<div class="column">
<div class="dbox profile-links">
@ -17,7 +40,7 @@
</div>
<div class="dbox-bot">
<ul class="links-list">
<li><span class="links-item links-linked">royalnet:{{ royal.username }}</span></li>
<li><span class="links-item links-linked" title="{{ royal.uid }}">royalnet:{{ royal.username }}</span></li>
{% for telegram in royal.telegram %}
{% if telegram.username %}
<li><a class="links-item links-linked" href="https://t.me/{{ telegram.username }}">telegram:{{ telegram.mention() }}</a></li>
@ -70,11 +93,5 @@
</div>
</div>
</div>
<h2>
Medals
</h2>
<p>
TODO
</p>
</div>
{% endblock %}

View file

@ -24,12 +24,12 @@ def wikiedit_newpage():
elif f.request.method == "POST":
fd = f.request.form
if not ("title" in fd and "content" in fd and "css" in fd):
if not ("title" in fd and "contents" in fd and "css" in fd):
return error(400, "Uno dei campi obbligatori non è stato compilato. Controlla e riprova!")
alchemy, alchemy_session = f.current_app.config["ALCHEMY"], f.current_app.config["ALCHEMY_SESSION"]
page = alchemy.WikiPage(page_id=uuid.uuid4(),
title=fd["title"],
content=fd["content"],
contents=fd["contents"],
format="markdown",
css=fd["css"] if fd["css"] != "None" else None)
revision = alchemy.WikiRevision(revision_id=uuid.uuid4(),
@ -37,7 +37,7 @@ def wikiedit_newpage():
author_id=f.session["royal"]["uid"],
timestamp=datetime.datetime.now(),
reason=fd.get("reason"),
diff="\n".join(difflib.unified_diff([], page.content.split("\n"))))
diff="\n".join(difflib.unified_diff([], page.contents.split("\n"))))
alchemy_session.add(page)
alchemy_session.add(revision)
alchemy_session.commit()
@ -61,7 +61,7 @@ def wikiedit_by_id(page_id: str, title: str):
elif f.request.method == "POST":
fd = f.request.form
if not ("title" in fd and "content" in fd and "css" in fd):
if not ("title" in fd and "contents" in fd and "css" in fd):
return error(400, "Uno dei campi obbligatori non è stato compilato. Controlla e riprova!")
# Create new revision
revision = alchemy.WikiRevision(revision_id=uuid.uuid4(),
@ -69,10 +69,10 @@ def wikiedit_by_id(page_id: str, title: str):
author_id=f.session["royal"]["uid"],
timestamp=datetime.datetime.now(),
reason=fd.get("reason"),
diff="\n".join(difflib.unified_diff(page.content.split("\n"), fd["content"].split("\n"))))
diff="\n".join(difflib.unified_diff(page.contents.split("\n"), fd["contents"].split("\n"))))
alchemy_session.add(revision)
# Apply changes
page.content = fd["content"]
page.contents = fd["contents"]
page.title = fd["title"]
page.css = fd["css"] if fd["css"] != "None" else None
alchemy_session.commit()

View file

@ -27,7 +27,7 @@
<input id="wikiedit-format" type="text" name="format" disabled value="markdown">
</label>
<!--suppress HtmlFormInputWithoutLabel -->
<textarea id="wikiedit-content" name="content">{{ page.content }}</textarea>
<textarea id="wikiedit-contents" name="contents">{{ page.contents }}</textarea>
<label for="wikiedit-css">
<span class="label-text">Tema</span>
<select id="wikiedit-css" name="css">
@ -51,7 +51,7 @@
<script>
//TODO: maybe enable autosave?
let simplemde = new SimpleMDE({
element: document.getElementById("wikiedit-content"),
element: document.getElementById("wikiedit-contents"),
blockStyles: {
italic: "_"
},

View file

@ -1,12 +1,11 @@
"""A Royal Games Wiki viewer :py:class:`royalnet.web.Royalprint`. Doesn't support any kind of edit."""
import flask as f
import markdown2
import re
import os
from ...royalprint import Royalprint
from ...shortcuts import error, from_urluuid
from ....database.tables import Royal, WikiPage, WikiRevision
from ....utils.wikirender import prepare_page_markdown, RenderError
tmpl_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates')
@ -14,48 +13,21 @@ rp = Royalprint("wikiview", __name__, url_prefix="/wiki/view", template_folder=t
required_tables={Royal, WikiPage, WikiRevision})
class WikiRenderError(Exception):
"""An error occurred while trying to render the wiki page."""
def prepare_page_markdown(page):
if list(page.content).count(">") > 99:
raise WikiRenderError("Too many nested quotes")
converted_md = markdown2.markdown(page.content.replace("<", "&lt;"),
extras=["spoiler", "tables", "smarty-pants", "fenced-code-blocks"])
converted_md = re.sub(r"{https?://(?:www\.)?(?:youtube\.com/watch\?.*?&?v=|youtu.be/)([0-9A-Za-z-]+).*?}",
r'<div class="youtube-embed">'
r' <iframe src="https://www.youtube-nocookie.com/embed/\1?rel=0&amp;showinfo=0"'
r' frameborder="0"'
r' allow="autoplay; encrypted-media"'
r' allowfullscreen'
r' width="640px"'
r' height="320px">'
r' </iframe>'
r'</div>', converted_md)
converted_md = re.sub(r"{https?://clyp.it/([a-z0-9]+)}",
r'<div class="clyp-embed">'
r' <iframe width="100%" height="160" src="https://clyp.it/\1/widget" frameborder="0">'
r' </iframe>'
r'</div>', converted_md)
return f.Markup(converted_md)
def prepare_page(page):
try:
if page.format == "markdown":
return f.render_template("wikiview_page.html",
page=page,
parsed_content=f.Markup(prepare_page_markdown(page)),
parsed_contents=f.Markup(prepare_page_markdown(page.contents)),
css=page.css)
elif page.format == "html":
return f.render_template("wikiview_page.html",
page=page,
parsed_content=f.Markup(page.content),
parsed_contents=f.Markup(page.contents),
css=page.css)
else:
return error(500, f"Non esiste nessun handler in grado di preparare pagine con il formato {page.format}.")
except WikiRenderError as e:
except RenderError as e:
return error(500, f"La pagina Wiki non può essere preparata a causa di un errore: {str(e)}")

View file

@ -17,8 +17,6 @@
{% else %}
<a class="edit no-icon disabled" title="Devi fare il login per modificare pagine!">Modifica</a>
{% endif %}
|
<a class="permalink no-icon" href="{{ url_for("wikiview.wikiview_by_id", page_id=page.page_short_id, title=page.title) }}">Permalink</a>
</span>
</div>
<div class="dbox-bot">
@ -26,8 +24,8 @@
<h1 class="wikiview-title">
{{ page.title }}
</h1>
<div class="wikiview-content">
{{ parsed_content }}
<div class="wikiview-contents">
{{ parsed_contents }}
</div>
</div>
</div>

View file

@ -160,16 +160,6 @@ blockquote {
padding: 2px 4px 2px 8px;
margin: 8px;
}
blockquote.spoiler {
color: transparent;
background-color: rgba(255, 125, 125, 0.1);
border: 1px solid rgba(255, 125, 125, 0.1);
border-radius: 2px;
padding: 4px;
}
blockquote.spoiler:hover {
color: #ff7d7d;
}
textarea {
background-color: rgba(160, 204, 255, 0.1);
color: #a0ccff;
@ -353,6 +343,7 @@ form.full label.label-big * {
border-left: 1px solid rgba(160, 204, 255, 0.2) !important;
border-right: 1px solid rgba(160, 204, 255, 0.2) !important;
border-radius: 0 !important;
caret-color: white;
}
.CodeMirror .cm-link {
color: #7dffff !important;
@ -378,6 +369,9 @@ form.full label.label-big * {
.CodeMirror .cm-header {
color: #ffffff !important;
}
.CodeMirror .CodeMirror-cursor {
border-left: 1px solid #a0ccff !important;
}
.editor-toolbar,
.editor-statusbar {
background-color: #1c2b4f !important;
@ -404,6 +398,30 @@ form.full label.label-big * {
background-color: rgba(160, 204, 255, 0.2) !important;
color: #ffffff !important;
}
.editor-toolbar .fas,
.editor-statusbar .fas,
.editor-toolbar .far,
.editor-statusbar .far,
.editor-toolbar .fab,
.editor-statusbar .fab {
color: #a0ccff !important;
}
.editor-toolbar .fas:hover,
.editor-statusbar .fas:hover,
.editor-toolbar .far:hover,
.editor-statusbar .far:hover,
.editor-toolbar .fab:hover,
.editor-statusbar .fab:hover {
color: #a0ccff !important;
}
.editor-toolbar .fas:active,
.editor-statusbar .fas:active,
.editor-toolbar .far:active,
.editor-statusbar .far:active,
.editor-toolbar .fab:active,
.editor-statusbar .fab:active {
color: white !important;
}
.editor-toolbar {
border-top: 1px solid #2b3e62 !important;
border-left: 1px solid #2b3e62 !important;
@ -607,31 +625,56 @@ form.full label.label-big * {
}
.spoiler {
color: transparent;
border: 1px solid rgba(160, 204, 255, 0.1);
background-color: rgba(255, 125, 125, 0.1);
border: 1px solid rgba(255, 125, 125, 0.1);
border-radius: 2px;
padding: 4px;
}
.spoiler b,
.spoiler strong,
.spoiler i,
.spoiler em {
color: inherit;
}
.spoiler b:hover,
.spoiler strong:hover,
.spoiler i:hover,
.spoiler em:hover {
color: inherit;
}
.spoilerblockquote {
border-radius: 2px;
padding: 4px;
}
.spoiler:hover {
color: #a0ccff;
color: #ff7d7d;
}
.wiki .wiki-doublebox .dbox-bot {
.wiki .wiki-doublebox .dbox-bot,
.profile-bio .wiki-doublebox .dbox-bot {
background-color: transparent;
border-top: 0;
border-bottom: 4px solid rgba(160, 204, 255, 0.2);
border-left: 4px solid rgba(160, 204, 255, 0.2);
border-right: 4px solid rgba(160, 204, 255, 0.2);
}
.wiki .wiki-doublebox .dbox-bot .wikiview-title {
.wiki .wiki-doublebox .dbox-bot .wikiview-title,
.profile-bio .wiki-doublebox .dbox-bot .wikiview-title {
text-align: center;
font-size: 42px;
margin-top: 0;
}
.wiki h1,
.profile-bio h1,
.wiki h2,
.profile-bio h2,
.wiki h3,
.profile-bio h3,
.wiki h4,
.profile-bio h4,
.wiki h5,
.wiki h6 {
.profile-bio h5,
.wiki h6,
.profile-bio h6 {
margin-top: revert;
margin-bottom: revert;
}

File diff suppressed because one or more lines are too long

View file

@ -209,18 +209,6 @@ blockquote {
background-color: fade(@pastel-lime, 10%);
padding: 2px 4px 2px 8px;
margin: 8px;
&.spoiler {
color: transparent;
background-color: fade(@pastel-red, 10%);
border: 1px solid fade(@pastel-red, 10%);
border-radius: 2px;
padding: 4px;
&:hover {
color: @pastel-red;
}
}
}
textarea {
@ -440,6 +428,7 @@ form.full {
border-left: 1px solid @fg-twenty !important;
border-right: 1px solid @fg-twenty !important;
border-radius: 0 !important;
caret-color: white;
.cm-link {
color: @pastel-cyan !important;
@ -472,6 +461,10 @@ form.full {
.cm-header {
color: @ec !important;
}
.CodeMirror-cursor {
border-left: 1px solid @fg !important;
}
}
.editor-toolbar, .editor-statusbar {
background-color: @fg-ten-hard !important;
@ -497,6 +490,18 @@ form.full {
}
}
}
.fas, .far, .fab {
color: @fg !important;
&:hover {
color: @fg !important;
}
&:active {
color: white !important;
}
}
}
.editor-toolbar {
border-top: 1px solid @fg-twenty-hard !important;
@ -754,17 +759,31 @@ form.full {
//A spoiler
.spoiler {
color: transparent;
border: 1px solid @fg-ten;
background-color: fade(@pastel-red, 10%);
border: 1px solid fade(@pastel-red, 10%);
border-radius: 2px;
padding: 4px;
b, strong, i, em {
color: inherit;
&:hover {
color: @fg;
color: inherit;
}
}
&blockquote {
border-radius: 2px;
padding: 4px;
}
&:hover {
color: @pastel-red;
}
}
//Wiki page
.wiki {
.wiki, .profile-bio {
.wiki-doublebox {
.dbox-bot {
background-color: transparent;

View file

@ -36,7 +36,7 @@
<div class="nav-right">
<span>
{% if session["royal"] %}
<a href="{{ url_for("profile.profile_by_username", username=session["royal"]["username"]) }}" class="no-icon nav-profile">
<a href="{{ url_for("profile.profile_page", username=session["royal"]["username"]) }}" class="no-icon nav-profile">
<span class="nav-accountname">{{ session["royal"]["username"] }}</span>
{% if session["royal"]["avatar"] %}
<img class="nav-image" alt="" src="{{ session["royal"]["avatar"] }}">