add revert page

This commit is contained in:
Matthew Scragg 2013-10-15 15:32:17 -05:00
parent 9fed2175de
commit a16b45cdba
10 changed files with 79 additions and 47 deletions

View file

@ -226,20 +226,30 @@ def create_app(subdomain=None):
User.logout() User.logout()
return redirect(url_for('root')) return redirect(url_for('root'))
@app.route("/commit/<sha>/<name>") @app.route("/_commit/<sha>/<name>")
def commit_sha(name, sha): def commit_sha(name, sha):
cname = to_canonical(name) cname = to_canonical(name)
data = w.get_page(cname, sha=sha) data = w.get_page(cname, sha=sha)
if data: if data:
return render_template('page/page.html', page=data) return render_template('page/page.html', name=name, page=data, commit=sha)
else: else:
return redirect('/create/'+cname) return redirect('/create/'+cname)
@app.route("/compare/<name>/<regex('[^.]+'):fsha><regex('\.{2,3}'):dots><regex('.+'):lsha>") @app.route("/_compare/<name>/<regex('[^.]+'):fsha><regex('\.{2,3}'):dots><regex('.+'):lsha>")
def compare(name, fsha, dots, lsha): def compare(name, fsha, dots, lsha):
diff = w.compare(name, fsha, lsha) diff = w.compare(name, fsha, lsha)
return render_template('page/compare.html', name=name, diff=diff) return render_template('page/compare.html', name=name, diff=diff, old=fsha, new=lsha)
@app.route("/_revert", methods=['POST'])
def revert():
if request.method == 'POST':
name = request.form.get('name')
commit = request.form.get('commit')
cname = to_canonical(name)
w.revert_page(name, commit, message="Reverting %s" % cname, username=CurrentUser.get('username'))
flash('Page reverted', 'success')
return redirect("/" + cname)
@app.route("/register", methods=['GET', 'POST']) @app.route("/register", methods=['GET', 'POST'])
def register(): def register():
@ -263,12 +273,12 @@ def create_app(subdomain=None):
else: else:
return render_template('account/login.html') return render_template('account/login.html')
@app.route("/history/<name>") @app.route("/_history/<name>")
def history(name): def history(name):
history = w.get_history(name) history = w.get_history(name)
return render_template('page/history.html', name=name, history=history) return render_template('page/history.html', name=name, history=history)
@app.route("/edit/<name>", methods=['GET', 'POST']) @app.route("/_edit/<name>", methods=['GET', 'POST'])
@login_required @login_required
def edit(name): def edit(name):
data = w.get_page(name) data = w.get_page(name)
@ -277,7 +287,8 @@ def create_app(subdomain=None):
edit_cname = to_canonical(request.form['name']) edit_cname = to_canonical(request.form['name'])
if edit_cname.lower() != cname.lower(): if edit_cname.lower() != cname.lower():
w.rename_page(cname, edit_cname) w.rename_page(cname, edit_cname)
w.write_page(edit_cname, request.form['content'], message=request.form['message'], w.write_page(edit_cname, request.form['content'],
message=request.form['message'],
username=CurrentUser.get('username')) username=CurrentUser.get('username'))
return redirect("/" + edit_cname) return redirect("/" + edit_cname)
else: else:
@ -288,13 +299,13 @@ def create_app(subdomain=None):
else: else:
return redirect('/create/'+cname) return redirect('/create/'+cname)
@app.route("/delete/<name>", methods=['POST']) @app.route("/_delete/<name>", methods=['POST'])
@login_required @login_required
def delete(name): def delete(name):
pass pass
@app.route("/create/", methods=['GET', 'POST']) @app.route("/_create/", methods=['GET', 'POST'])
@app.route("/create/<name>", methods=['GET', 'POST']) @app.route("/_create/<name>", methods=['GET', 'POST'])
@login_required @login_required
def create(name=None): def create(name=None):
cname = "" cname = ""
@ -304,12 +315,14 @@ def create_app(subdomain=None):
# Page exists, edit instead # Page exists, edit instead
return redirect("/edit/" + cname) return redirect("/edit/" + cname)
if request.method == 'POST': if request.method == 'POST':
w.write_page(request.form['name'], request.form['content'], message=request.form['message'], create=True) w.write_page(request.form['name'], request.form['content'],
message=request.form['message'],
create=True,
username=CurrentUser.get('username'))
return redirect("/" + cname) return redirect("/" + cname)
else: else:
return render_template('page/edit.html', name=cname, content="") return render_template('page/edit.html', name=cname, content="")
@app.route("/<name>") @app.route("/<name>")
def render(name): def render(name):
cname = to_canonical(name) cname = to_canonical(name)

