browsercms 3.3.2 → 3.3.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|