This commit is contained in:
Matthew Scragg 2013-10-09 16:35:06 -05:00
parent 9d3751f241
commit 3cc69ce06f
24 changed files with 698 additions and 8837 deletions

3
.bowerrc Normal file
View file

@ -0,0 +1,3 @@
{
"directory": "realms/static/vendor"
}

5
.gitignore vendored
View file

@ -1,5 +1,8 @@
.vagrant
.idea
.webassets-cache
*.pyc
packed-*.js
config.py
config.sls
config.sls
realms/static/vendor

16
bower.json Normal file
View file

@ -0,0 +1,16 @@
{
"name": "realms",
"version": "0.1.1",
"dependencies": {
"components-bootstrap": "3.0.0",
"jquery": "1.9.1",
"components-underscore": "~1.5.1",
"requirejs": "~2.1.8",
"highlightjs": "~7.3.0",
"handlebars": "~1.0.0",
"components-font-awesome": "~3.2.1",
"showdown": "~0.3.1",
"keymaster": "madrobby/keymaster",
"ace": "~1.0.0"
}
}

View file

@ -6,7 +6,7 @@ from threading import Lock
import rethinkdb as rdb
from flask import Flask, request, render_template, url_for, redirect, flash
from flask.ext.login import LoginManager, login_required
from flask.ext.assets import Environment
from flask.ext.assets import Environment, Bundle
from recaptcha.client import captcha
from werkzeug.routing import BaseConverter
@ -18,10 +18,18 @@ from models import Site, User, CurrentUser
from ratelimit import get_view_rate_limit, ratelimiter
from services import db
# Flask instance container
instances = {}
# Flask extension objects
login_manager = LoginManager()
assets = Environment()
class SubdomainDispatcher(object):
"""
Application factory
"""
def __init__(self, domain, create_app):
self.domain = domain
self.create_app = create_app
@ -44,6 +52,9 @@ class SubdomainDispatcher(object):
def init_db(dbname):
"""
Assures DB has minimal setup
"""
if not dbname in rdb.db_list().run(db):
print "Creating DB %s" % dbname
rdb.db_create(dbname).run(db)
@ -65,6 +76,9 @@ def init_db(dbname):
class RegexConverter(BaseConverter):
"""
Enables Regex matching on endpoints
"""
def __init__(self, url_map, *items):
super(RegexConverter, self).__init__(url_map)
self.regex = items[0]
@ -85,7 +99,17 @@ def validate_captcha():
return response.is_valid
def format_subdomain(s):
s = s.lower()
s = to_canonical(s)
if s in ['www']:
# Not allowed
s = ""
return s
def make_app(subdomain):
subdomain = format_subdomain(subdomain)
if subdomain and not Wiki.is_registered(subdomain):
return redirect("http://%s/_new/?site=%s" % (config.hostname, subdomain))
return create_app(subdomain)
@ -100,7 +124,6 @@ def create_app(subdomain=None):
app.session_interface = RedisSessionInterface()
app.url_map.converters['regex'] = RegexConverter
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'
@ -108,9 +131,26 @@ def create_app(subdomain=None):
def load_user(user_id):
return CurrentUser(user_id)
assets = Environment(app)
assets.url = app.static_url_path
assets.directory = app.static_folder
assets.init_app(app)
if 'js_common' not in assets._named_bundles:
js = Bundle('vendor/jquery/jquery.js',
'vendor/components-underscore/underscore.js',
'vendor/components-bootstrap/js/bootstrap.js',
'vendor/handlebars/handlebars.js',
'vendor/showdown/src/showdown.js',
'js/html-sanitizer-minified.js',
'js/wmd.js',
'vendor/highlightjs/highlight.pack.js',
filters='uglifyjs', output='packed-common.js')
assets.register('js_common', js)
if 'js_editor' not in assets._named_bundles:
js = Bundle('js/ace/ace.js',
'js/ace/mode-markdown.js',
'vendor/keymaster/keymaster.js',
'js/dillinger.js',
filters='uglifyjs', output='packed-editor.js')
assets.register('js_editor', js)
repo_dir = config.repos['dir']
repo_name = subdomain if subdomain else "_"

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -1,7 +0,0 @@
/*!
Autosize v1.17.8 - 2013-09-07
Automatically adjust textarea height based on user input.
(c) 2013 Jack Moore - http://www.jacklmoore.com/autosize
license: http://www.opensource.org/licenses/mit-license.php
*/
(function(e){"function"==typeof define&&define.amd?define(["jquery"],e):e(window.jQuery||window.$)})(function(e){var t,o={className:"autosizejs",append:"",callback:!1,resizeDelay:10},i='<textarea tabindex="-1" style="position:absolute; top:-999px; left:0; right:auto; bottom:auto; border:0; padding: 0; -moz-box-sizing:content-box; -webkit-box-sizing:content-box; box-sizing:content-box; word-wrap:break-word; height:0 !important; min-height:0 !important; overflow:hidden; transition:none; -webkit-transition:none; -moz-transition:none;"/>',n=["fontFamily","fontSize","fontWeight","fontStyle","letterSpacing","textTransform","wordSpacing","textIndent"],s=e(i).data("autosize",!0)[0];s.style.lineHeight="99px","99px"===e(s).css("lineHeight")&&n.push("lineHeight"),s.style.lineHeight="",e.fn.autosize=function(i){return this.length?(i=e.extend({},o,i||{}),s.parentNode!==document.body&&e(document.body).append(s),this.each(function(){function o(){var t,o;"getComputedStyle"in window?(t=window.getComputedStyle(u),o=u.getBoundingClientRect().width,e.each(["paddingLeft","paddingRight","borderLeftWidth","borderRightWidth"],function(e,i){o-=parseInt(t[i],10)}),s.style.width=o+"px"):s.style.width=Math.max(p.width(),0)+"px"}function a(){var a={};if(t=u,s.className=i.className,d=parseInt(p.css("maxHeight"),10),e.each(n,function(e,t){a[t]=p.css(t)}),e(s).css(a),o(),window.chrome){var r=u.style.width;u.style.width="0px",u.offsetWidth,u.style.width=r}}function r(){var e,n;t!==u?a():o(),s.value=u.value+i.append,s.style.overflowY=u.style.overflowY,n=parseInt(u.style.height,10),s.scrollTop=0,s.scrollTop=9e4,e=s.scrollTop,d&&e>d?(u.style.overflowY="scroll",e=d):(u.style.overflowY="hidden",c>e&&(e=c)),e+=f,n!==e&&(u.style.height=e+"px",w&&i.callback.call(u,u))}function l(){clearTimeout(h),h=setTimeout(function(){var e=p.width();e!==g&&(g=e,r())},parseInt(i.resizeDelay,10))}var d,c,h,u=this,p=e(u),f=0,w=e.isFunction(i.callback),z={height:u.style.height,overflow:u.style.overflow,overflowY:u.style.overflowY,wordWrap:u.style.wordWrap,resize:u.style.resize},g=p.width();p.data("autosize")||(p.data("autosize",!0),("border-box"===p.css("box-sizing")||"border-box"===p.css("-moz-box-sizing")||"border-box"===p.css("-webkit-box-sizing"))&&(f=p.outerHeight()-p.height()),c=Math.max(parseInt(p.css("minHeight"),10)-f||0,p.height()),p.css({overflow:"hidden",overflowY:"hidden",wordWrap:"break-word",resize:"none"===p.css("resize")||"vertical"===p.css("resize")?"none":"horizontal"}),"onpropertychange"in u?"oninput"in u?p.on("input.autosize keyup.autosize",r):p.on("propertychange.autosize",function(){"value"===event.propertyName&&r()}):p.on("input.autosize",r),i.resizeDelay!==!1&&e(window).on("resize.autosize",l),p.on("autosize.resize",r),p.on("autosize.resizeIncludeStyle",function(){t=null,r()}),p.on("autosize.destroy",function(){t=null,clearTimeout(h),e(window).off("resize",l),p.off("autosize").off(".autosize").css(z).removeData("autosize")}),r())})):this}});

