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
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
class Browsercms315 < ActiveRecord::Migration
|
|
2
|
+
def self.up
|
|
3
|
+
generate_ancestry_from_section_id
|
|
4
|
+
update_latest_version_cache
|
|
5
|
+
|
|
6
|
+
INDEXES.each do |index|
|
|
7
|
+
add_index *index
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.down
|
|
12
|
+
# This migration is not reversible since it removes the original section_id column.
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Add some very commonly used indexes to improve the site performance as the # of pages/content grows (i.e. several thousand pages)
|
|
16
|
+
INDEXES = [
|
|
17
|
+
[:pages, :deleted],
|
|
18
|
+
[:pages, :path],
|
|
19
|
+
[:pages, :version],
|
|
20
|
+
[:page_versions, :page_id],
|
|
21
|
+
[:groups, :code],
|
|
22
|
+
[:groups, :group_type_id],
|
|
23
|
+
[:group_types, :cms_access],
|
|
24
|
+
[:group_sections, :section_id],
|
|
25
|
+
[:group_sections, :group_id],
|
|
26
|
+
[:users, :expires_at],
|
|
27
|
+
[:user_group_memberships, :group_id],
|
|
28
|
+
[:user_group_memberships, :user_id],
|
|
29
|
+
[:group_permissions, :group_id],
|
|
30
|
+
[:group_permissions, :permission_id],
|
|
31
|
+
[:group_permissions, [:group_id, :permission_id]],
|
|
32
|
+
[:section_nodes, :node_type],
|
|
33
|
+
[:section_nodes, :ancestry],
|
|
34
|
+
[:connectors, :page_id],
|
|
35
|
+
[:connectors, :page_version],
|
|
36
|
+
[:html_blocks, :deleted],
|
|
37
|
+
[:html_block_versions, :html_block_id],
|
|
38
|
+
[:html_block_versions, :version],
|
|
39
|
+
[:portlet_attributes, :portlet_id],
|
|
40
|
+
[:portlets, :name],
|
|
41
|
+
[:sections, :path],
|
|
42
|
+
[:redirects, :from_path],
|
|
43
|
+
[:connectors, :connectable_version],
|
|
44
|
+
[:connectors, :connectable_type],
|
|
45
|
+
[:content_types, :content_type_group_id],
|
|
46
|
+
[:content_types, :name],
|
|
47
|
+
[:file_block_versions, :file_block_id],
|
|
48
|
+
[:file_block_versions, :version],
|
|
49
|
+
[:file_blocks, :deleted],
|
|
50
|
+
[:file_blocks, :type],
|
|
51
|
+
[:attachment_versions, :attachment_id],
|
|
52
|
+
[:tasks, :page_id],
|
|
53
|
+
[:tasks, :completed_at],
|
|
54
|
+
[:tasks, :assigned_to_id],
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
# v3.1.5 uses Ancestry to manage the parent child relationship between sections and their children.
|
|
60
|
+
# This converts the data from the old section_id to use the ancestry column.
|
|
61
|
+
def self.generate_ancestry_from_section_id
|
|
62
|
+
add_column :section_nodes, :ancestry, :string
|
|
63
|
+
add_column :section_nodes, :temp_parent_id, :integer
|
|
64
|
+
|
|
65
|
+
SectionNode.reset_column_information
|
|
66
|
+
root_section = Section.root.first
|
|
67
|
+
SectionNode.create!(:node => root_section) if root_section
|
|
68
|
+
|
|
69
|
+
all_nodes_but_root = SectionNode.find(:all, :conditions=>["section_id IS NOT NULL"])
|
|
70
|
+
all_nodes_but_root.each do |sn|
|
|
71
|
+
parent_node = SectionNode.find(:first, :conditions => ["node_id = ? and node_type = 'Section'", sn.section_id])
|
|
72
|
+
sn.temp_parent_id = parent_node.id
|
|
73
|
+
sn.save!
|
|
74
|
+
end
|
|
75
|
+
rename_column :section_nodes, :temp_parent_id, :parent_id # Ancestry works off the 'parent_id' column.
|
|
76
|
+
|
|
77
|
+
SectionNode.build_ancestry_from_parent_ids!
|
|
78
|
+
remove_column :section_nodes, :section_id
|
|
79
|
+
remove_column :section_nodes, :parent_id
|
|
80
|
+
SectionNode.reset_column_information
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Adds a 'latest_version' pointer to pages and links. Greatly reduces the number of queries the sitemap requires to determine if pages are in draft/published mode
|
|
84
|
+
def self.update_latest_version_cache
|
|
85
|
+
add_column :pages, :latest_version, :integer
|
|
86
|
+
add_column :links, :latest_version, :integer
|
|
87
|
+
Page.all.each do |p|
|
|
88
|
+
p.update_latest_version
|
|
89
|
+
end
|
|
90
|
+
Link.all.each do |link|
|
|
91
|
+
link.update_latest_version
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
# This is a new seed data file (new timestamp and name) so migrations will work in order.
|
|
2
|
+
# It should be able to peacefully coexist next to the previous load_seed_data
|
|
3
|
+
class LoadSeeds < ActiveRecord::Migration
|
|
2
4
|
extend Cms::DataLoader
|
|
3
5
|
def self.up
|
|
6
|
+
if User.count > 0
|
|
7
|
+
puts "Database has already been seeded. So skip reloading."
|
|
8
|
+
return
|
|
9
|
+
end
|
|
10
|
+
|
|
4
11
|
if %w[development test dev local].include?(Rails.env)
|
|
5
12
|
pwd = "cmsadmin"
|
|
6
13
|
else
|
data/lib/acts_as_list.rb
CHANGED
|
@@ -7,7 +7,7 @@ module ActsAsList
|
|
|
7
7
|
# The class that has this specified needs to have a +position+ column defined as an integer on
|
|
8
8
|
# the mapped database table.
|
|
9
9
|
#
|
|
10
|
-
#
|
|
10
|
+
# To Do list example:
|
|
11
11
|
#
|
|
12
12
|
# class TodoList < ActiveRecord::Base
|
|
13
13
|
# has_many :todo_items, :order => "position"
|
data/lib/browsercms.rb
CHANGED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Represents any object which exists in a Sitemap.
|
|
2
|
+
#
|
|
3
|
+
# Can have parents (using SectionNodes) and children.
|
|
4
|
+
module Addressable
|
|
5
|
+
|
|
6
|
+
# Returns a list of all Addressable objects that are ancestors to this record.
|
|
7
|
+
# @param [Hash] options
|
|
8
|
+
# @option [Symbol] :include_self If this object should be included in the Array
|
|
9
|
+
# @return [Array<Addressable]
|
|
10
|
+
#
|
|
11
|
+
def ancestors(options={})
|
|
12
|
+
ancestor_nodes = node.ancestors
|
|
13
|
+
ancestors = ancestor_nodes.collect { |node| node.node }
|
|
14
|
+
ancestors << self if options[:include_self]
|
|
15
|
+
ancestors
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def parent
|
|
19
|
+
@parent if @parent
|
|
20
|
+
node ? node.section : nil
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def cache_parent(section)
|
|
24
|
+
@parent = section
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def parent=(sec)
|
|
28
|
+
if node
|
|
29
|
+
node.move_to_end(sec)
|
|
30
|
+
else
|
|
31
|
+
build_section_node(:node => self, :section => sec)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Computes the name of the partial used to render this object in the sitemap.
|
|
36
|
+
def partial_for
|
|
37
|
+
self.class.name.underscore
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Pages/Links/Attachments use their parent to determine access
|
|
41
|
+
module LeafNode
|
|
42
|
+
def access_status
|
|
43
|
+
parent.status
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
module NodeAccessors
|
|
48
|
+
def node
|
|
49
|
+
section_node
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def node=(n)
|
|
53
|
+
self.section_node = n
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
# These exist for backwards compatibility to avoid having to change tests.
|
|
57
|
+
# I want to get rid of these in favor of parent and parent_id
|
|
58
|
+
module DeprecatedPageAccessors
|
|
59
|
+
include LeafNode
|
|
60
|
+
include NodeAccessors
|
|
61
|
+
|
|
62
|
+
def build_node(opts)
|
|
63
|
+
build_section_node(opts)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def section_id
|
|
67
|
+
section ? section.id : nil
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def section_id=(sec_id)
|
|
71
|
+
self.section = Section.find(sec_id)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def section
|
|
75
|
+
parent
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def section=(sec)
|
|
79
|
+
self.parent = sec
|
|
80
|
+
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -1,34 +1,36 @@
|
|
|
1
1
|
module Cms
|
|
2
2
|
module Behaviors
|
|
3
3
|
module Attaching
|
|
4
|
-
SANITIZATION_REGEXES = [
|
|
5
|
-
|
|
4
|
+
SANITIZATION_REGEXES = [[/\s/, '_'], [/[&+()]/, '-'], [/[=?!'"{}\[\]#<>%]/, '']]
|
|
5
|
+
|
|
6
6
|
def self.included(model_class)
|
|
7
7
|
model_class.extend(MacroMethods)
|
|
8
8
|
end
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
module MacroMethods
|
|
10
11
|
def belongs_to_attachment?
|
|
11
12
|
!!@belongs_to_attachment
|
|
12
13
|
end
|
|
14
|
+
|
|
13
15
|
def belongs_to_attachment(options={})
|
|
14
16
|
@belongs_to_attachment = true
|
|
15
17
|
include InstanceMethods
|
|
16
|
-
before_validation :process_attachment
|
|
18
|
+
before_validation :process_attachment
|
|
17
19
|
before_save :update_attachment_if_changed
|
|
18
20
|
after_save :clear_attachment_ivars
|
|
19
|
-
belongs_to :attachment, :dependent => :destroy
|
|
20
|
-
|
|
21
|
+
belongs_to :attachment, :dependent => :destroy
|
|
22
|
+
|
|
21
23
|
validates_each :attachment_file do |record, attr, value|
|
|
22
24
|
if record.attachment && !record.attachment.valid?
|
|
23
25
|
record.attachment.errors.each do |err_field, err_value|
|
|
24
26
|
if err_field.to_sym == :file_path
|
|
25
27
|
record.errors.add(:attachment_file_path, err_value)
|
|
26
|
-
else
|
|
28
|
+
else
|
|
27
29
|
record.errors.add(:attachment_file, err_value)
|
|
28
30
|
end
|
|
29
|
-
end
|
|
31
|
+
end
|
|
30
32
|
end
|
|
31
|
-
end
|
|
33
|
+
end
|
|
32
34
|
end
|
|
33
35
|
end
|
|
34
36
|
module InstanceMethods
|
|
@@ -92,20 +94,30 @@ module Cms
|
|
|
92
94
|
unless attachment_section_id.blank?
|
|
93
95
|
errors.add(:attachment_file, "You must upload a file")
|
|
94
96
|
return false
|
|
95
|
-
end
|
|
97
|
+
end
|
|
96
98
|
else
|
|
97
|
-
build_attachment if attachment.nil?
|
|
98
|
-
attachment.temp_file = attachment_file
|
|
99
|
-
|
|
99
|
+
build_attachment if attachment.nil?
|
|
100
|
+
attachment.temp_file = attachment_file
|
|
101
|
+
handle_setting_attachment_path
|
|
100
102
|
if attachment.file_path.blank?
|
|
101
103
|
errors.add(:attachment_file_path, "File Name is required for attachment")
|
|
102
104
|
return false
|
|
103
105
|
end
|
|
104
|
-
|
|
105
|
-
|
|
106
|
+
handle_setting_attachment_section
|
|
107
|
+
unless attachment.section
|
|
106
108
|
errors.add(:attachment_file, "Section is required for attachment")
|
|
107
109
|
return false
|
|
108
110
|
end
|
|
111
|
+
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Define at :set_attachment_path if you would like to override the way file_path is set
|
|
116
|
+
def handle_setting_attachment_path
|
|
117
|
+
if self.respond_to? :set_attachment_path
|
|
118
|
+
set_attachment_path
|
|
119
|
+
else
|
|
120
|
+
use_default_attachment_path
|
|
109
121
|
end
|
|
110
122
|
end
|
|
111
123
|
|
|
@@ -113,18 +125,26 @@ module Cms
|
|
|
113
125
|
@attachment_file = nil
|
|
114
126
|
@attachment_file_path = nil
|
|
115
127
|
@attachment_section_id = nil
|
|
116
|
-
@attachment_section = nil
|
|
128
|
+
@attachment_section = nil
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Implement a :set_attachment_section method if you would like to override the way the section is set
|
|
132
|
+
def handle_setting_attachment_section
|
|
133
|
+
if self.respond_to? :set_attachment_section
|
|
134
|
+
set_attachment_section
|
|
135
|
+
else
|
|
136
|
+
use_default_attachment_section
|
|
137
|
+
end
|
|
117
138
|
end
|
|
118
139
|
|
|
119
|
-
#
|
|
120
|
-
def
|
|
140
|
+
# Default behavior for assigning a section, if a block does not define its own.
|
|
141
|
+
def use_default_attachment_section
|
|
121
142
|
if !attachment_file.blank?
|
|
122
143
|
attachment.section = Section.root.first
|
|
123
144
|
end
|
|
124
145
|
end
|
|
125
146
|
|
|
126
|
-
|
|
127
|
-
def set_attachment_file_path
|
|
147
|
+
def use_default_attachment_path
|
|
128
148
|
if !attachment_file.blank?
|
|
129
149
|
attachment.file_path = "/attachments/#{File.basename(attachment_file.original_filename).to_s.downcase}"
|
|
130
150
|
end
|
|
@@ -168,16 +188,16 @@ module Cms
|
|
|
168
188
|
(published? && live_version?) ? attachment_file_path : "/cms/attachments/#{attachment_id}?version=#{attachment_version}"
|
|
169
189
|
else
|
|
170
190
|
nil
|
|
171
|
-
end
|
|
191
|
+
end
|
|
172
192
|
end
|
|
173
|
-
|
|
193
|
+
|
|
174
194
|
# Forces this record to be changed, even if nothing has changed
|
|
175
195
|
# This is necessary if just the section.id has changed, for example
|
|
176
196
|
def dirty!
|
|
177
197
|
# Seems like a hack, is there a better way?
|
|
178
198
|
self.updated_at = Time.now
|
|
179
|
-
end
|
|
180
|
-
|
|
199
|
+
end
|
|
200
|
+
|
|
181
201
|
end
|
|
182
202
|
end
|
|
183
203
|
end
|
|
@@ -106,15 +106,24 @@ module Cms
|
|
|
106
106
|
end
|
|
107
107
|
|
|
108
108
|
def status
|
|
109
|
-
|
|
110
|
-
|
|
109
|
+
return @status if @status
|
|
110
|
+
@status = live? ? :published : :draft
|
|
111
|
+
end
|
|
111
112
|
|
|
112
113
|
def status_name
|
|
113
114
|
status.to_s.titleize
|
|
114
115
|
end
|
|
115
116
|
|
|
116
117
|
def live?
|
|
117
|
-
self.class.versioned?
|
|
118
|
+
if self.class.versioned?
|
|
119
|
+
if (respond_to?(:latest_version) && self.latest_version)
|
|
120
|
+
version == latest_version && published?
|
|
121
|
+
else
|
|
122
|
+
live_version.version == draft.version && published?
|
|
123
|
+
end
|
|
124
|
+
else
|
|
125
|
+
true
|
|
126
|
+
end
|
|
118
127
|
end
|
|
119
128
|
|
|
120
129
|
end
|
|
@@ -4,10 +4,12 @@ module Cms
|
|
|
4
4
|
def self.included(model_class)
|
|
5
5
|
model_class.extend(MacroMethods)
|
|
6
6
|
end
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
module MacroMethods
|
|
8
9
|
def versioned?
|
|
9
10
|
!!@is_versioned
|
|
10
11
|
end
|
|
12
|
+
|
|
11
13
|
def is_versioned(options={})
|
|
12
14
|
@is_versioned = true
|
|
13
15
|
|
|
@@ -17,25 +19,29 @@ module Cms
|
|
|
17
19
|
extend ClassMethods
|
|
18
20
|
include InstanceMethods
|
|
19
21
|
|
|
20
|
-
has_many :versions, :class_name
|
|
22
|
+
has_many :versions, :class_name => version_class_name, :foreign_key => version_foreign_key
|
|
21
23
|
|
|
22
24
|
before_validation_on_create :initialize_version
|
|
23
25
|
|
|
24
26
|
attr_accessor :revert_to_version
|
|
25
27
|
|
|
26
28
|
#Define the version class
|
|
27
|
-
const_set("Version", Class.new(ActiveRecord::Base)).class_eval do
|
|
28
|
-
class << self;
|
|
29
|
+
const_set("Version", Class.new(ActiveRecord::Base)).class_eval do
|
|
30
|
+
class << self;
|
|
31
|
+
attr_accessor :versioned_class
|
|
32
|
+
end
|
|
29
33
|
|
|
30
34
|
def versioned_class
|
|
31
35
|
self.class.versioned_class
|
|
32
36
|
end
|
|
37
|
+
|
|
33
38
|
def versioned_object_id
|
|
34
39
|
send("#{versioned_class.name.underscore}_id")
|
|
35
40
|
end
|
|
41
|
+
|
|
36
42
|
def versioned_object
|
|
37
43
|
send(versioned_class.name.underscore.to_sym)
|
|
38
|
-
end
|
|
44
|
+
end
|
|
39
45
|
end unless self.const_defined?("Version")
|
|
40
46
|
|
|
41
47
|
version_class.versioned_class = self
|
|
@@ -46,33 +52,47 @@ module Cms
|
|
|
46
52
|
|
|
47
53
|
end
|
|
48
54
|
end
|
|
49
|
-
module ClassMethods
|
|
55
|
+
module ClassMethods
|
|
50
56
|
def version_class
|
|
51
57
|
const_get "Version"
|
|
52
58
|
end
|
|
53
59
|
|
|
54
60
|
def version_class_name
|
|
55
61
|
"#{name}::Version"
|
|
56
|
-
end
|
|
62
|
+
end
|
|
57
63
|
|
|
58
64
|
def version_foreign_key
|
|
59
65
|
@version_foreign_key
|
|
60
66
|
end
|
|
61
67
|
|
|
62
|
-
def version_table_name
|
|
68
|
+
def version_table_name
|
|
63
69
|
@version_table_name
|
|
64
70
|
end
|
|
65
71
|
|
|
66
72
|
def versioned_columns
|
|
67
|
-
@versioned_columns ||= (version_class.new.attributes.keys -
|
|
68
|
-
|
|
69
|
-
end
|
|
73
|
+
@versioned_columns ||= (version_class.new.attributes.keys -
|
|
74
|
+
(%w[id lock_version position version_comment created_at updated_at created_by_id updated_by_id type] + [version_foreign_key]))
|
|
75
|
+
end
|
|
70
76
|
end
|
|
71
77
|
module InstanceMethods
|
|
72
78
|
def initialize_version
|
|
73
79
|
self.version = 1
|
|
74
80
|
end
|
|
75
81
|
|
|
82
|
+
def after_save
|
|
83
|
+
update_latest_version
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Used in migrations and as a callback.
|
|
87
|
+
def update_latest_version
|
|
88
|
+
#Rails 3 could use update_column here instead
|
|
89
|
+
if respond_to? :latest_version
|
|
90
|
+
sql = "UPDATE #{self.class.table_name} SET latest_version = #{draft.version} where id = #{self.id}"
|
|
91
|
+
connection.execute sql
|
|
92
|
+
self.latest_version = draft.version # So we don't need to #reload this object. Probably marks it as dirty though, which could have weird side effects.
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
76
96
|
def build_new_version
|
|
77
97
|
# First get the values from the draft
|
|
78
98
|
attrs = draft_attributes
|
|
@@ -83,7 +103,7 @@ module Cms
|
|
|
83
103
|
end
|
|
84
104
|
|
|
85
105
|
attrs[:version_comment] = @version_comment || default_version_comment
|
|
86
|
-
@version_comment = nil
|
|
106
|
+
@version_comment = nil
|
|
87
107
|
new_version = versions.build(attrs)
|
|
88
108
|
new_version.version = new_record? ? 1 : (draft.version.to_i + 1)
|
|
89
109
|
after_build_new_version(new_version) if respond_to?(:after_build_new_version)
|
|
@@ -94,8 +114,8 @@ module Cms
|
|
|
94
114
|
# When there is no draft, we'll just copy the attibutes from this object
|
|
95
115
|
# Otherwise we need to use the draft
|
|
96
116
|
d = new_record? ? self : draft
|
|
97
|
-
self.class.versioned_columns.inject({}){|attrs, col| attrs[col] = d.send(col); attrs }
|
|
98
|
-
end
|
|
117
|
+
self.class.versioned_columns.inject({}) { |attrs, col| attrs[col] = d.send(col); attrs }
|
|
118
|
+
end
|
|
99
119
|
|
|
100
120
|
def default_version_comment
|
|
101
121
|
if new_record?
|
|
@@ -108,22 +128,22 @@ module Cms
|
|
|
108
128
|
def save(perform_validations=true)
|
|
109
129
|
transaction do
|
|
110
130
|
#logger.info "..... Calling valid?"
|
|
111
|
-
return false unless !perform_validations || valid?
|
|
112
|
-
|
|
131
|
+
return false unless !perform_validations || valid?
|
|
132
|
+
|
|
113
133
|
if different_from_last_draft?
|
|
114
134
|
#logger.info "..... Changes => #{changes.inspect}"
|
|
115
135
|
else
|
|
116
136
|
#logger.info "..... No Changes"
|
|
117
137
|
return true
|
|
118
138
|
end
|
|
119
|
-
|
|
139
|
+
|
|
120
140
|
#logger.info "..... Calling before_save"
|
|
121
141
|
return false if callback(:before_save) == false
|
|
122
142
|
|
|
123
143
|
if new_record?
|
|
124
144
|
#logger.info "..... Calling before_create"
|
|
125
145
|
return false if callback(:before_create) == false
|
|
126
|
-
else
|
|
146
|
+
else
|
|
127
147
|
#logger.info "..... Calling before_update"
|
|
128
148
|
return false if callback(:before_update) == false
|
|
129
149
|
end
|
|
@@ -140,12 +160,12 @@ module Cms
|
|
|
140
160
|
#logger.info "..... Calling after_save"
|
|
141
161
|
callback(:after_save)
|
|
142
162
|
end
|
|
143
|
-
|
|
163
|
+
|
|
144
164
|
if @publish_on_save
|
|
145
165
|
publish
|
|
146
166
|
@publish_on_save = nil
|
|
147
|
-
end
|
|
148
|
-
changed_attributes.clear
|
|
167
|
+
end
|
|
168
|
+
changed_attributes.clear
|
|
149
169
|
end
|
|
150
170
|
result
|
|
151
171
|
elsif new_version
|
|
@@ -156,11 +176,11 @@ module Cms
|
|
|
156
176
|
#logger.info "..... Calling after_update"
|
|
157
177
|
callback(:after_save)
|
|
158
178
|
end
|
|
159
|
-
|
|
179
|
+
|
|
160
180
|
if @publish_on_save
|
|
161
181
|
publish
|
|
162
182
|
@publish_on_save = nil
|
|
163
|
-
end
|
|
183
|
+
end
|
|
164
184
|
changed_attributes.clear
|
|
165
185
|
end
|
|
166
186
|
result
|
|
@@ -174,13 +194,13 @@ module Cms
|
|
|
174
194
|
end
|
|
175
195
|
|
|
176
196
|
def draft
|
|
177
|
-
versions.first(:order => "version desc")
|
|
197
|
+
versions.first(:order => "version desc")
|
|
178
198
|
end
|
|
179
|
-
|
|
199
|
+
|
|
180
200
|
def draft_version?
|
|
181
201
|
version == draft.version
|
|
182
202
|
end
|
|
183
|
-
|
|
203
|
+
|
|
184
204
|
def live_version
|
|
185
205
|
find_version(self.class.find(id).version)
|
|
186
206
|
end
|
|
@@ -192,38 +212,19 @@ module Cms
|
|
|
192
212
|
def current_version
|
|
193
213
|
find_version(self.version)
|
|
194
214
|
end
|
|
195
|
-
|
|
215
|
+
|
|
196
216
|
def find_version(number)
|
|
197
|
-
versions.first(:conditions => {
|
|
217
|
+
versions.first(:conditions => {:version => number})
|
|
198
218
|
end
|
|
199
219
|
|
|
200
220
|
def as_of_draft_version
|
|
201
|
-
|
|
221
|
+
build_object_from_version(draft)
|
|
202
222
|
end
|
|
203
223
|
|
|
204
224
|
def as_of_version(version)
|
|
205
225
|
v = find_version(version)
|
|
206
226
|
raise ActiveRecord::RecordNotFound.new("version #{version.inspect} does not exist for <#{self.class}:#{id}>") unless v
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
(self.class.versioned_columns + [:version, :created_at, :created_by_id, :updated_at, :updated_by_id]).each do |a|
|
|
210
|
-
obj.send("#{a}=", v.send(a))
|
|
211
|
-
end
|
|
212
|
-
obj.id = id
|
|
213
|
-
obj.lock_version = lock_version
|
|
214
|
-
|
|
215
|
-
# Need to do this so associations can be loaded
|
|
216
|
-
obj.instance_variable_set("@new_record", false)
|
|
217
|
-
|
|
218
|
-
# Callback to allow us to load other data when an older version is loaded
|
|
219
|
-
obj.after_as_of_version if obj.respond_to?(:after_as_of_version)
|
|
220
|
-
|
|
221
|
-
# Last but not least, clear the changed attributes
|
|
222
|
-
if changed_attrs = obj.send(:changed_attributes)
|
|
223
|
-
changed_attrs.clear
|
|
224
|
-
end
|
|
225
|
-
|
|
226
|
-
obj
|
|
227
|
+
build_object_from_version(v)
|
|
227
228
|
end
|
|
228
229
|
|
|
229
230
|
def revert
|
|
@@ -237,15 +238,15 @@ module Cms
|
|
|
237
238
|
raise "Could not find version #{version}" unless revert_to_version
|
|
238
239
|
(self.class.versioned_columns - ["version"]).each do |a|
|
|
239
240
|
send("#{a}=", revert_to_version.send(a))
|
|
240
|
-
end
|
|
241
|
+
end
|
|
241
242
|
self.version_comment = "Reverted to version #{version}"
|
|
242
|
-
self
|
|
243
|
-
end
|
|
243
|
+
self
|
|
244
|
+
end
|
|
244
245
|
|
|
245
246
|
def revert_to(version)
|
|
246
247
|
revert_to_without_save(version)
|
|
247
248
|
save
|
|
248
|
-
end
|
|
249
|
+
end
|
|
249
250
|
|
|
250
251
|
def version_comment
|
|
251
252
|
@version_comment
|
|
@@ -266,6 +267,35 @@ module Cms
|
|
|
266
267
|
return false
|
|
267
268
|
end
|
|
268
269
|
|
|
270
|
+
private
|
|
271
|
+
|
|
272
|
+
# Given a ::Version object of a given type, create an original object from its attributes.
|
|
273
|
+
#
|
|
274
|
+
# @param [Class#name::Version] version (i.e. HtmlBlock::Version)
|
|
275
|
+
# @return [Class#name] i.e. HtmlBlock
|
|
276
|
+
def build_object_from_version(version_of_object)
|
|
277
|
+
obj = self.class.new
|
|
278
|
+
|
|
279
|
+
(self.class.versioned_columns + [:version, :created_at, :created_by_id, :updated_at, :updated_by_id]).each do |a|
|
|
280
|
+
obj.send("#{a}=", version_of_object.send(a))
|
|
281
|
+
end
|
|
282
|
+
obj.id = id
|
|
283
|
+
obj.lock_version = lock_version
|
|
284
|
+
|
|
285
|
+
# Need to do this so associations can be loaded
|
|
286
|
+
obj.instance_variable_set("@new_record", false)
|
|
287
|
+
|
|
288
|
+
# Callback to allow us to load other data when an older version is loaded
|
|
289
|
+
obj.after_as_of_version if obj.respond_to?(:after_as_of_version)
|
|
290
|
+
|
|
291
|
+
# Last but not least, clear the changed attributes
|
|
292
|
+
if changed_attrs = obj.send(:changed_attributes)
|
|
293
|
+
changed_attrs.clear
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
obj
|
|
297
|
+
end
|
|
298
|
+
|
|
269
299
|
end
|
|
270
300
|
end
|
|
271
301
|
|