browsercms 3.1.4 → 3.1.5
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/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 +16 -1
- data/app/models/attachment.rb +17 -35
- 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 +31 -34
- data/app/models/section.rb +82 -44
- data/app/models/section_node.rb +39 -24
- data/app/models/user.rb +5 -0
- 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 -1
- 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 +28 -16
- data/app/views/layouts/templates/default.html.erb +17 -0
- data/browsercms.gemspec +28 -1413
- data/db/migrate/20120117144039_browsercms315.rb +94 -0
- data/db/migrate/{20081114172307_load_seed_data.rb → 20121114172307_load_seeds.rb} +8 -1
- data/lib/acts_as_list.rb +1 -1
- data/lib/browsercms.rb +2 -0
- data/lib/cms/addressable.rb +83 -0
- data/lib/cms/behaviors/attaching.rb +44 -24
- data/lib/cms/behaviors/connecting.rb +2 -1
- data/lib/cms/behaviors/publishing.rb +12 -3
- data/lib/cms/behaviors/versioning.rb +83 -53
- data/lib/cms/content_rendering_support.rb +3 -3
- data/lib/cms/error_pages.rb +8 -0
- data/lib/cms/init.rb +5 -3
- data/lib/cms/version.rb +1 -1
- data/templates/blank.rb +2 -0
- data/templates/demo.rb +2 -0
- data/templates/module.rb +2 -0
- data/test/custom_assertions.rb +7 -1
- data/test/factories.rb +3 -1
- data/test/factories/sitemap_factories.rb +28 -0
- data/test/fixtures/connectors.yml +97 -0
- data/test/fixtures/content_type_groups.yml +13 -0
- data/test/fixtures/content_types.yml +50 -0
- data/test/fixtures/dynamic_view_versions.yml +26 -0
- data/test/fixtures/dynamic_views.yml +26 -0
- data/test/fixtures/group_permissions.yml +16 -0
- data/test/fixtures/group_sections.yml +31 -0
- data/test/fixtures/group_type_permissions.yml +11 -0
- data/test/fixtures/group_types.yml +25 -0
- data/test/fixtures/groups.yml +25 -0
- data/test/fixtures/html_block_versions.yml +67 -0
- data/test/fixtures/html_blocks.yml +63 -0
- data/test/fixtures/page_versions.yml +265 -0
- data/test/fixtures/pages.yml +85 -0
- data/test/fixtures/permissions.yml +28 -0
- data/test/fixtures/section_nodes.yml +46 -0
- data/test/fixtures/sections.yml +19 -0
- data/test/fixtures/sites.yml +9 -0
- data/test/fixtures/user_group_memberships.yml +11 -0
- data/test/fixtures/users.yml +15 -0
- data/test/functional/cms/content_controller_test.rb +6 -1
- data/test/functional/cms/file_blocks_controller_test.rb +1 -0
- data/test/functional/cms/html_blocks_controller_test.rb +1 -0
- data/test/functional/cms/image_blocks_controller_test.rb +39 -32
- data/test/functional/cms/section_nodes_controller_test.rb +48 -20
- data/test/functional/cms/sections_controller_test.rb +3 -1
- data/test/functional/tests/pretend_controller_test.rb +6 -3
- data/test/integration/cms/ckeditor_test.rb +5 -2
- data/test/integration/sitemap_performance_test.rb +26 -0
- data/test/selenium-core/Blank.html +7 -0
- data/test/selenium-core/InjectedRemoteRunner.html +8 -0
- data/test/selenium-core/RemoteRunner.html +110 -0
- data/test/selenium-core/SeleniumLog.html +109 -0
- data/test/selenium-core/TestPrompt.html +145 -0
- data/test/selenium-core/TestRunner-splash.html +55 -0
- data/test/selenium-core/TestRunner.hta +176 -0
- data/test/selenium-core/TestRunner.html +176 -0
- data/test/selenium-core/domviewer/butmin.gif +0 -0
- data/test/selenium-core/domviewer/butplus.gif +0 -0
- data/test/selenium-core/domviewer/domviewer.css +298 -0
- data/test/selenium-core/domviewer/domviewer.html +16 -0
- data/test/selenium-core/domviewer/selenium-domviewer.js +205 -0
- data/test/selenium-core/icons/all.png +0 -0
- data/test/selenium-core/icons/continue.png +0 -0
- data/test/selenium-core/icons/continue_disabled.png +0 -0
- data/test/selenium-core/icons/pause.png +0 -0
- data/test/selenium-core/icons/pause_disabled.png +0 -0
- data/test/selenium-core/icons/selected.png +0 -0
- data/test/selenium-core/icons/step.png +0 -0
- data/test/selenium-core/icons/step_disabled.png +0 -0
- data/test/selenium-core/iedoc-core.xml +1515 -0
- data/test/selenium-core/iedoc.xml +1469 -0
- data/test/selenium-core/lib/cssQuery/cssQuery-p.js +6 -0
- data/test/selenium-core/lib/cssQuery/src/cssQuery-level2.js +142 -0
- data/test/selenium-core/lib/cssQuery/src/cssQuery-level3.js +150 -0
- data/test/selenium-core/lib/cssQuery/src/cssQuery-standard.js +53 -0
- data/test/selenium-core/lib/cssQuery/src/cssQuery.js +356 -0
- data/test/selenium-core/lib/prototype.js +2006 -0
- data/test/selenium-core/lib/scriptaculous/builder.js +101 -0
- data/test/selenium-core/lib/scriptaculous/controls.js +815 -0
- data/test/selenium-core/lib/scriptaculous/dragdrop.js +915 -0
- data/test/selenium-core/lib/scriptaculous/effects.js +958 -0
- data/test/selenium-core/lib/scriptaculous/scriptaculous.js +47 -0
- data/test/selenium-core/lib/scriptaculous/slider.js +283 -0
- data/test/selenium-core/lib/scriptaculous/unittest.js +383 -0
- data/test/selenium-core/scripts/find_matching_child.js +69 -0
- data/test/selenium-core/scripts/htmlutils.js +894 -0
- data/test/selenium-core/scripts/injection.html +72 -0
- data/test/selenium-core/scripts/js2html.js +70 -0
- data/test/selenium-core/scripts/narcissus-defs.js +175 -0
- data/test/selenium-core/scripts/narcissus-exec.js +1054 -0
- data/test/selenium-core/scripts/narcissus-parse.js +1003 -0
- data/test/selenium-core/scripts/se2html.js +63 -0
- data/test/selenium-core/scripts/selenium-api.js +2409 -0
- data/test/selenium-core/scripts/selenium-browserbot.js +2203 -0
- data/test/selenium-core/scripts/selenium-browserdetect.js +150 -0
- data/test/selenium-core/scripts/selenium-commandhandlers.js +377 -0
- data/test/selenium-core/scripts/selenium-executionloop.js +175 -0
- data/test/selenium-core/scripts/selenium-logging.js +147 -0
- data/test/selenium-core/scripts/selenium-remoterunner.js +571 -0
- data/test/selenium-core/scripts/selenium-testrunner.js +1333 -0
- data/test/selenium-core/scripts/selenium-version.js +5 -0
- data/test/selenium-core/scripts/user-extensions.js +3 -0
- data/test/selenium-core/scripts/user-extensions.js.sample +75 -0
- data/test/selenium-core/scripts/xmlextras.js +153 -0
- data/test/selenium-core/selenium-logo.png +0 -0
- data/test/selenium-core/selenium-test.css +43 -0
- data/test/selenium-core/selenium.css +299 -0
- data/test/selenium-core/xpath/dom.js +428 -0
- data/test/selenium-core/xpath/misc.js +252 -0
- data/test/selenium-core/xpath/xpath.js +2223 -0
- data/test/selenium/_login_as_cmsadmin.rsel +4 -0
- data/test/selenium/dashboard.rsel +5 -0
- data/test/selenium/html_blocks.rsel +4 -0
- data/test/selenium/login/failed_login.rsel +8 -0
- data/test/selenium/login/successful_login.rsel +9 -0
- data/test/selenium/page_templates.rsel +12 -0
- data/test/selenium/pages/edit_properties.rsel +5 -0
- data/test/selenium/site/view_home_page.rsel +4 -0
- data/test/selenium/sitemap/move_page.rsel +9 -0
- data/test/selenium/sitemap/open_section.rsel +6 -0
- data/test/selenium/sitemap/select_page.rsel +12 -0
- data/test/selenium/sitemap/select_section.rsel +17 -0
- data/test/test_helper.rb +30 -12
- data/test/unit/behaviors/attaching_test.rb +4 -6
- data/test/unit/behaviors/connectable_test.rb +29 -0
- data/test/unit/behaviors/publishable_test.rb +40 -9
- data/test/unit/behaviors/versioning_test.rb +36 -0
- data/test/unit/helpers/menu_helper_test.rb +5 -2
- data/test/unit/helpers/page_helper_test.rb +2 -0
- data/test/unit/lib/cms/sitemap_test.rb +206 -0
- data/test/unit/models/attachment_test.rb +51 -31
- data/test/unit/models/file_block_test.rb +74 -55
- data/test/unit/models/link_test.rb +44 -0
- data/test/unit/models/page_test.rb +290 -224
- data/test/unit/models/sections_test.rb +144 -44
- data/test/unit/models/user_test.rb +28 -18
- metadata +581 -350
- data/app/views/cms/section_nodes/_section_node.html.erb +0 -10
- data/test/unit/models/section_node_test.rb +0 -92
@@ -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,7 +4,10 @@ class AbstractFileBlock < ActiveRecord::Base
|
|
4
4
|
|
5
5
|
validates_presence_of :name
|
6
6
|
|
7
|
-
named_scope :by_section, lambda { |section| {
|
7
|
+
named_scope :by_section, lambda { |section| {
|
8
|
+
:include => {:attachment => :section_node },
|
9
|
+
:conditions => ["section_nodes.ancestry = ?", section.node.ancestry_path] }
|
10
|
+
}
|
8
11
|
|
9
12
|
def path
|
10
13
|
attachment_file_path
|
@@ -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
@@ -20,48 +20,27 @@ class Attachment < ActiveRecord::Base
|
|
20
20
|
before_validation :extract_file_type_from_temp_file
|
21
21
|
before_validation :extract_file_size_from_temp_file
|
22
22
|
before_validation :set_file_location
|
23
|
-
before_save :process_section
|
24
23
|
|
25
24
|
after_save :write_temp_file_to_storage_location
|
26
25
|
after_save :clear_ivars
|
27
26
|
|
28
27
|
#----- Associations ----------------------------------------------------------
|
29
28
|
|
29
|
+
include Addressable
|
30
|
+
include Addressable::DeprecatedPageAccessors
|
30
31
|
has_one :section_node, :as => :node
|
32
|
+
alias :node :section_node
|
31
33
|
|
32
34
|
#----- Validations -----------------------------------------------------------
|
33
35
|
|
34
|
-
validates_presence_of :temp_file,
|
35
|
-
:message => "You must upload a file", :on => :create
|
36
|
+
validates_presence_of :temp_file, :message => "You must upload a file", :on => :create
|
36
37
|
validates_presence_of :file_path
|
37
38
|
validates_uniqueness_of :file_path
|
38
|
-
validates_presence_of :section_id
|
39
|
-
|
40
|
-
#----- Virtual Attributes ----------------------------------------------------
|
41
|
-
|
42
|
-
def section_id
|
43
|
-
@section_id ||= section_node ? section_node.section_id : nil
|
44
|
-
end
|
45
|
-
|
46
|
-
def section_id=(section_id)
|
47
|
-
if @section_id != section_id
|
48
|
-
dirty!
|
49
|
-
@section_id = section_id
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def section
|
54
|
-
@section ||= section_node ? section_node.section : nil
|
55
|
-
end
|
56
39
|
|
57
40
|
def section=(section)
|
58
|
-
if
|
59
|
-
|
60
|
-
@section_id = section ? section.id : nil
|
61
|
-
@section = section
|
62
|
-
end
|
41
|
+
dirty! if self.section != section
|
42
|
+
super(section)
|
63
43
|
end
|
64
|
-
|
65
44
|
#----- Callbacks Methods -----------------------------------------------------
|
66
45
|
|
67
46
|
def make_dirty_if_temp_file
|
@@ -103,14 +82,17 @@ class Attachment < ActiveRecord::Base
|
|
103
82
|
end
|
104
83
|
end
|
105
84
|
|
106
|
-
def process_section
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
85
|
+
#def process_section
|
86
|
+
# if section_node
|
87
|
+
# section_node.move_to_end(parent)
|
88
|
+
# #end
|
89
|
+
# #logger.info "processing section, section_id => #{section_id}, section_node => #{section_node.inspect}"
|
90
|
+
# #if section_node && !section_node.new_record? && section_node.section_id != section_id
|
91
|
+
# # section_node.move_to_end(Section.find(section_id))
|
92
|
+
# else
|
93
|
+
# build_section_node(:node => self, :parent => parent)
|
94
|
+
# end
|
95
|
+
#end
|
114
96
|
|
115
97
|
def write_temp_file_to_storage_location
|
116
98
|
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
|
named_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,5 @@
|
|
1
1
|
class Page < ActiveRecord::Base
|
2
|
-
|
2
|
+
|
3
3
|
is_archivable
|
4
4
|
flush_cache_on_change
|
5
5
|
is_hideable
|
@@ -42,8 +42,12 @@ class Page < ActiveRecord::Base
|
|
42
42
|
end
|
43
43
|
}
|
44
44
|
|
45
|
-
has_one :section_node, :as => :node, :dependent => :destroy
|
46
|
-
|
45
|
+
has_one :section_node, :as => :node, :dependent => :destroy, :inverse_of => :node
|
46
|
+
|
47
|
+
|
48
|
+
include Addressable
|
49
|
+
include Addressable::DeprecatedPageAccessors
|
50
|
+
|
47
51
|
has_many :tasks
|
48
52
|
|
49
53
|
before_validation :append_leading_slash_to_path
|
@@ -168,26 +172,6 @@ class Page < ActiveRecord::Base
|
|
168
172
|
"?"
|
169
173
|
end
|
170
174
|
|
171
|
-
def section_id
|
172
|
-
section ? section.id : nil
|
173
|
-
end
|
174
|
-
|
175
|
-
def section
|
176
|
-
section_node ? section_node.section : nil
|
177
|
-
end
|
178
|
-
|
179
|
-
def section_id=(sec_id)
|
180
|
-
self.section = Section.find(sec_id)
|
181
|
-
end
|
182
|
-
|
183
|
-
def section=(sec)
|
184
|
-
if section_node
|
185
|
-
section_node.move_to_end(sec)
|
186
|
-
else
|
187
|
-
build_section_node(:node => self, :section => sec)
|
188
|
-
end
|
189
|
-
end
|
190
|
-
|
191
175
|
def public?
|
192
176
|
section ? section.public? : false
|
193
177
|
end
|
@@ -223,16 +207,25 @@ class Page < ActiveRecord::Base
|
|
223
207
|
template_file_name && PageTemplate.display_name(template_file_name)
|
224
208
|
end
|
225
209
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
210
|
+
# Determines if a page is a descendant of a given Section.
|
211
|
+
#
|
212
|
+
# @param [String | Section] section_or_section_name
|
230
213
|
def in_section?(section_or_section_name)
|
231
|
-
|
232
|
-
|
233
|
-
section_or_section_name
|
234
|
-
|
235
|
-
|
214
|
+
found = false
|
215
|
+
ancestors.each do |a|
|
216
|
+
if section_or_section_name.is_a?(String)
|
217
|
+
if a.name == section_or_section_name
|
218
|
+
found = true
|
219
|
+
break
|
220
|
+
end
|
221
|
+
else
|
222
|
+
if a == section_or_section_name
|
223
|
+
found = true
|
224
|
+
break
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
found
|
236
229
|
end
|
237
230
|
|
238
231
|
#Returns true if the block attached to each connector in the given container are published
|
@@ -256,12 +249,16 @@ class Page < ActiveRecord::Base
|
|
256
249
|
(a[1..a.size].map{|a| a.name} + [name]).join(" / ")
|
257
250
|
end
|
258
251
|
|
259
|
-
# This will return the "top level section" for
|
252
|
+
# This will return the "top level section" for this page, which is the section directly
|
260
253
|
# below the root (a.k.a My Site) that this page is in. If this page is in root,
|
261
254
|
# then this will return root.
|
255
|
+
#
|
256
|
+
# @return [Section] The first non-root ancestor if available, root otherwise.
|
262
257
|
def top_level_section
|
258
|
+
# Cache the results of this since many projects will call it repeatly on current_page in menus.
|
259
|
+
return @top_level_section if @top_level_section
|
263
260
|
a = ancestors
|
264
|
-
(a.size > 0 &&
|
261
|
+
@top_level_section = (a.size > 0 && a[1]) ? a[1] : Section.root.first
|
265
262
|
end
|
266
263
|
|
267
264
|
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
|
@@ -21,59 +22,83 @@ class Section < ActiveRecord::Base
|
|
21
22
|
named_scope :hidden, :conditions => {:hidden => true}
|
22
23
|
named_scope :not_hidden, :conditions => {:hidden => false}
|
23
24
|
|
24
|
-
named_scope :named, lambda{|name| {:conditions => ['sections.name = ?', name]}}
|
25
|
-
named_scope :with_path, lambda{|path| {:conditions => ['sections.path = ?', path]}}
|
25
|
+
named_scope :named, lambda { |name| {:conditions => ['sections.name = ?', name]} }
|
26
|
+
named_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
|
-
def
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
[s.node] << s.node.all_children_with_name
|
50
|
-
end
|
51
|
-
end.flatten.compact
|
43
|
+
def before_validation
|
44
|
+
unless node
|
45
|
+
self.node = build_section_node
|
46
|
+
end
|
52
47
|
end
|
53
48
|
|
54
|
-
|
55
|
-
|
49
|
+
# Returns a list of all children which are sections.
|
50
|
+
# @return [Array<Section>]
|
51
|
+
def sections
|
52
|
+
child_nodes.of_type("Section").fetch_nodes.in_order.collect do |section_node|
|
53
|
+
section_node.node
|
54
|
+
end
|
56
55
|
end
|
57
56
|
|
58
|
-
|
59
|
-
|
57
|
+
alias :child_sections :sections
|
58
|
+
|
59
|
+
# Since #sections isn't an association anymore, callers can use this rather than #sections.build
|
60
|
+
def build_section
|
61
|
+
Section.new(:parent=>self)
|
60
62
|
end
|
61
63
|
|
62
|
-
|
63
|
-
|
64
|
+
# Used by the sitemap to find children to iterate over.
|
65
|
+
def child_nodes
|
66
|
+
self.node.children
|
64
67
|
end
|
65
68
|
|
66
|
-
def
|
67
|
-
|
68
|
-
node.
|
69
|
-
else
|
70
|
-
build_node(:node => self, :section => sec)
|
69
|
+
def pages
|
70
|
+
child_pages = self.node.children.collect do |section_node|
|
71
|
+
section_node.node if section_node.page?
|
71
72
|
end
|
73
|
+
child_pages.compact
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.sitemap
|
77
|
+
SectionNode.of_type(["Page", "Link", "Section"]).fetch_nodes.arrange(:order=>:position)
|
72
78
|
end
|
73
79
|
|
74
|
-
def
|
75
|
-
|
76
|
-
|
80
|
+
def visible_child_nodes(options={})
|
81
|
+
children = child_nodes.of_type(["Section", "Page", "Link"]).fetch_nodes.in_order.all
|
82
|
+
visible_children = children.select { |sn| sn.visible? }
|
83
|
+
options[:limit] ? visible_children[0...options[:limit]] : visible_children
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
# Returns a complete list of all sections that are desecendants of this sections, in order, as a single flat list.
|
88
|
+
# Used by Section selectors where users have to pick a single section from a complete list of all sections.
|
89
|
+
def master_section_list
|
90
|
+
sections.map do |section|
|
91
|
+
section.full_path = root? ? section.name : "#{name} / #{section.name}"
|
92
|
+
[section] << section.master_section_list
|
93
|
+
end.flatten.compact
|
94
|
+
end
|
95
|
+
|
96
|
+
def parent_id
|
97
|
+
parent ? parent.id : nil
|
98
|
+
end
|
99
|
+
|
100
|
+
def parent_id=(sec_id)
|
101
|
+
self.parent = Section.find(sec_id)
|
77
102
|
end
|
78
103
|
|
79
104
|
def with_ancestors(options = {})
|
@@ -94,33 +119,46 @@ class Section < ActiveRecord::Base
|
|
94
119
|
end
|
95
120
|
|
96
121
|
def empty?
|
97
|
-
child_nodes.
|
122
|
+
child_nodes.empty?
|
98
123
|
end
|
99
124
|
|
125
|
+
# Callback to determine if this section can be deleted.
|
100
126
|
def deletable?
|
101
127
|
!root? && empty?
|
102
128
|
end
|
103
129
|
|
130
|
+
# Callback to clean up related nodes
|
131
|
+
def destroy_node
|
132
|
+
node.destroy
|
133
|
+
end
|
134
|
+
|
104
135
|
def editable_by_group?(group)
|
105
136
|
group.editable_by_section(self)
|
106
137
|
end
|
107
138
|
|
108
139
|
def status
|
109
|
-
public? ? :unlocked : :locked
|
140
|
+
@status ||= public? ? :unlocked : :locked
|
110
141
|
end
|
111
142
|
|
143
|
+
# Used by the file browser to look up a section by the combined names as a path.
|
144
|
+
# i.e. /A/B/
|
145
|
+
# @return [Section] nil if not found
|
112
146
|
def self.find_by_name_path(name_path)
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
147
|
+
current_section = Section.root.first
|
148
|
+
path_names = name_path.split("/")[1..-1] || []
|
149
|
+
|
150
|
+
# This implementation is very slow as it has to loop over the entire tree in memory to match each name element.
|
151
|
+
path_names.each do |name|
|
152
|
+
current_section.sections.each do |s|
|
153
|
+
current_section = s if s.name == name
|
154
|
+
end
|
117
155
|
end
|
118
|
-
|
156
|
+
current_section
|
119
157
|
end
|
120
158
|
|
121
159
|
#The first page that is a decendent of this section
|
122
160
|
def first_page_or_link
|
123
|
-
section_node = child_nodes.of_type(['Link', 'Page']).first
|
161
|
+
section_node = child_nodes.of_type(['Link', 'Page']).fetch_nodes.in_order.first
|
124
162
|
return section_node.node if section_node
|
125
163
|
sections.each do |s|
|
126
164
|
node = s.first_page_or_link
|