kuhsaft 1.7.1 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 76dbe731782470b70b5f4e800df56a90940025dd
4
+ data.tar.gz: 863e59213b956cb04fbb9c289e0e2a9cda6f04c3
5
+ SHA512:
6
+ metadata.gz: 3225ea8917bd257a8bad15171de011d8cd6a4a400d2297eb5c1136418323fba0ef25e1eb8bb0172436ac513f0f2a7aa1cbadde5a527589b51e09c525a9fae8c9
7
+ data.tar.gz: 2babddfa702eb7b132703f846d44b37d25af7b7564d1256b5a740b7660dcd8df1b56a34ba2e25d6d738c272aac45d4d39b3567a15a9f7fefe0e76c6e68679679
data/README.md CHANGED
@@ -61,6 +61,18 @@ Load the Kuhsaft assets into your app, so you have working grids, widgets etc:
61
61
  # application.js.coffee
62
62
  //= require 'kuhsaft/application'
63
63
 
64
+ Finally, you need to define the image sizes for the image brick or use
65
+ the defaults:
66
+
67
+ # your_app/config/initializers/kuhsaft.rb
68
+ Rails.application.config.to_prepare do
69
+ Kuhsaft::Engine.configure do
70
+ config.image_sizes.build_defaults! # creates 960x540 and 320x180 sizes
71
+ end
72
+ end
73
+
74
+ See "Customizing The Image Brick" for more details.
75
+
64
76
  ## Authentication
65
77
 
66
78
  Kuhsaft itself does not ship with any form of authentication. However, it is fairly easy to add by plugging into the Kuhsaft::Cms::AdminController. An example with devise:
@@ -9,6 +9,7 @@
9
9
  #= require jquery_ujs
10
10
  #= require bootstrap
11
11
  #= require ckeditor/init
12
+ #= require jquery.nestable
12
13
  #= require_tree .
13
14
 
14
15
  CKEDITOR.config.customConfig = '/assets/kuhsaft/cms/ck-config.js'
@@ -79,3 +80,14 @@ $(document).ready ->
79
80
 
80
81
  $('#page_page_type').change ->
81
82
  checkPageType()
83
+
84
+ $('.dd').nestable(
85
+ collapseBtnHTML: '<button data-action="collapse"><i class="icon-minus-sign"></i></button>'
86
+ expandBtnHTML: '<button data-action="expand"><i class="icon-plus-sign"></i></button>'
87
+ )
88
+
89
+ $('.dd').on('change', ->
90
+ pageTree = $('.dd').nestable('serialize')
91
+ url = '/cms/pages/sort'
92
+ $.post(url, { page_tree: pageTree }, "json")
93
+ )
@@ -11,6 +11,7 @@ $borderRadiusSmall: 0px
11
11
 
12
12
  @import bootstrap
13
13
  @import compass/css3
14
+ @import nestable
14
15
 
15
16
  // base styles
16
17
 
@@ -118,8 +119,6 @@ form
118
119
  padding: 0.35em 0
119
120
 
120
121
  .unpublished
121
- background-color: #f1f1f1
122
-
123
122
  a
124
123
  color: #666
125
124
 
@@ -1,3 +1,5 @@
1
+ require 'kuhsaft/page_tree'
2
+
1
3
  module Kuhsaft
2
4
  module Cms
3
5
  class PagesController < AdminController
@@ -49,6 +51,10 @@ module Kuhsaft
49
51
  @page.destroy
50
52
  redirect_to kuhsaft.cms_pages_path
51
53
  end
54
+
55
+ def sort
56
+ Kuhsaft::PageTree.update(params[:page_tree])
57
+ end
52
58
  end
53
59
  end
54
60
  end
@@ -24,7 +24,8 @@ class Kuhsaft::Page < ActiveRecord::Base
24
24
  :parent_id,
25
25
  :keywords,
26
26
  :description,
27
- :published
27
+ :published,
28
+ :position
28
29
 
29
30
  default_scope order('position ASC')
30
31
 
@@ -1,12 +1,13 @@
1
1
  - unless pages.blank?
2
- - pages.each do |page|
3
- .branch-group{ :'data-id' => page.id, :'data-put-url' => kuhsaft.cms_page_path(page) }
4
- .row-fluid.branch{ :class => page.state_class }
5
- .span9
6
- .branch-title
7
- = link_to page.title.presence, kuhsaft.edit_cms_page_path(page), :class => 'btn btn-link'
8
- .span3
9
- .pull-right
2
+ %ol.dd-list
3
+ - pages.each do |page|
4
+ %li.dd-item.dd3-item{ 'data-id' => page.id }
5
+ .dd-handle.dd3-handle
6
+ handle
7
+ .dd3-content{ class: page.state_class }
8
+ = link_to page.title.presence, kuhsaft.edit_cms_page_path(page)
9
+
10
+ .button-group
10
11
  - if page.children.empty?
11
12
  = link_to kuhsaft.cms_page_path(page), :confirm => t('kuhsaft.cms.admin.confirm_delete', :name => page.title), :method => :delete, :class => 'btn btn-small btn-danger' do
12
13
  %i.icon-trash.icon-white
@@ -15,5 +16,5 @@
15
16
  %i.icon-plus-sign
16
17
  = t('kuhsaft.cms.pages.index.add_page')
17
18
 
18
- - unless page.children.empty?
19
- = render :partial => 'branch', :locals => { :pages => page.children }
19
+ - unless page.children.empty?
20
+ = render partial: 'branch', locals: { pages: page.children }
@@ -2,12 +2,13 @@
2
2
  .actions.row
3
3
  .span5
4
4
  .span3
5
- = link_to kuhsaft.new_cms_page_path, :class => 'btn btn-primary' do
5
+ = link_to kuhsaft.new_cms_page_path, class: 'btn btn-primary' do
6
6
  %i.icon-plus-sign.icon-white
7
7
  = t('.add_page')
8
8
  .span4
9
9
 
10
10
  .row
11
11
  .span8.pages-list
12
- = render :partial => 'branch', :locals => { :pages => @pages }
12
+ .dd
13
+ = render partial: 'branch', locals: { pages: @pages }
13
14
  .span4
@@ -1,6 +1,8 @@
1
1
  Kuhsaft::Engine.routes.draw do
2
2
  namespace :cms do
