This commit is contained in:
Matthew Scragg 2014-08-30 10:06:12 -05:00
parent b02d3db684
commit 86f0549e44
24 changed files with 710 additions and 398 deletions

View file

@ -1,8 +1,7 @@
from realms.lib.assets import register
from realms import assets
register(
'editor',
'js/ace/ace.js',
'js/ace/mode-markdown.js',
'vendor/keymaster/keymaster.js',
'js/dillinger.js')
assets.register('editor',
'js/ace/ace.js',
'js/ace/mode-markdown.js',
'vendor/keymaster/keymaster.js',
'js/dillinger.js')

View file

@ -0,0 +1,165 @@
import os
import re
import lxml.html
from lxml.html.clean import Cleaner
import ghdiff
import gittle.utils
from gittle import Gittle
from dulwich.repo import NotGitRepository
from werkzeug.utils import escape, unescape
from realms.lib.util import to_canonical
from realms import cache
class MyGittle(Gittle):
def file_history(self, path):
"""Returns all commits where given file was modified
"""
versions = []
commits_info = self.commit_info()
seen_shas = set()
for commit in commits_info:
try:
files = self.get_commit_files(commit['sha'], paths=[path])
file_path, file_data = files.items()[0]
except IndexError:
continue
file_sha = file_data['sha']
if file_sha in seen_shas:
continue
else:
seen_shas.add(file_sha)
versions.append(dict(author=commit['author']['name'],
time=commit['time'],
file_sha=file_sha,
sha=commit['sha'],
message=commit['message']))
return versions
def mv_fs(self, file_pair):
old_name, new_name = file_pair
os.rename(self.path + "/" + old_name, self.path + "/" + new_name)
class Wiki():
path = None
base_path = '/'
default_ref = 'master'
default_committer_name = 'Anon'
default_committer_email = 'anon@anon.anon'
index_page = 'home'
repo = None
def __init__(self, path):
try:
self.repo = MyGittle(path)
except NotGitRepository:
self.repo = MyGittle.init(path)
self.path = path
def revert_page(self, name, commit_sha, message, username):
page = self.get_page(name, commit_sha)
if not page:
# Page not found
return None
commit_info = gittle.utils.git.commit_info(self.repo[commit_sha.encode('latin-1')])
message = commit_info['message']
return self.write_page(name, page['data'], message=message, username=username)
def write_page(self, name, content, message=None, create=False, username=None, email=None):
def escape_repl(m):
if m.group(1):
return "```" + escape(m.group(1)) + "```"
def unescape_repl(m):
if m.group(1):
return "```" + unescape(m.group(1)) + "```"
# prevents p tag from being added, we remove this later
content = '<div>' + content + '</div>'
content = re.sub(r"```(.*?)```", escape_repl, content, flags=re.DOTALL)
tree = lxml.html.fromstring(content)
cleaner = Cleaner(remove_unknown_tags=False,
kill_tags=set(['style']),
safe_attrs_only=False)
tree = cleaner.clean_html(tree)
content = lxml.html.tostring(tree, encoding='utf-8', method='html')
# remove added div tags
content = content[5:-6]
# FIXME this is for block quotes, doesn't work for double ">"
content = re.sub(r"(\n&gt;)", "\n>", content)
content = re.sub(r"(^&gt;)", ">", content)
content = re.sub(r"```(.*?)```", unescape_repl, content, flags=re.DOTALL)
cname = to_canonical(name)
filename = self.cname_to_filename(cname)
with open(self.path + "/" + filename, 'w') as f:
f.write(content)
if create:
self.repo.add(filename)
if not message:
message = "Updated %s" % name
if not username:
username = self.default_committer_name
if not email:
email = self.default_committer_email
ret = self.repo.commit(name=username,
email=email,
message=message,
files=[filename])
cache.delete_memoized(Wiki.get_page, cname)
return ret
def rename_page(self, old_name, new_name):
old_name, new_name = map(self.cname_to_filename, [old_name, new_name])
self.repo.mv([(old_name, new_name)])
self.repo.commit(name=self.default_committer_name,
email=self.default_committer_email,
message="Moving %s to %s" % (old_name, new_name),
files=[old_name])
cache.delete_memoized(Wiki.get_page, old_name)
cache.delete_memoized(Wiki.get_page, new_name)
@cache.memoize()
def get_page(self, name, sha='HEAD'):
# commit = gittle.utils.git.commit_info(self.repo[sha])
name = self.cname_to_filename(name).encode('latin-1')
sha = sha.encode('latin-1')
try:
return self.repo.get_commit_files(sha, paths=[name]).get(name)
except KeyError:
# HEAD doesn't exist yet
return None
def compare(self, name, old_sha, new_sha):
old = self.get_page(name, sha=old_sha)
new = self.get_page(name, sha=new_sha)
return ghdiff.diff(old['data'], new['data'])
def get_history(self, name):
return self.repo.file_history(self.cname_to_filename(name))
@staticmethod
def cname_to_filename(cname):
return cname.lower() + ".md"

View file

@ -1,5 +0,0 @@
import realms
c = realms.app.test_client()
print c.get('/wiki/_create')
print c.get('/wiki/_create/blah')

View file

@ -1,15 +1,19 @@
from flask import g, render_template, request, redirect, Blueprint, flash, url_for
from flask import g, render_template, request, redirect, Blueprint, flash, url_for, current_app
from flask.ext.login import login_required
from realms.lib.util import to_canonical, remove_ext
from realms import config
from realms.modules.wiki.models import Wiki
from realms import config, current_user
blueprint = Blueprint('wiki', __name__, url_prefix=config.RELATIVE_PATH)
wiki = Wiki(config.WIKI_PATH)
@blueprint.route("/_commit/<sha>/<name>")
def commit(name, sha):
cname = to_canonical(name)
data = g.current_wiki.get_page(cname, sha=sha)
data = wiki.get_page(cname, sha=sha)
if data:
return render_template('wiki/page.html', name=name, page=data, commit=sha)
else:
@ -18,41 +22,42 @@ def commit(name, sha):
@blueprint.route("/_compare/<name>/<regex('[^.]+'):fsha><regex('\.{2,3}'):dots><regex('.+'):lsha>")
def compare(name, fsha, dots, lsha):
diff = g.current_wiki.compare(name, fsha, lsha)
diff = wiki.compare(name, fsha, lsha)
return render_template('wiki/compare.html', name=name, diff=diff, old=fsha, new=lsha)
@blueprint.route("/_revert", methods=['POST'])
@login_required
def revert():
if request.method == 'POST':
name = request.form.get('name')
commit = request.form.get('commit')
cname = to_canonical(name)
g.current_wiki.revert_page(name, commit, message="Reverting %s" % cname,
username=g.current_user.get('username'))
flash('Page reverted', 'success')
return redirect(url_for('wiki.page', name=cname))
name = request.form.get('name')
commit = request.form.get('commit')
cname = to_canonical(name)
wiki.revert_page(name, commit, message="Reverting %s" % cname,
username=g.current_user.username)
flash('Page reverted', 'success')
return redirect(url_for('wiki.page', name=cname))
@blueprint.route("/_history/<name>")
def history(name):
history = g.current_wiki.get_history(name)
history = wiki.get_history(name)
return render_template('wiki/history.html', name=name, history=history, wiki_home=url_for('wiki.page'))
@blueprint.route("/_edit/<name>", methods=['GET', 'POST'])
@login_required
def edit(name):
data = g.current_wiki.get_page(name)
data = wiki.get_page(name)
cname = to_canonical(name)
if request.method == 'POST':
edit_cname = to_canonical(request.form['name'])
if edit_cname.lower() != cname.lower():
g.current_wiki.rename_page(cname, edit_cname)
wiki.rename_page(cname, edit_cname)
g.current_wiki.write_page(edit_cname,
request.form['content'],
message=request.form['message'],
username=g.current_user.get('username'))
wiki.write_page(edit_cname,
request.form['content'],
message=request.form['message'],
username=g.current_user.username)
else:
if data:
name = remove_ext(data['name'])
@ -64,22 +69,24 @@ def edit(name):
@blueprint.route("/_delete/<name>", methods=['POST'])
@login_required
def delete(name):
pass
@blueprint.route("/_create/", defaults={'name': None}, methods=['GET', 'POST'])
@blueprint.route("/_create/<name>", methods=['GET', 'POST'])
@login_required
def create(name):
if request.method == 'POST':
g.current_wiki.write_page(request.form['name'],
request.form['content'],
message=request.form['message'],
create=True,
username=g.current_user.get('username'))
wiki.write_page(request.form['name'],
request.form['content'],
message=request.form['message'],
create=True,
username=g.current_user.username)
else:
cname = to_canonical(name) if name else ""
if cname and g.current_wiki.get_page(cname):
if cname and wiki.get_page(cname):
# Page exists, edit instead
return redirect(url_for('wiki.edit', name=cname))
@ -94,7 +101,7 @@ def page(name):
if cname != name:
return redirect(url_for('wiki.page', name=cname))
data = g.current_wiki.get_page(cname)
data = wiki.get_page(cname)
if data:
return render_template('wiki/page.html', name=cname, page=data)