version advance + some refactoring
This commit is contained in:
parent
ded90d1ac4
commit
910e01b691
6 changed files with 49 additions and 72 deletions
|
|
@ -1,5 +1,14 @@
|
|||
# What’s 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
|
||||
|
||||
+ Schema changes:
|
||||
|
|
|
|||
15
README.md
15
README.md
|
|
@ -24,16 +24,13 @@ suitable as a community or team knowledge base.
|
|||
+ **Peewee** ORM.
|
||||
+ **Markdown** for page rendering.
|
||||
+ **Python-I18n**.
|
||||
* **Python-Dotenv**.
|
||||
+ The database drivers needed for the type of database.
|
||||
|
||||
### Optional requirements
|
||||
|
||||
* **Markdown-KaTeX** if you want to display math inside pages.
|
||||
|
||||
## Usage
|
||||
|
||||
+ 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]
|
||||
|
|
@ -41,6 +38,13 @@ name = Salvi
|
|||
|
||||
[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.
|
||||
|
|
@ -51,6 +55,7 @@ directory = /path/to/database/
|
|||
|
||||
+ The whole application is untested.
|
||||
+ 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
|
||||
|
||||
|
|
|
|||
75
app.py
75
app.py
|
|
@ -21,7 +21,7 @@ from werkzeug.security import generate_password_hash, check_password_hash
|
|||
from werkzeug.routing import BaseConverter
|
||||
from peewee import *
|
||||
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
|
||||
from functools import lru_cache, partial
|
||||
from urllib.parse import quote
|
||||
|
|
@ -29,8 +29,9 @@ from configparser import ConfigParser
|
|||
import i18n
|
||||
import gzip
|
||||
from getpass import getpass
|
||||
import dotenv
|
||||
|
||||
__version__ = '0.8'
|
||||
__version__ = '0.9-dev'
|
||||
|
||||
#### CONSTANTS ####
|
||||
|
||||
|
|
@ -45,11 +46,17 @@ PING_RE = r'(?<!\w)@(' + USERNAME_RE + r')'
|
|||
|
||||
#### 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 = {
|
||||
('site', 'title'): 'Salvi',
|
||||
('database', 'directory'): APP_BASE_DIR + "/database",
|
||||
('site', 'title'): os.getenv('APP_NAME', 'Salvi'),
|
||||
}
|
||||
|
||||
_cfp = ConfigParser()
|
||||
|
|
@ -69,15 +76,6 @@ else:
|
|||
print('Uh oh, site.conf not found.')
|
||||
exit(-1)
|
||||
|
||||
#### OPTIONAL IMPORTS ####
|
||||
|
||||
markdown_katex = None
|
||||
try:
|
||||
if _getconf('appearance', 'math') != 'off':
|
||||
import markdown_katex #pragma: no cover
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
#### misc. helpers ####
|
||||
|
||||
def _makelist(l):
|
||||
|
|
@ -134,11 +132,12 @@ class SpoilerExtension(markdown.extensions.Extension):
|
|||
|
||||
#### DATABASE SCHEMA ####
|
||||
|
||||
database_url = _getconf('database', 'url')
|
||||
database_url = os.getenv("DATABASE_URL") or _getconf('database', 'url')
|
||||
if database_url:
|
||||
database = dbconnect(database_url)
|
||||
else:
|
||||
database = SqliteDatabase(_getconf("database", "directory") + '/data.sqlite')
|
||||
print("Database URL required.")
|
||||
exit(-1)
|
||||
|
||||
class BaseModel(Model):
|
||||
class Meta:
|
||||
|
|
@ -241,7 +240,7 @@ class Page(BaseModel):
|
|||
flags = BitField()
|
||||
is_redirect = flags.flag(1)
|
||||
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_cw = flags.flag(16)
|
||||
@property
|
||||
|
|
@ -254,7 +253,7 @@ class Page(BaseModel):
|
|||
if self.is_cw:
|
||||
return '(Content Warning: we are not allowed to show a description.)'
|
||||
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 '')
|
||||
def change_tags(self, new_tags):
|
||||
old_tags = set(x.name for x in self.tags)
|
||||
|
|
@ -366,10 +365,10 @@ class PageRevision(BaseModel):
|
|||
@property
|
||||
def text(self):
|
||||
return self.textref.get_content()
|
||||
def html(self, *, math=True):
|
||||
return md(self.text, math=self.page.is_math_enabled and math)
|
||||
def html_and_toc(self, *, math=True):
|
||||
return md_and_toc(self.text, math=self.page.is_math_enabled and math)
|
||||
def html(self):
|
||||
return md(self.text)
|
||||
def html_and_toc(self):
|
||||
return md_and_toc(self.text)
|
||||
def human_pub_date(self):
|
||||
delta = datetime.datetime.now() - self.pub_date
|
||||
T = partial(get_string, g.lang)
|
||||
|
|
@ -571,10 +570,7 @@ def has_perms(user, flags, page=None):
|
|||
|
||||
#### WIKI SYNTAX ####
|
||||
|
||||
def md_and_toc(text, expand_magic=False, toc=True, math=True):
|
||||
if expand_magic:
|
||||
# DEPRECATED seeking for a better solution.
|
||||
warnings.warn('Magic words are no more supported.', DeprecationWarning)
|
||||
def md_and_toc(text, toc=True):
|
||||
extensions = ['tables', 'footnotes', 'fenced_code', 'sane_lists']
|
||||
extension_configs = {}
|
||||
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())
|
||||
if 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:
|
||||
converter = markdown.Markdown(extensions=extensions, extension_configs=extension_configs)
|
||||
if toc:
|
||||
|
|
@ -597,14 +587,14 @@ def md_and_toc(text, expand_magic=False, toc=True, math=True):
|
|||
except Exception as 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):
|
||||
return md_and_toc(text, expand_magic=expand_magic, toc=toc, math=math)[0]
|
||||
def md(text, toc=True):
|
||||
return md_and_toc(text, toc=toc)[0]
|
||||
|
||||
def remove_tags(text, convert=True, headings=True):
|
||||
if headings:
|
||||
text = re.sub(r'\#[^\n]*', '', text)
|
||||
if convert:
|
||||
text = md(text, toc=False, math=False)
|
||||
text = md(text, toc=False)
|
||||
return re.sub(r'<.*?>', '', text)
|
||||
|
||||
def is_username(s):
|
||||
|
|
@ -670,12 +660,10 @@ def _inject_variables():
|
|||
|
||||
return {
|
||||
'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),
|
||||
'app_version': __version__,
|
||||
'math_version': markdown_katex.__version__ if markdown_katex else None,
|
||||
'material_icons_url': _getconf('site', 'material_icons_url'),
|
||||
'katex_url': _getconf('site', 'katex_url')
|
||||
'material_icons_url': _getconf('site', 'material_icons_url')
|
||||
}
|
||||
|
||||
@login_manager.user_loader
|
||||
|
|
@ -694,7 +682,7 @@ app.template_filter(name='markdown')(md)
|
|||
|
||||
@app.route('/')
|
||||
def homepage():
|
||||
page_limit = _getconf("appearance","items_per_page",20,cast=int)
|
||||
page_limit = _getconf("appearance", "items_per_page", 20, cast=int)
|
||||
return render_template('home.jinja2', new_notes=Page.select()
|
||||
.order_by(Page.touched.desc()).limit(page_limit))
|
||||
|
||||
|
|
@ -731,7 +719,7 @@ def error_500(body):
|
|||
# Middle point during page editing.
|
||||
def savepoint(form, is_preview=False, pageobj=None):
|
||||
if is_preview:
|
||||
preview = md(form['text'], math='enablemath' in form)
|
||||
preview = md(form['text'])
|
||||
else:
|
||||
preview = None
|
||||
pl_js_info = dict()
|
||||
|
|
@ -747,7 +735,6 @@ def savepoint(form, is_preview=False, pageobj=None):
|
|||
pl_text=form['text'],
|
||||
pl_tags=form['tags'],
|
||||
pl_comment=form['comment'],
|
||||
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,
|
||||
|
|
@ -787,7 +774,6 @@ def create():
|
|||
touched=datetime.datetime.now(),
|
||||
owner_id=current_user.id,
|
||||
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_cw = 'cw' in request.form
|
||||
)
|
||||
|
|
@ -842,7 +828,6 @@ def edit(id):
|
|||
p.url = p_url
|
||||
p.title = request.form['title']
|
||||
p.touched = datetime.datetime.now()
|
||||
p.is_math_enabled = 'enablemath' in request.form
|
||||
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
|
||||
|
|
@ -867,8 +852,6 @@ def edit(id):
|
|||
"tags": ','.join(x.name for x in p.tags),
|
||||
"comment": ""
|
||||
}
|
||||
if p.is_math_enabled:
|
||||
form["enablemath"] = "1"
|
||||
if p.is_locked:
|
||||
form["lockpage"] = "1"
|
||||
if p.calendar:
|
||||
|
|
|
|||
|
|
@ -12,19 +12,6 @@
|
|||
{% else %}
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
||||
{% 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 %}
|
||||
</head>
|
||||
<body {% if request.cookies.get('dark') == '1' %}class="dark"{% endif %}>
|
||||
|
|
|
|||
|
|
@ -45,9 +45,6 @@
|
|||
{% if not pl_readonly %}
|
||||
<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>
|
||||
{% 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 class="over-text-input"></div>
|
||||
{% else %}
|
||||
|
|
@ -68,10 +65,6 @@
|
|||
<input type="text" name="comment" value="{{ pl_comment }}" placeholder="{{ T('write-a-comment') }}" />
|
||||
</div>
|
||||
<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 %}
|
||||
<div>
|
||||
<input type="checkbox" id="CB__lockpage" name="lockpage" {% if pl_is_locked %}checked=""{% endif %}>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
{% 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 %}
|
||||
<article>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue