realms-wiki/realms/modules/wiki/models.py

201 lines
6.2 KiB
Python
Raw Normal View History

2013-10-02 07:32:53 +03:00
import os
import re
2013-10-08 22:47:49 +03:00
import lxml.html
2013-12-03 22:09:57 +02:00
from lxml.html.clean import Cleaner
2013-10-03 17:58:07 +03:00
import ghdiff
2013-10-15 23:32:17 +03:00
import gittle.utils
2014-09-07 19:54:51 +03:00
import yaml
from gittle import Gittle
2013-10-05 00:42:45 +03:00
from dulwich.repo import NotGitRepository
2013-11-08 20:20:40 +02:00
from werkzeug.utils import escape, unescape
2014-08-30 18:06:12 +03:00
from realms.lib.util import to_canonical
from realms import cache
2013-09-29 00:33:00 +03:00
2013-10-02 04:50:48 +03:00
class MyGittle(Gittle):
2013-10-03 17:58:07 +03:00
2013-10-02 04:50:48 +03:00
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
2013-10-02 07:32:53 +03:00
def mv_fs(self, file_pair):
old_name, new_name = file_pair
os.rename(self.path + "/" + old_name, self.path + "/" + new_name)
2013-10-02 04:50:48 +03:00
class Wiki():
path = None
base_path = '/'
default_ref = 'master'
default_committer_name = 'Anon'
default_committer_email = 'anon@anon.anon'
2013-10-02 04:50:48 +03:00
index_page = 'home'
2013-09-29 00:33:00 +03:00
repo = None
2013-10-01 07:10:10 +03:00
def __init__(self, path):
try:
2013-10-02 04:50:48 +03:00
self.repo = MyGittle(path)
2013-10-05 00:42:45 +03:00
except NotGitRepository:
self.repo = MyGittle.init(path)
2013-10-01 07:10:10 +03:00
self.path = path
def __repr__(self):
return "Wiki: %s" % self.path
2013-10-15 23:32:17 +03:00
def revert_page(self, name, commit_sha, message, username):
page = self.get_page(name, commit_sha)
if not page:
# Page not found
return None
2014-08-20 18:28:25 +03:00
commit_info = gittle.utils.git.commit_info(self.repo[commit_sha.encode('latin-1')])
2013-10-15 23:32:17 +03:00
message = commit_info['message']
return self.write_page(name, page['data'], message=message, username=username)
2013-10-03 17:58:07 +03:00
def write_page(self, name, content, message=None, create=False, username=None, email=None):
2013-10-08 22:47:49 +03:00
2013-11-08 20:20:40 +02:00
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)) + "```"
cname = to_canonical(name)
2013-10-08 22:47:49 +03:00
# 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)
2013-12-03 22:09:57 +02:00
cleaner = Cleaner(remove_unknown_tags=False,
kill_tags=set(['style']),
safe_attrs_only=False)
2013-10-08 22:47:49 +03:00
tree = cleaner.clean_html(tree)
content = lxml.html.tostring(tree, encoding='utf-8', method='html')
2014-08-30 18:06:12 +03:00
# remove added div tags
2013-10-08 01:03:23 +03:00
content = content[5:-6]
2013-11-08 20:20:40 +02:00
2013-10-15 23:32:17 +03:00
# 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)
2013-11-08 20:20:40 +02:00
2014-09-07 19:54:51 +03:00
# Handlebars partial ">"
content = re.sub(r"\{\{&gt;(.*?)\}\}", r'{{>\1}}', content)
2014-09-10 18:53:22 +03:00
# Handlebars, allow {{}} inside HTML links
content = content.replace("%7B", "{")
content = content.replace("%7D", "}")
2013-10-08 22:47:49 +03:00
content = re.sub(r"```(.*?)```", unescape_repl, content, flags=re.DOTALL)
2014-08-30 18:06:12 +03:00
filename = self.cname_to_filename(cname)
2014-08-20 18:28:25 +03:00
with open(self.path + "/" + filename, 'w') as f:
f.write(content)
2013-10-01 07:10:10 +03:00
if create:
self.repo.add(filename)
2013-10-03 17:58:07 +03:00
if not message:
message = "Updated %s" % name
if not username:
username = self.default_committer_name
if not email:
2014-08-20 18:28:25 +03:00
email = self.default_committer_email
2013-10-03 17:58:07 +03:00
2014-08-30 18:06:12 +03:00
ret = self.repo.commit(name=username,
email=email,
message=message,
files=[filename])
cache.delete(cname)
2014-08-30 18:06:12 +03:00
return ret
2013-10-01 07:10:10 +03:00
def rename_page(self, old_name, new_name):
2013-10-02 07:32:53 +03:00
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_many(old_name, new_name)
2013-10-02 04:50:48 +03:00
def get_page(self, name, sha='HEAD'):
cached = cache.get(name)
if cached:
return cached
2013-11-08 20:20:40 +02:00
# commit = gittle.utils.git.commit_info(self.repo[sha])
2014-08-20 18:28:25 +03:00
name = self.cname_to_filename(name).encode('latin-1')
sha = sha.encode('latin-1')
2013-10-01 07:10:10 +03:00
try:
data = self.repo.get_commit_files(sha, paths=[name]).get(name)
if not data:
return None
partials = {}
if data.get('data'):
meta = self.get_meta(data['data'])
if meta and 'import' in meta:
for partial_name in meta['import']:
partials[partial_name] = self.get_page(partial_name)
data['partials'] = partials
return data
2013-10-01 07:10:10 +03:00
except KeyError:
# HEAD doesn't exist yet
2013-10-02 04:50:48 +03:00
return None
2014-09-07 19:54:51 +03:00
def get_meta(self, content):
if not content.startswith("---"):
return None
meta_end = re.search("\n(\.{3}|\-{3})", content)
if not meta_end:
return None
try:
return yaml.safe_load(content[0:meta_end.start()])
except Exception as e:
return {'error': e.message}
2014-09-07 19:54:51 +03:00
2013-10-15 23:32:17 +03:00
def compare(self, name, old_sha, new_sha):
2013-10-03 17:58:07 +03:00
old = self.get_page(name, sha=old_sha)
new = self.get_page(name, sha=new_sha)
return ghdiff.diff(old['data'], new['data'])
2013-10-02 04:50:48 +03:00
def get_history(self, name):
2013-10-02 07:32:53 +03:00
return self.repo.file_history(self.cname_to_filename(name))
2014-08-30 18:06:12 +03:00
@staticmethod
def cname_to_filename(cname):
2013-10-02 07:32:53 +03:00
return cname.lower() + ".md"