various updates
This commit is contained in:
parent
86cb2d7c13
commit
2233205e0e
|
@ -3,6 +3,7 @@ import redis
|
||||||
import logging
|
import logging
|
||||||
import rethinkdb as rdb
|
import rethinkdb as rdb
|
||||||
import os
|
import os
|
||||||
|
import time
|
||||||
from flask import Flask, request, render_template, url_for, redirect
|
from flask import Flask, request, render_template, url_for, redirect
|
||||||
from flask.ext.bcrypt import Bcrypt
|
from flask.ext.bcrypt import Bcrypt
|
||||||
from flask.ext.login import LoginManager
|
from flask.ext.login import LoginManager
|
||||||
|
@ -10,6 +11,7 @@ from flask.ext.assets import Environment
|
||||||
from session import RedisSessionInterface
|
from session import RedisSessionInterface
|
||||||
from wiki import Wiki
|
from wiki import Wiki
|
||||||
from util import to_canonical, remove_ext
|
from util import to_canonical, remove_ext
|
||||||
|
from recaptcha.client import captcha
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.config.update(config.flask)
|
app.config.update(config.flask)
|
||||||
|
@ -49,6 +51,11 @@ def redirect_url():
|
||||||
return request.args.get('next') or request.referrer or url_for('index')
|
return request.args.get('next') or request.referrer or url_for('index')
|
||||||
|
|
||||||
|
|
||||||
|
@app.template_filter('datetime')
|
||||||
|
def _jinja2_filter_datetime(ts):
|
||||||
|
return time.strftime('%b %d, %Y %I:%M %p', time.localtime(ts))
|
||||||
|
|
||||||
|
|
||||||
@app.errorhandler(404)
|
@app.errorhandler(404)
|
||||||
def page_not_found(e):
|
def page_not_found(e):
|
||||||
return render_template('errors/404.html'), 404
|
return render_template('errors/404.html'), 404
|
||||||
|
@ -62,7 +69,47 @@ def page_error(e):
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
def root():
|
def root():
|
||||||
return redirect('/Home')
|
return redirect('/home')
|
||||||
|
|
||||||
|
@app.route("/commit/<sha>/<name>")
|
||||||
|
def commit_sha(name, sha):
|
||||||
|
cname = to_canonical(name)
|
||||||
|
|
||||||
|
data = w.get_page(cname, sha=sha)
|
||||||
|
if data:
|
||||||
|
return render_template('page/page.html', page=data)
|
||||||
|
else:
|
||||||
|
return redirect('/create/'+cname)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/register", methods=['GET', 'POST'])
|
||||||
|
def register():
|
||||||
|
if request.method == 'POST':
|
||||||
|
response = captcha.submit(
|
||||||
|
request.form['recaptcha_challenge_field'],
|
||||||
|
request.form['recaptcha_response_field'],
|
||||||
|
app.config['RECAPTCHA_PRIVATE_KEY'],
|
||||||
|
request.remote_addr)
|
||||||
|
if not response.is_valid:
|
||||||
|
return redirect('/register?fail')
|
||||||
|
else:
|
||||||
|
return redirect("/")
|
||||||
|
else:
|
||||||
|
return render_template('account/register.html')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/login", methods=['GET', 'POST'])
|
||||||
|
def login():
|
||||||
|
if request.method == 'POST':
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return render_template('account/login.html')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/history/<name>")
|
||||||
|
def history(name):
|
||||||
|
history = w.get_history(name)
|
||||||
|
return render_template('page/history.html', name=name, history=history)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/edit/<name>", methods=['GET', 'POST'])
|
@app.route("/edit/<name>", methods=['GET', 'POST'])
|
||||||
|
@ -73,7 +120,7 @@ def edit(name):
|
||||||
edit_cname = to_canonical(request.form['name'])
|
edit_cname = to_canonical(request.form['name'])
|
||||||
if edit_cname != cname:
|
if edit_cname != cname:
|
||||||
w.rename_page(cname, edit_cname)
|
w.rename_page(cname, edit_cname)
|
||||||
w.write_page(edit_cname, request.form['content'])
|
w.write_page(edit_cname, request.form['content'], message=request.form['message'])
|
||||||
return redirect("/" + edit_cname)
|
return redirect("/" + edit_cname)
|
||||||
else:
|
else:
|
||||||
if data:
|
if data:
|
||||||
|
@ -89,18 +136,20 @@ def delete(name):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/create/", methods=['GET', 'POST'])
|
||||||
@app.route("/create/<name>", methods=['GET', 'POST'])
|
@app.route("/create/<name>", methods=['GET', 'POST'])
|
||||||
def create(name):
|
def create(name=None):
|
||||||
|
cname = ""
|
||||||
|
if name:
|
||||||
cname = to_canonical(name)
|
cname = to_canonical(name)
|
||||||
if w.get_page(cname):
|
if w.get_page(cname):
|
||||||
# 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'], create=True)
|
w.write_page(request.form['name'], request.form['content'], message=request.form['message'], create=True)
|
||||||
return redirect("/" + cname)
|
return redirect("/" + cname)
|
||||||
else:
|
else:
|
||||||
return render_template('page/create.html', name=cname)
|
return render_template('page/edit.html', name=cname, content="")
|
||||||
|
|
||||||
|
|
||||||
@app.route("/<name>")
|
@app.route("/<name>")
|
||||||
|
@ -111,7 +160,9 @@ def render(name):
|
||||||
|
|
||||||
data = w.get_page(cname)
|
data = w.get_page(cname)
|
||||||
if data:
|
if data:
|
||||||
return render_template('page/page.html', page=data)
|
#if data.get('data'):
|
||||||
|
# data['data'] = markdown(data['data'])
|
||||||
|
return render_template('page/page.html', name=cname, page=data)
|
||||||
else:
|
else:
|
||||||
return redirect('/create/'+cname)
|
return redirect('/create/'+cname)
|
||||||
|
|
||||||
|
|
|
@ -1,30 +1,28 @@
|
||||||
import rethinkdb as rdb
|
import rethinkdb as rdb
|
||||||
from reimagine import conn
|
from reimagine import conn
|
||||||
|
from rethinkORM import RethinkModel
|
||||||
|
|
||||||
|
|
||||||
def get_one(cur):
|
class BaseModel(RethinkModel):
|
||||||
res = cur.chunks[0]
|
|
||||||
return res[0] if len(res) else None
|
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
if not kwargs.get('conn'):
|
||||||
|
kwargs['conn'] = conn
|
||||||
|
|
||||||
class BaseModel():
|
super(BaseModel, self).__init__(**kwargs)
|
||||||
__table__ = None
|
|
||||||
_conn = conn
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def filter(cls, f, limit=None):
|
def create(cls, **kwargs):
|
||||||
q = rdb.table(cls.__table__).filter(f)
|
return super(BaseModel, cls).create(**kwargs)
|
||||||
if limit:
|
|
||||||
q.limit(int(limit))
|
|
||||||
return q.run(cls._conn)
|
|
||||||
|
|
||||||
|
|
||||||
class Site(BaseModel):
|
class Site(BaseModel):
|
||||||
__table__ = 'sites'
|
table = 'sites'
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_by_name(cls, name):
|
class User(BaseModel):
|
||||||
return get_one(cls.filter({'name': name}, limit=1))
|
table = 'users'
|
||||||
|
|
||||||
|
|
||||||
|
def login(self, login, password):
|
||||||
|
pass
|
|
@ -2,6 +2,11 @@
|
||||||
margin-bottom: 25px;
|
margin-bottom: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.checkbox-cell {
|
||||||
|
width: 4em;
|
||||||
|
padding: 0.3em;
|
||||||
|
}
|
||||||
|
|
||||||
#app-wrap {
|
#app-wrap {
|
||||||
top: 60px;
|
top: 60px;
|
||||||
left: 10px;
|
left: 10px;
|
||||||
|
@ -24,6 +29,17 @@
|
||||||
top: 60px;
|
top: 60px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
background: rgba(255,255,255,0.9);
|
background: rgba(255,255,255,0.9);
|
||||||
|
border: 1px solid #EEE;
|
||||||
|
-webkit-border-radius: 4px;
|
||||||
|
-moz-border-radius: 4px;
|
||||||
|
-webkit-border-radius: 4px;
|
||||||
|
-moz-border-radius: 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
-webkit-box-shadow: 0 0 80px rgba(0,0,0,0.3) inset 0 0 5px rgba(0,0,0,0.6);
|
||||||
|
-moz-box-shadow: 0 0 80px rgba(0,0,0,0.3) inset 0 0 5px rgba(0,0,0,0.6);
|
||||||
|
-webkit-box-shadow: 0 0 80px rgba(0,0,0,0.3) inset 0 0 5px rgba(0,0,0,0.6);
|
||||||
|
-moz-box-shadow: 0 0 80px rgba(0,0,0,0.3) inset 0 0 5px rgba(0,0,0,0.6);
|
||||||
|
box-shadow: 0 0 80px rgba(0,0,0,0.3) inset 0 0 5px rgba(0,0,0,0.6);
|
||||||
-webkit-box-sizing: border-box;
|
-webkit-box-sizing: border-box;
|
||||||
-moz-box-sizing: border-box;
|
-moz-box-sizing: border-box;
|
||||||
-webkit-box-sizing: border-box;
|
-webkit-box-sizing: border-box;
|
||||||
|
|
|
@ -16,7 +16,7 @@ $(function(){
|
||||||
, interval: 3000 // might be too aggressive; don't want to block UI for large saves.
|
, interval: 3000 // might be too aggressive; don't want to block UI for large saves.
|
||||||
}
|
}
|
||||||
, wordcount: true
|
, wordcount: true
|
||||||
, current_filename : 'Untitled Document'
|
, current_filename : $("#page-name").val()
|
||||||
, dropbox:
|
, dropbox:
|
||||||
{
|
{
|
||||||
filepath: '/Dillinger/'
|
filepath: '/Dillinger/'
|
||||||
|
@ -36,7 +36,8 @@ $(function(){
|
||||||
, $wordcount = $('#wordcount')
|
, $wordcount = $('#wordcount')
|
||||||
, $import_github = $('#import_github')
|
, $import_github = $('#import_github')
|
||||||
, $wordcounter = $('#wordcounter')
|
, $wordcounter = $('#wordcounter')
|
||||||
, $filename = $('#filename')
|
, $filename = $('#filename'),
|
||||||
|
$pagename = $("#page-name")
|
||||||
|
|
||||||
|
|
||||||
// Hash of themes and their respective background colors
|
// Hash of themes and their respective background colors
|
||||||
|
@ -115,10 +116,10 @@ $(function(){
|
||||||
*/
|
*/
|
||||||
function getUserProfile(){
|
function getUserProfile(){
|
||||||
|
|
||||||
var p
|
var p;
|
||||||
|
|
||||||
try{
|
try{
|
||||||
p = JSON.parse( localStorage.profile )
|
p = JSON.parse( localStorage.profile );
|
||||||
// Need to merge in any undefined/new properties from last release
|
// Need to merge in any undefined/new properties from last release
|
||||||
// Meaning, if we add new features they may not have them in profile
|
// Meaning, if we add new features they may not have them in profile
|
||||||
p = $.extend(true, profile, p)
|
p = $.extend(true, profile, p)
|
||||||
|
@ -126,9 +127,11 @@ $(function(){
|
||||||
p = profile
|
p = profile
|
||||||
}
|
}
|
||||||
|
|
||||||
profile = p
|
if (p.filename != $pagename.val()) {
|
||||||
|
updateUserProfile({ filename: $pagename.val(), currentMd: "" });
|
||||||
|
|
||||||
// console.dir(profile)
|
}
|
||||||
|
profile = p
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -307,7 +310,7 @@ $(function(){
|
||||||
smartLists: true,
|
smartLists: true,
|
||||||
smartypants: false,
|
smartypants: false,
|
||||||
langPrefix: 'lang-'
|
langPrefix: 'lang-'
|
||||||
})
|
});
|
||||||
|
|
||||||
converter = marked;
|
converter = marked;
|
||||||
|
|
||||||
|
@ -354,10 +357,10 @@ $(function(){
|
||||||
fetchTheme(profile.theme, function(){
|
fetchTheme(profile.theme, function(){
|
||||||
$theme.find('li > a[data-value="'+profile.theme+'"]').addClass('selected')
|
$theme.find('li > a[data-value="'+profile.theme+'"]').addClass('selected')
|
||||||
|
|
||||||
editor.getSession().setUseWrapMode(true)
|
editor.getSession().setUseWrapMode(true);
|
||||||
editor.setShowPrintMargin(false)
|
editor.setShowPrintMargin(false);
|
||||||
|
|
||||||
editor.getSession().setMode('ace/mode/markdown')
|
editor.getSession().setMode('ace/mode/markdown');
|
||||||
|
|
||||||
editor.getSession().setValue( profile.currentMd || editor.getSession().getValue())
|
editor.getSession().setValue( profile.currentMd || editor.getSession().getValue())
|
||||||
|
|
||||||
|
@ -366,9 +369,6 @@ $(function(){
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set/unset paper background image on preview
|
|
||||||
// TODO: FIX THIS BUG
|
|
||||||
$preview.css('backgroundImage', profile.showPaper ? 'url("'+paperImgPath+'")' : 'url("")' )
|
|
||||||
|
|
||||||
// Set text for dis/enable autosave / word counter
|
// Set text for dis/enable autosave / word counter
|
||||||
$autosave.html( profile.autosave.enabled ? '<i class="icon-remove"></i> Disable Autosave' : '<i class="icon-ok"></i> Enable Autosave' )
|
$autosave.html( profile.autosave.enabled ? '<i class="icon-remove"></i> Disable Autosave' : '<i class="icon-ok"></i> Enable Autosave' )
|
||||||
|
@ -419,6 +419,7 @@ $(function(){
|
||||||
if (isManual) {
|
if (isManual) {
|
||||||
var data = {
|
var data = {
|
||||||
name: $("#page-name").val(),
|
name: $("#page-name").val(),
|
||||||
|
message: $("#page-message").val(),
|
||||||
content: editor.getSession().getValue()
|
content: editor.getSession().getValue()
|
||||||
};
|
};
|
||||||
$.post(window.location, data, function(){
|
$.post(window.location, data, function(){
|
||||||
|
|
20
reimagine/templates/account/login.html
Normal file
20
reimagine/templates/account/login.html
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{% extends 'layout.html' %}
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
|
<h2>Login</h2>
|
||||||
|
|
||||||
|
<form role="form" method="post">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="username">Username or Email</label>
|
||||||
|
<input type="text" class="form-control" id="username" name="username" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password">Password</label>
|
||||||
|
<input type="password" name="password" id="password" class="form-control" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input type="submit" class="btn btn-primary" value="Login" />
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
30
reimagine/templates/account/register.html
Normal file
30
reimagine/templates/account/register.html
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
{% import 'macros.html' as macros %}
|
||||||
|
{% extends 'layout.html' %}
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
|
<h2>Register</h2>
|
||||||
|
|
||||||
|
<form role="form" method="post">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="username">Username</label>
|
||||||
|
<input type="text" class="form-control" id="username" name="username" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="email">Email</label>
|
||||||
|
<input type="text" class="form-control" id="email" name="email" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password">Password</label>
|
||||||
|
<input type="password" name="password" id="password" class="form-control" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
{{ macros.recaptcha(config) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input type="submit" class="btn btn-primary" value="Submit" />
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -6,7 +6,7 @@
|
||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="author" content="">
|
<meta name="author" content="">
|
||||||
|
|
||||||
<title>Reborn</title>
|
<title>ReImagine</title>
|
||||||
|
|
||||||
<link href="/static/css/cerulean.bootstrap.min.css" rel="stylesheet">
|
<link href="/static/css/cerulean.bootstrap.min.css" rel="stylesheet">
|
||||||
<link href="/static/css/font-awesome.min.css" rel="stylesheet">
|
<link href="/static/css/font-awesome.min.css" rel="stylesheet">
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
padding-top: 70px;
|
padding-top: 60px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
||||||
|
@ -35,15 +35,18 @@
|
||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
</button>
|
</button>
|
||||||
<a class="navbar-brand" href="/">Reimagine</a>
|
<a class="navbar-brand" href="/">ReImagine</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="navbar-collapse collapse">
|
<div class="navbar-collapse collapse">
|
||||||
<ul class="nav navbar-nav">
|
<ul class="nav navbar-nav">
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="nav navbar-nav navbar-right">
|
<ul class="nav navbar-nav navbar-right">
|
||||||
<li><a>Create</a></li>
|
<li><a href="/create/"><i class="icon-plus"></i> Create</a></li>
|
||||||
<li><a href="/edit{{- request.path -}}">Edit</a></li>
|
{% if name %}
|
||||||
<li><a href="/login">Login</a></li>
|
<li><a href="/edit/{{- name -}}"><i class="icon-edit"></i> Edit</a></li>
|
||||||
|
<li><a href="/history/{{- name -}}"><i class="icon-time"></i> History</a></li>
|
||||||
|
{% endif %}
|
||||||
|
<li><a href="/login"><i class="icon-user"></i> Login</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div><!--/.nav-collapse -->
|
</div><!--/.nav-collapse -->
|
||||||
</div>
|
</div>
|
||||||
|
@ -54,15 +57,18 @@
|
||||||
<script src="/static/js/jquery-1.10.2.min.js"></script>
|
<script src="/static/js/jquery-1.10.2.min.js"></script>
|
||||||
<script src="/static/js/bootstrap.min.js"></script>
|
<script src="/static/js/bootstrap.min.js"></script>
|
||||||
<script src="/static/js/marked.js"></script>
|
<script src="/static/js/marked.js"></script>
|
||||||
{% block js %}{% endblock %}
|
|
||||||
<script>
|
<script>
|
||||||
|
marked.setOptions({
|
||||||
|
gfm: true,
|
||||||
|
tables: true,
|
||||||
|
pedantic: false,
|
||||||
|
sanitize: false,
|
||||||
|
smartLists: true,
|
||||||
|
smartypants: false,
|
||||||
|
langPrefix: 'lang-'
|
||||||
|
});
|
||||||
var converter = marked;
|
var converter = marked;
|
||||||
$(function(){
|
|
||||||
$(".markdown").each(function(){
|
|
||||||
var md = converter($(this).html());
|
|
||||||
$(this).html(md);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
{% block js %}{% endblock %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
9
reimagine/templates/macros.html
Normal file
9
reimagine/templates/macros.html
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{% macro recaptcha(config) -%}
|
||||||
|
<script>
|
||||||
|
var RecaptchaOptions = {
|
||||||
|
theme : '{{ config.RECAPTCHA_OPTIONS['theme'] }}'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<script src="http://www.google.com/recaptcha/api/challenge?k={{ config.RECAPTCHA_PUBLIC_KEY }}">
|
||||||
|
</script>
|
||||||
|
{%- endmacro %}
|
|
@ -4,27 +4,16 @@
|
||||||
<script src="/static/js/ace/mode-markdown.js"></script>
|
<script src="/static/js/ace/mode-markdown.js"></script>
|
||||||
<script src="/static/js/keymaster.min.js"></script>
|
<script src="/static/js/keymaster.min.js"></script>
|
||||||
<script src="/static/js/dillinger.js"></script>
|
<script src="/static/js/dillinger.js"></script>
|
||||||
<script>
|
|
||||||
$(function(){
|
|
||||||
$("#save").click(function(){
|
|
||||||
$(this).prop('disable', true);
|
|
||||||
var data = {
|
|
||||||
name: $("#page-name").val(),
|
|
||||||
content: editor.getSession().getValue()
|
|
||||||
}
|
|
||||||
$.post(window.location, data, function(){
|
|
||||||
location.href = "/" + data['name'];
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
|
||||||
<div id="app-wrap" class="container-fluid">
|
<div id="app-wrap" class="container-fluid">
|
||||||
<div id="app-controls" class="row">
|
<div id="app-controls" class="row">
|
||||||
<div class="col-xs-6">
|
<div class="col-xs-3">
|
||||||
<input id="page-name" type="text" class="form-control" id="page" name="name" placeholder="Name" value="{{- name -}}" />
|
<input id="page-name" type="text" class="form-control" name="name" placeholder="Name" value="{{- name -}}" />
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-3">
|
||||||
|
<input id="page-message" type="text" class="form-control" name="page-message" placeholder="Comment" value="" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-xs-6">
|
<div class="col-xs-6">
|
||||||
|
|
16
reimagine/templates/page/history.html
Normal file
16
reimagine/templates/page/history.html
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{% extends 'layout.html' %}
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
|
<h2>History</h2>
|
||||||
|
<table class="table table-bordered">
|
||||||
|
{% for h in history %}
|
||||||
|
<tr>
|
||||||
|
<td class="checkbox-cell text-center"><input type="checkbox" /></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>{{ h.time|datetime }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -1,6 +1,15 @@
|
||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
|
||||||
<div class="markdown">{{- page.data|safe -}}</div>
|
<div id="page-content" style="display:none">
|
||||||
|
{{ page.data|safe }}
|
||||||
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
{% block js %}
|
||||||
|
<script>
|
||||||
|
$(function(){
|
||||||
|
$("#page-content").html(converter({{ page.data|tojson|safe }})).show();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -51,7 +51,7 @@ def to_canonical(s):
|
||||||
"""
|
"""
|
||||||
s = s.encode('ascii', 'ignore')
|
s = s.encode('ascii', 'ignore')
|
||||||
s = str(s)
|
s = str(s)
|
||||||
s = re.sub(r"\s\s+", "-", s)
|
s = re.sub(r"\s\s*", "-", s)
|
||||||
s = re.sub(r"\-\-+", "-", s)
|
s = re.sub(r"\-\-+", "-", s)
|
||||||
s = re.sub(r"[^a-zA-Z0-9\-]", "", s)
|
s = re.sub(r"[^a-zA-Z0-9\-]", "", s)
|
||||||
s = s[:64]
|
s = s[:64]
|
||||||
|
|
|
@ -3,21 +3,51 @@ from util import to_canonical
|
||||||
from lxml.html.clean import clean_html
|
from lxml.html.clean import clean_html
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
class Wiki():
|
class Wiki():
|
||||||
path = None
|
path = None
|
||||||
base_path = '/'
|
base_path = '/'
|
||||||
default_ref = 'master'
|
default_ref = 'master'
|
||||||
default_committer_name = 'Anon'
|
default_committer_name = 'Anon'
|
||||||
default_committer_email = 'anon@anon.anon'
|
default_committer_email = 'anon@anon.anon'
|
||||||
index_page = 'Home'
|
index_page = 'home'
|
||||||
repo = None
|
repo = None
|
||||||
|
|
||||||
def __init__(self, path):
|
def __init__(self, path):
|
||||||
try:
|
try:
|
||||||
self.repo = Gittle.init(path)
|
self.repo = MyGittle.init(path)
|
||||||
except OSError:
|
except OSError:
|
||||||
# Repo already exists
|
# Repo already exists
|
||||||
self.repo = Gittle(path)
|
self.repo = MyGittle(path)
|
||||||
|
|
||||||
self.path = path
|
self.path = path
|
||||||
|
|
||||||
|
@ -40,10 +70,14 @@ class Wiki():
|
||||||
def rename_page(self, old_name, new_name):
|
def rename_page(self, old_name, new_name):
|
||||||
self.repo.mv([old_name, new_name])
|
self.repo.mv([old_name, new_name])
|
||||||
|
|
||||||
def get_page(self, name):
|
def get_page(self, name, sha='HEAD'):
|
||||||
name = name.lower() + ".md"
|
name = name.lower() + ".md"
|
||||||
try:
|
try:
|
||||||
return self.repo.get_commit_files('HEAD', paths=[name]).get(name)
|
return self.repo.get_commit_files(sha, paths=[name]).get(name)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# HEAD doesn't exist yet
|
# HEAD doesn't exist yet
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def get_history(self, name):
|
||||||
|
name = name.lower() + ".md"
|
||||||
|
return self.repo.file_history(name)
|
|
@ -5,7 +5,7 @@ python-pkgs:
|
||||||
- python-pip
|
- python-pip
|
||||||
- build-essential
|
- build-essential
|
||||||
|
|
||||||
{% for pkg in ['tornado', 'pyzmq', 'itsdangerous', 'boto', 'redis', 'simplejson', 'sockjs-tornado', 'flask', 'flask-bcrypt', 'flask-login', 'flask-assets', 'gittle', 'gevent', 'lxml' ] %}
|
{% for pkg in ['tornado', 'pyzmq', 'itsdangerous', 'boto', 'redis', 'simplejson', 'sockjs-tornado', 'flask', 'flask-bcrypt', 'flask-login', 'flask-assets', 'gittle', 'gevent', 'lxml', 'markdown2', 'recaptcha', 'pyRethinkORM' ] %}
|
||||||
{{ pkg }}-pip:
|
{{ pkg }}-pip:
|
||||||
pip:
|
pip:
|
||||||
- name: {{ pkg }}
|
- name: {{ pkg }}
|
||||||
|
|
Loading…
Reference in a new issue