View file

@ -1,4 +0,0 @@
// keymaster.js
// (c) 2011 Thomas Fuchs
// keymaster.js may be freely distributed under the MIT license.
(function(a){function m(a,b,c){a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent&&a.attachEvent("on"+b,function(){c(window.event)})}function l(a){e=a||"all"}function k(a,b,d){var e,h,i,j;d===undefined&&(d=b,b="all"),a=a.replace(/\s/g,""),e=a.split(","),e[e.length-1]==""&&(e[e.length-2]+=",");for(i=0;i<e.length;i++){h=[],a=e[i].split("+");if(a.length>1){h=a.slice(0,a.length-1);for(j=0;j<h.length;j++)h[j]=f[h[j]];a=[a[a.length-1]]}a=a[0],a=g[a]||a.toUpperCase().charCodeAt(0),a in c||(c[a]=[]),c[a].push({shortcut:e[i],scope:b,method:d,key:e[i],mods:h})}}function j(a){var b=a.keyCode,c;if(b==93||b==224)b=91;if(b in d){d[b]=!1;for(c in f)f[c]==b&&(k[c]=!1)}}function i(a){var b,g,i,j,l,m;g=(a.target||a.srcElement).tagName,b=a.keyCode;if(b==93||b==224)b=91;if(b in d){d[b]=!0;for(j in f)f[j]==b&&(k[j]=!0)}else{if(g=="INPUT"||g=="SELECT"||g=="TEXTAREA")return;if(!(b in c))return;for(l=0;l<c[b].length;l++){i=c[b][l];if(i.scope==e||i.scope=="all"){m=i.mods.length>0;for(j in d)if(!d[j]&&h(i.mods,+j)>-1||d[j]&&h(i.mods,+j)==-1)m=!1;(i.mods.length==0&&!d[16]&&!d[18]&&!d[17]&&!d[91]||m)&&i.method(a,i)===!1&&(a.preventDefault?a.preventDefault():a.returnValue=!1,a.stopPropagation&&a.stopPropagation(),a.cancelBubble&&(a.cancelBubble=!0))}}}}function h(a,b){var c=a.length;while(c--)if(a[c]===b)return c;return-1}var b,c={},d={16:!1,18:!1,17:!1,91:!1},e="all",f={"⇧":16,shift:16,"⌥":18,alt:18,option:18,"⌃":17,ctrl:17,control:17,"⌘":91,command:91},g={backspace:8,tab:9,clear:12,enter:13,"return":13,esc:27,escape:27,space:32,left:37,up:38,right:39,down:40,del:46,"delete":46,home:36,end:35,pageup:33,pagedown:34,",":188,".":190,"/":191,"`":192,"-":189,"=":187,";":186,"'":222,"[":219,"]":221,"\\":220};for(b=1;b<20;b++)f["f"+b]=111+b;for(b in f)k[b]=!1;m(document,"keydown",i),m(document,"keyup",j),a.key=k,a.key.setScope=l,typeof module!="undefined"&&(module.exports=key)})(this)

File diff suppressed because it is too large Load diff

View file

@ -1,5 +0,0 @@
//
// Github Extension (WIP)
// ~~strike-through~~ -> <del>strike-through</del>
//
(function(){var a=function(a){return[{type:"lang",regex:"(~T){2}([^~]+)(~T){2}",replace:function(a,b,c,d){return"<del>"+c+"</del>"}}]};typeof window!="undefined"&&window.Showdown&&window.Showdown.extensions&&(window.Showdown.extensions.github=a),typeof module!="undefined"&&(module.exports=a)})();

View file

@ -1,6 +0,0 @@
//
// Google Prettify
// A showdown extension to add Google Prettify (http://code.google.com/p/google-code-prettify/)
// hints to showdown's HTML output.
//
(function(){var a=function(a){return[{type:"output",filter:function(a){return a.replace(/(<pre>)?<code>/gi,function(a,b){return b?'<pre class="prettyprint linenums" tabIndex="0"><code data-inner="1">':'<code class="prettyprint">'})}}]};typeof window!="undefined"&&window.Showdown&&window.Showdown.extensions&&(window.Showdown.extensions.googlePrettify=a),typeof module!="undefined"&&(module.exports=a)})();

View file

@ -1,105 +0,0 @@
/*global module:true*/
/*
* Basic table support with re-entrant parsing, where cell content
* can also specify markdown.
*
* Tables
* ======
*
* | Col 1 | Col 2 |
* |======== |====================================================|
* |**bold** | ![Valid XHTML] (http://w3.org/Icons/valid-xhtml10) |
* | Plain | Value |
*
*/
(function(){
var table = function(converter) {
var tables = {}, style = 'text-align:left;', filter;
tables.th = function(header){
if (header.trim() === "") { return "";}
var id = header.trim().replace(/ /g, '_').toLowerCase();
return '<th id="' + id + '" style="'+style+'">' + header + '</th>';
};
tables.td = function(cell) {
return '<td style="'+style+'">' + converter.makeHtml(cell) + '</td>';
};
tables.ths = function(){
var out = "", i = 0, hs = [].slice.apply(arguments);
for (i;i<hs.length;i+=1) {
out += tables.th(hs[i]) + '\n';
}
return out;
};
tables.tds = function(){
var out = "", i = 0, ds = [].slice.apply(arguments);
for (i;i<ds.length;i+=1) {
out += tables.td(ds[i]) + '\n';
}
return out;
};
tables.thead = function() {
var out, i = 0, hs = [].slice.apply(arguments);
out = "<thead>\n";
out += "<tr>\n";
out += tables.ths.apply(this, hs);
out += "</tr>\n";
out += "</thead>\n";
return out;
};
tables.tr = function() {
var out, i = 0, cs = [].slice.apply(arguments);
out = "<tr>\n";
out += tables.tds.apply(this, cs);
out += "</tr>\n";
return out;
};
filter = function(text) {
var i=0, lines = text.split('\n'), tbl = [], line, hs, rows, out = [];
for (i; i<lines.length;i+=1) {
line = lines[i];
// looks like a table heading
if (line.trim().match(/^[|]{1}.*[|]{1}$/)) {
line = line.trim();
tbl.push('<table>');
hs = line.substring(1, line.length -1).split('|');
tbl.push(tables.thead.apply(this, hs));
line = lines[++i];
if (!line.trim().match(/^[|]{1}[-=| ]+[|]{1}$/)) {
// not a table rolling back
line = lines[--i];
}
else {
line = lines[++i];
tbl.push('<tbody>');
while (line.trim().match(/^[|]{1}.*[|]{1}$/)) {
line = line.trim();
tbl.push(tables.tr.apply(this, line.substring(1, line.length -1).split('|')));
line = lines[++i];
}
tbl.push('</tbody>');
tbl.push('</table>');
// we are done with this table and we move along
out.push(tbl.join('\n'));
continue;
}
}
out.push(line);
}
return out.join('\n');
};
return [
{
type: 'lang',
filter: filter
}
];
};
// Client-side export
if (typeof window !== 'undefined' && window.Showdown && window.Showdown.extensions) { window.Showdown.extensions.table = table; }
// Server-side export
if (typeof module !== 'undefined') {
module.exports = table;
}
}());