3
- resources :pages, :except => :show
3
+ resources :pages, :except => :show do
4
+ post :sort, :on => :collection
5
+ end
4
6
  resources :bricks, :except => [:new, :edit, :index] do
5
7
  post :sort, :on => :collection
6
8
  end
@@ -0,0 +1,15 @@
1
+ module Kuhsaft
2
+ module PageTree
3
+ extend self
4
+
5
+ def update(params, parent_id = nil)
6
+ params.each do |page_position, page_content|
7
+ page = Page.find(page_content['id'])
8
+ page.update_attributes(parent_id: parent_id, position: page_position)
9
+ unless page_content['children'].nil?
10
+ update(page_content['children'], page.id)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,3 +1,3 @@
1
1
  module Kuhsaft
2
- VERSION = "1.7.1"
2
+ VERSION = "1.8.0"
3
3
  end
@@ -2,6 +2,7 @@ FactoryGirl.define do
2
2
  sequence(:title) { |n| n }
3
3
 
4
4
  factory :page, :class => 'Kuhsaft::Page' do |p|
5
+ p.parent nil
5
6
  p.position 1
6
7
  p.title { "English Title #{FactoryGirl.generate(:title)}" }
7
8
  p.published 1
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+ require_relative '../../lib/kuhsaft/page_tree'
3
+ require_relative '../../app/models/kuhsaft/page'
4
+
5
+ # TODO: THESE SPECS ONLY WORK WHEN THE FULL SUITE IS RUN. FIX THAT!
6
+
7
+ module Kuhsaft
8
+ describe PageTree do
9
+ let(:page_tree) { {"0"=>{"id"=>"1", "children"=>{"0"=>{"id"=>"2"}}},
10
+ "1"=>{"id"=>"3"}}}
11
+
12
+ before :each do
13
+ @page1 = FactoryGirl.create(:page, id: 1, position: 10)
14
+ @page2 = FactoryGirl.create(:page, id: 2, position: 10)
15
+ @page3 = FactoryGirl.create(:page, id: 3, position: 10)
16
+ end
17
+
18
+ describe 'update' do
19
+ it 'sets the correct position of the root nodes' do
20
+ PageTree.update(page_tree)
21
+ @page1.reload.position.should == 0
22
+ @page2.reload.position.should == 0
23
+ @page3.reload.position.should == 1
24
+ end
25
+
26
+ it 'sets the correct parent attribute for the nodes' do
27
+ PageTree.update(page_tree)
28
+ @page1.reload.parent_id.should be_nil
29
+ @page2.reload.parent_id.should == 1
30
+ @page3.reload.parent_id.should be nil
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,485 @@
1
+ /*!
2
+ * Nestable jQuery Plugin - Copyright (c) 2012 David Bushell - http://dbushell.com/
3
+ * Dual-licensed under the BSD or MIT licenses
4
+ */
5
+ ;(function($, window, document, undefined)
6
+ {
7
+ var hasTouch = 'ontouchstart' in window;
8
+
9
+ /**
10
+ * Detect CSS pointer-events property
11
+ * events are normally disabled on the dragging element to avoid conflicts
12
+ * https://github.com/ausi/Feature-detection-technique-for-pointer-events/blob/master/modernizr-pointerevents.js
13
+ */
14
+ var hasPointerEvents = (function()
15
+ {
16
+ var el = document.createElement('div'),
17
+ docEl = document.documentElement;
18
+ if (!('pointerEvents' in el.style)) {
19
+ return false;
20
+ }
21
+ el.style.pointerEvents = 'auto';
22
+ el.style.pointerEvents = 'x';
23
+ docEl.appendChild(el);
24
+ var supports = window.getComputedStyle && window.getComputedStyle(el, '').pointerEvents === 'auto';
25
+ docEl.removeChild(el);
26
+ return !!supports;
27
+ })();
28
+
29
+ var eStart = hasTouch ? 'touchstart' : 'mousedown',
30
+ eMove = hasTouch ? 'touchmove' : 'mousemove',
31
+ eEnd = hasTouch ? 'touchend' : 'mouseup';
32
+ eCancel = hasTouch ? 'touchcancel' : 'mouseup';
33
+
34
+ var defaults = {
35
+ listNodeName : 'ol',
36
+ itemNodeName : 'li',
37
+ rootClass : 'dd',
38
+ listClass : 'dd-list',
39
+ itemClass : 'dd-item',
40
+ dragClass : 'dd-dragel',
41
+ handleClass : 'dd-handle',
42
+ collapsedClass : 'dd-collapsed',
43
+ placeClass : 'dd-placeholder',
44
+ noDragClass : 'dd-nodrag',
45
+ emptyClass : 'dd-empty',
46
+ expandBtnHTML : '<button data-action="expand" type="button">Expand</button>',
47
+ collapseBtnHTML : '<button data-action="collapse" type="button">Collapse</button>',
48
+ group : 0,
49
+ maxDepth : 5,
50
+ threshold : 20
51
+ };
52
+
53
+ function Plugin(element, options)
54
+ {
55
+ this.w = $(window);
56
+ this.el = $(element);
57
+ this.options = $.extend({}, defaults, options);
58
+ this.init();
59
+ }
60
+
61
+ Plugin.prototype = {
62
+
63
+ init: function()
64
+ {
65
+ var list = this;
66
+
67
+ list.reset();
68
+
69
+ list.el.data('nestable-group', this.options.group);
70
+
71
+ list.placeEl = $('<div class="' + list.options.placeClass + '"/>');
72
+
73
+ $.each(this.el.find(list.options.itemNodeName), function(k, el) {
74
+ list.setParent($(el));
75
+ });
76
+
77
+ list.el.on('click', 'button', function(e) {
78
+ if (list.dragEl || (!hasTouch && e.button !== 0)) {
79
+ return;
80
+ }
81
+ var target = $(e.currentTarget),
82
+ action = target.data('action'),
83
+ item = target.parent(list.options.itemNodeName);
84
+ if (action === 'collapse') {
85
+ list.collapseItem(item);
86
+ }
87
+ if (action === 'expand') {
88
+ list.expandItem(item);
89
+ }
90
+ });
91
+
92
+ var onStartEvent = function(e)
93
+ {
94
+ var handle = $(e.target);
95
+ if (!handle.hasClass(list.options.handleClass)) {
96
+ if (handle.closest('.' + list.options.noDragClass).length) {
97
+ return;
98
+ }
99
+ handle = handle.closest('.' + list.options.handleClass);
100
+ }
101
+ if (!handle.length || list.dragEl || (!hasTouch && e.button !== 0) || (hasTouch && e.touches.length !== 1)) {
102
+ return;
103
+ }
104
+ e.preventDefault();
105
+ list.dragStart(hasTouch ? e.touches[0] : e);
106
+ };
107
+
108
+ var onMoveEvent = function(e)
109
+ {
110
+ if (list.dragEl) {
111
+ e.preventDefault();
112
+ list.dragMove(hasTouch ? e.touches[0] : e);
113
+ }
114
+ };
115
+
116
+ var onEndEvent = function(e)
117
+ {
118
+ if (list.dragEl) {
119
+ e.preventDefault();
120
+ list.dragStop(hasTouch ? e.touches[0] : e);
121
+ }
122
+ };
123
+
124
+ if (hasTouch) {
125
+ list.el[0].addEventListener(eStart, onStartEvent, false);
126
+ window.addEventListener(eMove, onMoveEvent, false);
127
+ window.addEventListener(eEnd, onEndEvent, false);
128
+ window.addEventListener(eCancel, onEndEvent, false);
129
+ } else {
130
+ list.el.on(eStart, onStartEvent);
131
+ list.w.on(eMove, onMoveEvent);
132
+ list.w.on(eEnd, onEndEvent);
133
+ }
134
+
135
+ },
136
+
137
+ serialize: function()
138
+ {
139
+ var data,
140
+ depth = 0,
141
+ list = this;
142
+ step = function(level, depth)
143
+ {
144
+ var array = [ ],
145
+ items = level.children(list.options.itemNodeName);
146
+ items.each(function()
147
+ {
148
+ var li = $(this),
149
+ item = $.extend({}, li.data()),
150
+ sub = li.children(list.options.listNodeName);
151
+ if (sub.length) {
152
+ item.children = step(sub, depth + 1);
153
+ }
154
+ array.push(item);
155
+ });
156
+ return array;
157
+ };
158
+ data = step(list.el.find(list.options.listNodeName).first(), depth);
159
+ return data;
160
+ },
161
+
162
+ serialise: function()
163
+ {
164
+ return this.serialize();
165
+ },
166
+
167
+ reset: function()
168
+ {
169
+ this.mouse = {
170
+ offsetX : 0,
171
+ offsetY : 0,
172
+ startX : 0,
173
+ startY : 0,
174
+ lastX : 0,
175
+ lastY : 0,
176
+ nowX : 0,
177
+ nowY : 0,
178
+ distX : 0,
179
+ distY : 0,
180
+ dirAx : 0,
181
+ dirX : 0,
182
+ dirY : 0,
183
+ lastDirX : 0,
184
+ lastDirY : 0,
185
+ distAxX : 0,
186
+ distAxY : 0
187
+ };
188
+ this.moving = false;
189
+ this.dragEl = null;
190
+ this.dragRootEl = null;
191
+ this.dragDepth = 0;
192
+ this.hasNewRoot = false;
193
+ this.pointEl = null;
194
+ },
195
+
196
+ expandItem: function(li)
197
+ {
198
+ li.removeClass(this.options.collapsedClass);
199
+ li.children('[data-action="expand"]').hide();
200
+ li.children('[data-action="collapse"]').show();
201
+ li.children(this.options.listNodeName).show();
202
+ },
203
+
204
+ collapseItem: function(li)
205
+ {
206
+ var lists = li.children(this.options.listNodeName);
207
+ if (lists.length) {
208
+ li.addClass(this.options.collapsedClass);
209
+ li.children('[data-action="collapse"]').hide();
210
+ li.children('[data-action="expand"]').show();
211
+ li.children(this.options.listNodeName).hide();
212
+ }
213
+ },
214
+
215
+ expandAll: function()
216
+ {
217
+ var list = this;
218
+ list.el.find(list.options.itemNodeName).each(function() {
219
+ list.expandItem($(this));
220
+ });
221
+ },
222
+
223
+ collapseAll: function()
224
+ {
225
+ var list = this;
226
+ list.el.find(list.options.itemNodeName).each(function() {
227
+ list.collapseItem($(this));
228
+ });
229
+ },
230
+
231
+ setParent: function(li)
232
+ {
233
+ if (li.children(this.options.listNodeName).length) {
234
+ li.prepend($(this.options.expandBtnHTML));
235
+ li.prepend($(this.options.collapseBtnHTML));
236
+ }
237
+ li.children('[data-action="expand"]').hide();
238
+ },
239
+
240
+ unsetParent: function(li)
241
+ {
242
+ li.removeClass(this.options.collapsedClass);
243
+ li.children('[data-action]').remove();
244
+ li.children(this.options.listNodeName).remove();
245
+ },
246
+
247
+ dragStart: function(e)
248
+ {
249
+ var mouse = this.mouse,
250
+ target = $(e.target),
251
+ dragItem = target.closest(this.options.itemNodeName);
252
+
253
+ this.placeEl.css('height', dragItem.height());
254
+
255
+ mouse.offsetX = e.offsetX !== undefined ? e.offsetX : e.pageX - target.offset().left;
256
+ mouse.offsetY = e.offsetY !== undefined ? e.offsetY : e.pageY - target.offset().top;
257
+ mouse.startX = mouse.lastX = e.pageX;
258
+ mouse.startY = mouse.lastY = e.pageY;
259
+
260
+ this.dragRootEl = this.el;
261
+
262
+ this.dragEl = $(document.createElement(this.options.listNodeName)).addClass(this.options.listClass + ' ' + this.options.dragClass);
263
+ this.dragEl.css('width', dragItem.width());
264
+
265
+ // fix for zepto.js
266
+ //dragItem.after(this.placeEl).detach().appendTo(this.dragEl);
267
+ dragItem.after(this.placeEl);
268
+ dragItem[0].parentNode.removeChild(dragItem[0]);
269
+ dragItem.appendTo(this.dragEl);
270
+
271
+ $(document.body).append(this.dragEl);
272
+ this.dragEl.css({
273
+ 'left' : e.pageX - mouse.offsetX,
274
+ 'top' : e.pageY - mouse.offsetY
275
+ });
276
+ // total depth of dragging item
277
+ var i, depth,
278
+ items = this.dragEl.find(this.options.itemNodeName);
279
+ for (i = 0; i < items.length; i++) {
280
+ depth = $(items[i]).parents(this.options.listNodeName).length;
281
+ if (depth > this.dragDepth) {
282
+ this.dragDepth = depth;
283
+ }
284
+ }
285
+ },
286
+
287
+ dragStop: function(e)
288
+ {
289
+ // fix for zepto.js
290
+ //this.placeEl.replaceWith(this.dragEl.children(this.options.itemNodeName + ':first').detach());
291
+ var el = this.dragEl.children(this.options.itemNodeName).first();
292
+ el[0].parentNode.removeChild(el[0]);
293
+ this.placeEl.replaceWith(el);
294
+
295
+ this.dragEl.remove();
296
+ this.el.trigger('change');
297
+ if (this.hasNewRoot) {
298
+ this.dragRootEl.trigger('change');
299
+ }
300
+ this.reset();
301
+ },
302
+
303
+ dragMove: function(e)
304
+ {
305
+ var list, parent, prev, next, depth,
306
+ opt = this.options,
307
+ mouse = this.mouse;
308
+
309
+ this.dragEl.css({
310
+ 'left' : e.pageX - mouse.offsetX,
311
+ 'top' : e.pageY - mouse.offsetY
312
+ });
313
+
314
+ // mouse position last events
315
+ mouse.lastX = mouse.nowX;
316
+ mouse.lastY = mouse.nowY;
317
+ // mouse position this events
318
+ mouse.nowX = e.pageX;
319
+ mouse.nowY = e.pageY;
320
+ // distance mouse moved between events
321
+ mouse.distX = mouse.nowX - mouse.lastX;
322
+ mouse.distY = mouse.nowY - mouse.lastY;
323
+ // direction mouse was moving
324
+ mouse.lastDirX = mouse.dirX;
325
+ mouse.lastDirY = mouse.dirY;
326
+ // direction mouse is now moving (on both axis)
327
+ mouse.dirX = mouse.distX === 0 ? 0 : mouse.distX > 0 ? 1 : -1;
328
+ mouse.dirY = mouse.distY === 0 ? 0 : mouse.distY > 0 ? 1 : -1;
329
+ // axis mouse is now moving on
330
+ var newAx = Math.abs(mouse.distX) > Math.abs(mouse.distY) ? 1 : 0;
331
+
332
+ // do nothing on first move
333
+ if (!mouse.moving) {
334
+ mouse.dirAx = newAx;
335
+ mouse.moving = true;
336
+ return;
337
+ }
338
+
339
+ // calc distance moved on this axis (and direction)
340
+ if (mouse.dirAx !== newAx) {
341
+ mouse.distAxX = 0;
342
+ mouse.distAxY = 0;
343
+ } else {
344
+ mouse.distAxX += Math.abs(mouse.distX);
345
+ if (mouse.dirX !== 0 && mouse.dirX !== mouse.lastDirX) {
346
+ mouse.distAxX = 0;
347
+ }
348
+ mouse.distAxY += Math.abs(mouse.distY);
349
+ if (mouse.dirY !== 0 && mouse.dirY !== mouse.lastDirY) {
350
+ mouse.distAxY = 0;
351
+ }
352
+ }
353
+ mouse.dirAx = newAx;
354
+
355
+ /**
356
+ * move horizontal
357
+ */
358
+ if (mouse.dirAx && mouse.distAxX >= opt.threshold) {
359
+ // reset move distance on x-axis for new phase
360
+ mouse.distAxX = 0;
361
+ prev = this.placeEl.prev(opt.itemNodeName);
362
+ // increase horizontal level if previous sibling exists and is not collapsed
363
+ if (mouse.distX > 0 && prev.length && !prev.hasClass(opt.collapsedClass)) {
364
+ // cannot increase level when item above is collapsed
365
+ list = prev.find(opt.listNodeName).last();
366
+ // check if depth limit has reached
367
+ depth = this.placeEl.parents(opt.listNodeName).length;
368
+ if (depth + this.dragDepth <= opt.maxDepth) {
369
+ // create new sub-level if one doesn't exist
370
+ if (!list.length) {
371
+ list = $('<' + opt.listNodeName + '/>').addClass(opt.listClass);
372
+ list.append(this.placeEl);
373
+ prev.append(list);
374
+ this.setParent(prev);
375
+ } else {
376
+ // else append to next level up
377
+ list = prev.children(opt.listNodeName).last();
378
+ list.append(this.placeEl);
379
+ }
380
+ }
381
+ }
382
+ // decrease horizontal level
383
+ if (mouse.distX < 0) {
384
+ // we can't decrease a level if an item preceeds the current one
385
+ next = this.placeEl.next(opt.itemNodeName);
386
+ if (!next.length) {
387
+ parent = this.placeEl.parent();
388
+ this.placeEl.closest(opt.itemNodeName).after(this.placeEl);
389
+ if (!parent.children().length) {
390
+ this.unsetParent(parent.parent());
391
+ }
392
+ }
393
+ }
394
+ }
395
+
396
+ var isEmpty = false;
397
+
398
+ // find list item under cursor
399
+ if (!hasPointerEvents) {
400
+ this.dragEl[0].style.visibility = 'hidden';
401
+ }
402
+ this.pointEl = $(document.elementFromPoint(e.pageX - document.body.scrollLeft, e.pageY - (window.pageYOffset || document.documentElement.scrollTop)));
403
+ if (!hasPointerEvents) {
404
+ this.dragEl[0].style.visibility = 'visible';
405
+ }
406
+ if (this.pointEl.hasClass(opt.handleClass)) {
407
+ this.pointEl = this.pointEl.parent(opt.itemNodeName);
408
+ }
409
+ if (this.pointEl.hasClass(opt.emptyClass)) {
410
+ isEmpty = true;
411
+ }
412
+ else if (!this.pointEl.length || !this.pointEl.hasClass(opt.itemClass)) {
413
+ return;
414
+ }
415
+
416
+ // find parent list of item under cursor
417
+ var pointElRoot = this.pointEl.closest('.' + opt.rootClass),
418
+ isNewRoot = this.dragRootEl.data('nestable-id') !== pointElRoot.data('nestable-id');
419
+
420
+ /**
421
+ * move vertical
422
+ */
423
+ if (!mouse.dirAx || isNewRoot || isEmpty) {
424
+ // check if groups match if dragging over new root
425
+ if (isNewRoot && opt.group !== pointElRoot.data('nestable-group')) {
426
+ return;
427
+ }
428
+ // check depth limit
429
+ depth = this.dragDepth - 1 + this.pointEl.parents(opt.listNodeName).length;
430
+ if (depth > opt.maxDepth) {
431
+ return;
432
+ }
433
+ var before = e.pageY < (this.pointEl.offset().top + this.pointEl.height() / 2);
434
+ parent = this.placeEl.parent();
435
+ // if empty create new list to replace empty placeholder
436
+ if (isEmpty) {
437
+ list = $(document.createElement(opt.listNodeName)).addClass(opt.listClass);
438
+ list.append(this.placeEl);
439
+ this.pointEl.replaceWith(list);
440
+ }
441
+ else if (before) {
442
+ this.pointEl.before(this.placeEl);
443
+ }
444
+ else {
445
+ this.pointEl.after(this.placeEl);
446
+ }
447
+ if (!parent.children().length) {
448
+ this.unsetParent(parent.parent());
449
+ }
450
+ if (!this.dragRootEl.find(opt.itemNodeName).length) {
451
+ this.dragRootEl.append('<div class="' + opt.emptyClass + '"/>');
452
+ }
453
+ // parent root list has changed
454
+ if (isNewRoot) {
455
+ this.dragRootEl = pointElRoot;
456
+ this.hasNewRoot = this.el[0] !== this.dragRootEl[0];
457
+ }
458
+ }
459
+ }
460
+
461
+ };
462
+
463
+ $.fn.nestable = function(params)
464
+ {
465
+ var lists = this,
466
+ retval = this;
467
+
468
+ lists.each(function()
469
+ {
470
+ var plugin = $(this).data("nestable");
471
+
472
+ if (!plugin) {
473
+ $(this).data("nestable", new Plugin(this, params));
474
+ $(this).data("nestable-id", new Date().getTime());
475
+ } else {
476
+ if (typeof params === 'string' && typeof plugin[params] === 'function') {
477
+ retval = plugin[params]();
478
+ }
479
+ }
480
+ });
481
+
482
+ return retval || lists;
483
+ };
484
+
485
+ })(window.jQuery || window.Zepto, window, document);
@@ -0,0 +1,84 @@
1
+ .dd { position: relative; display: block; margin: 0; padding: 0; list-style: none; font-size: 13px; line-height: 20px; }
2
+
3
+ .dd-list { display: block; position: relative; margin: 0; padding: 0; list-style: none; }
4
+ .dd-list .dd-list { padding-left: 30px; }
5
+ .dd-collapsed .dd-list { display: none; }
6
+
7
+ .dd-item,
8
+ .dd-empty,
9
+ .dd-placeholder { display: block; position: relative; margin: 0; padding: 0; min-height: 40px; font-size: 13px; line-height: 40px; }
10
+
11
+ .dd-handle { display: block; height: 40px; margin: 5px 0; padding: 5px 10px; color: #333; text-decoration: none; font-weight: bold; border: 1px solid #ccc;
12
+ background: #fafafa;
13
+ background: -webkit-linear-gradient(top, #fafafa 0%, #eee 100%);
14
+ background: -moz-linear-gradient(top, #fafafa 0%, #eee 100%);
15
+ background: linear-gradient(top, #fafafa 0%, #eee 100%);
16
+ -webkit-border-radius: 3px;
17
+ border-radius: 3px;
18
+ box-sizing: border-box; -moz-box-sizing: border-box;
19
+ }
20
+ .dd-handle:hover { color: #2ea8e5; background: #fff; }
21
+
22
+ .dd-item > button { display: block; position: relative; cursor: pointer; float: left; width: 25px; height: 30px; margin: 5px 0; padding: 0; white-space: nowrap; overflow: hidden; border: 0; background: transparent; font-size: 12px; line-height: 1; text-align: center; font-weight: bold; }
23
+ /*.dd-item > button:before { content: ''; display: block; position: absolute; width: 100%; text-align: center; text-indent: 0; }*/
24
+ /*.dd-item > button[data-action="collapse"]:before { content: ''; }*/
25
+
26
+ .dd-placeholder,
27
+ .dd-empty { margin: 5px 0; padding: 0; min-height: 40px; background: #f2fbff; border: 1px dashed #b6bcbf; box-sizing: border-box; -moz-box-sizing: border-box; }
28
+ .dd-empty { border: 1px dashed #bbb; min-height: 100px; background-color: #e5e5e5;
29
+ background-image: -webkit-linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff),
30
+ -webkit-linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff);
31
+ background-image: -moz-linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff),
32
+ -moz-linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff);
33
+ background-image: linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff),
34
+ linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff);
35
+ background-size: 60px 60px;
36
+ background-position: 0 0, 30px 30px;
37
+ }
38
+
39
+ .dd-dragel { position: absolute; pointer-events: none; z-index: 9999; }
40
+ .dd-dragel > .dd-item .dd-handle { margin-top: 0; }
41
+ .dd-dragel .dd-handle {
42
+ -webkit-box-shadow: 2px 4px 6px 0 rgba(0,0,0,.1);
43
+ box-shadow: 2px 4px 6px 0 rgba(0,0,0,.1);
44
+ }
45
+
46
+ .dd3-content { display: block; height: 40px; margin: 5px 0; padding: 0px 10px 0px 38px; color: #333; text-decoration: none; font-weight: bold; border: 1px solid #ccc; line-height: 40px;
47
+ background: #fafafa;
48
+ background: -webkit-linear-gradient(top, #fafafa 0%, #eee 100%);
49
+ background: -moz-linear-gradient(top, #fafafa 0%, #eee 100%);
50
+ background: linear-gradient(top, #fafafa 0%, #eee 100%);
51
+ -webkit-border-radius: 3px;
52
+ border-radius: 3px;
53
+ box-sizing: border-box; -moz-box-sizing: border-box;
54
+ }
55
+ .dd3-content:hover { color: #2ea8e5; background: #fff; }
56
+ .dd3-content.unpublished { color: #2ea8e5; background: #e9e9e9; }
57
+
58
+ .dd-dragel > .dd3-item > .dd3-content { margin: 0; }
59
+
60
+ .dd3-item > button { margin-left: 30px; }
61
+
62
+ .dd3-handle { position: absolute; margin: 0; left: 0; top: 0; cursor: pointer; width: 30px; text-indent: 100%; white-space: nowrap; overflow: hidden;
63
+ border: 1px solid #aaa;
64
+ background: #ddd;
65
+ background: -webkit-linear-gradient(top, #ddd 0%, #bbb 100%);
66
+ background: -moz-linear-gradient(top, #ddd 0%, #bbb 100%);
67
+ background: linear-gradient(top, #ddd 0%, #bbb 100%);
68
+ border-top-right-radius: 0;
69
+ border-bottom-right-radius: 0;
70
+ }
71
+ .dd3-handle:before { content: '≡'; display: block; position: absolute; left: 0; top: 0px; width: 100%; text-align: center; text-indent: 0; color: #fff; font-size: 20px; font-weight: normal; }
72
+ .dd3-handle:hover { background: #ddd; }
73
+
74
+ .dd3-content .btn {
75
+ margin-bottom: 6px;
76
+ }
77
+
78
+ .button-group {
79
+ float: right;
80
+ }
81
+
82
+ button i {
83
+ opacity: 0.5;
84
+ }
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kuhsaft
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.1
5
- prerelease:
4
+ version: 1.8.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Immanuel Häussermann
@@ -13,124 +12,109 @@ authors:
13
12
  autorequire:
14
13
  bindir: bin
15
14
  cert_chain: []
16
- date: 2013-06-06 00:00:00.000000000 Z
15
+ date: 2013-07-15 00:00:00.000000000 Z
17
16
  dependencies:
18
17
  - !ruby/object:Gem::Dependency
19
18
  name: rspec
20
19
  requirement: !ruby/object:Gem::Requirement
21
- none: false
22
20
  requirements:
23
- - - ! '>='
21
+ - - '>='
24
22
  - !ruby/object:Gem::Version
25
23
  version: '0'
26
24
  type: :development
27
25
  prerelease: false
28
26
  version_requirements: !ruby/object:Gem::Requirement
29
- none: false
30
27
  requirements:
31
- - - ! '>='
28
+ - - '>='
32
29
  - !ruby/object:Gem::Version
33
30
  version: '0'
34
31
  - !ruby/object:Gem::Dependency
35
32
  name: rspec-rails
36
33
  requirement: !ruby/object:Gem::Requirement
37
- none: false
38
34
  requirements:
39
- - - ! '>='
35
+ - - ~>
40
36
  - !ruby/object:Gem::Version
41
- version: '2.6'
37
+ version: '2.13'
42
38
  type: :development
43
39
  prerelease: false
44
40
  version_requirements: !ruby/object:Gem::Requirement
45
- none: false
46
41
  requirements:
47
- - - ! '>='
42
+ - - ~>
48
43
  - !ruby/object:Gem::Version
49
- version: '2.6'
44
+ version: '2.13'
50
45
  - !ruby/object:Gem::Dependency
51
46
  name: factory_girl_rails
52
47
  requirement: !ruby/object:Gem::Requirement
53
- none: false
54
48
  requirements:
55
- - - ! '>='
49
+ - - '>='
56
50
  - !ruby/object:Gem::Version
57
51
  version: '0'
58
52
  type: :development
59
53
  prerelease: false
60
54
  version_requirements: !ruby/object:Gem::Requirement
61
- none: false
62
55
  requirements:
63
- - - ! '>='
56
+ - - '>='
64
57
  - !ruby/object:Gem::Version
65
58
  version: '0'
66
59
  - !ruby/object:Gem::Dependency
67
60
  name: capybara
68
61
  requirement: !ruby/object:Gem::Requirement
69
- none: false
70
62
  requirements:
71
- - - ! '>='
63
+ - - '>='
72
64
  - !ruby/object:Gem::Version
73
65
  version: '2.0'
74
66
  type: :development
75
67
  prerelease: false
76
68
  version_requirements: !ruby/object:Gem::Requirement
77
- none: false
78
69
  requirements:
79
- - - ! '>='
70
+ - - '>='
80
71
  - !ruby/object:Gem::Version
81
72
  version: '2.0'
82
73
  - !ruby/object:Gem::Dependency
83
74
  name: mysql2
84
75
  requirement: !ruby/object:Gem::Requirement
85
- none: false
86
76
  requirements:
87
- - - ! '>='
77
+ - - '>='
88
78
  - !ruby/object:Gem::Version
89
79
  version: '0'
90
80
  type: :development
91
81
  prerelease: false
92
82
  version_requirements: !ruby/object:Gem::Requirement
93
- none: false
94
83
  requirements:
95
- - - ! '>='
84
+ - - '>='
96
85
  - !ruby/object:Gem::Version
97
86
  version: '0'
98
87
  - !ruby/object:Gem::Dependency
99
88
  name: launchy
100
89
  requirement: !ruby/object:Gem::Requirement
101
- none: false
102
90
  requirements:
103
- - - ! '>='
91
+ - - '>='
104
92
  - !ruby/object:Gem::Version
105
93
  version: '0'
106
94
  type: :development
107
95
  prerelease: false
108
96
  version_requirements: !ruby/object:Gem::Requirement
109
- none: false
110
97
  requirements:
111
- - - ! '>='
98
+ - - '>='
112
99
  - !ruby/object:Gem::Version
113
100
  version: '0'
114
101
  - !ruby/object:Gem::Dependency
115
102
  name: rake
116
103
  requirement: !ruby/object:Gem::Requirement
117
- none: false
118
104
  requirements:
119
- - - ! '>='
105
+ - - '>='
120
106
  - !ruby/object:Gem::Version
121
107
  version: '0'
122
108
  type: :development
123
109
  prerelease: false
124
110
  version_requirements: !ruby/object:Gem::Requirement
125
- none: false
126
111
  requirements:
127
- - - ! '>='
112
+ - - '>='
128
113
  - !ruby/object:Gem::Version
129
114
  version: '0'
130
115
  - !ruby/object:Gem::Dependency
131
116
  name: rails
132
117
  requirement: !ruby/object:Gem::Requirement
133
- none: false
134
118
  requirements:
135
119
  - - ~>
136
120
  - !ruby/object:Gem::Version
@@ -138,7 +122,6 @@ dependencies:
138
122
  type: :runtime
139
123
  prerelease: false
140
124
  version_requirements: !ruby/object:Gem::Requirement
141
- none: false
142
125
  requirements:
143
126
  - - ~>
144
127
  - !ruby/object:Gem::Version
@@ -146,167 +129,146 @@ dependencies:
146
129
  - !ruby/object:Gem::Dependency
147
130
  name: haml
148
131
  requirement: !ruby/object:Gem::Requirement
149
- none: false
150
132
  requirements:
151
- - - ! '>='
133
+ - - '>='
152
134
  - !ruby/object:Gem::Version
153
135
  version: '3.1'
154
136
  type: :runtime
155
137
  prerelease: false
156
138
  version_requirements: !ruby/object:Gem::Requirement
157
- none: false
158
139
  requirements:
159
- - - ! '>='
140
+ - - '>='
160
141
  - !ruby/object:Gem::Version
161
142
  version: '3.1'
162
143
  - !ruby/object:Gem::Dependency
163
144
  name: compass
164
145
  requirement: !ruby/object:Gem::Requirement
165
- none: false
166
146
  requirements:
167
- - - ! '>='
147
+ - - '>='
168
148
  - !ruby/object:Gem::Version
169
149
  version: 0.11.1
170
150
  type: :runtime
171
151
  prerelease: false
172
152
  version_requirements: !ruby/object:Gem::Requirement
173
- none: false
174
153
  requirements:
175
- - - ! '>='
154
+ - - '>='
176
155
  - !ruby/object:Gem::Version
177
156
  version: 0.11.1
178
157
  - !ruby/object:Gem::Dependency
179
158
  name: compass-rails
180
159
  requirement: !ruby/object:Gem::Requirement
181
- none: false
182
160
  requirements:
183
- - - ! '>='
161
+ - - '>='
184
162
  - !ruby/object:Gem::Version
185
163
  version: '0'
186
164
  type: :runtime
187
165
  prerelease: false
188
166
  version_requirements: !ruby/object:Gem::Requirement
189
- none: false
190
167
  requirements:
191
- - - ! '>='
168
+ - - '>='
192
169
  - !ruby/object:Gem::Version
193
170
  version: '0'
194
171
  - !ruby/object:Gem::Dependency
195
172
  name: coffee-rails
196
173
  requirement: !ruby/object:Gem::Requirement
197
- none: false
198
174
  requirements:
199
- - - ! '>='
175
+ - - '>='
200
176
  - !ruby/object:Gem::Version
201
177
  version: '0'
202
178
  type: :runtime
203
179
  prerelease: false
204
180
  version_requirements: !ruby/object:Gem::Requirement
205
- none: false
206
181
  requirements:
207
- - - ! '>='
182
+ - - '>='
208
183
  - !ruby/object:Gem::Version
209
184
  version: '0'
210
185
  - !ruby/object:Gem::Dependency
211
186
  name: simple_form
212
187
  requirement: !ruby/object:Gem::Requirement
213
- none: false
214
188
  requirements:
215
- - - ! '>='
189
+ - - '>='
216
190
  - !ruby/object:Gem::Version
217
191
  version: '2.0'
218
192
  type: :runtime
219
193
  prerelease: false
220
194
  version_requirements: !ruby/object:Gem::Requirement
221
- none: false
222
195
  requirements:
223
- - - ! '>='
196
+ - - '>='
224
197
  - !ruby/object:Gem::Version
225
198
  version: '2.0'
226
199
  - !ruby/object:Gem::Dependency
227
200
  name: carrierwave
228
201
  requirement: !ruby/object:Gem::Requirement
229
- none: false
230
202
  requirements:
231
- - - ! '>='
203
+ - - '>='
232
204
  - !ruby/object:Gem::Version
233
205
  version: 0.7.1
234
206
  type: :runtime
235
207
  prerelease: false
236
208
  version_requirements: !ruby/object:Gem::Requirement
237
- none: false
238
209
  requirements:
239
- - - ! '>='
210
+ - - '>='
240
211
  - !ruby/object:Gem::Version
241
212
  version: 0.7.1
242
213
  - !ruby/object:Gem::Dependency
243
214
  name: mini_magick
244
215
  requirement: !ruby/object:Gem::Requirement
245
- none: false
246
216
  requirements:
247
- - - ! '>='
217
+ - - '>='
248
218
  - !ruby/object:Gem::Version
249
219
  version: '3.4'
250
220
  type: :runtime
251
221
  prerelease: false
252
222
  version_requirements: !ruby/object:Gem::Requirement
253
- none: false
254
223
  requirements:
255
- - - ! '>='
224
+ - - '>='
256
225
  - !ruby/object:Gem::Version
257
226
  version: '3.4'
258
227
  - !ruby/object:Gem::Dependency
259
228
  name: rdiscount
260
229
  requirement: !ruby/object:Gem::Requirement
261
- none: false
262
230
  requirements:
263
- - - ! '>='
231
+ - - '>='
264
232
  - !ruby/object:Gem::Version
265
233
  version: '1.6'
266
234
  type: :runtime
267
235
  prerelease: false
268
236
  version_requirements: !ruby/object:Gem::Requirement
269
- none: false
270
237
  requirements:
271
- - - ! '>='
238
+ - - '>='
272
239
  - !ruby/object:Gem::Version
273
240
  version: '1.6'
274
241
  - !ruby/object:Gem::Dependency
275
242
  name: ancestry
276
243
  requirement: !ruby/object:Gem::Requirement
277
- none: false
278
244
  requirements:
279
- - - ! '>='
245
+ - - '>='
280
246
  - !ruby/object:Gem::Version
281
247
  version: '0'
282
248
  type: :runtime
283
249
  prerelease: false
284
250
  version_requirements: !ruby/object:Gem::Requirement
285
- none: false
286
251
  requirements:
287
- - - ! '>='
252
+ - - '>='
288
253
  - !ruby/object:Gem::Version
289
254
  version: '0'
290
255
  - !ruby/object:Gem::Dependency
291
256
  name: bootstrap-sass
292
257
  requirement: !ruby/object:Gem::Requirement
293
- none: false
294
258
  requirements:
295
- - - ! '>='
259
+ - - '>='
296
260
  - !ruby/object:Gem::Version
297
261
  version: 2.2.2.0
298
262
  type: :runtime
299
263
  prerelease: false
300
264
  version_requirements: !ruby/object:Gem::Requirement
301
- none: false
302
265
  requirements:
303
- - - ! '>='
266
+ - - '>='
304
267
  - !ruby/object:Gem::Version
305
268
  version: 2.2.2.0
306
269
  - !ruby/object:Gem::Dependency
307
270
  name: ckeditor
308
271
  requirement: !ruby/object:Gem::Requirement
309
- none: false
310
272
  requirements:
311
273
  - - '='
312
274
  - !ruby/object:Gem::Version
@@ -314,7 +276,6 @@ dependencies:
314
276
  type: :runtime
315
277
  prerelease: false
316
278
  version_requirements: !ruby/object:Gem::Requirement
317
- none: false
318
279
  requirements:
319
280
  - - '='
320
281
  - !ruby/object:Gem::Version
@@ -322,33 +283,29 @@ dependencies:
322
283
  - !ruby/object:Gem::Dependency
323
284
  name: pg_search
324
285
  requirement: !ruby/object:Gem::Requirement
325
- none: false
326
286
  requirements:
327
- - - ! '>='
287
+ - - '>='
328
288
  - !ruby/object:Gem::Version
329
289
  version: '0'
330
290
  type: :runtime
331
291
  prerelease: false
332
292
  version_requirements: !ruby/object:Gem::Requirement
333
- none: false
334
293
  requirements:
335
- - - ! '>='
294
+ - - '>='
336
295
  - !ruby/object:Gem::Version
337
296
  version: '0'
338
297
  - !ruby/object:Gem::Dependency
339
298
  name: htmlentities
340
299
  requirement: !ruby/object:Gem::Requirement
341
- none: false
342
300
  requirements:
343
- - - ! '>='
301
+ - - '>='
344
302
  - !ruby/object:Gem::Version
345
303
  version: '0'
346
304
  type: :runtime
347
305
  prerelease: false
348
306
  version_requirements: !ruby/object:Gem::Requirement
349
- none: false
350
307
  requirements:
351
- - - ! '>='
308
+ - - '>='
352
309
  - !ruby/object:Gem::Version
353
310
  version: '0'
354
311
  description: Kuhsaft is a Rails engine that offers a simple CMS.
@@ -428,6 +385,7 @@ files:
428
385
  - app/views/kuhsaft/cms/pages/index.html.haml
429
386
  - app/views/kuhsaft/cms/pages/new.html.haml
430
387
  - app/views/kuhsaft/cms/pages/show.html.haml
388
+ - app/views/kuhsaft/cms/pages/sort.js.erb
431
389
  - app/views/kuhsaft/column_bricks/_column_brick.html.haml
432
390
  - app/views/kuhsaft/column_bricks/column_brick/_edit.html.haml
433
391
  - app/views/kuhsaft/image_bricks/_image_brick.html.haml
@@ -497,6 +455,7 @@ files:
497
455
  - lib/kuhsaft/brick_list.rb
498
456
  - lib/kuhsaft/engine.rb
499
457
  - lib/kuhsaft/orderable.rb
458
+ - lib/kuhsaft/page_tree.rb
500
459
  - lib/kuhsaft/partial_extractor.rb
501
460
  - lib/kuhsaft/searchable.rb
502
461
  - lib/kuhsaft/translatable.rb
@@ -507,6 +466,8 @@ files:
507
466
  - lib/templates/kuhsaft/assets/customizations.css.sass
508
467
  - lib/templates/kuhsaft/assets/customizations.js.coffee
509
468
  - lib/templates/kuhsaft/translations/add_translation.html.erb
469
+ - vendor/assets/javascripts/jquery.nestable.js
470
+ - vendor/assets/stylesheets/nestable.css
510
471
  - Rakefile
511
472
  - README.md
512
473
  - spec/controllers/kuhsaft/pages_controller_spec.rb
@@ -551,6 +512,7 @@ files:
551
512
  - spec/kuhsaft_spec.rb
552
513
  - spec/lib/brick_list_spec.rb
553
514
  - spec/lib/engine_spec.rb
515
+ - spec/lib/page_tree_spec.rb
554
516
  - spec/lib/searchable_spec.rb
555
517
  - spec/lib/translatable_spec.rb
556
518
  - spec/models/accordion_brick_spec.rb
@@ -576,33 +538,26 @@ files:
576
538
  - spec/support/kuhsaft_spec_helper.rb
577
539
  homepage: http://github.com/screenconcept/kuhsaft
578
540
  licenses: []
541
+ metadata: {}
579
542
  post_install_message:
580
543
  rdoc_options: []
581
544
  require_paths:
582
545
  - lib
583
546
  required_ruby_version: !ruby/object:Gem::Requirement
584
- none: false
585
547
  requirements:
586
- - - ! '>='
548
+ - - '>='
587
549
  - !ruby/object:Gem::Version
588
550
  version: '0'
589
- segments:
590
- - 0
591
- hash: -4607812915694865046
592
551
  required_rubygems_version: !ruby/object:Gem::Requirement
593
- none: false
594
552
  requirements:
595
- - - ! '>='
553
+ - - '>='
596
554
  - !ruby/object:Gem::Version
597
555
  version: '0'
598
- segments:
599
- - 0
600
- hash: -4607812915694865046
601
556
  requirements: []
602
557
  rubyforge_project: kuhsaft
603
- rubygems_version: 1.8.24
558
+ rubygems_version: 2.0.3
604
559
  signing_key:
605
- specification_version: 3
560
+ specification_version: 4
606
561
  summary: A tool that helps you to manage your content within your app.
607
562
  test_files:
608
563
  - spec/controllers/kuhsaft/pages_controller_spec.rb
@@ -647,6 +602,7 @@ test_files:
647
602
  - spec/kuhsaft_spec.rb
648
603
  - spec/lib/brick_list_spec.rb
649
604
  - spec/lib/engine_spec.rb
605
+ - spec/lib/page_tree_spec.rb
650
606
  - spec/lib/searchable_spec.rb
651
607
  - spec/lib/translatable_spec.rb
652
608
  - spec/models/accordion_brick_spec.rb