View file

@ -102,9 +102,6 @@ class CurrentUser():
if id: if id:
user = User() user = User()
session['user'] = user.get_by_id(id) session['user'] = user.get_by_id(id)
#import sys
#sys.exit()
#session['user'] = to_dict(user.get_one(id, 'id'), True)
def get_id(self): def get_id(self):
return self.id return self.id

View file

@ -93,6 +93,12 @@ body {
box-shadow: 0 1px 2px rgba(0,0,0,.15); box-shadow: 0 1px 2px rgba(0,0,0,.15);
} }
#page-action-bar {
position: relative;
z-index: 100;
float: right;
}
.user-avatar a img { .user-avatar a img {
width: 24px; width: 24px;
height: 24px; height: 24px;

View file

@ -10,7 +10,6 @@ $(function(){
var editor var editor
, autoInterval , autoInterval
, keyCheck // used to detect changes not made via keyup
, profile = , profile =
{ {
theme: 'ace/theme/idle_fingers' theme: 'ace/theme/idle_fingers'
@ -331,11 +330,6 @@ $(function(){
* @return {Void} * @return {Void}
*/ */
function saveFile(isManual){ function saveFile(isManual){
if (!keyCheck && profile.currentMd != editor.getSession().getValue()) {
previewMd();
console.log(keyCheck);
}
keyCheck = false;
updateUserProfile({currentMd: editor.getSession().getValue()}); updateUserProfile({currentMd: editor.getSession().getValue()});
if (isManual) { if (isManual) {
@ -536,8 +530,7 @@ $(function(){
* @return {Void} * @return {Void}
*/ */
function bindPreview(){ function bindPreview(){
$('#editor').bind('keyup', function() { editor.getSession().on('change', function(e) {
keyCheck = true;
previewMd(); previewMd();
}); });
} }

View file

@ -635,19 +635,18 @@ scope:scope,method:method,key:keys[i],mods:mods})}}function unbindKey(key,scope)
scope)handlers.splice(i,1);else i++}}function getKeys(key){var keys;key=key.replace(/\s/g,"");keys=key.split(",");if(keys[keys.length-1]=="")keys[keys.length-2]+=",";return keys}function getMods(key){var mods=key.slice(0,key.length-1);for(var mi=0;mi<mods.length;mi++)mods[mi]=_MODIFIERS[mods[mi]];return mods}function addEvent(object,event,method){if(object.addEventListener)object.addEventListener(event,method,false);else if(object.attachEvent)object.attachEvent("on"+event,function(){method(window.event)})} scope)handlers.splice(i,1);else i++}}function getKeys(key){var keys;key=key.replace(/\s/g,"");keys=key.split(",");if(keys[keys.length-1]=="")keys[keys.length-2]+=",";return keys}function getMods(key){var mods=key.slice(0,key.length-1);for(var mi=0;mi<mods.length;mi++)mods[mi]=_MODIFIERS[mods[mi]];return mods}function addEvent(object,event,method){if(object.addEventListener)object.addEventListener(event,method,false);else if(object.attachEvent)object.attachEvent("on"+event,function(){method(window.event)})}
addEvent(document,"keydown",function(event){dispatch(event)});addEvent(document,"keyup",clearModifier);addEvent(window,"focus",resetModifiers);var previousKey=global.key;function noConflict(){var k=global.key;global.key=previousKey;return k}global.key=assignKey;global.key.setScope=setScope;global.key.getScope=getScope;global.key.deleteScope=deleteScope;global.key.filter=filter;global.key.isPressed=isPressed;global.key.getPressedKeyCodes=getPressedKeyCodes;global.key.noConflict=noConflict;global.key.unbind= addEvent(document,"keydown",function(event){dispatch(event)});addEvent(document,"keyup",clearModifier);addEvent(window,"focus",resetModifiers);var previousKey=global.key;function noConflict(){var k=global.key;global.key=previousKey;return k}global.key=assignKey;global.key.setScope=setScope;global.key.getScope=getScope;global.key.deleteScope=deleteScope;global.key.filter=filter;global.key.isPressed=isPressed;global.key.getPressedKeyCodes=getPressedKeyCodes;global.key.noConflict=noConflict;global.key.unbind=
unbindKey;if(typeof module!=="undefined")module.exports=key})(this); unbindKey;if(typeof module!=="undefined")module.exports=key})(this);
$(function(){var $theme=$("#theme-list"),$preview=$("#preview"),$autosave=$("#autosave"),$wordcount=$("#wordcount"),$wordcounter=$("#wordcounter"),$pagename=$("#page-name");var editor,autoInterval,keyCheck,profile={theme:"ace/theme/idle_fingers",currentMd:"",autosave:{enabled:true,interval:3E3},current_filename:$pagename.val()};var dillinger="dillinger",dillingerElem=document.createElement(dillinger),dillingerStyle=dillingerElem.style,domPrefixes="Webkit Moz O ms Khtml".split(" ");function asyncLoad(filename, $(function(){var $theme=$("#theme-list"),$preview=$("#preview"),$autosave=$("#autosave"),$wordcount=$("#wordcount"),$wordcounter=$("#wordcounter"),$pagename=$("#page-name");var editor,autoInterval,profile={theme:"ace/theme/idle_fingers",currentMd:"",autosave:{enabled:true,interval:3E3},current_filename:$pagename.val()};var dillinger="dillinger",dillingerElem=document.createElement(dillinger),dillingerStyle=dillingerElem.style,domPrefixes="Webkit Moz O ms Khtml".split(" ");function asyncLoad(filename,
cb){(function(d,t){var leScript=d.createElement(t),scripts=d.getElementsByTagName(t)[0];leScript.async=1;leScript.src=filename;scripts.parentNode.insertBefore(leScript,scripts);leScript.onload=function(){cb&&cb()}})(document,"script")}function hasLocalStorage(){var storage;try{if(localStorage.getItem)storage=localStorage}catch(e){}return storage}function getUserProfile(){var p;try{p=JSON.parse(localStorage.profile);p=$.extend(true,profile,p)}catch(e){p=profile}if(p.filename!=$pagename.val())updateUserProfile({filename:$pagename.val(), cb){(function(d,t){var leScript=d.createElement(t),scripts=d.getElementsByTagName(t)[0];leScript.async=1;leScript.src=filename;scripts.parentNode.insertBefore(leScript,scripts);leScript.onload=function(){cb&&cb()}})(document,"script")}function hasLocalStorage(){var storage;try{if(localStorage.getItem)storage=localStorage}catch(e){}return storage}function getUserProfile(){var p;try{p=JSON.parse(localStorage.profile);p=$.extend(true,profile,p)}catch(e){p=profile}if(p.filename!=$pagename.val())updateUserProfile({filename:$pagename.val(),
currentMd:""});profile=p}function updateUserProfile(obj){localStorage.clear();localStorage.profile=JSON.stringify($.extend(true,profile,obj))}function prefixed(prop){return testPropsAll(prop,"pfx")}function testProps(props,prefixed){for(var i in props)if(dillingerStyle[props[i]]!==undefined)return prefixed==="pfx"?props[i]:true;return false}function testPropsAll(prop,prefixed){var ucProp=prop.charAt(0).toUpperCase()+prop.substr(1),props=(prop+" "+domPrefixes.join(ucProp+" ")+ucProp).split(" ");return testProps(props, currentMd:""});profile=p}function updateUserProfile(obj){localStorage.clear();localStorage.profile=JSON.stringify($.extend(true,profile,obj))}function prefixed(prop){return testPropsAll(prop,"pfx")}function testProps(props,prefixed){for(var i in props)if(dillingerStyle[props[i]]!==undefined)return prefixed==="pfx"?props[i]:true;return false}function testPropsAll(prop,prefixed){var ucProp=prop.charAt(0).toUpperCase()+prop.substr(1),props=(prop+" "+domPrefixes.join(ucProp+" ")+ucProp).split(" ");return testProps(props,
prefixed)}function normalizeTransitionEnd(){var transEndEventNames={"WebkitTransition":"webkitTransitionEnd","MozTransition":"transitionend","OTransition":"oTransitionEnd","msTransition":"msTransitionEnd","transition":"transitionend"};return transEndEventNames[prefixed("transition")]}function getCurrentFilenameFromField(){return $('#filename > span[contenteditable="true"]').text()}function setCurrentFilenameField(str){$('#filename > span[contenteditable="true"]').text(str||profile.current_filename|| prefixed)}function normalizeTransitionEnd(){var transEndEventNames={"WebkitTransition":"webkitTransitionEnd","MozTransition":"transitionend","OTransition":"oTransitionEnd","msTransition":"msTransitionEnd","transition":"transitionend"};return transEndEventNames[prefixed("transition")]}function getCurrentFilenameFromField(){return $('#filename > span[contenteditable="true"]').text()}function setCurrentFilenameField(str){$('#filename > span[contenteditable="true"]').text(str||profile.current_filename||
"Untitled Document")}function getTextInElement(node){if(node.nodeType===3)return node.data;var txt="";if(node=node.firstChild){do txt+=getTextInElement(node);while(node=node.nextSibling)}return txt}function countWords(string){var words=string.replace(/W+/g," ").match(/\S+/g);return words&&words.length||0}function init(){if(!hasLocalStorage())sadPanda();else{$.support.transitionEnd=normalizeTransitionEnd();getUserProfile();initAce();initUi();bindPreview();bindNav();bindKeyboard();autoSave()}}function initAce(){editor= "Untitled Document")}function getTextInElement(node){if(node.nodeType===3)return node.data;var txt="";if(node=node.firstChild){do txt+=getTextInElement(node);while(node=node.nextSibling)}return txt}function countWords(string){var words=string.replace(/W+/g," ").match(/\S+/g);return words&&words.length||0}function init(){if(!hasLocalStorage())sadPanda();else{$.support.transitionEnd=normalizeTransitionEnd();getUserProfile();initAce();initUi();bindPreview();bindNav();bindKeyboard();autoSave()}}function initAce(){editor=
ace.edit("editor")}function initUi(){fetchTheme(profile.theme,function(){$theme.find('li > a[data-value="'+profile.theme+'"]').addClass("selected");editor.getSession().setUseWrapMode(true);editor.setShowPrintMargin(false);editor.getSession().setMode("ace/mode/markdown");editor.getSession().setValue(profile.currentMd||editor.getSession().getValue());previewMd()});$autosave.html(profile.autosave.enabled?'<i class="icon-remove"></i>&nbsp;Disable Autosave':'<i class="icon-ok"></i>&nbsp;Enable Autosave'); ace.edit("editor")}function initUi(){fetchTheme(profile.theme,function(){$theme.find('li > a[data-value="'+profile.theme+'"]').addClass("selected");editor.getSession().setUseWrapMode(true);editor.setShowPrintMargin(false);editor.getSession().setMode("ace/mode/markdown");editor.getSession().setValue(profile.currentMd||editor.getSession().getValue());previewMd()});$autosave.html(profile.autosave.enabled?'<i class="icon-remove"></i>&nbsp;Disable Autosave':'<i class="icon-ok"></i>&nbsp;Enable Autosave');
$wordcount.html(!profile.wordcount?'<i class="icon-remove"></i>&nbsp;Disabled Word Count':'<i class="icon-ok"></i>&nbsp;Enabled Word Count');setCurrentFilenameField();$(".dropdown-toggle").dropdown()}function clearSelection(){editor.getSession().setValue("");previewMd()}function saveFile(isManual){if(!keyCheck&&profile.currentMd!=editor.getSession().getValue()){previewMd();console.log(keyCheck)}keyCheck=false;updateUserProfile({currentMd:editor.getSession().getValue()});if(isManual){updateUserProfile({currentMd:""}); $wordcount.html(!profile.wordcount?'<i class="icon-remove"></i>&nbsp;Disabled Word Count':'<i class="icon-ok"></i>&nbsp;Enabled Word Count');setCurrentFilenameField();$(".dropdown-toggle").dropdown()}function clearSelection(){editor.getSession().setValue("");previewMd()}function saveFile(isManual){updateUserProfile({currentMd:editor.getSession().getValue()});if(isManual){updateUserProfile({currentMd:""});var data={name:$pagename.val(),message:$("#page-message").val(),content:editor.getSession().getValue()};
var data={name:$pagename.val(),message:$("#page-message").val(),content:editor.getSession().getValue()};$.post(window.location,data,function(){location.href="/"+data["name"]})}}function autoSave(){if(profile.autosave.enabled)autoInterval=setInterval(function(){saveFile()},profile.autosave.interval);else clearInterval(autoInterval)}$("#save-native").on("click",function(){saveFile(true)});function resetProfile(){localStorage.clear();profile.autosave.enabled=false;delete localStorage.profile;window.location.reload()} $.post(window.location,data,function(){location.href="/"+data["name"]})}}function autoSave(){if(profile.autosave.enabled)autoInterval=setInterval(function(){saveFile()},profile.autosave.interval);else clearInterval(autoInterval)}$("#save-native").on("click",function(){saveFile(true)});function resetProfile(){localStorage.clear();profile.autosave.enabled=false;delete localStorage.profile;window.location.reload()}function changeTheme(e){var $target=$(e.target);if($target.attr("data-value")===profile.theme)return;
function changeTheme(e){var $target=$(e.target);if($target.attr("data-value")===profile.theme)return;else{$theme.find("li > a.selected").removeClass("selected");$target.addClass("selected");var newTheme=$target.attr("data-value");$(e.target).blur();fetchTheme(newTheme,function(){})}}function fetchTheme(th,cb){var name=th.split("/").pop();asyncLoad("/static/js/ace/theme-"+name+".js",function(){editor.setTheme(th);cb&&cb();updateBg(name);updateUserProfile({theme:th})})}function updateBg(name){}function previewMd(){var unmd= else{$theme.find("li > a.selected").removeClass("selected");$target.addClass("selected");var newTheme=$target.attr("data-value");$(e.target).blur();fetchTheme(newTheme,function(){})}}function fetchTheme(th,cb){var name=th.split("/").pop();asyncLoad("/static/js/ace/theme-"+name+".js",function(){editor.setTheme(th);cb&&cb();updateBg(name);updateUserProfile({theme:th})})}function updateBg(name){}function previewMd(){var unmd=editor.getSession().getValue(),md=MDR.convert(unmd,true);$preview.html("").html(md)}
editor.getSession().getValue(),md=MDR.convert(unmd,true);$preview.html("").html(md)}function updateFilename(str){var f;if(typeof str==="string")f=str;else f=getCurrentFilenameFromField();updateUserProfile({current_filename:f})}function showHtml(){var unmd=editor.getSession().getValue();function _doneHandler(jqXHR,data,response){var resp=JSON.parse(response.responseText);$("#myModalBody").text(resp.data);$("#myModal").modal()}function _failHandler(){alert("Roh-roh. Something went wrong. :(")}var config= function updateFilename(str){var f;if(typeof str==="string")f=str;else f=getCurrentFilenameFromField();updateUserProfile({current_filename:f})}function showHtml(){var unmd=editor.getSession().getValue();function _doneHandler(jqXHR,data,response){var resp=JSON.parse(response.responseText);$("#myModalBody").text(resp.data);$("#myModal").modal()}function _failHandler(){alert("Roh-roh. Something went wrong. :(")}var config={type:"POST",data:"unmd="+encodeURIComponent(unmd),dataType:"json",url:"/factory/fetch_html_direct",
{type:"POST",data:"unmd="+encodeURIComponent(unmd),dataType:"json",url:"/factory/fetch_html_direct",error:_failHandler,success:_doneHandler};$.ajax(config)}function sadPanda(){alert("Sad Panda - No localStorage for you!")}function toggleAutoSave(){$autosave.html(profile.autosave.enabled?'<i class="icon-remove"></i>&nbsp;Disable Autosave':'<i class="icon-ok"></i>&nbsp;Enable Autosave');updateUserProfile({autosave:{enabled:!profile.autosave.enabled}});autoSave()}function bindPreview(){$("#editor").bind("keyup", error:_failHandler,success:_doneHandler};$.ajax(config)}function sadPanda(){alert("Sad Panda - No localStorage for you!")}function toggleAutoSave(){$autosave.html(profile.autosave.enabled?'<i class="icon-remove"></i>&nbsp;Disable Autosave':'<i class="icon-ok"></i>&nbsp;Enable Autosave');updateUserProfile({autosave:{enabled:!profile.autosave.enabled}});autoSave()}function bindPreview(){editor.getSession().on("change",function(e){previewMd()})}function bindNav(){$theme.find("li > a").bind("click",function(e){changeTheme(e);
function(){keyCheck=true;previewMd()})}function bindNav(){$theme.find("li > a").bind("click",function(e){changeTheme(e);return false});$("#clear").on("click",function(){clearSelection();return false});$("#autosave").on("click",function(){toggleAutoSave();return false});$("#reset").on("click",function(){resetProfile();return false});$("#cheat").on("click",function(){window.open("https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet","_blank");return false})}function bindKeyboard(){key("command+s, ctrl+s", return false});$("#clear").on("click",function(){clearSelection();return false});$("#autosave").on("click",function(){toggleAutoSave();return false});$("#reset").on("click",function(){resetProfile();return false});$("#cheat").on("click",function(){window.open("https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet","_blank");return false})}function bindKeyboard(){key("command+s, ctrl+s",function(e){saveFile(true);e.preventDefault()});var saveCommand={name:"save",bindKey:{mac:"Command-S",
function(e){saveFile(true);e.preventDefault()});var saveCommand={name:"save",bindKey:{mac:"Command-S",win:"Ctrl-S"},exec:function(){saveFile(true)}};editor.commands.addCommand(saveCommand)}init()}); win:"Ctrl-S"},exec:function(){saveFile(true)}};editor.commands.addCommand(saveCommand)}init()});function getScrollHeight($prevFrame){if($prevFrame[0].scrollHeight!==undefined)return $prevFrame[0].scrollHeight;else if($prevFrame.find("html")[0].scrollHeight!==undefined&&$prevFrame.find("html")[0].scrollHeight!==0)return $prevFrame.find("html")[0].scrollHeight;else return $prevFrame.find("body")[0].scrollHeight}
function getScrollHeight($prevFrame){if($prevFrame[0].scrollHeight!==undefined)return $prevFrame[0].scrollHeight;else if($prevFrame.find("html")[0].scrollHeight!==undefined&&$prevFrame.find("html")[0].scrollHeight!==0)return $prevFrame.find("html")[0].scrollHeight;else return $prevFrame.find("body")[0].scrollHeight}
function syncPreview(){var $ed=window.ace.edit("editor");var $prev=$("#preview");var editorScrollRange=$ed.getSession().getLength();var previewScrollRange=getScrollHeight($prev);var scrollFactor=$ed.getFirstVisibleRow()/editorScrollRange;$prev.scrollTop(scrollFactor*previewScrollRange)} function syncPreview(){var $ed=window.ace.edit("editor");var $prev=$("#preview");var editorScrollRange=$ed.getSession().getLength();var previewScrollRange=getScrollHeight($prev);var scrollFactor=$ed.getFirstVisibleRow()/editorScrollRange;$prev.scrollTop(scrollFactor*previewScrollRange)}
window.onload=function(){var $loading=$("#loading");if($.support.transition)$loading.bind($.support.transitionEnd,function(){$("#main").removeClass("bye");$loading.remove()}).addClass("fade_slow");else{$("#main").removeClass("bye");$loading.remove()}window.ace.edit("editor").session.on("changeScrollTop",syncPreview);window.ace.edit("editor").session.selection.on("changeCursor",syncPreview)}; window.onload=function(){var $loading=$("#loading");if($.support.transition)$loading.bind($.support.transitionEnd,function(){$("#main").removeClass("bye");$loading.remove()}).addClass("fade_slow");else{$("#main").removeClass("bye");$loading.remove()}window.ace.edit("editor").session.on("changeScrollTop",syncPreview);window.ace.edit("editor").session.selection.on("changeCursor",syncPreview)};

View file

@ -43,10 +43,10 @@
<i class="icon-caret-down"></i></a> <i class="icon-caret-down"></i></a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li class="dropdown-header">Page Options</li> <li class="dropdown-header">Page Options</li>
<li><a href="/create/">Create Page</a></li> <li><a href="/_create/">Create Page</a></li>
{% if name %} {% if name %}
<li><a href="/edit/{{ name }}">Edit Page</a></li> <li><a href="/_edit/{{ name }}">Edit Page</a></li>
<li><a href="/history/{{ name }}">History</a></li> <li><a href="/_history/{{ name }}">History</a></li>
{% endif %} {% endif %}
<li class="divider"></li> <li class="divider"></li>
<li class="dropdown-header">Site Options</li> <li class="dropdown-header">Site Options</li>

View file

@ -2,15 +2,19 @@
{% block body %} {% block body %}
<h2>History for <strong>{{ name }}</strong></h2> <h2>History for <strong>{{ name }}</strong></h2>
<div class="pull-right">
<a href="/_commit/{{ old }}/{{ name }}" class="btn btn-default btn-sm">View Old</a>
<a href="/_commit/{{ new }}/{{ name }}" class="btn btn-info btn-sm">View New</a>
</div>
<p> <p>
<a class="btn btn-default btn-sm" href="/history/{{ name }}">Back to History</a> <a class="btn btn-default btn-sm" href="/_history/{{ name }}">Back to History</a>
<a href="" class="btn btn-default btn-sm">Revert Changes</a>
</p> </p>
{{ diff|safe }} {{ diff|safe }}
<p></p> <p></p>
<p> <p>
<a class="btn btn-default btn-sm" href="/history/{{ name }}">Back to History</a> <a class="btn btn-default btn-sm" href="/_history/{{ name }}">Back to History</a>
</p> </p>
{% endblock %} {% endblock %}

View file

@ -21,7 +21,7 @@
<input type="checkbox" name="versions[]" value="{{ h.sha }}" /> <input type="checkbox" name="versions[]" value="{{ h.sha }}" />
</td> </td>
<td>{{ h.author }}</td> <td>{{ h.author }}</td>
<td><a href="/commit/{{ h.sha }}/{{ name }}" class='label label-primary'>{{ h.sha|truncate(7, True, end="") }}</a> {{ h.message }} </td> <td><a href="/_commit/{{ h.sha }}/{{ name }}" class='label label-primary'>View</a> {{ h.message }} </td>
<td>{{ h.time|datetime }}</td> <td>{{ h.time|datetime }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
@ -50,8 +50,9 @@ $(function(){
$.each($cs, function(i, v){ $.each($cs, function(i, v){
revs.push(v.value); revs.push(v.value);
}); });
revs = revs.join("..."); revs.reverse();
location.href = "/compare/{{ name }}/" + revs; revs = revs.join("..");
location.href = "/_compare/{{ name }}/" + revs;
}); });
}); });
</script> </script>

View file

@ -1,13 +1,21 @@
{% extends 'layout.html' %} {% extends 'layout.html' %}
{% block page_menu %} {% block page_menu %}
<div class="controls pull-right"> <div class="controls pull-right">
<a class="btn btn-default btn-sm" href="/edit/{{ name }}">Edit</a> <a class="btn btn-default btn-sm" href="/_edit/{{ name }}">Edit</a>
<a class="btn btn-default btn-sm" href="/history/{{ name }}">History</a> <a class="btn btn-default btn-sm" href="/_history/{{ name }}">History</a>
</div> </div>
{% endblock %} {% endblock %}
{% block body %} {% block body %}
{% if commit %}
<div id="page-action-bar">
<form method="POST" action="/_revert">
<input type="hidden" value="{{ name }}" name="name" />
<input type="hidden" value="{{ commit }}" name="commit" />
<input type="submit" class="btn btn-danger btn-sm" title="Revert back to this revision" value="Revert" />
</form>
</div>
{% endif %}
<div id="page-content" style="display:none"> <div id="page-content" style="display:none">
{{ page.data }} {{ page.data }}
</div> </div>

View file

@ -4,6 +4,7 @@ import lxml.html
from lxml.html import clean from lxml.html import clean
import ghdiff import ghdiff
import gittle.utils
from gittle import Gittle from gittle import Gittle
from dulwich.repo import NotGitRepository from dulwich.repo import NotGitRepository
@ -68,6 +69,15 @@ class Wiki():
s = Site() s = Site()
return True if s.get_by_name(name) else False return True if s.get_by_name(name) else False
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])
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 write_page(self, name, content, message=None, create=False, username=None, email=None):
# prevents p tag from being added, we remove this later # prevents p tag from being added, we remove this later
@ -76,13 +86,14 @@ class Wiki():
tree = lxml.html.fromstring(content) tree = lxml.html.fromstring(content)
cleaner = clean.Cleaner(remove_unknown_tags=False, style=False, safe_attrs_only=False) cleaner = clean.Cleaner(remove_unknown_tags=False, kill_tags=set(['style']), safe_attrs_only=False)
tree = cleaner.clean_html(tree) tree = cleaner.clean_html(tree)
content = lxml.html.tostring(tree, encoding='utf-8', method='html') content = lxml.html.tostring(tree, encoding='utf-8', method='html')
# post processing to fix errors # post processing to fix errors
content = content[5:-6] 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"(\n&gt;)", "\n>", content)
content = re.sub(r"(^&gt;)", ">", content) content = re.sub(r"(^&gt;)", ">", content)
content = re.sub(r"```(.*?)```", unescape_repl, content, flags=re.DOTALL) content = re.sub(r"```(.*?)```", unescape_repl, content, flags=re.DOTALL)
@ -118,6 +129,7 @@ class Wiki():
files=[old_name]) files=[old_name])
def get_page(self, name, sha='HEAD'): def get_page(self, name, sha='HEAD'):
commit = gittle.utils.git.commit_info(self.repo[sha])
name = self.cname_to_filename(name) name = self.cname_to_filename(name)
try: try:
return self.repo.get_commit_files(sha, paths=[name]).get(name) return self.repo.get_commit_files(sha, paths=[name]).get(name)
@ -125,10 +137,9 @@ class Wiki():
# HEAD doesn't exist yet # HEAD doesn't exist yet
return None return None
def compare(self, name, new_sha, old_sha): def compare(self, name, old_sha, new_sha):
old = self.get_page(name, sha=old_sha) old = self.get_page(name, sha=old_sha)
new = self.get_page(name, sha=new_sha) new = self.get_page(name, sha=new_sha)
return ghdiff.diff(old['data'], new['data']) return ghdiff.diff(old['data'], new['data'])
def get_history(self, name): def get_history(self, name):