browsercms 3.3.2 → 3.3.3
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.
- data/app/controllers/cms/content_block_controller.rb +2 -2
- data/app/controllers/cms/content_controller.rb +1 -1
- data/app/controllers/cms/section_nodes_controller.rb +6 -1
- data/app/controllers/cms/sections_controller.rb +1 -1
- data/app/helpers/cms/application_helper.rb +1 -1
- data/app/helpers/cms/content_block_helper.rb +27 -0
- data/app/helpers/cms/section_nodes_helper.rb +43 -5
- data/app/models/abstract_file_block.rb +17 -2
- data/app/models/attachment.rb +16 -34
- data/app/models/file_block.rb +0 -12
- data/app/models/image_block.rb +0 -12
- data/app/models/link.rb +4 -21
- data/app/models/page.rb +36 -35
- data/app/models/section.rb +82 -42
- data/app/models/section_node.rb +41 -24
- data/app/views/cms/blocks/index.html.erb +4 -4
- data/app/views/cms/file_blocks/_form.html.erb +1 -1
- data/app/views/cms/image_blocks/_form.html.erb +1 -1
- data/app/views/cms/section_nodes/_link.html.erb +6 -3
- data/app/views/cms/section_nodes/_node.html.erb +11 -2
- data/app/views/cms/section_nodes/_page.html.erb +13 -7
- data/app/views/cms/section_nodes/_section.html.erb +24 -8
- data/app/views/cms/section_nodes/index.html.erb +18 -6
- data/app/views/cms/shared/_pagination.html.erb +4 -4
- data/app/views/portlets/reset_password/render.html.erb +0 -2
- data/browsercms.gemspec +1 -4
- data/db/migrate/20100117144039_browsercms315.rb +94 -0
- data/lib/acts_as_list.rb +1 -1
- data/lib/browsercms.rb +3 -1
- data/lib/cms/addressable.rb +83 -0
- data/lib/cms/behaviors/attaching.rb +42 -24
- data/lib/cms/behaviors/connecting.rb +2 -1
- data/lib/cms/behaviors/publishing.rb +12 -3
- data/lib/cms/behaviors/versioning.rb +43 -24
- data/lib/cms/content_rendering_support.rb +3 -3
- data/lib/cms/error_pages.rb +8 -0
- data/lib/cms/version.rb +2 -2
- data/lib/generators/browser_cms/cms/cms_generator.rb +1 -0
- data/lib/generators/cms/upgrade_module/templates/GPL.txt +1 -1
- data/lib/tasks/data.rake +43 -0
- metadata +27 -8
- data/app/views/cms/section_nodes/_section_node.html.erb +0 -10
@@ -121,8 +121,8 @@ class Cms::ContentBlockController < Cms::BaseController
|
|
121
121
|
def load_blocks
|
122
122
|
options = {}
|
123
123
|
if params[:section_id] && params[:section_id] != 'all'
|
124
|
-
options[:include] = { :attachment =>
|
125
|
-
options[:conditions] = ["
|
124
|
+
options[:include] = { :attachment => :section_node }
|
125
|
+
options[:conditions] = ["section_nodes.ancestry = ?", Section.find(params[:section_id]).ancestry_path]
|
126
126
|
end
|
127
127
|
options[:page] = params[:page]
|
128
128
|
options[:order] = model_class.default_order if model_class.respond_to?(:default_order)
|
@@ -3,7 +3,12 @@ class Cms::SectionNodesController < Cms::BaseController
|
|
3
3
|
|
4
4
|
def index
|
5
5
|
@toolbar_tab = :sitemap
|
6
|
-
@
|
6
|
+
@modifiable_sections = current_user.modifiable_sections
|
7
|
+
@public_sections = Group.guest.sections.all # Load once here so that every section doesn't need to.
|
8
|
+
|
9
|
+
@sitemap = Section.sitemap
|
10
|
+
@root_section_node = @sitemap.keys.first
|
11
|
+
@section = @root_section_node.node
|
7
12
|
end
|
8
13
|
def move_before
|
9
14
|
move(:before)
|
@@ -23,7 +23,7 @@ module Cms
|
|
23
23
|
def searchable_sections(selected = nil)
|
24
24
|
root = Section.root.first
|
25
25
|
options = [['All sections', 'all'], [root.name, root.id]]
|
26
|
-
root.
|
26
|
+
root.master_section_list.each { |s| options << [s.full_path, s.id] }
|
27
27
|
options_for_select(options, selected.to_i)
|
28
28
|
end
|
29
29
|
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Cms
|
2
|
+
module ContentBlockHelper
|
3
|
+
|
4
|
+
|
5
|
+
# Prints the <tr> for each block. Adds classes based on:
|
6
|
+
# * Name/id of the block
|
7
|
+
# * If a block is published/draft
|
8
|
+
# * If the user can edit/publish it
|
9
|
+
def block_row_tag(block)
|
10
|
+
cname = class_name_for(block)
|
11
|
+
can_modify = current_user.able_to_modify?(block)
|
12
|
+
|
13
|
+
options = {
|
14
|
+
:id => "#{cname}_#{block.id}",
|
15
|
+
:class => cname
|
16
|
+
}
|
17
|
+
options[:class] += block.class.publishable? && !block.published? ? ' draft' : ' published'
|
18
|
+
options[:class] += ' non-editable' unless can_modify && current_user.able_to?(:edit_content)
|
19
|
+
options[:class] += ' non-publishable' unless can_modify && current_user.able_to?(:publish_content)
|
20
|
+
tag "tr", options, true
|
21
|
+
end
|
22
|
+
|
23
|
+
def class_name_for(block)
|
24
|
+
block.class.name.underscore
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -1,13 +1,51 @@
|
|
1
1
|
module Cms
|
2
2
|
module SectionNodesHelper
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
|
4
|
+
def access_status(section_node, public_sections)
|
5
|
+
access_icon = :unlocked
|
6
|
+
unless public_sections.include?(section_node)
|
7
|
+
access_icon = :locked
|
8
|
+
end
|
9
|
+
access_icon
|
10
|
+
end
|
11
|
+
|
12
|
+
def section_icons(section_node, children=[])
|
13
|
+
folder_style = ""
|
14
|
+
expander_image = "expand.png"
|
15
|
+
if top_level_section?(section_node)
|
16
|
+
folder_style = " large"
|
17
|
+
expander_image = "gray_expand.png"
|
18
|
+
end
|
19
|
+
if children.empty?
|
20
|
+
image_tag("cms/sitemap/no_contents.png", :class => "no_folder_toggle#{folder_style}")
|
7
21
|
else
|
8
|
-
|
22
|
+
image_tag("cms/sitemap/#{expander_image}", :class => "folder_toggle#{folder_style}")
|
9
23
|
end
|
10
24
|
end
|
11
25
|
|
26
|
+
# Renders the ul for a given node (Page/Section/Link/etc)
|
27
|
+
# Default look:
|
28
|
+
# - First level pages/sections use 'big' icons
|
29
|
+
# - All non-first level items should be hidden.
|
30
|
+
def sitemap_ul_tag(node)
|
31
|
+
opts = {
|
32
|
+
:id => "section_node_#{node.section_node.id}",
|
33
|
+
:class => "section_node"
|
34
|
+
}
|
35
|
+
opts[:class] += " rootlet" if in_first_level?(node)
|
36
|
+
opts[:style] = "display: none" unless in_first_level?(node)
|
37
|
+
tag("ul", opts, true)
|
38
|
+
end
|
39
|
+
|
40
|
+
def in_first_level?(node)
|
41
|
+
node.section_node.depth == 1
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def top_level_section?(node)
|
47
|
+
node.depth <= 2
|
48
|
+
end
|
49
|
+
|
12
50
|
end
|
13
51
|
end
|
@@ -4,8 +4,11 @@ class AbstractFileBlock < ActiveRecord::Base
|
|
4
4
|
|
5
5
|
validates_presence_of :name
|
6
6
|
|
7
|
-
scope :by_section, lambda { |section| {
|
8
|
-
|
7
|
+
scope :by_section, lambda { |section| {
|
8
|
+
:include => {:attachment => :section_node },
|
9
|
+
:conditions => ["section_nodes.ancestry = ?", section.node.ancestry_path] }
|
10
|
+
}
|
11
|
+
|
9
12
|
def path
|
10
13
|
attachment_file_path
|
11
14
|
end
|
@@ -14,4 +17,16 @@ class AbstractFileBlock < ActiveRecord::Base
|
|
14
17
|
true
|
15
18
|
end
|
16
19
|
|
20
|
+
def set_attachment_path
|
21
|
+
if @attachment_file_path && @attachment_file_path != attachment.file_path
|
22
|
+
attachment.file_path = @attachment_file_path
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def set_attachment_section
|
27
|
+
if @attachment_section_id && @attachment_section_id != attachment.section
|
28
|
+
attachment.section_id = @attachment_section_id
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
17
32
|
end
|
data/app/models/attachment.rb
CHANGED
@@ -28,7 +28,6 @@ class Attachment < ActiveRecord::Base
|
|
28
28
|
before_validation :extract_file_type_from_temp_file
|
29
29
|
before_validation :extract_file_size_from_temp_file
|
30
30
|
before_validation :set_file_location
|
31
|
-
before_save :process_section
|
32
31
|
|
33
32
|
after_save :write_temp_file_to_storage_location
|
34
33
|
after_save :clear_ivars
|
@@ -36,41 +35,21 @@ class Attachment < ActiveRecord::Base
|
|
36
35
|
|
37
36
|
#----- Associations ----------------------------------------------------------
|
38
37
|
|
38
|
+
include Addressable
|
39
|
+
include Addressable::DeprecatedPageAccessors
|
39
40
|
has_one :section_node, :as => :node
|
41
|
+
alias :node :section_node
|
40
42
|
|
41
43
|
#----- Validations -----------------------------------------------------------
|
42
44
|
|
43
45
|
validates_presence_of :temp_file, :message => "You must upload a file", :on => :create
|
44
46
|
validates_presence_of :file_path
|
45
47
|
validates_uniqueness_of :file_path
|
46
|
-
validates_presence_of :section_id
|
47
|
-
|
48
|
-
#----- Virtual Attributes ----------------------------------------------------
|
49
|
-
|
50
|
-
def section_id
|
51
|
-
@section_id ||= section_node ? section_node.section_id : nil
|
52
|
-
end
|
53
|
-
|
54
|
-
def section_id=(section_id)
|
55
|
-
if @section_id != section_id
|
56
|
-
dirty!
|
57
|
-
@section_id = section_id
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def section
|
62
|
-
@section ||= section_node ? section_node.section : nil
|
63
|
-
end
|
64
48
|
|
65
49
|
def section=(section)
|
66
|
-
|
67
|
-
|
68
|
-
dirty!
|
69
|
-
@section_id = section ? section.id : nil
|
70
|
-
@section = section
|
71
|
-
end
|
50
|
+
dirty! if self.section != section
|
51
|
+
super(section)
|
72
52
|
end
|
73
|
-
|
74
53
|
#----- Callbacks Methods -----------------------------------------------------
|
75
54
|
|
76
55
|
def make_dirty_if_temp_file
|
@@ -117,14 +96,17 @@ class Attachment < ActiveRecord::Base
|
|
117
96
|
end
|
118
97
|
end
|
119
98
|
|
120
|
-
def process_section
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
99
|
+
#def process_section
|
100
|
+
# if section_node
|
101
|
+
# section_node.move_to_end(parent)
|
102
|
+
# #end
|
103
|
+
# #logger.info "processing section, section_id => #{section_id}, section_node => #{section_node.inspect}"
|
104
|
+
# #if section_node && !section_node.new_record? && section_node.section_id != section_id
|
105
|
+
# # section_node.move_to_end(Section.find(section_id))
|
106
|
+
# else
|
107
|
+
# build_section_node(:node => self, :parent => parent)
|
108
|
+
# end
|
109
|
+
#end
|
128
110
|
|
129
111
|
def write_temp_file_to_storage_location
|
130
112
|
unless temp_file.blank?
|
data/app/models/file_block.rb
CHANGED
@@ -1,18 +1,6 @@
|
|
1
1
|
class FileBlock < AbstractFileBlock
|
2
2
|
|
3
3
|
acts_as_content_block :belongs_to_attachment => true, :taggable => true
|
4
|
-
|
5
|
-
def set_attachment_file_path
|
6
|
-
if @attachment_file_path && @attachment_file_path != attachment.file_path
|
7
|
-
attachment.file_path = @attachment_file_path
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
def set_attachment_section
|
12
|
-
if @attachment_section_id && @attachment_section_id != attachment.section_id
|
13
|
-
attachment.section_id = @attachment_section_id
|
14
|
-
end
|
15
|
-
end
|
16
4
|
|
17
5
|
def self.display_name
|
18
6
|
"File"
|
data/app/models/image_block.rb
CHANGED
@@ -3,18 +3,6 @@ class ImageBlock < AbstractFileBlock
|
|
3
3
|
acts_as_content_block :versioned => { :version_foreign_key => :file_block_id },
|
4
4
|
:belongs_to_attachment => true, :taggable => true
|
5
5
|
|
6
|
-
def set_attachment_file_path
|
7
|
-
if @attachment_file_path && @attachment_file_path != attachment.file_path
|
8
|
-
attachment.file_path = @attachment_file_path
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
def set_attachment_section
|
13
|
-
if @attachment_section_id && @attachment_section_id != attachment.section_id
|
14
|
-
attachment.section_id = @attachment_section_id
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
6
|
def self.display_name
|
19
7
|
"Image"
|
20
8
|
end
|
data/app/models/link.rb
CHANGED
@@ -3,29 +3,12 @@ class Link < ActiveRecord::Base
|
|
3
3
|
|
4
4
|
scope :named, lambda{|name| {:conditions => ['links.name = ?', name]}}
|
5
5
|
|
6
|
-
has_one :section_node, :as => :node, :dependent => :destroy
|
7
|
-
|
6
|
+
has_one :section_node, :as => :node, :dependent => :destroy, :inverse_of => :node
|
7
|
+
|
8
8
|
validates_presence_of :name
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
end
|
13
|
-
|
14
|
-
def section
|
15
|
-
section_node ? section_node.section : nil
|
16
|
-
end
|
17
|
-
|
18
|
-
def section_id=(sec_id)
|
19
|
-
self.section = Section.find(sec_id)
|
20
|
-
end
|
21
|
-
|
22
|
-
def section=(sec)
|
23
|
-
if section_node
|
24
|
-
section_node.move_to_end(sec)
|
25
|
-
else
|
26
|
-
build_section_node(:node => self, :section => sec)
|
27
|
-
end
|
28
|
-
end
|
10
|
+
include Addressable
|
11
|
+
include Addressable::DeprecatedPageAccessors
|
29
12
|
|
30
13
|
#needed by menu_helper
|
31
14
|
def path
|
data/app/models/page.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
class Page < ActiveRecord::Base
|
2
|
-
|
2
|
+
|
3
|
+
def actual_path
|
4
|
+
path
|
5
|
+
end
|
6
|
+
|
3
7
|
is_archivable
|
4
8
|
flush_cache_on_change
|
5
9
|
is_hideable
|
@@ -57,8 +61,12 @@ class Page < ActiveRecord::Base
|
|
57
61
|
end
|
58
62
|
}
|
59
63
|
|
60
|
-
has_one :section_node, :as => :node, :dependent => :destroy
|
61
|
-
|
64
|
+
has_one :section_node, :as => :node, :dependent => :destroy, :inverse_of => :node
|
65
|
+
|
66
|
+
|
67
|
+
include Addressable
|
68
|
+
include Addressable::DeprecatedPageAccessors
|
69
|
+
|
62
70
|
has_many :tasks
|
63
71
|
|
64
72
|
before_validation :append_leading_slash_to_path, :remove_trailing_slash_from_path
|
@@ -123,7 +131,7 @@ class Page < ActiveRecord::Base
|
|
123
131
|
:page_version => options[:to_version_number],
|
124
132
|
:connectable => connectable,
|
125
133
|
:connectable_version => version,
|
126
|
-
:container => c.container,
|
134
|
+
:container => c.container,
|
127
135
|
:position => c.position
|
128
136
|
)
|
129
137
|
logger.debug {"Built new connector #{new_connector}."}
|
@@ -200,26 +208,6 @@ class Page < ActiveRecord::Base
|
|
200
208
|
"?"
|
201
209
|
end
|
202
210
|
|
203
|
-
def section_id
|
204
|
-
section ? section.id : nil
|
205
|
-
end
|
206
|
-
|
207
|
-
def section
|
208
|
-
section_node ? section_node.section : nil
|
209
|
-
end
|
210
|
-
|
211
|
-
def section_id=(sec_id)
|
212
|
-
self.section = Section.find(sec_id)
|
213
|
-
end
|
214
|
-
|
215
|
-
def section=(sec)
|
216
|
-
if section_node
|
217
|
-
section_node.move_to_end(sec)
|
218
|
-
else
|
219
|
-
build_section_node(:node => self, :section => sec)
|
220
|
-
end
|
221
|
-
end
|
222
|
-
|
223
211
|
def public?
|
224
212
|
section ? section.public? : false
|
225
213
|
end
|
@@ -261,16 +249,25 @@ class Page < ActiveRecord::Base
|
|
261
249
|
template_file_name && PageTemplate.display_name(template_file_name)
|
262
250
|
end
|
263
251
|
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
252
|
+
# Determines if a page is a descendant of a given Section.
|
253
|
+
#
|
254
|
+
# @param [String | Section] section_or_section_name
|
268
255
|
def in_section?(section_or_section_name)
|
269
|
-
|
270
|
-
|
271
|
-
section_or_section_name
|
272
|
-
|
273
|
-
|
256
|
+
found = false
|
257
|
+
ancestors.each do |a|
|
258
|
+
if section_or_section_name.is_a?(String)
|
259
|
+
if a.name == section_or_section_name
|
260
|
+
found = true
|
261
|
+
break
|
262
|
+
end
|
263
|
+
else
|
264
|
+
if a == section_or_section_name
|
265
|
+
found = true
|
266
|
+
break
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
found
|
274
271
|
end
|
275
272
|
|
276
273
|
#Returns true if the block attached to each connector in the given container are published
|
@@ -294,12 +291,16 @@ class Page < ActiveRecord::Base
|
|
294
291
|
(a[1..a.size].map{|a| a.name} + [name]).join(" / ")
|
295
292
|
end
|
296
293
|
|
297
|
-
# This will return the "top level section" for
|
294
|
+
# This will return the "top level section" for this page, which is the section directly
|
298
295
|
# below the root (a.k.a My Site) that this page is in. If this page is in root,
|
299
296
|
# then this will return root.
|
297
|
+
#
|
298
|
+
# @return [Section] The first non-root ancestor if available, root otherwise.
|
300
299
|
def top_level_section
|
300
|
+
# Cache the results of this since many projects will call it repeatly on current_page in menus.
|
301
|
+
return @top_level_section if @top_level_section
|
301
302
|
a = ancestors
|
302
|
-
(a.size > 0 &&
|
303
|
+
@top_level_section = (a.size > 0 && a[1]) ? a[1] : Section.root.first
|
303
304
|
end
|
304
305
|
|
305
306
|
def current_task
|
data/app/models/section.rb
CHANGED
@@ -3,14 +3,15 @@ class Section < ActiveRecord::Base
|
|
3
3
|
flush_cache_on_change
|
4
4
|
|
5
5
|
#The node that links this section to its parent
|
6
|
-
has_one :
|
6
|
+
has_one :section_node, :class_name => "SectionNode", :as => :node, :inverse_of => :node
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
has_many :child_sections, :class_name => "SectionNode", :conditions => ["node_type = ?", "Section"], :order => 'section_nodes.position'
|
8
|
+
include Addressable
|
9
|
+
include Addressable::NodeAccessors
|
11
10
|
|
12
|
-
|
13
|
-
|
11
|
+
# Cannot use dependent => :destroy to do this. Ancestry's callbacks trigger before the before_destroy callback.
|
12
|
+
# So sections would always get deleted since deletable? would return true
|
13
|
+
after_destroy :destroy_node
|
14
|
+
before_destroy :deletable?
|
14
15
|
|
15
16
|
has_many :group_sections
|
16
17
|
has_many :groups, :through => :group_sections
|
@@ -25,55 +26,81 @@ class Section < ActiveRecord::Base
|
|
25
26
|
scope :with_path, lambda{|path| {:conditions => ['sections.path = ?', path]}}
|
26
27
|
|
27
28
|
validates_presence_of :name, :path
|
28
|
-
#validates_presence_of :parent_id, :if => Proc.new {root.count > 0}, :message => "section is required"
|
29
29
|
|
30
30
|
# Disabling '/' in section name for interoperability with FCKEditor file browser
|
31
31
|
validates_format_of :name, :with => /\A[^\/]*\Z/, :message => "cannot contain '/'"
|
32
32
|
|
33
33
|
validate :path_not_reserved
|
34
34
|
|
35
|
-
before_destroy :deletable?
|
36
|
-
|
37
35
|
attr_accessor :full_path
|
38
36
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
37
|
+
delegate :ancestry_path, :to => :node
|
38
|
+
|
39
|
+
def ancestry
|
40
|
+
self.node.ancestry
|
43
41
|
end
|
44
42
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
end.flatten.compact
|
43
|
+
before_validation :ensure_section_node_exists
|
44
|
+
|
45
|
+
def ensure_section_node_exists
|
46
|
+
unless node
|
47
|
+
self.node = build_section_node
|
48
|
+
end
|
52
49
|
end
|
53
50
|
|
54
|
-
|
55
|
-
|
51
|
+
# Returns a list of all children which are sections.
|
52
|
+
# @return [Array<Section>]
|
53
|
+
def sections
|
54
|
+
child_nodes.of_type("Section").fetch_nodes.in_order.collect do |section_node|
|
55
|
+
section_node.node
|
56
|
+
end
|
56
57
|
end
|
57
58
|
|
58
|
-
|
59
|
-
|
59
|
+
alias :child_sections :sections
|
60
|
+
|
61
|
+
# Since #sections isn't an association anymore, callers can use this rather than #sections.build
|
62
|
+
def build_section
|
63
|
+
Section.new(:parent=>self)
|
60
64
|
end
|
61
65
|
|
62
|
-
|
63
|
-
|
66
|
+
# Used by the sitemap to find children to iterate over.
|
67
|
+
def child_nodes
|
68
|
+
self.node.children
|
64
69
|
end
|
65
70
|
|
66
|
-
def
|
67
|
-
|
68
|
-
node.
|
69
|
-
else
|
70
|
-
build_node(:node => self, :section => sec)
|
71
|
+
def pages
|
72
|
+
child_pages = self.node.children.collect do |section_node|
|
73
|
+
section_node.node if section_node.page?
|
71
74
|
end
|
75
|
+
child_pages.compact
|
72
76
|
end
|
73
77
|
|
74
|
-
def
|
75
|
-
|
76
|
-
|
78
|
+
def self.sitemap
|
79
|
+
SectionNode.of_type(["Page", "Link", "Section"]).fetch_nodes.arrange(:order=>:position)
|
80
|
+
end
|
81
|
+
|
82
|
+
def visible_child_nodes(options={})
|
83
|
+
children = child_nodes.of_type(["Section", "Page", "Link"]).fetch_nodes.in_order.all
|
84
|
+
visible_children = children.select { |sn| sn.visible? }
|
85
|
+
options[:limit] ? visible_children[0...options[:limit]] : visible_children
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
# Returns a complete list of all sections that are desecendants of this sections, in order, as a single flat list.
|
90
|
+
# Used by Section selectors where users have to pick a single section from a complete list of all sections.
|
91
|
+
def master_section_list
|
92
|
+
sections.map do |section|
|
93
|
+
section.full_path = root? ? section.name : "#{name} / #{section.name}"
|
94
|
+
[section] << section.master_section_list
|
95
|
+
end.flatten.compact
|
96
|
+
end
|
97
|
+
|
98
|
+
def parent_id
|
99
|
+
parent ? parent.id : nil
|
100
|
+
end
|
101
|
+
|
102
|
+
def parent_id=(sec_id)
|
103
|
+
self.parent = Section.find(sec_id)
|
77
104
|
end
|
78
105
|
|
79
106
|
def with_ancestors(options = {})
|
@@ -94,33 +121,46 @@ class Section < ActiveRecord::Base
|
|
94
121
|
end
|
95
122
|
|
96
123
|
def empty?
|
97
|
-
child_nodes.
|
124
|
+
child_nodes.empty?
|
98
125
|
end
|
99
126
|
|
127
|
+
# Callback to determine if this section can be deleted.
|
100
128
|
def deletable?
|
101
129
|
!root? && empty?
|
102
130
|
end
|
103
131
|
|
132
|
+
# Callback to clean up related nodes
|
133
|
+
def destroy_node
|
134
|
+
node.destroy
|
135
|
+
end
|
136
|
+
|
104
137
|
def editable_by_group?(group)
|
105
138
|
group.editable_by_section(self)
|
106
139
|
end
|
107
140
|
|
108
141
|
def status
|
109
|
-
public? ? :unlocked : :locked
|
142
|
+
@status ||= public? ? :unlocked : :locked
|
110
143
|
end
|
111
144
|
|
145
|
+
# Used by the file browser to look up a section by the combined names as a path.
|
146
|
+
# i.e. /A/B/
|
147
|
+
# @return [Section] nil if not found
|
112
148
|
def self.find_by_name_path(name_path)
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
149
|
+
current_section = Section.root.first
|
150
|
+
path_names = name_path.split("/")[1..-1] || []
|
151
|
+
|
152
|
+
# This implementation is very slow as it has to loop over the entire tree in memory to match each name element.
|
153
|
+
path_names.each do |name|
|
154
|
+
current_section.sections.each do |s|
|
155
|
+
current_section = s if s.name == name
|
156
|
+
end
|
117
157
|
end
|
118
|
-
|
158
|
+
current_section
|
119
159
|
end
|
120
160
|
|
121
161
|
#The first page that is a decendent of this section
|
122
162
|
def first_page_or_link
|
123
|
-
section_node = child_nodes.of_type(['Link', 'Page']).first
|
163
|
+
section_node = child_nodes.of_type(['Link', 'Page']).fetch_nodes.in_order.first
|
124
164
|
return section_node.node if section_node
|
125
165
|
sections.each do |s|
|
126
166
|
node = s.first_page_or_link
|