burp_cms 1.3.33 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.markdown +1 -1
- data/app/assets/javascripts/burp/cms_helper.js +0 -52
- data/app/assets/packages/burp/editing.js +2 -0
- data/app/assets/packages/burp/editing/js/content-decorator.js +86 -114
- data/app/assets/packages/burp/editing/js/disableJavascript.js +49 -0
- data/app/assets/packages/burp/editing/js/jquery.html2markdown.js +6 -2
- data/app/assets/packages/burp/editing/js/main.js +215 -132
- data/app/assets/packages/burp/editing/js/markdown-fix.js +19 -3
- data/app/assets/packages/burp/editing/js/snippets.js +59 -0
- data/app/controllers/burp/files_controller.rb +3 -2
- data/app/lib/burp/util.rb +15 -4
- data/lib/burp/engine.rb +1 -1
- data/lib/burp/version.rb +1 -1
- data/lib/tasks/burp_tasks.rake +1 -1
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 564123161aa7b130c03532e1a5fb5035034aa38c
|
4
|
+
data.tar.gz: d79287f16ca452d61eb030245a8fc297516e949b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb0d67f9ae6ca1c42de01bfd85782bf44035aaeddb6d783e534c7c3d0860ed421edd96b19f6ee5401f4acfa9ab0f1937fa702bcdc226877aebcdab53e6b5462e
|
7
|
+
data.tar.gz: d3b0fb6a094a7595ecd2a7f5954ad6cb8c4789ebedc490fab09aea7dd1c6a36424e0b7384c614cb13df872874aa36936937fb04018f003cf424b7d94cdf7fc43
|
data/README.markdown
CHANGED
@@ -59,7 +59,7 @@ To use snippets on pages not cought by the CatchAllController. The or part is to
|
|
59
59
|
|
60
60
|
before_filter :load_cms_page
|
61
61
|
def load_cms_page
|
62
|
-
@cms_page = Burp.find_page(request.path) || Burp::Page.new(:
|
62
|
+
@cms_page = Burp.find_page(request.path) || Burp::Page.new(:id => request.path)
|
63
63
|
end
|
64
64
|
|
65
65
|
## Title
|
@@ -1,52 +0,0 @@
|
|
1
|
-
(function(window,jQuery) {
|
2
|
-
|
3
|
-
var $ = jQuery;
|
4
|
-
|
5
|
-
window.snippets = function() {
|
6
|
-
|
7
|
-
var snippets = {names:[],snippets:{}};
|
8
|
-
|
9
|
-
var snippetComments = $("*:not(iframe)").contents().filter(function() {
|
10
|
-
try {
|
11
|
-
return this.nodeType === 8 && this.data.match(/snippet data-type=\"start\"/);
|
12
|
-
} catch (e) {
|
13
|
-
// Yay for not being allow to look at dom elements with external content, aka iframes
|
14
|
-
return false;
|
15
|
-
}
|
16
|
-
});
|
17
|
-
|
18
|
-
snippetComments.map(function() {
|
19
|
-
|
20
|
-
var name = this.data.match(/data-name=\"(.*?)\"/)[1];
|
21
|
-
var pageId = this.data.match(/data-page-id=\"(.*?)\"/)[1];
|
22
|
-
|
23
|
-
snippets.names.push(name);
|
24
|
-
|
25
|
-
snippets.snippets[name] = {
|
26
|
-
pageId:pageId,
|
27
|
-
comment:this,
|
28
|
-
elements:function() {
|
29
|
-
|
30
|
-
var snippetElements = [];
|
31
|
-
var element = this.comment;
|
32
|
-
while(element.nextSibling) {
|
33
|
-
element = element.nextSibling;
|
34
|
-
if(element.nodeType === 8 && element.data.match(/snippet data-type=\"end\"/)) {
|
35
|
-
break;
|
36
|
-
}
|
37
|
-
snippetElements.push(element);
|
38
|
-
}
|
39
|
-
|
40
|
-
return $(snippetElements);
|
41
|
-
},
|
42
|
-
update:function(newElements) {
|
43
|
-
this.elements().remove();
|
44
|
-
$(newElements).insertAfter(this.comment);
|
45
|
-
}
|
46
|
-
};
|
47
|
-
});
|
48
|
-
|
49
|
-
return snippets;
|
50
|
-
};
|
51
|
-
|
52
|
-
}(window,jQuery));
|
@@ -14,6 +14,8 @@
|
|
14
14
|
//= require ./editing/dep/CodeMirror-2.3/mode/htmlmixed/htmlmixed.js
|
15
15
|
//= require ./editing/dep/CodeMirror-2.3/mode/xml/xml.js
|
16
16
|
//= require ./editing/dep/CodeMirror-2.3/mode/markdown/markdown.js
|
17
|
+
//= require ./editing/js/disableJavascript.js
|
18
|
+
//= require ./editing/js/snippets.js
|
17
19
|
//= require ./editing/js/main.js
|
18
20
|
|
19
21
|
|
@@ -1,22 +1,23 @@
|
|
1
1
|
/*global
|
2
|
-
|
2
|
+
markdown2Html snippets
|
3
3
|
*/
|
4
4
|
(function($) {
|
5
5
|
|
6
6
|
var javascript_warning_has_been_shown = false;
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
8
|
+
function ContentDecorator(snippetName, options) {
|
9
|
+
this.snippetName = snippetName;
|
10
|
+
this.callbacks = {
|
11
|
+
remove: function(element) {
|
12
|
+
console.debug("remove", element);
|
13
|
+
},
|
14
|
+
insertBefore: function(beforeElement, element) {
|
15
|
+
console.debug('insertBefore', beforeElement, element);
|
16
|
+
},
|
17
|
+
append: function(snippetName, element) {
|
18
|
+
console.debug('append', snippetName, element);
|
19
|
+
}
|
20
|
+
};
|
20
21
|
|
21
22
|
if (typeof(options) === 'object') {
|
22
23
|
this.onUpdate = options['update'];
|
@@ -59,7 +60,8 @@
|
|
59
60
|
var bodyOffset = $('body').offset();
|
60
61
|
var bodyPosition = $('body').css('position');
|
61
62
|
|
62
|
-
|
63
|
+
var snippetElements = snippets().snippets[contentEditor.snippetName].elements();
|
64
|
+
$(snippetElements).filter('h1, h2, h3, h4, h5, p, img, blockquote, ul, ol').each(function() {
|
63
65
|
|
64
66
|
var position = $(this).offset();
|
65
67
|
var size = {width:$(this).outerWidth(),height:$(this).outerHeight()};
|
@@ -144,7 +146,7 @@
|
|
144
146
|
var img = createCallback(ui.draggable[0], className);
|
145
147
|
|
146
148
|
initializeMovable(contentEditor, img, function(element, positionClass) {
|
147
|
-
$(element).removeClass('left center right');
|
149
|
+
$(element).removeClass('left center right ui-droppable movable ui-draggable');
|
148
150
|
$(element).addClass(positionClass);
|
149
151
|
return element;
|
150
152
|
});
|
@@ -157,10 +159,13 @@
|
|
157
159
|
}
|
158
160
|
|
159
161
|
if($(this).parent().is(".bottom-dropbox")) {
|
160
|
-
|
162
|
+
snippets().snippets[contentEditor.snippetName].append(img);
|
163
|
+
contentEditor.callbacks.append(contentEditor.snippetName, img);
|
161
164
|
} else if(img !== markdown) {
|
162
165
|
$(img).insertBefore(markdown);
|
166
|
+
contentEditor.callbacks.insertBefore($(markdown), img);
|
163
167
|
}
|
168
|
+
|
164
169
|
clearDropBoxes();
|
165
170
|
}
|
166
171
|
|
@@ -184,14 +189,6 @@
|
|
184
189
|
}
|
185
190
|
}
|
186
191
|
|
187
|
-
function removeDraggable( element ) {
|
188
|
-
element.find( ".movable" ).draggable( "destroy" );
|
189
|
-
}
|
190
|
-
|
191
|
-
function removeRemoveZone() {
|
192
|
-
$('.remove-zone').removeClass('remove-zone').droppable( "destroy" );
|
193
|
-
}
|
194
|
-
|
195
192
|
$.extend(ContentDecorator.prototype, {
|
196
193
|
|
197
194
|
init: function() {
|
@@ -199,112 +196,86 @@
|
|
199
196
|
$(this.element).find("> .movable").each(function() {
|
200
197
|
$(this).removeClass('movable');
|
201
198
|
initializeMovable(contentEditor, this, function(element, positionClass) {
|
202
|
-
$(element).removeClass('left center right');
|
199
|
+
$(element).removeClass('left center right ui-droppable movable ui-draggable');
|
203
200
|
$(element).addClass(positionClass);
|
204
201
|
return element;
|
205
202
|
});
|
206
203
|
});
|
207
204
|
},
|
208
205
|
|
209
|
-
|
210
|
-
|
211
|
-
removeDraggable(this.element);
|
212
|
-
removeRemoveZone();
|
206
|
+
setSnippetName: function(snippetName) {
|
207
|
+
this.snippetName = snippetName;
|
213
208
|
},
|
214
209
|
|
215
|
-
|
216
|
-
|
217
|
-
var html = this.element.clone();
|
218
|
-
html.find('script[type="text/dont-run-javascript"]').each(function() {
|
219
|
-
$(this).attr("type",'text/javascript');
|
220
|
-
});
|
221
|
-
|
222
|
-
return html.find('.movable').removeClass('ui-draggable ui-droppable').end().html();
|
223
|
-
},
|
224
|
-
|
225
|
-
getMarkdown: function() {
|
226
|
-
return this.markdown;
|
210
|
+
setCallbacks: function(callbacks) {
|
211
|
+
this.callbacks = callbacks;
|
227
212
|
},
|
228
213
|
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
this.element.html("");
|
288
|
-
this.element.append(tempElement.children());
|
289
|
-
|
290
|
-
setTimeout(function() {
|
291
|
-
initializeMovable(_this, _this.element.find('> img'), function(element, positionClass) {
|
292
|
-
$(element).removeClass('left center right');
|
293
|
-
$(element).addClass(positionClass);
|
294
|
-
return element;
|
295
|
-
});
|
296
|
-
},10);
|
297
|
-
},
|
214
|
+
// updateContent: function() {
|
215
|
+
// var html = window.markdown2Html(this.markdown);
|
216
|
+
//
|
217
|
+
// if(this.lastHtml === html) {
|
218
|
+
// return;
|
219
|
+
// }
|
220
|
+
// this.lastHtml = html;
|
221
|
+
//
|
222
|
+
// var children = $(html);
|
223
|
+
//
|
224
|
+
// // Fix script escaping of text in script elements
|
225
|
+
// children.each(function() {
|
226
|
+
// if($(this).is("script")) {
|
227
|
+
// $(this).text(unescapeJavascript($(this).text()));
|
228
|
+
// $(this).attr('type','text/dont-run-javascript');
|
229
|
+
// } else {
|
230
|
+
// $(this).find('script').each(function() {
|
231
|
+
// $(this).text(unescapeJavascript($(this).text()));
|
232
|
+
// $(this).attr('type','text/dont-run-javascript');
|
233
|
+
// });
|
234
|
+
// }
|
235
|
+
// });
|
236
|
+
//
|
237
|
+
// var tempElement = $('<div></div>');
|
238
|
+
// tempElement.append(children);
|
239
|
+
//
|
240
|
+
// if(tempElement.find("script").length > 0 && !javascript_warning_has_been_shown) {
|
241
|
+
// $.gritter.add({
|
242
|
+
// title: 'WARNING!',
|
243
|
+
// text: ' Javascript found! The javascript will not be previewed but it will be saved.<br><br>Save and reload to test the javascript.',
|
244
|
+
// time: 20000
|
245
|
+
// });
|
246
|
+
//
|
247
|
+
// javascript_warning_has_been_shown = true;
|
248
|
+
// }
|
249
|
+
//
|
250
|
+
// // Fixes so that we don't reload images on each update
|
251
|
+
// var _this = this;
|
252
|
+
// tempElement.find('img').each(function() {
|
253
|
+
// var element = _this.element.find('img[src="'+$(this).attr('src')+'"]');
|
254
|
+
// if(element.length === 1) {
|
255
|
+
// if(element[0].outerHTML === this.outerHTML) {
|
256
|
+
// $(this).replaceWith(element);
|
257
|
+
// }
|
258
|
+
// }
|
259
|
+
// });
|
260
|
+
//
|
261
|
+
// this.element.html("");
|
262
|
+
// this.element.append(tempElement.children());
|
263
|
+
//
|
264
|
+
// setTimeout(function() {
|
265
|
+
// initializeMovable(_this, _this.element.find('> img'), function(element, positionClass) {
|
266
|
+
// $(element).removeClass('left center right');
|
267
|
+
// $(element).addClass(positionClass);
|
268
|
+
// return element;
|
269
|
+
// });
|
270
|
+
// },10);
|
271
|
+
// },
|
298
272
|
|
299
273
|
makeDroppable: function(elements, createCallback) {
|
300
274
|
initializeMovable(this, elements, createCallback);
|
301
275
|
},
|
302
276
|
|
303
|
-
removeDroppable: function(elements) {
|
304
|
-
$(elements).draggable( 'destroy' );
|
305
|
-
},
|
306
|
-
|
307
277
|
addRemoveZone: function(element) {
|
278
|
+
var contentEditor = this;
|
308
279
|
$(element).addClass('remove-zone');
|
309
280
|
$(element).droppable({
|
310
281
|
hoverClass: 'remove-hover',
|
@@ -313,6 +284,7 @@
|
|
313
284
|
if(ui.draggable.parents("#gallery").length === 0) {
|
314
285
|
setTimeout(function() {
|
315
286
|
// jquery-ui breaks if we remove the element during the callback
|
287
|
+
contentEditor.callbacks.remove(ui.draggable);
|
316
288
|
ui.draggable.remove();
|
317
289
|
},0);
|
318
290
|
ui.draggable.data("removed",true);
|
@@ -0,0 +1,49 @@
|
|
1
|
+
/*global
|
2
|
+
|
3
|
+
*/
|
4
|
+
|
5
|
+
(function(window) {
|
6
|
+
|
7
|
+
if(typeof(window.burp) == "undefined") {
|
8
|
+
window.burp = {};
|
9
|
+
}
|
10
|
+
|
11
|
+
function unescapeJavascript(script) {
|
12
|
+
return script.replace(/"/g,'"').replace(/'/g,"'").replace(/</g,"<").replace(/>/g,"<");
|
13
|
+
}
|
14
|
+
|
15
|
+
var javascript_warning_has_been_shown = false;
|
16
|
+
|
17
|
+
function warnAboutJavascript() {
|
18
|
+
if(!javascript_warning_has_been_shown) {
|
19
|
+
$.gritter.add({
|
20
|
+
title: 'WARNING!',
|
21
|
+
text: ' Javascript found! The javascript will not be previewed but it will be saved.<br><br>Save and reload to test the javascript.',
|
22
|
+
time: 20000
|
23
|
+
});
|
24
|
+
|
25
|
+
javascript_warning_has_been_shown = true;
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
function disableScriptElements(elements) {
|
30
|
+
$(elements).each(function() {
|
31
|
+
if($(this).is("script")) {
|
32
|
+
$(this).text(unescapeJavascript($(this).text()));
|
33
|
+
$(this).attr('type','text/dont-run-javascript');
|
34
|
+
warnAboutJavascript();
|
35
|
+
} else {
|
36
|
+
$(this).find('script').each(function() {
|
37
|
+
$(this).text(unescapeJavascript($(this).text()));
|
38
|
+
$(this).attr('type','text/dont-run-javascript');
|
39
|
+
warnAboutJavascript();
|
40
|
+
});
|
41
|
+
}
|
42
|
+
});
|
43
|
+
|
44
|
+
return elements;
|
45
|
+
}
|
46
|
+
|
47
|
+
window.burp.disableScriptElements = disableScriptElements;
|
48
|
+
|
49
|
+
}(window));
|
@@ -101,7 +101,7 @@ function Html2Markdown(value) {
|
|
101
101
|
|
102
102
|
dom.find('> blockquote').each(function() {
|
103
103
|
if(!shouldSkip(this)) {
|
104
|
-
$(this).replaceWith("> "+$.trim($(this).html()).replace(/\n{2,20}/g,"\n\n").replace(/\n/g,'\n> ').replace(/> \n/g,">\n"));
|
104
|
+
$(this).replaceWith("> "+$.trim($(this).html()).replace(/\n{2,20}/g,"\n\n").replace(/\n/g,'\n> ').replace(/> \n/g,">\n") + "\n\n");
|
105
105
|
}
|
106
106
|
});
|
107
107
|
|
@@ -134,5 +134,9 @@ function Html2Markdown(value) {
|
|
134
134
|
});
|
135
135
|
});
|
136
136
|
|
137
|
-
|
137
|
+
dom.find("> img").each(function() {
|
138
|
+
$(this).replaceWith($("<div></div>").append($(this).clone()).html() + "\n");
|
139
|
+
});
|
140
|
+
|
141
|
+
return $.trim(dom.html().replace(/^>/mg,'>').replace(/^</mg,'<').replace(/\n{2,20}/g,"\n\n"));
|
138
142
|
}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
/*global
|
2
|
-
snippets CodeMirror ContentDecorator qq Html2Markdown
|
2
|
+
snippets CodeMirror ContentDecorator qq Html2Markdown markdown2Html burp
|
3
3
|
*/
|
4
4
|
|
5
5
|
$(function() {
|
@@ -12,76 +12,66 @@ $(function() {
|
|
12
12
|
var contentDecorator;
|
13
13
|
var editor;
|
14
14
|
|
15
|
-
function
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
$(snippet.elements()).each(function() {
|
21
|
-
if($(this).is("script")) {
|
22
|
-
$(this).attr("type",'text/dont-run-javascript');
|
23
|
-
} else {
|
24
|
-
$(this).find("script").each(function() {
|
25
|
-
$(this).attr("type",'text/dont-run-javascript');
|
26
|
-
});
|
27
|
-
}
|
28
|
-
});
|
29
|
-
|
30
|
-
snippet.update($('<div data-snippet-name="'+name+'" class="snippet-wrapper snippet-'+name+'"></div>').append($(snippet.elements())));
|
31
|
-
});
|
15
|
+
function getPathFor(snippetName) {
|
16
|
+
var path = window.burp_path || snippets().snippets[snippetName].pageId || window.location.pathname;
|
17
|
+
if(path === "/") {
|
18
|
+
path = "/$root";
|
19
|
+
}
|
32
20
|
|
33
|
-
|
34
|
-
$('.burp-remove, .remove-on-save').remove();
|
35
|
-
$('.burp-unwrap').each(function() {$(this).replaceWith(this.children);});
|
21
|
+
return path;
|
36
22
|
}
|
37
23
|
|
38
|
-
|
39
|
-
container.find("p").each(function() {
|
40
|
-
if($(this).children().length === $(this).find('img').length) {
|
41
|
-
$(this).children().unwrap();
|
42
|
-
}
|
43
|
-
});
|
44
|
-
}
|
24
|
+
var snippetCache = {};
|
45
25
|
|
46
|
-
function
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
26
|
+
function getHTMLForSnippet(_snippetName, callback) {
|
27
|
+
var path = getPathFor(snippetName);
|
28
|
+
|
29
|
+
var cacheKey = path + _snippetName;
|
30
|
+
if(snippetCache[cacheKey]) {
|
31
|
+
setTimeout(function() {
|
32
|
+
callback(snippetCache[cacheKey]);
|
33
|
+
}, 0);
|
34
|
+
} else {
|
35
|
+
$.ajax("/burp/pages/"+path,{
|
36
|
+
cache:false,
|
37
|
+
dataType:'json',
|
38
|
+
success:function(data) {
|
39
|
+
data = data || {snippets:{}};
|
40
|
+
snippetCache[cacheKey] = data.snippets[snippetName];
|
41
|
+
callback(snippetCache[cacheKey]);
|
42
|
+
}
|
43
|
+
});
|
51
44
|
}
|
52
45
|
}
|
53
46
|
|
54
|
-
|
55
|
-
|
56
|
-
var element = snippets().snippets[snippetName].elements().clone();
|
57
|
-
element.find('.markdown').each(function() {
|
58
|
-
$(this).removeClass('markdown');
|
59
|
-
if($(this).attr('class') === "") {
|
60
|
-
$(this).removeAttr('class');
|
61
|
-
}
|
62
|
-
});
|
63
|
-
|
64
|
-
element.find('script[type="text/dont-run-javascript"]').each(function() {
|
65
|
-
$(this).attr("type",'text/javascript');
|
66
|
-
});
|
67
|
-
|
68
|
-
element.find('img.movable').each(function() {
|
69
|
-
$(this).removeClass('movable ui-draggable ui-droppable');
|
70
|
-
});
|
71
|
-
|
72
|
-
editor.setValue(Html2Markdown(element.children()));
|
73
|
-
}
|
47
|
+
var snippetEditorState = {};
|
74
48
|
|
75
|
-
function loadSnippet() {
|
76
|
-
var path =
|
77
|
-
|
78
|
-
|
49
|
+
function loadSnippet(snippetName, callback) {
|
50
|
+
var path = getPathFor(snippetName);
|
51
|
+
|
52
|
+
if(typeof(callback) === "undefined") {
|
53
|
+
callback = function() {};
|
79
54
|
}
|
80
55
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
56
|
+
if(snippetEditorState[snippetName]) {
|
57
|
+
callback(snippetEditorState[snippetName]);
|
58
|
+
} else {
|
59
|
+
getHTMLForSnippet(snippetName, function(html) {
|
60
|
+
|
61
|
+
var element = $("<div>" + html + "</div>");
|
62
|
+
|
63
|
+
element.find('script[type="text/dont-run-javascript"]').each(function() {
|
64
|
+
$(this).attr("type",'text/javascript');
|
65
|
+
});
|
66
|
+
|
67
|
+
element.find('img.movable').each(function() {
|
68
|
+
$(this).removeClass('movable ui-draggable ui-droppable');
|
69
|
+
});
|
70
|
+
|
71
|
+
snippetEditorState[snippetName] = Html2Markdown(element.children());
|
72
|
+
callback(snippetEditorState[snippetName]);
|
73
|
+
});
|
74
|
+
}
|
85
75
|
}
|
86
76
|
|
87
77
|
function loadFiles() {
|
@@ -119,18 +109,56 @@ $(function() {
|
|
119
109
|
}
|
120
110
|
|
121
111
|
function selectSnippet(_snippetName) {
|
112
|
+
|
113
|
+
snippetName = _snippetName;
|
114
|
+
loadSnippet(snippetName, function(snippet) {
|
115
|
+
editor.setValue(snippet);
|
116
|
+
editor.refresh();
|
117
|
+
});
|
122
118
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
119
|
+
contentDecorator.setSnippetName(snippetName);
|
120
|
+
}
|
121
|
+
|
122
|
+
function removeIDs(elements) {
|
127
123
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
$('
|
124
|
+
$(elements).each(function() {
|
125
|
+
$(this).removeAttr("eid");
|
126
|
+
});
|
127
|
+
|
128
|
+
$(elements).find('*').each(function() {
|
129
|
+
$(this).removeAttr("eid");
|
130
|
+
});
|
133
131
|
|
132
|
+
return elements;
|
133
|
+
}
|
134
|
+
|
135
|
+
function addIDs(snippetName, elements) {
|
136
|
+
|
137
|
+
var count = 0;
|
138
|
+
|
139
|
+
$(elements).each(function() {
|
140
|
+
$(this).attr("eid", snippetName + "-" + String(count++));
|
141
|
+
});
|
142
|
+
|
143
|
+
$(elements).find('*').each(function() {
|
144
|
+
$(this).attr("eid", snippetName + "-" +String(count++));
|
145
|
+
});
|
146
|
+
|
147
|
+
return elements;
|
148
|
+
}
|
149
|
+
|
150
|
+
var domSnippetState = {};
|
151
|
+
|
152
|
+
function updateSnippetWithMarkdown(snippetName, markdown) {
|
153
|
+
var html = markdown2Html(markdown);
|
154
|
+
var elements = burp.disableScriptElements($(html));
|
155
|
+
elements = addIDs(snippetName, elements);
|
156
|
+
domSnippetState[snippetName] = elements.clone();
|
157
|
+
snippets().snippets[snippetName].update(elements);
|
158
|
+
|
159
|
+
contentDecorator.makeDroppable(elements, function(element, positionClass) {
|
160
|
+
return $("<img src='" + $(element).attr('src') + "' class='" + positionClass + "' />");
|
161
|
+
});
|
134
162
|
}
|
135
163
|
|
136
164
|
function addEditor() {
|
@@ -144,8 +172,9 @@ $(function() {
|
|
144
172
|
matchBrackets: true,
|
145
173
|
lineWrapping: true,
|
146
174
|
theme: "default",
|
147
|
-
onChange:function(editor,changes) {
|
148
|
-
|
175
|
+
onChange:function(editor, changes) {
|
176
|
+
snippetEditorState[snippetName] = editor.getValue();
|
177
|
+
updateSnippetWithMarkdown(snippetName, snippetEditorState[snippetName]);
|
149
178
|
}
|
150
179
|
});
|
151
180
|
|
@@ -169,22 +198,16 @@ $(function() {
|
|
169
198
|
editor.replaceRange(content,editor.getCursor(true),editor.getCursor(false));
|
170
199
|
});
|
171
200
|
|
172
|
-
contentDecorator.addRemoveZone('#gallery');
|
173
|
-
|
174
|
-
// update(editor.getValue());
|
175
201
|
loadFiles();
|
176
202
|
|
177
203
|
$.adminDock.title('');
|
178
204
|
$.adminDock.footer.addButton({ icon: 'picture', text: "Pictures", showModule: $('#gallery') });
|
179
205
|
$.adminDock.footer.addButton({ icon: 'edit', text: "Edit text", showModule: $('#myContentEditor'), show: function() {
|
180
|
-
loadHTML();
|
181
206
|
editor.refresh();
|
182
207
|
} });
|
183
208
|
|
184
209
|
|
185
210
|
$(document).on('image-drop-done.burp', function() {
|
186
|
-
loadHTML();
|
187
|
-
editor.refresh();
|
188
211
|
});
|
189
212
|
|
190
213
|
var snippet_names = [];
|
@@ -202,13 +225,10 @@ $(function() {
|
|
202
225
|
'default': snippet_names[0],
|
203
226
|
change: function(option) {
|
204
227
|
|
205
|
-
|
206
228
|
selectSnippet(option);
|
207
|
-
loadSnippet()
|
208
|
-
|
209
|
-
|
210
|
-
contentDecorator.makeDroppable('#gallery img', function(element, positionClass) {
|
211
|
-
return $("<img src='" + $(element).attr('src') + "' class='" + positionClass + "' />");
|
229
|
+
loadSnippet(snippetName, function(snippet) {
|
230
|
+
editor.setValue(snippet);
|
231
|
+
editor.refresh();
|
212
232
|
});
|
213
233
|
|
214
234
|
console.debug("Switching to " + option);
|
@@ -242,38 +262,44 @@ $(function() {
|
|
242
262
|
}});
|
243
263
|
|
244
264
|
$.adminDock.footer.addButton({ icon: 'undo', text: 'Discard', secondary: true, click:function() {
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
265
|
+
|
266
|
+
var oldSnippetEditorState = snippetEditorState;
|
267
|
+
snippetEditorState = {};
|
268
|
+
|
269
|
+
$.each(oldSnippetEditorState, function(snippetName, editorState) {
|
270
|
+
loadSnippet(snippetName, function(snippet) {
|
271
|
+
updateSnippetWithMarkdown(snippetName, snippet);
|
272
|
+
});
|
273
|
+
});
|
274
|
+
|
275
|
+
loadSnippet(snippetName, function(snippet) {
|
276
|
+
editor.setValue(snippet);
|
277
|
+
editor.refresh();
|
278
|
+
});
|
249
279
|
}});
|
250
280
|
|
251
281
|
$.adminDock.footer.addButton({ icon: 'save', text: 'Save', secondary: true, click:function() {
|
252
282
|
|
253
|
-
var path =
|
254
|
-
|
255
|
-
path = "/$root";
|
256
|
-
}
|
283
|
+
var path = getPathFor(snippetName);
|
284
|
+
snippetCache = {};
|
257
285
|
|
258
286
|
$.ajax("/burp/pages/"+path,{
|
259
|
-
cache:false,
|
260
|
-
dataType:'json',
|
261
|
-
success:function(data) {
|
287
|
+
cache: false,
|
288
|
+
dataType: 'json',
|
289
|
+
success: function(data) {
|
262
290
|
|
263
291
|
data = data || {snippets:{}};
|
264
292
|
|
265
|
-
|
266
|
-
|
293
|
+
$.each(snippetEditorState, function(snippetName, snippetState) {
|
294
|
+
data.snippets[snippetName] = markdown2Html(snippetEditorState[snippetName]);
|
295
|
+
});
|
296
|
+
|
267
297
|
$.ajax("/burp/pages/"+path,{
|
268
298
|
type:"post",
|
269
299
|
data:{page:data,'_method':"put"},
|
270
300
|
dataType:'json',
|
271
301
|
success:function() {
|
272
|
-
|
273
|
-
originalValue = contentDecorator.getMarkdown();
|
274
|
-
originalHtml = contentDecorator.getHtml();
|
275
|
-
|
276
|
-
alert("The page was saved!");
|
302
|
+
alert("The page has been saved!");
|
277
303
|
}
|
278
304
|
});
|
279
305
|
}
|
@@ -287,24 +313,6 @@ $(function() {
|
|
287
313
|
|
288
314
|
var initDone = false;
|
289
315
|
|
290
|
-
function init() {
|
291
|
-
if(!initDone) {
|
292
|
-
initDone = true;
|
293
|
-
|
294
|
-
wrapContent();
|
295
|
-
selectSnippet(snippets().names.sort(function(a, b) { return a.toLowerCase() > b.toLowerCase(); })[0]);
|
296
|
-
addEditor();
|
297
|
-
|
298
|
-
loadSnippet();
|
299
|
-
|
300
|
-
$('#gallery').trigger('refresh');
|
301
|
-
setTimeout(function() { $('#gallery').trigger('refresh'); },300);
|
302
|
-
setTimeout(function() { $('#gallery').trigger('refresh'); },600);
|
303
|
-
setTimeout(function() { $('#gallery').trigger('refresh'); },1200);
|
304
|
-
setTimeout(function() { $('#gallery').trigger('refresh'); },5000);
|
305
|
-
}
|
306
|
-
}
|
307
|
-
|
308
316
|
function trigger_http_basic_auth(callback) {
|
309
317
|
if(initDone) {
|
310
318
|
callback();
|
@@ -313,21 +321,96 @@ $(function() {
|
|
313
321
|
}
|
314
322
|
}
|
315
323
|
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
324
|
+
function init(callback) {
|
325
|
+
trigger_http_basic_auth(function() {
|
326
|
+
if(!initDone) {
|
327
|
+
initDone = true;
|
328
|
+
|
329
|
+
contentDecorator = new ContentDecorator("");
|
330
|
+
contentDecorator.setCallbacks({
|
331
|
+
remove: function(element) {
|
332
|
+
var eid = element.attr('eid');
|
333
|
+
var snippetName = eid.split(/-/)[0];
|
334
|
+
|
335
|
+
var cssSelector = '[eid="'+ eid +'"]';
|
336
|
+
|
337
|
+
var root = $('<div></div>');
|
338
|
+
root.append(domSnippetState[snippetName]);
|
339
|
+
root.find(cssSelector).remove();
|
340
|
+
domSnippetState[snippetName] = root.children();
|
341
|
+
|
342
|
+
snippetEditorState[snippetName] = Html2Markdown(removeIDs(domSnippetState[snippetName]));
|
343
|
+
loadSnippet(snippetName, function(snippet) {
|
344
|
+
editor.setValue(snippet);
|
345
|
+
editor.refresh();
|
346
|
+
});
|
347
|
+
},
|
348
|
+
insertBefore: function(beforeElement, element) {
|
349
|
+
$(element).removeClass('ui-droppable movable ui-draggable');
|
350
|
+
|
351
|
+
var eid = beforeElement.attr('eid');
|
352
|
+
var snippetName = eid.split(/-/)[0];
|
353
|
+
|
354
|
+
var cssSelector = '[eid="'+ eid +'"]';
|
355
|
+
|
356
|
+
var root = $('<div></div>');
|
357
|
+
root.append(domSnippetState[snippetName]);
|
358
|
+
element.insertBefore(root.find(cssSelector));
|
359
|
+
domSnippetState[snippetName] = root.children();
|
360
|
+
|
361
|
+
snippetEditorState[snippetName] = Html2Markdown(removeIDs(domSnippetState[snippetName]));
|
362
|
+
loadSnippet(snippetName, function(snippet) {
|
363
|
+
editor.setValue(snippet);
|
364
|
+
editor.refresh();
|
365
|
+
});
|
366
|
+
},
|
367
|
+
append: function(snippetName, element) {
|
368
|
+
$(element).removeClass('ui-droppable movable ui-draggable');
|
369
|
+
|
370
|
+
domSnippetState[snippetName] = $('<div></div>').append(domSnippetState[snippetName]).append(element).children();
|
371
|
+
|
372
|
+
snippetEditorState[snippetName] = Html2Markdown(removeIDs(domSnippetState[snippetName]));
|
373
|
+
loadSnippet(snippetName, function(snippet) {
|
374
|
+
editor.setValue(snippet);
|
375
|
+
editor.refresh();
|
376
|
+
});
|
377
|
+
}
|
378
|
+
});
|
379
|
+
|
380
|
+
selectSnippet(snippets().names.sort(function(a, b) { return a.toLowerCase() > b.toLowerCase(); })[0]);
|
381
|
+
addEditor();
|
323
382
|
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
383
|
+
contentDecorator.addRemoveZone('#gallery');
|
384
|
+
|
385
|
+
loadSnippet(snippetName, function(snippet) {
|
386
|
+
editor.setValue(snippet);
|
387
|
+
editor.refresh();
|
329
388
|
});
|
389
|
+
|
390
|
+
$('#gallery').trigger('refresh');
|
391
|
+
setTimeout(function() { $('#gallery').trigger('refresh'); },300);
|
392
|
+
setTimeout(function() { $('#gallery').trigger('refresh'); },600);
|
393
|
+
setTimeout(function() { $('#gallery').trigger('refresh'); },1200);
|
394
|
+
setTimeout(function() { $('#gallery').trigger('refresh'); },5000);
|
330
395
|
}
|
396
|
+
|
397
|
+
callback();
|
398
|
+
});
|
399
|
+
}
|
400
|
+
|
401
|
+
function isCtrlOrAltEscape(event) {
|
402
|
+
return (event.altKey === true || event.ctrlKey === true ) && event.keyCode === 27;
|
403
|
+
}
|
404
|
+
|
405
|
+
function isCtrlAltSpace(event) {
|
406
|
+
return event.altKey === true && event.ctrlKey === true && event.keyCode === 32;
|
407
|
+
}
|
408
|
+
|
409
|
+
$(window).keydown(function(event) {
|
410
|
+
if (isCtrlOrAltEscape(event) || isCtrlAltSpace(event)) {
|
411
|
+
init(function() {
|
412
|
+
$.adminDock.toggle();
|
413
|
+
});
|
331
414
|
}
|
332
415
|
});
|
333
416
|
|
@@ -4,6 +4,12 @@
|
|
4
4
|
|
5
5
|
(function() {
|
6
6
|
|
7
|
+
marked.setOptions({
|
8
|
+
gfm: true,
|
9
|
+
pedantic: false,
|
10
|
+
sanitize: false
|
11
|
+
});
|
12
|
+
|
7
13
|
function includeForMarkdown(element) {
|
8
14
|
// Include text
|
9
15
|
if(element.nodeType === 3) {
|
@@ -20,7 +26,7 @@
|
|
20
26
|
$(nodes).each(function(index, element) {
|
21
27
|
var last = newArray.length-1;
|
22
28
|
if(includeForMarkdown(element)) {
|
23
|
-
var data = $("<div></div>").append(element).html();
|
29
|
+
var data = (element.nodeType === 3) ? element.data : $("<div></div>").append(element).html();
|
24
30
|
|
25
31
|
if(typeof(newArray[last]) === 'string') {
|
26
32
|
newArray[last] = newArray[last] + data;
|
@@ -34,6 +40,16 @@
|
|
34
40
|
return newArray;
|
35
41
|
}
|
36
42
|
|
43
|
+
function unwrapImagesFromParagraphs(container) {
|
44
|
+
container.find("p").each(function() {
|
45
|
+
if($(this).children().length === $(this).find('img').length) {
|
46
|
+
$(this).children().unwrap();
|
47
|
+
}
|
48
|
+
});
|
49
|
+
|
50
|
+
return container;
|
51
|
+
}
|
52
|
+
|
37
53
|
function toHtml(markdown) {
|
38
54
|
|
39
55
|
var elements = [];
|
@@ -45,8 +61,8 @@
|
|
45
61
|
elements.push(value);
|
46
62
|
}
|
47
63
|
});
|
48
|
-
|
49
|
-
return $("<div></div>").append(elements).html();
|
64
|
+
|
65
|
+
return unwrapImagesFromParagraphs($("<div></div>").append(elements)).html();
|
50
66
|
}
|
51
67
|
|
52
68
|
window.markdown2Html = toHtml;
|
@@ -0,0 +1,59 @@
|
|
1
|
+
(function(window,jQuery) {
|
2
|
+
|
3
|
+
var $ = jQuery;
|
4
|
+
|
5
|
+
window.snippets = function() {
|
6
|
+
|
7
|
+
var snippets = {names:[],snippets:{}};
|
8
|
+
|
9
|
+
var snippetComments = $("*:not(iframe)").contents().filter(function() {
|
10
|
+
try {
|
11
|
+
return this.nodeType === 8 && this.data.match(/snippet data-type=\"start\"/);
|
12
|
+
} catch (e) {
|
13
|
+
// Yay for not being allow to look at dom elements with external content, aka iframes
|
14
|
+
return false;
|
15
|
+
}
|
16
|
+
});
|
17
|
+
|
18
|
+
snippetComments.map(function() {
|
19
|
+
|
20
|
+
var name = this.data.match(/data-name=\"(.*?)\"/)[1];
|
21
|
+
var pageId = this.data.match(/data-page-id=\"(.*?)\"/)[1];
|
22
|
+
|
23
|
+
snippets.names.push(name);
|
24
|
+
|
25
|
+
snippets.snippets[name] = {
|
26
|
+
pageId:pageId,
|
27
|
+
comment:this,
|
28
|
+
elements:function() {
|
29
|
+
|
30
|
+
var snippetElements = [];
|
31
|
+
var element = this.comment;
|
32
|
+
while(element.nextSibling) {
|
33
|
+
element = element.nextSibling;
|
34
|
+
if(element.nodeType === 8 && element.data.match(/snippet data-type=\"end\"/)) {
|
35
|
+
break;
|
36
|
+
}
|
37
|
+
snippetElements.push(element);
|
38
|
+
}
|
39
|
+
|
40
|
+
return $(snippetElements);
|
41
|
+
},
|
42
|
+
update:function(newElements) {
|
43
|
+
this.elements().remove();
|
44
|
+
$(newElements).insertAfter(this.comment);
|
45
|
+
},
|
46
|
+
prepend:function(newElements) {
|
47
|
+
$(newElements).insertAfter(this.comment);
|
48
|
+
},
|
49
|
+
append:function(newElements) {
|
50
|
+
$(newElements).insertAfter(this.elements().last());
|
51
|
+
}
|
52
|
+
|
53
|
+
};
|
54
|
+
});
|
55
|
+
|
56
|
+
return snippets;
|
57
|
+
};
|
58
|
+
|
59
|
+
}(window,jQuery));
|
@@ -25,12 +25,13 @@ module Burp
|
|
25
25
|
if File.expand_path(file_path) != file_path
|
26
26
|
render :text => "403, Forbiden!", :status => 403, :content_type => "text/plain"
|
27
27
|
else
|
28
|
-
File.unlink(file_path)
|
28
|
+
File.unlink(file_path)
|
29
|
+
Util.remove_all_versions_of_image(file_path)
|
29
30
|
end
|
30
31
|
|
31
32
|
Util.commit("Burp: removed a file")
|
32
33
|
|
33
|
-
redirect_to files_path
|
34
|
+
redirect_to files_path, notice: "#{File.basename(file_path)} has been removed."
|
34
35
|
end
|
35
36
|
|
36
37
|
def show
|
data/app/lib/burp/util.rb
CHANGED
@@ -5,21 +5,27 @@ require 'RMagick'
|
|
5
5
|
module Burp
|
6
6
|
module Util
|
7
7
|
|
8
|
+
SIZES = {:small => [200,300],:medium => [600,900], :large => [1000,1500]}
|
9
|
+
|
8
10
|
def self.commit(message = "auto commit", options = {})
|
9
11
|
options[:path] ||= Burp.content_directory
|
10
12
|
raise "missing git repo in burp cms directory" if `cd #{options[:path]}; git st 2>&1`.match(/Not a git repository/)
|
11
13
|
`cd #{options[:path]}; git add .; git commit -a -m "Burp: #{message}"`
|
12
14
|
end
|
13
15
|
|
16
|
+
def self.remove_all_versions_of_image(file_path)
|
17
|
+
SIZES.each_pair do |key,value|
|
18
|
+
target_path = "#{upload_directory_path}#{key.to_s}/#{File.basename(file_path)}"
|
19
|
+
File.unlink(target_path) if File.exist?(target_path)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
14
23
|
def self.create_smaller_images(file_path)
|
15
24
|
|
16
|
-
upload_directory_path = "#{Burp.content_directory}uploads/"
|
17
|
-
|
18
|
-
sizes = {:small => [200,300],:medium => [600,900], :large => [1000,1500]}
|
19
25
|
image = Magick::ImageList.new(file_path).first
|
20
26
|
image.auto_orient!
|
21
27
|
|
22
|
-
|
28
|
+
SIZES.each_pair do |key,value|
|
23
29
|
|
24
30
|
FileUtils.mkdir_p("#{upload_directory_path}#{key.to_s}")
|
25
31
|
target_path = "#{upload_directory_path}#{key.to_s}/#{File.basename(file_path)}"
|
@@ -35,6 +41,11 @@ module Burp
|
|
35
41
|
image.destroy!
|
36
42
|
end
|
37
43
|
|
44
|
+
private
|
45
|
+
|
46
|
+
def self.upload_directory_path
|
47
|
+
"#{Burp.content_directory}uploads/"
|
48
|
+
end
|
38
49
|
|
39
50
|
|
40
51
|
end
|
data/lib/burp/engine.rb
CHANGED
@@ -12,7 +12,7 @@ module Burp
|
|
12
12
|
# Enabling assets precompiling under rails 3.1
|
13
13
|
if Rails.version >= '3.1'
|
14
14
|
initializer :assets do |config|
|
15
|
-
Rails.application.config.assets.precompile += %w( burp/editing.css burp/editing.js burp/
|
15
|
+
Rails.application.config.assets.precompile += %w( burp/editing.css burp/editing.js burp/snippets.js)
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
data/lib/burp/version.rb
CHANGED
data/lib/tasks/burp_tasks.rake
CHANGED
@@ -56,7 +56,7 @@ namespace :burp do
|
|
56
56
|
end
|
57
57
|
|
58
58
|
BurpRakeHelper.write('/app/assets/javascripts/burp.js') do |file|
|
59
|
-
file.write("// Includes burp related javascript.\n//\n//= require 'burp/editing'\n//= require 'burp/
|
59
|
+
file.write("// Includes burp related javascript.\n//\n//= require 'burp/editing'\n//= require 'burp/snippets'")
|
60
60
|
end
|
61
61
|
|
62
62
|
BurpRakeHelper.write("/app/assets/stylesheets/burp.css") do |file|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: burp_cms
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Darwin
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-05-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: jquery-rails
|
@@ -200,6 +200,8 @@ extra_rdoc_files:
|
|
200
200
|
- LICENSE.txt
|
201
201
|
- README.markdown
|
202
202
|
files:
|
203
|
+
- LICENSE.txt
|
204
|
+
- README.markdown
|
203
205
|
- Rakefile
|
204
206
|
- app/assets/images/burp/add-group-to-menu-1.png
|
205
207
|
- app/assets/images/burp/add-group-to-menu-2.png
|
@@ -326,10 +328,12 @@ files:
|
|
326
328
|
- app/assets/packages/burp/editing/dep/FontAwesome/less/font-awesome.less
|
327
329
|
- app/assets/packages/burp/editing/js/admin-dock.js
|
328
330
|
- app/assets/packages/burp/editing/js/content-decorator.js
|
331
|
+
- app/assets/packages/burp/editing/js/disableJavascript.js
|
329
332
|
- app/assets/packages/burp/editing/js/jquery.html2markdown.js
|
330
333
|
- app/assets/packages/burp/editing/js/main.js
|
331
334
|
- app/assets/packages/burp/editing/js/markdown-fix.js
|
332
335
|
- app/assets/packages/burp/editing/js/marked.js
|
336
|
+
- app/assets/packages/burp/editing/js/snippets.js
|
333
337
|
- app/assets/packages/gritter/README.markdown
|
334
338
|
- app/assets/packages/gritter/css/jquery.gritter.less
|
335
339
|
- app/assets/packages/gritter/gritter.js
|
@@ -427,8 +431,6 @@ files:
|
|
427
431
|
- lib/burp/version.rb
|
428
432
|
- lib/burp_cms.rb
|
429
433
|
- lib/tasks/burp_tasks.rake
|
430
|
-
- LICENSE.txt
|
431
|
-
- README.markdown
|
432
434
|
homepage: http://github.com/bjornblomqvist/burp
|
433
435
|
licenses:
|
434
436
|
- LGPL3
|
@@ -449,7 +451,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
449
451
|
version: '0'
|
450
452
|
requirements: []
|
451
453
|
rubyforge_project:
|
452
|
-
rubygems_version: 2.
|
454
|
+
rubygems_version: 2.2.2
|
453
455
|
signing_key:
|
454
456
|
specification_version: 4
|
455
457
|
summary: A CMS that tries hard to not get in your way!
|