Remove magic words and uploads, deprecate upload endpoints, style changes, version advance
This commit is contained in:
parent
5d18cee75e
commit
733ea8cce1
8 changed files with 87 additions and 156 deletions
141
app.py
141
app.py
|
|
@ -31,12 +31,8 @@ try:
|
|||
from slugify import slugify
|
||||
except ImportError:
|
||||
slugify = None
|
||||
try:
|
||||
import markdown_strikethrough
|
||||
except Exception:
|
||||
markdown_strikethrough = None
|
||||
|
||||
__version__ = '0.4'
|
||||
__version__ = '0.5-dev'
|
||||
|
||||
#### CONSTANTS ####
|
||||
|
||||
|
|
@ -93,6 +89,28 @@ def _makelist(l):
|
|||
else:
|
||||
return []
|
||||
|
||||
|
||||
#### MARKDOWN EXTENSIONS ####
|
||||
|
||||
class StrikethroughExtension(markdown.extensions.Extension):
|
||||
def extendMarkdown(self, md, md_globals):
|
||||
postprocessor = StrikethroughPostprocessor(md)
|
||||
md.postprocessors.add('strikethrough', postprocessor, '>raw_html')
|
||||
|
||||
class StrikethroughPostprocessor(markdown.postprocessors.Postprocessor):
|
||||
pattern = re.compile(r"~~(((?!~~).)+)~~", re.DOTALL)
|
||||
|
||||
def run(self, html):
|
||||
return re.sub(self.pattern, self.convert, html)
|
||||
|
||||
def convert(self, match):
|
||||
return '<del>' + match.group(1) + '</del>'
|
||||
|
||||
### XXX it currently only detects spoilers that are not at the beginning of the line. To be fixed.
|
||||
class SpoilerExtension(markdown.extensions.Extension):
|
||||
def extendMarkdown(self, md, md_globals):
|
||||
md.inlinePatterns.register(markdown.inlinepatterns.SimpleTagInlineProcessor(r'()>!(.*?)!<', 'span class="spoiler"'), 'spoiler', 14)
|
||||
|
||||
#### DATABASE SCHEMA ####
|
||||
|
||||
database = SqliteDatabase(_getconf("config", "database_dir") + '/data.sqlite')
|
||||
|
|
@ -321,6 +339,9 @@ class PagePolicy(BaseModel):
|
|||
(('page', 'key'), True),
|
||||
)
|
||||
|
||||
# DEPRECATED
|
||||
# It will be possibly removed in v0.6.
|
||||
# Use external image URL instead.
|
||||
class Upload(BaseModel):
|
||||
name = CharField(256)
|
||||
url_name = CharField(256, null=True)
|
||||
|
|
@ -382,34 +403,11 @@ def init_db():
|
|||
|
||||
#### WIKI SYNTAX ####
|
||||
|
||||
magic_word_filters = {}
|
||||
|
||||
def _replace_magic_word(match):
|
||||
name = match.group(1)
|
||||
if name not in magic_word_filters:
|
||||
return match.group()
|
||||
f = magic_word_filters[name]
|
||||
try:
|
||||
return f(*(x.strip() for x in match.group(2).split('|')))
|
||||
except Exception:
|
||||
return ''
|
||||
|
||||
def expand_magic_words(text):
|
||||
'''
|
||||
Replace the special markups in double curly brackets.
|
||||
|
||||
Unknown keywords are not replaced. Valid keywords with invalid arguments are replaced with nothing.
|
||||
'''
|
||||
return re.sub(MAGIC_RE, _replace_magic_word, text)
|
||||
|
||||
def md(text, expand_magic=False, toc=True):
|
||||
if expand_magic:
|
||||
# DEPRECATED seeking for a better solution.
|
||||
warnings.warn('Magic words are no more supported.', DeprecationWarning)
|
||||
text = expand_magic_words(text)
|
||||
extensions = ['tables', 'footnotes', 'fenced_code', 'sane_lists']
|
||||
if markdown_strikethrough:
|
||||
extensions.append("markdown_strikethrough.extension")
|
||||
extensions = ['tables', 'footnotes', 'fenced_code', 'sane_lists', StrikethroughExtension(), SpoilerExtension()]
|
||||
if toc:
|
||||
extensions.append('toc')
|
||||
return markdown.Markdown(extensions=extensions).convert(text)
|
||||
|
|
@ -418,59 +416,9 @@ def remove_tags(text, convert=True, headings=True):
|
|||
if headings:
|
||||
text = re.sub(r'\#[^\n]*', '', text)
|
||||
if convert:
|
||||
text = md(text, expand_magic=False, toc=False)
|
||||
text = md(text, toc=False)
|
||||
return re.sub(r'<.*?>|\{\{.*?\}\}', '', text)
|
||||
|
||||
|
||||
### Magic words (deprecated!) ###
|
||||
|
||||
def expand_backto(pageid):
|
||||
p = Page[pageid]
|
||||
return '*« Main article: [{}]({}).*'.format(html.escape(p.title), p.get_url())
|
||||
|
||||
magic_word_filters['backto'] = expand_backto
|
||||
|
||||
def expand_upload(id, *opt):
|
||||
try:
|
||||
upload = Upload[id]
|
||||
except Upload.DoesNotExist:
|
||||
return ''
|
||||
if opt:
|
||||
desc = opt[-1]
|
||||
else:
|
||||
desc = None
|
||||
classname = 'fig-right'
|
||||
return '<figure class="{0}"><a href="/upload-info/{4}"><img alt="{1}" src="{2}" title="{1}"></a>{3}</figure>'.format(
|
||||
classname, html.escape(upload.name), upload.url,
|
||||
'<figcaption>{0}</figcaption>'.format(md(desc, expand_magic=False)) if desc else '',
|
||||
upload.id)
|
||||
|
||||
magic_word_filters['media'] = expand_upload
|
||||
|
||||
def make_gallery(items):
|
||||
result = []
|
||||
for upload, desc in items:
|
||||
result.append('<figure class="fig-gallery"><a href="/upload-info/{0}"><img alt="{1}" src="{2}" title="{1}"></a>{3}</figure>'.format(
|
||||
upload.id, html.escape(upload.name), upload.url,
|
||||
'<figcaption>{0}</figcaption>'.format(md(desc, expand_magic=False)) if desc else ''))
|
||||
return '<div class="gallery">' + ''.join(result) + '</div>'
|
||||
|
||||
def expand_gallery(*ids):
|
||||
items = []
|
||||
for i in ids:
|
||||
if ' ' in i:
|
||||
id, desc = i.split(' ', 1)
|
||||
else:
|
||||
id, desc = i, ''
|
||||
try:
|
||||
upload = Upload[id]
|
||||
except Upload.DoesNotExist:
|
||||
continue
|
||||
items.append((upload, desc))
|
||||
return make_gallery(items)
|
||||
|
||||
magic_word_filters['gallery'] = expand_gallery
|
||||
|
||||
#### I18N ####
|
||||
|
||||
lang_poses = {'en': 1, 'en-US': 1, 'it': 2, 'it-IT': 2}
|
||||
|
|
@ -549,8 +497,7 @@ def linebreaks(text):
|
|||
def homepage():
|
||||
page_limit = _getconf("appearance","items_per_page",20,cast=int)
|
||||
return render_template('home.html', new_notes=Page.select()
|
||||
.order_by(Page.touched.desc()).limit(page_limit),
|
||||
gallery=make_gallery((x, '') for x in Upload.select().order_by(Upload.upload_date.desc()).limit(3)))
|
||||
.order_by(Page.touched.desc()).limit(page_limit))
|
||||
|
||||
@app.route('/robots.txt')
|
||||
def robots():
|
||||
|
|
@ -795,28 +742,11 @@ def listtag(tag, page=1):
|
|||
def media(fp):
|
||||
return send_from_directory(UPLOAD_DIR, fp)
|
||||
|
||||
@app.route('/upload/', methods=['GET', 'POST'])
|
||||
# symbolic route as of v0.5
|
||||
@app.route('/upload/', methods=['GET'])
|
||||
def upload():
|
||||
if request.method == 'POST':
|
||||
name = request.form['name']
|
||||
file = request.files['file']
|
||||
filename = file.filename
|
||||
ext = os.path.splitext(filename)[1]
|
||||
content = file.read()
|
||||
try:
|
||||
upl = Upload.create_content(name, ext, content)
|
||||
flash('File uploaded successfully')
|
||||
return redirect('/upload-info/{}/'.format(upl.id))
|
||||
except Exception:
|
||||
sys.excepthook(*sys.exc_info())
|
||||
flash('Unable to upload file. Try again later.')
|
||||
return render_template('upload.html')
|
||||
|
||||
@app.route('/upload-info/<int:id>/')
|
||||
def upload_info(id):
|
||||
upl = Upload[id]
|
||||
return render_template('uploadinfo.html', upl=upl, type_list=upload_types_rev)
|
||||
|
||||
@app.route('/stats/')
|
||||
def stats():
|
||||
return render_template('stats.html',
|
||||
|
|
@ -881,7 +811,14 @@ def easter_y(y=None):
|
|||
|
||||
#### EXTENSIONS ####
|
||||
|
||||
active_extensions = ['contactnova']
|
||||
active_extensions = []
|
||||
|
||||
_i = 1
|
||||
_extension_name = _getconf('extensions', 'ext.{}'.format(_i))
|
||||
while _extension_name:
|
||||
active_extensions.append(_extension_name)
|
||||
_i += 1
|
||||
_extension_name = _getconf('extensions', 'ext.{}'.format(_i))
|
||||
|
||||
for ext in active_extensions:
|
||||
try:
|
||||
|
|
|
|||
8
static/content.js
Normal file
8
static/content.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
(function() {
|
||||
|
||||
for (let i of document.getElementsByClassName('spoiler')) {
|
||||
i.onclick = function() {
|
||||
this.classList.toggle('revealed');
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
|
@ -1,13 +1,14 @@
|
|||
/* basic styles */
|
||||
body{font-family:sans-serif}
|
||||
body{font-family:sans-serif;background-color:#faf5e9}
|
||||
.content{margin: 3em 1.3em}
|
||||
|
||||
/* content styles */
|
||||
#firstHeading {font-family: 'Oswald','Anton','Impact',sans-serif; text-align: center;font-size:3em}
|
||||
#firstHeading {font-family:sans-serif; text-align: center;font-size:3em; font-weight: normal}
|
||||
.inner-content{font-family:serif; margin: 1.7em auto; max-width: 1280px; line-height: 1.9; color: #1f2528}
|
||||
.inner-content em,.inner-content strong{color: black}
|
||||
.inner-content h1{color: #99081f}
|
||||
.inner-content table, .inner-content h2, .inner-content h3, .inner-content h4, .inner-content h5, .inner-content h6{font-family:sans-serif; color: black}
|
||||
.inner-content table {font-family: sans-serif}
|
||||
.inner-content h2, .inner-content h3, .inner-content h4, .inner-content h5, .inner-content h6{font-family:sans-serif; color: black; font-weight: normal}
|
||||
.inner-content h3{margin:0.8em 0}
|
||||
.inner-content h4{margin:0.6em 0}
|
||||
.inner-content h5{margin:0.5em 0}
|
||||
|
|
@ -19,6 +20,9 @@ body{font-family:sans-serif}
|
|||
.inner-content table > * > tr > th, .inner-content table > tr > th {background-color:#f9f9f9;border:#ccc 1px solid;padding:2px}
|
||||
.inner-content table > * > tr > td, .inner-content table > tr > td {border:#ccc 1px solid;padding:2px}
|
||||
|
||||
/* spoiler styles */
|
||||
.spoiler {color: black; background-color:black}
|
||||
.spoiler.revealed {color: #1f2528; background-color: transparent}
|
||||
|
||||
/* interface styles */
|
||||
.nl-list{list-style:none}
|
||||
|
|
@ -143,6 +147,8 @@ body.dark, .dark input, .dark textarea{background-color: #1f1f1f; color: white}
|
|||
a.dark-theme-toggle-off{display: none}
|
||||
.dark a.dark-theme-toggle-off{display: inline}
|
||||
.dark a.dark-theme-toggle-on{display: none}
|
||||
.dark .spoiler {color: white; background-color: white}
|
||||
.dark .spoiler.revealed {color: white; background-color: transparent}
|
||||
|
||||
/* ?????? */
|
||||
.wrap_responsive_cells {
|
||||
|
|
|
|||
|
|
@ -56,4 +56,5 @@
|
|||
|
||||
{% block scripts %}
|
||||
<script src="/static/edit.js"></script>
|
||||
<script src="/static/content.js"></script>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
<div class="nl-new">
|
||||
<a href="/create/"><button class="submit-primary">{{ T('new-note') }}</button></a>
|
||||
<a href="/upload/"><button class="submit-secondary">{{ T('upload-file') }}</button></a>
|
||||
</div>
|
||||
|
||||
<h2>{{ T('latest-notes') }}</h2>
|
||||
|
|
@ -31,8 +30,4 @@
|
|||
<li><a href="/p/most_recent/">{{ T('show-all') }}</a></li>
|
||||
</ul>
|
||||
|
||||
<h2>{{ T('latest-uploads') }}</h2>
|
||||
|
||||
{# TODO security flaw; find a way to do this in a more safe manner! #}
|
||||
{{ gallery|safe }}
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
<ul>
|
||||
<li>Number of pages: <strong>{{ notes_count }}</strong></li>
|
||||
<li>Number of pages with URL set: <strong>{{ notes_with_url }}</strong></li>
|
||||
<li>Number of uploads: <strong>{{ upload_count }}</strong></li>
|
||||
<li>Number of revisions: <strong>{{ revision_count }}</strong></li>
|
||||
<li>Average revisions per page: <strong>{{ (revision_count / notes_count)|round(2) }}</strong></li>
|
||||
</ul>
|
||||
|
|
|
|||
|
|
@ -3,41 +3,19 @@
|
|||
{% block content %}
|
||||
<h1>Upload new file</h1>
|
||||
|
||||
<p>Types supported: <strong>.jpeg</strong>, <strong>.jpg</strong>, <strong>.png</strong>.</p>
|
||||
<p>Uploads are no more supported. Please use this syntax instead for external images: <code></code>.</p>
|
||||
|
||||
<form method="POST" enctype="multipart/form-data">
|
||||
<div>
|
||||
<label for="name">Name: </label>
|
||||
<input type="text" id="name-input" name="name" required maxlength="256">
|
||||
<input type="text" id="name-input" name="name" required maxlength="256" disabled="">
|
||||
</div>
|
||||
<div>
|
||||
<label for="file">File: </label>
|
||||
<input type="file" id="file-input" name="file" accept="image/jpeg, image/png">
|
||||
<input type="file" id="file-input" name="file" accept="image/jpeg, image/png" disabled="">
|
||||
</div>
|
||||
<div>
|
||||
<input type="submit" class="submit-primary" value="Upload">
|
||||
<input type="submit" value="Upload" disabled="">
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
(function(){
|
||||
function last(a){
|
||||
return a[a.length-1];
|
||||
}
|
||||
|
||||
var fileInput = document.getElementById('file-input');
|
||||
var nameInput = document.getElementById('name-input');
|
||||
fileInput.onchange = function(){
|
||||
var name = last(fileInput.value.split(/[\/\\]/));
|
||||
if(name.indexOf('.') >= 0){
|
||||
name = name.replace(/\..*$/, '');
|
||||
}
|
||||
nameInput.value = name;
|
||||
|
||||
// TODO: add image preview
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -5,29 +5,32 @@
|
|||
{% block json_info %}<script>window.page_info={{ p.js_info()|tojson|safe }};</script>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1 id="firstHeading">{{ p.title }}</h1>
|
||||
<article>
|
||||
<h1 id="firstHeading">{{ p.title }}</h1>
|
||||
|
||||
<div class="jump-to-actions">
|
||||
<span>{{ T('last-changed') }} {{ rev.human_pub_date() }}</span> ·
|
||||
<a href="#page-actions">{{ T('jump-to-actions') }}</a>
|
||||
</div>
|
||||
<div class="jump-to-actions">
|
||||
<span>{{ T('last-changed') }} {{ rev.human_pub_date() }}</span> ·
|
||||
<a href="#page-actions">{{ T('jump-to-actions') }}</a>
|
||||
</div>
|
||||
|
||||
{% block history_nav %}{% endblock %}
|
||||
{% block history_nav %}{% endblock %}
|
||||
|
||||
<div class="inner-content">
|
||||
{{ rev.html()|safe }}
|
||||
</div>
|
||||
<div class="inner-content">
|
||||
{{ rev.html()|safe }}
|
||||
</div>
|
||||
|
||||
{% if p.tags %}
|
||||
<div class="page-tags">
|
||||
<p>{{ T('tags') }}:</p>
|
||||
<ul>
|
||||
{% for tag in p.tags %}
|
||||
<li><a href="/tags/{{ tag.name }}/">#{{ tag.name }}</a> <span class="tag-count">({{ tag.popularity() }})</span></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
</article>
|
||||
|
||||
{% if p.tags %}
|
||||
<div class="page-tags">
|
||||
<p>{{ T('tags') }}:</p>
|
||||
<ul>
|
||||
{% for tag in p.tags %}
|
||||
<li><a href="/tags/{{ tag.name }}/">#{{ tag.name }}</a> <span class="tag-count">({{ tag.popularity() }})</span></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block actions %}
|
||||
|
|
@ -36,3 +39,7 @@
|
|||
{{ 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 }}
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="/static/content.js"></script>
|
||||
{% endblock %}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue