releaf-content 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +24 -0
  3. data/app/assets/javascripts/releaf/controllers/releaf/content/nodes.js +88 -0
  4. data/app/assets/stylesheets/releaf/controllers/releaf/content/nodes.scss +234 -0
  5. data/app/builders/releaf/content/builders/action_dialog.rb +60 -0
  6. data/app/builders/releaf/content/builders/dialog.rb +15 -0
  7. data/app/builders/releaf/content/builders/tree.rb +84 -0
  8. data/app/builders/releaf/content/content_type_dialog_builder.rb +74 -0
  9. data/app/builders/releaf/content/copy_dialog_builder.rb +9 -0
  10. data/app/builders/releaf/content/go_to_dialog_builder.rb +9 -0
  11. data/app/builders/releaf/content/move_dialog_builder.rb +9 -0
  12. data/app/builders/releaf/content/nodes/content_form_builder.rb +7 -0
  13. data/app/builders/releaf/content/nodes/form_builder.rb +108 -0
  14. data/app/builders/releaf/content/nodes/index_builder.rb +24 -0
  15. data/app/builders/releaf/content/nodes/toolbox_builder.rb +33 -0
  16. data/app/controllers/releaf/content/nodes_controller.rb +166 -0
  17. data/app/middleware/releaf/content/routes_reloader.rb +25 -0
  18. data/app/validators/releaf/content/node/parent_validator.rb +48 -0
  19. data/app/validators/releaf/content/node/root_validator.rb +43 -0
  20. data/app/validators/releaf/content/node/singleness_validator.rb +102 -0
  21. data/app/views/releaf/content/nodes/content_type_dialog.ruby +1 -0
  22. data/app/views/releaf/content/nodes/copy_dialog.ruby +1 -0
  23. data/app/views/releaf/content/nodes/go_to_dialog.ruby +1 -0
  24. data/app/views/releaf/content/nodes/move_dialog.ruby +1 -0
  25. data/lib/releaf-content.rb +6 -0
  26. data/lib/releaf/content/acts_as_node.rb +73 -0
  27. data/lib/releaf/content/acts_as_node/action_controller/acts/node.rb +17 -0
  28. data/lib/releaf/content/acts_as_node/active_record/acts/node.rb +55 -0
  29. data/lib/releaf/content/builders_autoload.rb +18 -0
  30. data/lib/releaf/content/engine.rb +40 -0
  31. data/lib/releaf/content/node.rb +280 -0
  32. data/lib/releaf/content/node_mapper.rb +9 -0
  33. data/lib/releaf/content/route.rb +93 -0
  34. data/lib/releaf/content/router_proxy.rb +23 -0
  35. data/releaf-content.gemspec +20 -0
  36. data/spec/builders/content/nodes/content_form_builder_spec.rb +24 -0
  37. data/spec/builders/content/nodes/form_builder_spec.rb +218 -0
  38. data/spec/builders/content/nodes/toolbox_builder_spec.rb +108 -0
  39. data/spec/controllers/releaf/content/nodes_controller_spec.rb +21 -0
  40. data/spec/features/nodes_spec.rb +239 -0
  41. data/spec/lib/releaf/content/acts_as_node_spec.rb +118 -0
  42. data/spec/lib/releaf/content/node_spec.rb +779 -0
  43. data/spec/lib/releaf/content/route_spec.rb +85 -0
  44. data/spec/middleware/routes_reloader_spec.rb +48 -0
  45. data/spec/routing/node_mapper_spec.rb +142 -0
  46. data/spec/validators/content/node/parent_validator_spec.rb +56 -0
  47. data/spec/validators/content/node/root_validator_spec.rb +69 -0
  48. data/spec/validators/content/node/singleness_validator_spec.rb +145 -0
  49. metadata +145 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6a8d9437be334b0617ec00d8c8e3e4ea14b35dc6
4
+ data.tar.gz: 095e4645884b45c38afb81fa0157b80454e14027
5
+ SHA512:
6
+ metadata.gz: ce189035ffd84c6abdea56c57ad9d3a5d55b7074ab458d746b4faa7374b67b1e37739db42d71609b449fd1ae34c85d072cf1eeeb73c8c4da627ceb48e41980d9
7
+ data.tar.gz: 65a20465658ed0a1edef3723b909fe6d5522e46546dfbb321176a888428b7ed6956ee97d9ebb5141ff75cf306d938949be98f0da0057a77d725f93cfe507dca6
data/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ Copyright (c) 2012, CubeSystems <info@cubesystems.lv>
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+ * Redistributions of source code must retain the above copyright
7
+ notice, this list of conditions and the following disclaimer.
8
+ * Redistributions in binary form must reproduce the above copyright
9
+ notice, this list of conditions and the following disclaimer in the
10
+ documentation and/or other materials provided with the distribution.
11
+ * Neither the name of the CubeSystems nor the names of its contributors may
12
+ be used to endorse or promote products derived from this software without
13
+ specific prior written permission.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
+ DISCLAIMED. IN NO EVENT SHALL CubeSystems BE LIABLE FOR ANY
19
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,88 @@
1
+ jQuery(function()
2
+ {
3
+ var body = jQuery('body.controller-releaf-content-nodes');
4
+
5
+ body.on('contentloaded', function(e)
6
+ {
7
+ var block = jQuery(e.target);
8
+
9
+ // item collapse / expand
10
+ block.find('.collection li .collapser').click(function()
11
+ {
12
+ var item = jQuery(this).closest('.collection li');
13
+ var should_expand = item.is('.collapsed');
14
+ var event_name = should_expand ? 'nodeitemexpand' : 'nodeitemcollapse';
15
+
16
+ item.trigger(event_name);
17
+
18
+ var setting_key = 'content.tree.expanded.' + item.data('id');
19
+ body.trigger( 'settingssave', [ setting_key, should_expand ] );
20
+ });
21
+
22
+ block.find('.collection li').bind('nodeitemcollapse', function( e )
23
+ {
24
+ e.stopPropagation();
25
+
26
+ var item = jQuery(e.target);
27
+ item.addClass('collapsed');
28
+ item.children('.collapser-cell').find('.collapser i').removeClass('fa-chevron-down').addClass('fa-chevron-right');
29
+
30
+ });
31
+
32
+ block.find('.collection li').bind('nodeitemexpand', function( e )
33
+ {
34
+ e.stopPropagation();
35
+
36
+ var item = jQuery(e.target);
37
+ item.removeClass('collapsed');
38
+ item.children('.collapser-cell').find('.collapser i').removeClass('fa-chevron-right').addClass('fa-chevron-down');
39
+
40
+ });
41
+
42
+
43
+
44
+ // slug generation
45
+ var name_input = block.find('.node-fields .field[data-name="name"] input');
46
+ var slug_field = block.find('.node-fields .field[data-name="slug"]');
47
+
48
+ if (name_input.length && slug_field.length)
49
+ {
50
+ var slug_input = slug_field.find('input');
51
+ var slug_button = slug_field.find('.generate');
52
+ var slug_link = slug_field.find('a');
53
+
54
+ slug_input.on('sluggenerate', function()
55
+ {
56
+ var url = slug_input.attr('data-generator-url');
57
+
58
+ slug_button.trigger('loadingstart');
59
+ jQuery.get( url, { name: name_input.val() }, function( slug )
60
+ {
61
+ slug_input.val( slug );
62
+ slug_link.find('span').text( encodeURIComponent( slug ) );
63
+ slug_button.trigger('loadingend');
64
+ }, 'text');
65
+ });
66
+
67
+ slug_button.click(function()
68
+ {
69
+ slug_input.trigger('sluggenerate');
70
+ });
71
+
72
+ if (name_input.val() === '')
73
+ {
74
+ // bind onchange slug generation only if starting out with an empty name
75
+ name_input.change(function()
76
+ {
77
+ slug_input.trigger('sluggenerate');
78
+ });
79
+ }
80
+ }
81
+
82
+ });
83
+
84
+ body.on('click', '.dialog .node-cell label', function() {
85
+ jQuery('.dialog .node-cell label').removeClass('selected');
86
+ jQuery(this).addClass('selected');
87
+ });
88
+ });
@@ -0,0 +1,234 @@
1
+ @import 'releaf/environment';
2
+
3
+ .controller-releaf-content-nodes
4
+ {
5
+ main > section:not(.dialog) > .body:not(.empty)
6
+ {
7
+ border-bottom: none;
8
+ }
9
+
10
+ .collection
11
+ {
12
+ ul
13
+ {
14
+ @include block-list;
15
+ }
16
+
17
+ .collapsed ul
18
+ {
19
+ display: none;
20
+ }
21
+
22
+ li
23
+ {
24
+ @include clear-inside;
25
+ }
26
+
27
+ .collapser-cell
28
+ {
29
+ float: left;
30
+ }
31
+
32
+ .toolbox-cell
33
+ {
34
+ border-left: 1px solid $color-border-normal;
35
+ float: right;
36
+ }
37
+
38
+ .collapser-cell,
39
+ .toolbox-cell
40
+ {
41
+ .trigger
42
+ {
43
+ background: transparent;
44
+ }
45
+ }
46
+
47
+ .node-cell
48
+ {
49
+ border-bottom: 1px solid $color-border-normal;
50
+
51
+ .trigger
52
+ {
53
+ display: block;
54
+ color: $color-text-lightest;
55
+ text-decoration: none;
56
+ padding: steps(0.5) steps(3);
57
+ font-weight: bold;
58
+
59
+ &.selected,
60
+ &:hover
61
+ {
62
+ background: $color-highlight-lightest;
63
+ }
64
+
65
+ span
66
+ {
67
+ position: relative;
68
+ }
69
+ }
70
+
71
+ &.active .trigger
72
+ {
73
+ color: $color-text-normal;
74
+ }
75
+
76
+ span
77
+ {
78
+ margin-left: 0;
79
+ display: inline-block;
80
+ }
81
+
82
+ input[type="radio"]
83
+ {
84
+ display: none;
85
+ }
86
+
87
+ }
88
+
89
+ // nested item indentation
90
+ @for $i from 1 through 10
91
+ {
92
+ li[data-level="#{$i}"]
93
+ {
94
+ & > .collapser-cell
95
+ {
96
+ $previous: $i - 1;
97
+ position: absolute;
98
+ left: $previous * steps(2);
99
+ }
100
+
101
+ & > .node-cell span
102
+ {
103
+ margin-left: ($i - 1) * steps(2);
104
+ }
105
+ }
106
+ }
107
+
108
+ .nothing-found
109
+ {
110
+ padding: steps(0.5) steps(2);
111
+ }
112
+ }
113
+
114
+ main .collection li ul ul
115
+ {
116
+ background: $color-background-normal;
117
+ }
118
+
119
+ .dialog .collection > ul li
120
+ {
121
+ position: relative;
122
+ }
123
+
124
+
125
+ &.view-index > main > section
126
+ {
127
+ display: inline-block;
128
+ min-width: steps(40);
129
+ }
130
+
131
+
132
+ .dialog
133
+ {
134
+ width: steps(40);
135
+
136
+ &.content-type
137
+ {
138
+ &[data-columns="1"]
139
+ {
140
+ width: steps(24);
141
+ }
142
+
143
+ .body
144
+ {
145
+ padding: steps(2) steps(3);
146
+ text-align: center;
147
+
148
+ .description
149
+ {
150
+ padding: 0 0 steps(2) 0;
151
+ }
152
+
153
+ ul
154
+ {
155
+ width: steps(18);
156
+ margin-left: steps(2);
157
+ float: left;
158
+ }
159
+ }
160
+
161
+ .content-types
162
+ {
163
+ @include clear-inside;
164
+
165
+ ul
166
+ {
167
+ @include block-list;
168
+
169
+ li
170
+ {
171
+ margin-bottom: steps(1);
172
+ }
173
+ }
174
+ }
175
+ }
176
+
177
+ &:not(.content-type)
178
+ {
179
+ .body
180
+ {
181
+ height: steps(24);
182
+ overflow-y: auto;
183
+ }
184
+ }
185
+ }
186
+
187
+ &.view-edit
188
+ {
189
+ .node-fields
190
+ {
191
+ @include clear-inside;
192
+
193
+ background: $color-background-lighter;
194
+
195
+ .field[data-name="slug"]
196
+ {
197
+ .button.generate
198
+ {
199
+ position: absolute;
200
+ right: 0;
201
+ top: 0;
202
+ }
203
+
204
+ input[type="text"]
205
+ {
206
+ padding-right: steps(4);
207
+ }
208
+
209
+ .link
210
+ {
211
+ padding: steps(0.5) 0;
212
+ span
213
+ {
214
+ color: $color-text-normal;
215
+ }
216
+ }
217
+ }
218
+
219
+ .field[data-name="active"]
220
+ {
221
+ min-height: initial;
222
+ clear: both;
223
+
224
+ .value
225
+ {
226
+ padding-top: initial;
227
+ }
228
+ }
229
+
230
+ }
231
+
232
+ }
233
+ }
234
+
@@ -0,0 +1,60 @@
1
+ module Releaf::Content::Builders
2
+ module ActionDialog
3
+ include Releaf::Content::Builders::Dialog
4
+
5
+ def form_attributes
6
+ {
7
+ method: :post,
8
+ data: {
9
+ "remote" => true,
10
+ "remote-validation" => true,
11
+ "type" => :json
12
+ }
13
+ }
14
+ end
15
+
16
+ def root_level
17
+ tag(:ul, "data-level" => 0) do
18
+ tag(:li, class: "root") do
19
+ [tree_root_resource, super]
20
+ end
21
+ end
22
+ end
23
+
24
+ def tree_root_resource
25
+ field_id = "new_parent_id_0"
26
+ tag(:div, class: "node-cell") do
27
+ [radio_button_tag(:new_parent_id, '', false, id: 'new_parent_id_0'),
28
+ tag(:label, t("Root node"), class: "trigger", for: field_id)]
29
+ end
30
+ end
31
+
32
+ def tree_resource_name_button(resource)
33
+ field_id = "new_parent_id_#{resource.id}"
34
+
35
+ [radio_button_tag(:new_parent_id, resource.id, false, id: field_id),
36
+ tag(:label, tag(:span, resource.name), class: "trigger", for: field_id)]
37
+ end
38
+
39
+ def section_blocks
40
+ form_tag(url_for(action: action, id: resource.id), form_attributes) do
41
+ safe_join do
42
+ super
43
+ end
44
+ end
45
+ end
46
+
47
+ def section_header_text
48
+ message = "#{action.capitalize} node '%{node_name}' to"
49
+ t(message, default: message, node_name: resource.name )
50
+ end
51
+
52
+ def footer_primary_tools
53
+ super << confirm_button
54
+ end
55
+
56
+ def confirm_button
57
+ button(t(action), "check", class: "primary", type: "submit", data: { type: 'ok', disable: true })
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,15 @@
1
+ module Releaf::Content::Builders
2
+ module Dialog
3
+ include Releaf::Builders::ResourceDialog
4
+ include Releaf::Builders::Collection
5
+ include Releaf::Content::Builders::Tree
6
+
7
+ def footer_primary_tools
8
+ [cancel_button]
9
+ end
10
+
11
+ def cancel_button
12
+ button(t('Cancel'), "ban", class: "secondary", data: {type: 'cancel'}, href: url_for( action: 'index' ))
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,84 @@
1
+ module Releaf::Content::Builders
2
+ module Tree
3
+ def section_body
4
+ tag(:div, class: body_classes) do
5
+ tree
6
+ end
7
+ end
8
+
9
+ def body_classes
10
+ classes = [:body]
11
+ classes << :empty if collection.size < 1
12
+ classes
13
+ end
14
+
15
+ def tree
16
+ tag(:div, class: "collection") do
17
+ root_level
18
+ end
19
+ end
20
+
21
+ def root_level
22
+ return empty_body if collection.size < 1
23
+ tree_level(collection, 1)
24
+ end
25
+
26
+ def empty_body
27
+ tag(:div, class: "nothing-found") do
28
+ t("Nothing found")
29
+ end
30
+ end
31
+
32
+ def tree_level(list, level)
33
+ tag(:ul, "data-level" => level) do
34
+ list.collect do |resource|
35
+ tree_resource(resource, level)
36
+ end
37
+ end
38
+ end
39
+
40
+ def tree_resource(resource, level)
41
+ expanded = (layout_settings("content.tree.expanded.#{resource.id}") == true)
42
+ classes = []
43
+ classes << 'collapsed' unless expanded
44
+ classes << 'has-children' unless resource.children.empty?
45
+
46
+ tag(:li, class: classes, data: {level: level, id: resource.id}) do
47
+ tree_resource_blocks(resource, level, expanded)
48
+ end
49
+ end
50
+
51
+ def tree_resource_blocks(resource, level, expanded)
52
+ [tree_resource_collapser(resource, expanded),
53
+ tree_resource_name(resource), tree_resource_children(resource, level)]
54
+ end
55
+
56
+ def tree_resource_collapser(resource, expanded)
57
+ return if resource.children.empty?
58
+ tag(:div, class: "collapser-cell") do
59
+ button(nil, (expanded ? 'chevron-down' : 'chevron-right'), class: %w(secondary collapser trigger), title: t(expanded ? "collapse" : "expand"))
60
+ end
61
+ end
62
+
63
+ def tree_resource_children(resource, level)
64
+ return if resource.children.empty?
65
+ tree_level(resource.children, level + 1)
66
+ end
67
+
68
+ def tree_resource_name(resource)
69
+ classes = ["node-cell"]
70
+ classes << "active" if resource.active?
71
+
72
+ tag(:div, class: classes) do
73
+ tree_resource_name_button(resource)
74
+ end
75
+ end
76
+
77
+ def tree_resource_name_button(resource)
78
+ title = resource.content_id.present? ? "#{resource.content_type} ##{resource.content_id}" : resource.content_type
79
+ tag(:a, class: "trigger", href: url_for(action: "edit", id: resource.id), title: title) do
80
+ tag(:span, resource.name)
81
+ end
82
+ end
83
+ end
84
+ end