releaf-content 0.2.1

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.
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