version advance + some refactoring

This commit is contained in:
Yusur 2023-07-12 16:28:59 +02:00
parent ded90d1ac4
commit 910e01b691
6 changed files with 49 additions and 72 deletions

View file

@ -1,5 +1,14 @@
# Whats New # Whats New
## 0.9
+ Removed `markdown_katex` dependency, and therefore support for math.
It is bloat; moreover, it ships executables with it, negatively impacting the lightweightness of the app.
+ Added support for `.env` (dotenv) file.
+ Now a database URL is required. For example, `[database]directory = /path/to/data/` becomes
`[database]url = sqlite:////path/to/data/data.sqlite` (site.conf) or
`DATABASE_URL=sqlite:////path/to/data/data.sqlite` (.env).
## 0.8 ## 0.8
+ Schema changes: + Schema changes:

View file

@ -24,16 +24,13 @@ suitable as a community or team knowledge base.
+ **Peewee** ORM. + **Peewee** ORM.
+ **Markdown** for page rendering. + **Markdown** for page rendering.
+ **Python-I18n**. + **Python-I18n**.
* **Python-Dotenv**.
+ The database drivers needed for the type of database. + The database drivers needed for the type of database.
### Optional requirements
* **Markdown-KaTeX** if you want to display math inside pages.
## Usage ## Usage
+ Clone this repository: `git clone https://github.com/sakuragasaki46/salvi` + Clone this repository: `git clone https://github.com/sakuragasaki46/salvi`
+ Edit site.conf with the needed parameters. An example site.conf: + Edit site.conf (old way) or .env (new way) with the needed parameters. An example site.conf with SQLite:
``` ```
[site] [site]
@ -41,6 +38,13 @@ name = Salvi
[database] [database]
directory = /path/to/database/ directory = /path/to/database/
```
An example .env with MySQL:
```
APP_NAME=Salvi
DATABASE_URL=mysql://root:root@localhost/salvi
``` ```
+ Run `python3 -m app_init` to initialize the database and create the administrator user. + Run `python3 -m app_init` to initialize the database and create the administrator user.
@ -51,6 +55,7 @@ directory = /path/to/database/
+ The whole application is untested. + The whole application is untested.
+ If you forget the password, there is currently no way to reset it. + If you forget the password, there is currently no way to reset it.
+ This app comes with no content. It means, you have to write it yourself.
## License ## License

73
app.py
View file

