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 __repr__(self): return "Wiki: %s" % self.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 = '
' + content + '
' 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>)", "\n>", content) content = re.sub(r"(^>)", ">", 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(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_many(old_name, new_name) def get_page(self, name, sha='HEAD'): cached = cache.get(name) if cached: return cached # 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"