browsercms 3.1.4 → 3.1.5
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/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
|