@ -21,7 +21,7 @@ from werkzeug.security import generate_password_hash, check_password_hash
from werkzeug.routing import BaseConverter from werkzeug.routing import BaseConverter
from peewee import * from peewee import *
from playhouse.db_url import connect as dbconnect from playhouse.db_url import connect as dbconnect
import calendar, datetime, hashlib, html, importlib, json, markdown, os, random, \ import datetime, hashlib, html, importlib, json, markdown, os, random, \
re, sys, warnings re, sys, warnings
from functools import lru_cache, partial from functools import lru_cache, partial
from urllib.parse import quote from urllib.parse import quote
@ -29,8 +29,9 @@ from configparser import ConfigParser
import i18n import i18n
import gzip import gzip
from getpass import getpass from getpass import getpass
import dotenv
__version__ = '0.8' __version__ = '0.9-dev'
#### CONSTANTS #### #### CONSTANTS ####
@ -45,11 +46,17 @@ PING_RE = r'(?<!\w)@(' + USERNAME_RE + r')'
#### GENERAL CONFIG #### #### GENERAL CONFIG ####
CONFIG_FILE = os.environ.get('SALVI_CONF', APP_BASE_DIR + '/site.conf') dotenv.load_dotenv()
CONFIG_FILE = os.getenv('SALVI_CONF', APP_BASE_DIR + '/site.conf')
# security check: one may specify only configuration files INSIDE
# the code directory.
if not os.path.abspath(CONFIG_FILE).startswith(APP_BASE_DIR):
raise OSError("Invalid configuration file")
DEFAULT_CONF = { DEFAULT_CONF = {
('site', 'title'): 'Salvi', ('site', 'title'): os.getenv('APP_NAME', 'Salvi'),
('database', 'directory'): APP_BASE_DIR + "/database",
} }
_cfp = ConfigParser() _cfp = ConfigParser()
@ -69,15 +76,6 @@ else:
print('Uh oh, site.conf not found.') print('Uh oh, site.conf not found.')
exit(-1) exit(-1)
#### OPTIONAL IMPORTS ####
markdown_katex = None
try:
if _getconf('appearance', 'math') != 'off':
import markdown_katex #pragma: no cover
except ImportError:
pass
#### misc. helpers #### #### misc. helpers ####
def _makelist(l): def _makelist(l):
@ -134,11 +132,12 @@ class SpoilerExtension(markdown.extensions.Extension):
#### DATABASE SCHEMA #### #### DATABASE SCHEMA ####
database_url = _getconf('database', 'url') database_url = os.getenv("DATABASE_URL") or _getconf('database', 'url')
if database_url: if database_url:
database = dbconnect(database_url) database = dbconnect(database_url)
else: else:
database = SqliteDatabase(_getconf("database", "directory") + '/data.sqlite') print("Database URL required.")
exit(-1)
class BaseModel(Model): class BaseModel(Model):
class Meta: class Meta:
@ -241,7 +240,7 @@ class Page(BaseModel):
flags = BitField() flags = BitField()
is_redirect = flags.flag(1) is_redirect = flags.flag(1)
is_sync = flags.flag(2) is_sync = flags.flag(2)
is_math_enabled = flags.flag(4) is_math_enabled = flags.flag(4) # legacy, math is no more supported
is_locked = flags.flag(8) is_locked = flags.flag(8)
is_cw = flags.flag(16) is_cw = flags.flag(16)
@property @property
@ -254,7 +253,7 @@ class Page(BaseModel):
if self.is_cw: if self.is_cw:
return '(Content Warning: we are not allowed to show a description.)' return '(Content Warning: we are not allowed to show a description.)'
full_text = self.latest.text full_text = self.latest.text
text = remove_tags(full_text, convert = not self.is_math_enabled and not _getconf('appearance', 'simple_remove_tags', False)) text = remove_tags(full_text, convert = not _getconf('appearance', 'simple_remove_tags', False))
return text[:200] + ('\u2026' if len(text) > 200 else '') return text[:200] + ('\u2026' if len(text) > 200 else '')
def change_tags(self, new_tags): def change_tags(self, new_tags):
old_tags = set(x.name for x in self.tags) old_tags = set(x.name for x in self.tags)
@ -366,10 +365,10 @@ class PageRevision(BaseModel):
@property @property
def text(self): def text(self):
return self.textref.get_content() return self.textref.get_content()
def html(self, *, math=True): def html(self):
return md(self.text, math=self.page.is_math_enabled and math) return md(self.text)
def html_and_toc(self, *, math=True): def html_and_toc(self):
return md_and_toc(self.text, math=self.page.is_math_enabled and math) return md_and_toc(self.text)
def human_pub_date(self): def human_pub_date(self):
delta = datetime.datetime.now() - self.pub_date delta = datetime.datetime.now() - self.pub_date
T = partial(get_string, g.lang) T = partial(get_string, g.lang)
@ -571,10 +570,7 @@ def has_perms(user, flags, page=None):
#### WIKI SYNTAX #### #### WIKI SYNTAX ####
def md_and_toc(text, expand_magic=False, toc=True, math=True): def md_and_toc(text, toc=True):
if expand_magic:
# DEPRECATED seeking for a better solution.
warnings.warn('Magic words are no more supported.', DeprecationWarning)
extensions = ['tables', 'footnotes', 'fenced_code', 'sane_lists'] extensions = ['tables', 'footnotes', 'fenced_code', 'sane_lists']
extension_configs = {} extension_configs = {}
if not _getconf('markdown', 'disable_custom_extensions'): if not _getconf('markdown', 'disable_custom_extensions'):
@ -582,12 +578,6 @@ def md_and_toc(text, expand_magic=False, toc=True, math=True):
extensions.append(SpoilerExtension()) extensions.append(SpoilerExtension())
if toc: if toc:
extensions.append('toc') extensions.append('toc')
if math and markdown_katex and ('$`' in text or '```math' in text):
extensions.append('markdown_katex')
extension_configs['markdown_katex'] = {
'no_inline_svg': True,
'insert_fonts_css': not _getconf('site', 'katex_url')
}
try: try:
converter = markdown.Markdown(extensions=extensions, extension_configs=extension_configs) converter = markdown.Markdown(extensions=extensions, extension_configs=extension_configs)
if toc: if toc:
@ -597,14 +587,14 @@ def md_and_toc(text, expand_magic=False, toc=True, math=True):
except Exception as e: except Exception as e:
return '<p class="error">There was an error during rendering: {e.__class__.__name__}: {e}</p>'.format(e=e), '' return '<p class="error">There was an error during rendering: {e.__class__.__name__}: {e}</p>'.format(e=e), ''
def md(text, expand_magic=False, toc=True, math=True): def md(text, toc=True):
return md_and_toc(text, expand_magic=expand_magic, toc=toc, math=math)[0] return md_and_toc(text, toc=toc)[0]
def remove_tags(text, convert=True, headings=True): def remove_tags(text, convert=True, headings=True):
if headings: if headings:
text = re.sub(r'\#[^\n]*', '', text) text = re.sub(r'\#[^\n]*', '', text)
if convert: if convert:
text = md(text, toc=False, math=False) text = md(text, toc=False)
return re.sub(r'<.*?>', '', text) return re.sub(r'<.*?>', '', text)
def is_username(s): def is_username(s):
@ -670,12 +660,10 @@ def _inject_variables():
return { return {
'T': partial(get_string, _get_lang()), 'T': partial(get_string, _get_lang()),
'app_name': _getconf('site', 'title'), 'app_name': os.getenv("APP_NAME") or _getconf('site', 'title'),
'strong': lambda x:Markup('<strong>{0}</strong>').format(x), 'strong': lambda x:Markup('<strong>{0}</strong>').format(x),
'app_version': __version__, 'app_version': __version__,
'math_version': markdown_katex.__version__ if markdown_katex else None, 'material_icons_url': _getconf('site', 'material_icons_url')
'material_icons_url': _getconf('site', 'material_icons_url'),
'katex_url': _getconf('site', 'katex_url')
} }
@login_manager.user_loader @login_manager.user_loader
@ -731,7 +719,7 @@ def error_500(body):
# Middle point during page editing. # Middle point during page editing.
def savepoint(form, is_preview=False, pageobj=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'])
else: else:
preview = None preview = None
pl_js_info = dict() pl_js_info = dict()
@ -747,7 +735,6 @@ def savepoint(form, is_preview=False, pageobj=None):
pl_text=form['text'], pl_text=form['text'],
pl_tags=form['tags'], pl_tags=form['tags'],
pl_comment=form['comment'], pl_comment=form['comment'],
pl_enablemath='enablemath' in form,
pl_is_locked='lockpage' in form, pl_is_locked='lockpage' in form,
pl_owner_is_current_user=pageobj.is_owned_by(current_user) if pageobj else True, pl_owner_is_current_user=pageobj.is_owned_by(current_user) if pageobj else True,
preview=preview, preview=preview,
@ -787,7 +774,6 @@ def create():
touched=datetime.datetime.now(), touched=datetime.datetime.now(),
owner_id=current_user.id, owner_id=current_user.id,
calendar=datetime.date.fromisoformat(request.form["calendar"]) if 'usecalendar' in request.form else None, calendar=datetime.date.fromisoformat(request.form["calendar"]) if 'usecalendar' in request.form else None,
is_math_enabled='enablemath' in request.form,
is_locked = 'lockpage' in request.form, is_locked = 'lockpage' in request.form,
is_cw = 'cw' in request.form is_cw = 'cw' in request.form
) )
@ -842,7 +828,6 @@ def edit(id):
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_locked = 'lockpage' in request.form p.is_locked = 'lockpage' in request.form
p.is_cw = 'cw' 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.calendar = datetime.date.fromisoformat(request.form["calendar"]) if 'usecalendar' in request.form else None
@ -867,8 +852,6 @@ def edit(id):
"tags": ','.join(x.name for x in p.tags), "tags": ','.join(x.name for x in p.tags),
"comment": "" "comment": ""
} }
if p.is_math_enabled:
form["enablemath"] = "1"
if p.is_locked: if p.is_locked:
form["lockpage"] = "1" form["lockpage"] = "1"
if p.calendar: if p.calendar:

View file

@ -12,19 +12,6 @@
{% else %} {% else %}
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons"> <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
{% endif %} {% endif %}
{% if math_version and katex_url %}
<link rel="stylesheet" href="{{ katex_url }}" />
<style type="text/css">
.katex img {
object-fit: fill;
padding: unset;
display: block;
position: absolute;
width: 100%;
height: inherit;
}
</style>
{% endif %}
{% block json_info %}{% endblock %} {% block json_info %}{% endblock %}
</head> </head>
<body {% if request.cookies.get('dark') == '1' %}class="dark"{% endif %}> <body {% if request.cookies.get('dark') == '1' %}class="dark"{% endif %}>

View file

@ -45,9 +45,6 @@
{% if not pl_readonly %} {% 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 and pl_enablemath %}
<p>Math with KaTeX is <mark>enabled</mark>. <a href="https://katex.org/docs/supported.html">KaTeX guide</a></p>
{% endif %}
</div> </div>
<div class="over-text-input"></div> <div class="over-text-input"></div>
{% else %} {% else %}
@ -68,10 +65,6 @@
<input type="text" name="comment" value="{{ pl_comment }}" placeholder="{{ T('write-a-comment') }}" /> <input type="text" name="comment" value="{{ pl_comment }}" placeholder="{{ T('write-a-comment') }}" />
</div> </div>
<h3>Advanced options</h3> <h3>Advanced options</h3>
<div>
<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>
</div>
{% if pl_owner_is_current_user %} {% if pl_owner_is_current_user %}
<div> <div>
<input type="checkbox" id="CB__lockpage" name="lockpage" {% if pl_is_locked %}checked=""{% endif %}> <input type="checkbox" id="CB__lockpage" name="lockpage" {% if pl_is_locked %}checked=""{% endif %}>

View file

@ -9,7 +9,7 @@
{% block json_info %}<script>window.page_info={{ p.js_info()|tojson|safe }};</script>{% endblock %} {% block json_info %}<script>window.page_info={{ p.js_info()|tojson|safe }};</script>{% endblock %}
{% set html_and_toc = rev.html_and_toc(math = request.args.get('math') not in ['0', 'false', 'no', 'off']) %} {% set html_and_toc = rev.html_and_toc() %}
{% block content %} {% block content %}
<article> <article>