Skip to content

Commit

Permalink
문서 편집 기능 추가
Browse files Browse the repository at this point in the history
  • Loading branch information
ipari committed Aug 20, 2018
1 parent 4be3c1e commit 8648b6a
Show file tree
Hide file tree
Showing 9 changed files with 195 additions and 13 deletions.
59 changes: 59 additions & 0 deletions features/edit.py
@@ -0,0 +1,59 @@
import re
from flask import Blueprint, redirect, request, render_template

from .config import config
from .note import file_path, menu_list, note_meta, process_page, raw_page, \
render_markdown
from .user import logged_in


blueprint = Blueprint('edit', __name__)


def save_note(page_path, content):
path = file_path(page_path)
with open(path, 'w') as f:
f.write(content)


def edit_page(page_path):
base_url = config('note')['base_url']
content = raw_page(page_path)
# ` 문자는 ES6에서 템플릿 문자로 사용되므로 escape 해줘야 한다.
content = content.replace('`', '\`')
return render_template('edit.html',
pagename=page_path,
meta=note_meta(),
base_url=base_url,
menu=menu_list(),
content=content)


@blueprint.route('/preview', methods=['POST'])
def preview():
html = render_markdown(request.get_json()['raw_md'])

# 이미지 주소가 /edit 기준으로 렌더링되어있어 base_url 기준으로 고친다.
page_root = request.referrer.split('/')[-2]
base_url = config('note')['base_url']
rel_url = '{}/{}'.format(base_url, page_root)

def replace_path(matchobj):
return '{}/{}/{}{}'.format(matchobj.group(1), rel_url,
matchobj.group(2), matchobj.group(3))

pattern = r'(src=\")([^\"]*)(\")'
html = re.sub(pattern, replace_path, html)
return html


@blueprint.route('/edit/<path:page_path>', methods=['GET', 'POST'])
def view_edit(page_path):
if not logged_in():
return redirect('/login')
else:
if request.method == 'GET':
return edit_page(page_path)
else:
save_note(page_path, request.form['md'])
return process_page(page_path)
29 changes: 20 additions & 9 deletions features/note.py
Expand Up @@ -40,17 +40,28 @@ def page_permission(page_path):
return page_permissions().get(page_path, 0)


def render_page(page_path, meta, menu):
def raw_page(page_path):
path = file_path(page_path)
try:
with open(path, 'r') as f:
return f.read()
except IOError:
return None


def render_markdown(raw_md):
extensions = md_extensions()
return markdown.markdown(raw_md, extensions=extensions)

with open(path, 'r') as f:
extensions = md_extensions()
content = markdown.markdown(f.read(), extensions=extensions)
return render_template('page.html',
meta=meta,
pagename=page_path,
menu=menu,
content=content)

def render_page(page_path, meta, menu):
raw_md = raw_page(page_path)
content = render_markdown(raw_md)
return render_template('page.html',
meta=meta,
pagename=page_path,
menu=menu,
content=content)


def error_page(page_path, meta, menu, message):
Expand Down
3 changes: 2 additions & 1 deletion main.py
@@ -1,6 +1,6 @@
from flask import Flask, redirect, url_for

from features import archive, note, user
from features import archive, edit, note, user
from features.config import config


Expand All @@ -17,6 +17,7 @@

url_prefix = '/{}'.format(config('note')['base_url'])
app.register_blueprint(archive.blueprint)
app.register_blueprint(edit.blueprint)
app.register_blueprint(note.blueprint, url_prefix=url_prefix)
app.register_blueprint(user.blueprint)

Expand Down
7 changes: 7 additions & 0 deletions themes/yaong/static/css/simplemde.min.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 21 additions & 1 deletion themes/yaong/static/css/style.css
Expand Up @@ -418,6 +418,22 @@ div.alert p.admonition-title {
}


/* Editor */
form.editor {
margin: 1.5em 0;
}

form div.buttons {
text-align: right;
}

form a.button {
display: inline-block;
margin-top: .4em;
padding: .5em .8em;
}


/* Footnotes */
div.footnote ol {
padding: 0 1em;
Expand Down Expand Up @@ -457,7 +473,7 @@ div.error {
margin-bottom: .6em;
}

form div {
div.login form div {
margin-bottom: .6em;
}

Expand All @@ -482,6 +498,10 @@ input[type=submit] {
cursor: pointer;
}

input[type=submit]:hover {
background-color: #6cb0bf;
}


/* Footer */
footer {
Expand Down
18 changes: 16 additions & 2 deletions themes/yaong/static/js/script.js
Expand Up @@ -70,17 +70,31 @@ function selects(query) {
return document.querySelectorAll(query);
}


function getElementValue(query, property) {
let elem = select(query);
let style = window.getComputedStyle(elem, null);
return style.getPropertyValue(property);
}


function setElementValue(query, property, value) {
let elements = selects(query);
for (let element of elements) {
element.style[property] = value;
}
}

function previewMarkdown(preview, plainText) {
let ajax = new XMLHttpRequest();
let parameters = {
"raw_md": plainText
};

ajax.open("POST", "/preview", true);
ajax.setRequestHeader("Content-type", "application/json");
ajax.onreadystatechange = function() {
if (ajax.readyState === 4 && ajax.status === 200) {
preview.innerHTML = ajax.responseText;
}
};
ajax.send(JSON.stringify(parameters));
}
15 changes: 15 additions & 0 deletions themes/yaong/static/js/simplemde.min.js

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions themes/yaong/templates/_base.html
Expand Up @@ -4,10 +4,12 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="{{ url_for('static', filename='js/script.js') }}"></script>
{% block script %}{% endblock %}
<title>{% block pagename %}{% endblock %} - {{ meta.note_name }}</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/normalize.css') }}" />
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}" />
<link rel="stylesheet" href="{{ url_for('static', filename='css/pygments/arduino.css') }}" />
{% block css %}{% endblock %}
</head>
<body>
<header>
Expand Down
53 changes: 53 additions & 0 deletions themes/yaong/templates/edit.html
@@ -0,0 +1,53 @@
{% extends "_base.html" %}

{% block script %}
<script src="{{ url_for('static', filename='js/simplemde.min.js') }}"></script>
<script src="https://cdn.jsdelivr.net/highlight.js/latest/highlight.min.js"></script>
{% endblock %}

{% block css %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/simplemde.min.css') }}" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/highlight.js/latest/styles/github.min.css">
{% endblock %}

{% block pagename %}{{ pagename }}{% endblock %}

{% block content %}
<h1>{{ pagename }}</h1>

<form method="post" class="editor">
<textarea name="md" id="md"></textarea>
<div class="buttons">
<input type="submit" value="저장"/>
<a class="button" href="/{{ base_url }}/{{ pagename }}">취소</a>
</div>
</form>

{# https://github.com/sparksuite/simplemde-markdown-editor #}
<script>
let simplemde = new SimpleMDE({
autofocus: true,
autosave: {
enabled: true,
uniqueId: "{{ pagename }}",
delay: 10000
},
element: document.getElementById("md"),
indentWithTabs: false,
previewRender: function(plainText, preview) {
setTimeout(function() {
previewMarkdown(preview, plainText);
}, 250);
return "Loading...";
},
promptURLs: false,
renderingConfig: {
codeSyntaxHighlighting: true
},
spellChecker: false,
tabSize: 4
});
{# ES6 템플릿 문자열 ` 을 사용해야 한다. #}
simplemde.value(`{{ content|safe }}`);
</script>
{% endblock %}

0 comments on commit 8648b6a

Please sign in to comment.