View file

@ -1,6 +0,0 @@
//
// Twitter Extension
// @username -> <a href="http://twitter.com/username">@username</a>
// #hashtag -> <a href="http://twitter.com/search/%23hashtag">#hashtag</a>
//
(function(){var a=function(a){return[{type:"lang",regex:"\\B(\\\\)?@([\\S]+)\\b",replace:function(a,b,c){return b==="\\"?a:'<a href="http://twitter.com/'+c+'">@'+c+"</a>"}},{type:"lang",regex:"\\B(\\\\)?#([\\S]+)\\b",replace:function(a,b,c){return b==="\\"?a:'<a href="http://twitter.com/search/%23'+c+'">#'+c+"</a>"}},{type:"lang",regex:"\\\\@",replace:"@"}]};typeof window!="undefined"&&window.Showdown&&window.Showdown.extensions&&(window.Showdown.extensions.twitter=a),typeof module!="undefined"&&(module.exports=a)})();

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -3,6 +3,10 @@
* Copyright (c) 2010 Caolan McMahon
*/
/*
* Fork of https://github.com/caolan/wmd
*/
function escapeHtml(s) {
s = ('' + s); /* Coerce to string */
s = s.replace(/&/g, '&amp;');
@ -22,7 +26,9 @@ function escapeHtml(s) {
* @api public
*/
var WMD = function (content, options) {
var WMD = {};
WMD.convert = function(content, options) {
var doc = {raw: content, markdown: content};
var opt = WMD.readOptions(options);
WMD.preprocess(doc, opt);

View file

@ -10,8 +10,9 @@
<link href="/static/css/bootstrap/spacelab.css" rel="stylesheet">
<link href="/static/css/font-awesome.min.css" rel="stylesheet">
<link href="/static/js/highlight/styles/github.css" rel="stylesheet">
<link href="/static/vendor/highlightjs/styles/github.css" rel="stylesheet">
<link href="/static/css/style.css" rel="stylesheet">
{% block css %}{% endblock %}
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
@ -99,53 +100,35 @@
</div>
</div>
<script src="/static/js/jquery-1.10.2.min.js"></script>
<script src="/static/js/underscore.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
<!--<script src="/static/js/marked.js"></script>-->
<script src="/static/js/handlebars.js"></script>
<script src="/static/js/showdown/showdown.js"></script>
<script src="/static/js/html-sanitizer-minified.js"></script>
<script src="/static/js/showdown/wmd.js"></script>
<script src="/static/js/highlight/highlight.pack.js"></script>
{% assets "js_common" %}
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
{% endassets %}
<script>
/*
marked.setOptions({
gfm: true,
tables: true,
pedantic: false,
sanitize: false,
smartLists: true,
smartypants: false,
langPrefix: 'lang-'
});
*/
hljs.initHighlightingOnLoad();
// Markdown Renderer
MDR = {
doc: null,
callback: WMD,
callback: WMD.convert,
convert: function(md, sanitize){
this.doc = this.callback(md);
md = this.doc.html;
var html = this.doc.html;
if (sanitize) {
md = html_sanitize(md);
// Causes some problems with inline styles
html = html_sanitize(html);
}
md = this.hook(md);
return md;
html = this.hook(html);
return html;
},
hook: function(md) {
hook: function(html) {
if (!this.doc.metadata) {
return md;
return html;
}
try {
var template = Handlebars.compile(md);
var template = Handlebars.compile(html);
return template(this.doc.metadata);
} catch(e) {
return md;
return html;
}
}
};

View file

@ -1,21 +1,23 @@
{% extends 'layout.html' %}
{% block js %}
<script src="/static/js/ace/ace.js"></script>
<script src="/static/js/ace/mode-markdown.js"></script>
<script src="/static/js/keymaster.min.js"></script>
<script src="/static/js/dillinger.js"></script>
{% assets "js_editor" %}
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
{% endassets %}
{% endblock %}
{% block body %}
{% block css %}
<style>
#main-body {
background: inherit;
border: inherit;
padding: 0;
-webkit-box-shadow: 0;
-moz-box-shadow: 0;
box-shadow: 0;
}
#main-body {
background: inherit;
border: inherit;
padding: 0;
-webkit-box-shadow: 0;
-moz-box-shadow: 0;
box-shadow: 0;
}
</style>
{% endblock %}
<div id="app-wrap" class="container-fluid">
<div id="app-controls" class="row">
@ -37,7 +39,7 @@
<li><a tabindex="-1" href="#" data-value="ace/theme/cobalt" class="">Cobalt</a></li>
<li><a tabindex="-1" href="#" data-value="ace/theme/crimson_editor" class="">Crimson Editor</a></li>
<li><a tabindex="-1" href="#" data-value="ace/theme/dawn" class="selected">Dawn</a></li>
<li><a tabindex="-1" href="#" data-value="ace/theme/dawn" class="">Dreamweaver</a></li>
<li><a tabindex="-1" href="#" data-value="ace/theme/dreamweaver" class="">Dreamweaver</a></li>
<li><a tabindex="-1" href="#" data-value="ace/theme/eclipse" class="">Eclipse</a></li>
<li><a tabindex="-1" href="#" data-value="ace/theme/idle_fingers" class="">idleFingers</a></li>
<li><a tabindex="-1" href="#" data-value="ace/theme/kr_theme" class="">krTheme</a></li>

View file

@ -34,6 +34,7 @@ def unescape_html(s):
s = s.replace('&#39;', "'")
return s
def mkdir_safe(path):
if path and not(os.path.exists(path)):
os.makedirs(path)

View file

@ -6,6 +6,4 @@ nginx:
- enable: True
- reload: True
- require:
- pkg: nginx
/etc/
- pkg: nginx

9
srv/salt/nodejs/init.sls Normal file
View file

@ -0,0 +1,9 @@
node-repos:
pkgrepo.managed:
- ppa: chris-lea/node.js
nodejs:
pkg:
- installed
- require:
- pkgrepo.managed: node-repos