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
data/lib/browsercms.rb
CHANGED
@@ -2,6 +2,8 @@ require 'cms/engine'
|
|
2
2
|
require 'cms/extensions'
|
3
3
|
require 'cms/routes'
|
4
4
|
require 'cms/caching'
|
5
|
+
require 'cms/addressable'
|
6
|
+
require 'cms/error_pages'
|
5
7
|
|
6
8
|
#Load libraries that are included with CMS
|
7
9
|
require 'acts_as_list'
|
@@ -15,4 +17,4 @@ require 'cms/date_picker'
|
|
15
17
|
require 'cms/content_rendering_support'
|
16
18
|
|
17
19
|
# This shouldn't be necessary, except for the need to get into the loadpath for testing.
|
18
|
-
require 'command_line'
|
20
|
+
require 'command_line'
|
@@ -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
|
@@ -93,22 +95,30 @@ module Cms
|
|
93
95
|
unless attachment_section_id.blank?
|
94
96
|
errors.add(:attachment_file, "You must upload a file")
|
95
97
|
return false
|
96
|
-
end
|
98
|
+
end
|
97
99
|
else
|
98
100
|
build_attachment if attachment.nil?
|
99
101
|
attachment.temp_file = attachment_file
|
100
|
-
|
101
|
-
|
102
|
-
set_attachment_file_path
|
102
|
+
handle_setting_attachment_path
|
103
103
|
if attachment.file_path.blank?
|
104
104
|
errors.add(:attachment_file_path, "File Name is required for attachment")
|
105
105
|
return false
|
106
106
|
end
|
107
|
-
|
108
|
-
|
107
|
+
handle_setting_attachment_section
|
108
|
+
unless attachment.section
|
109
109
|
errors.add(:attachment_file, "Section is required for attachment")
|
110
110
|
return false
|
111
111
|
end
|
112
|
+
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Define at :set_attachment_path if you would like to override the way file_path is set
|
117
|
+
def handle_setting_attachment_path
|
118
|
+
if self.respond_to? :set_attachment_path
|
119
|
+
set_attachment_path
|
120
|
+
else
|
121
|
+
use_default_attachment_path
|
112
122
|
end
|
113
123
|
end
|
114
124
|
|
@@ -116,18 +126,26 @@ module Cms
|
|
116
126
|
@attachment_file = nil
|
117
127
|
@attachment_file_path = nil
|
118
128
|
@attachment_section_id = nil
|
119
|
-
@attachment_section = nil
|
129
|
+
@attachment_section = nil
|
120
130
|
end
|
121
131
|
|
122
|
-
#
|
123
|
-
def
|
132
|
+
# Implement a :set_attachment_section method if you would like to override the way the section is set
|
133
|
+
def handle_setting_attachment_section
|
134
|
+
if self.respond_to? :set_attachment_section
|
135
|
+
set_attachment_section
|
136
|
+
else
|
137
|
+
use_default_attachment_section
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Default behavior for assigning a section, if a block does not define its own.
|
142
|
+
def use_default_attachment_section
|
124
143
|
if !attachment_file.blank?
|
125
144
|
attachment.section = Section.root.first
|
126
145
|
end
|
127
146
|
end
|
128
147
|
|
129
|
-
|
130
|
-
def set_attachment_file_path
|
148
|
+
def use_default_attachment_path
|
131
149
|
if !attachment_file.blank?
|
132
150
|
attachment.file_path = "/attachments/#{File.basename(attachment_file.original_filename).to_s.downcase}"
|
133
151
|
end
|
@@ -173,16 +191,16 @@ module Cms
|
|
173
191
|
(published? && live_version?) ? attachment_file_path : "/cms/attachments/#{attachment_id}?version=#{attachment_version}"
|
174
192
|
else
|
175
193
|
nil
|
176
|
-
end
|
194
|
+
end
|
177
195
|
end
|
178
|
-
|
196
|
+
|
179
197
|
# Forces this record to be changed, even if nothing has changed
|
180
198
|
# This is necessary if just the section.id has changed, for example
|
181
199
|
def dirty!
|
182
200
|
# Seems like a hack, is there a better way?
|
183
201
|
self.updated_at = Time.now
|
184
|
-
end
|
185
|
-
|
202
|
+
end
|
203
|
+
|
186
204
|
end
|
187
205
|
end
|
188
206
|
end
|
@@ -125,15 +125,24 @@ module Cms
|
|
125
125
|
end
|
126
126
|
|
127
127
|
def status
|
128
|
-
|
129
|
-
|
128
|
+
return @status if @status
|
129
|
+
@status = live? ? :published : :draft
|
130
|
+
end
|
130
131
|
|
131
132
|
def status_name
|
132
133
|
status.to_s.titleize
|
133
134
|
end
|
134
135
|
|
135
136
|
def live?
|
136
|
-
self.class.versioned?
|
137
|
+
if self.class.versioned?
|
138
|
+
if (respond_to?(:latest_version) && self.latest_version)
|
139
|
+
version == latest_version && published?
|
140
|
+
else
|
141
|
+
live_version.version == draft.version && published?
|
142
|
+
end
|
143
|
+
else
|
144
|
+
true
|
145
|
+
end
|
137
146
|
end
|
138
147
|
|
139
148
|
end
|
@@ -39,7 +39,8 @@ module Cms
|
|
39
39
|
extend ClassMethods
|
40
40
|
include InstanceMethods
|
41
41
|
|
42
|
-
has_many :versions, :class_name
|
42
|
+
has_many :versions, :class_name => version_class_name, :foreign_key => version_foreign_key
|
43
|
+
after_save :update_latest_version
|
43
44
|
|
44
45
|
before_validation :initialize_version
|
45
46
|
before_save :build_new_version
|
@@ -93,7 +94,7 @@ module Cms
|
|
93
94
|
|
94
95
|
def versioned_columns
|
95
96
|
@versioned_columns ||= (version_class.new.attributes.keys -
|
96
|
-
|
97
|
+
(%w[id lock_version position version_comment created_at updated_at created_by_id updated_by_id type] + [version_foreign_key]))
|
97
98
|
end
|
98
99
|
end
|
99
100
|
module InstanceMethods
|
@@ -101,6 +102,15 @@ module Cms
|
|
101
102
|
self.version = 1 if new_record?
|
102
103
|
end
|
103
104
|
|
105
|
+
# Used in migrations and as a callback.
|
106
|
+
def update_latest_version
|
107
|
+
#Rails 3 could use update_column here instead
|
108
|
+
if respond_to? :latest_version
|
109
|
+
sql = "UPDATE #{self.class.table_name} SET latest_version = #{draft.version} where id = #{self.id}"
|
110
|
+
connection.execute sql
|
111
|
+
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.
|
112
|
+
end
|
113
|
+
end
|
104
114
|
|
105
115
|
def build_new_version_and_add_to_versions_list_for_saving
|
106
116
|
# First get the values from the draft
|
@@ -304,7 +314,7 @@ module Cms
|
|
304
314
|
end
|
305
315
|
|
306
316
|
def as_of_draft_version
|
307
|
-
|
317
|
+
build_object_from_version(draft)
|
308
318
|
end
|
309
319
|
|
310
320
|
# Find a Content Block as of a specific version.
|
@@ -314,27 +324,7 @@ module Cms
|
|
314
324
|
def as_of_version(version)
|
315
325
|
v = find_version(version)
|
316
326
|
raise ActiveRecord::RecordNotFound.new("version #{version.inspect} does not exist for <#{self.class}:#{id}>") unless v
|
317
|
-
|
318
|
-
|
319
|
-
(self.class.versioned_columns + [:version, :created_at, :created_by_id, :updated_at, :updated_by_id]).each do |a|
|
320
|
-
obj.send("#{a}=", v.send(a))
|
321
|
-
end
|
322
|
-
obj.id = id
|
323
|
-
obj.lock_version = lock_version
|
324
|
-
|
325
|
-
# Need to do this so associations can be loaded
|
326
|
-
obj.instance_variable_set("@persisted", true)
|
327
|
-
obj.instance_variable_set("@new_record", false)
|
328
|
-
|
329
|
-
# Callback to allow us to load other data when an older version is loaded
|
330
|
-
obj.after_as_of_version if obj.respond_to?(:after_as_of_version)
|
331
|
-
|
332
|
-
# Last but not least, clear the changed attributes
|
333
|
-
if changed_attrs = obj.send(:changed_attributes)
|
334
|
-
changed_attrs.clear
|
335
|
-
end
|
336
|
-
|
337
|
-
obj
|
327
|
+
build_object_from_version(v)
|
338
328
|
end
|
339
329
|
|
340
330
|
def revert
|
@@ -377,6 +367,35 @@ module Cms
|
|
377
367
|
return false
|
378
368
|
end
|
379
369
|
|
370
|
+
private
|
371
|
+
|
372
|
+
# Given a ::Version object of a given type, create an original object from its attributes.
|
373
|
+
#
|
374
|
+
# @param [Class#name::Version] version_of_object (i.e. HtmlBlock::Version)
|
375
|
+
# @return [Class#name] i.e. HtmlBlock
|
376
|
+
def build_object_from_version(version_of_object)
|
377
|
+
obj = self.class.new
|
378
|
+
|
379
|
+
(self.class.versioned_columns + [:version, :created_at, :created_by_id, :updated_at, :updated_by_id]).each do |a|
|
380
|
+
obj.send("#{a}=", version_of_object.send(a))
|
381
|
+
end
|
382
|
+
obj.id = id
|
383
|
+
obj.lock_version = lock_version
|
384
|
+
|
385
|
+
# Need to do this so associations can be loaded
|
386
|
+
obj.instance_variable_set("@persisted", true)
|
387
|
+
obj.instance_variable_set("@new_record", false)
|
388
|
+
|
389
|
+
# Callback to allow us to load other data when an older version is loaded
|
390
|
+
obj.after_as_of_version if obj.respond_to?(:after_as_of_version)
|
391
|
+
|
392
|
+
# Last but not least, clear the changed attributes
|
393
|
+
if changed_attrs = obj.send(:changed_attributes)
|
394
|
+
changed_attrs.clear
|
395
|
+
end
|
396
|
+
|
397
|
+
obj
|
398
|
+
end
|
380
399
|
end
|
381
400
|
end
|
382
401
|
|
@@ -14,18 +14,18 @@ module Cms
|
|
14
14
|
|
15
15
|
def handle_not_found_on_page(exception)
|
16
16
|
logger.warn "Page Not Found"
|
17
|
-
handle_error_with_cms_page(
|
17
|
+
handle_error_with_cms_page(Cms::ErrorPages::NOT_FOUND_PATH, exception, :not_found)
|
18
18
|
end
|
19
19
|
|
20
20
|
def handle_access_denied_on_page(exception)
|
21
21
|
logger.warn "Access Denied"
|
22
|
-
handle_error_with_cms_page(
|
22
|
+
handle_error_with_cms_page(Cms::ErrorPages::FORBIDDEN_PATH, exception, :forbidden)
|
23
23
|
end
|
24
24
|
|
25
25
|
def handle_server_error_on_page(exception)
|
26
26
|
logger.warn "Exception: #{exception.message}\n"
|
27
27
|
logger.warn "#{exception.backtrace.join("\n")}\n"
|
28
|
-
handle_error_with_cms_page(
|
28
|
+
handle_error_with_cms_page(Cms::ErrorPages::SERVER_ERROR_PATH, exception, :internal_server_error)
|
29
29
|
end
|
30
30
|
|
31
31
|
private
|
data/lib/cms/version.rb
CHANGED
@@ -22,6 +22,7 @@ module BrowserCms
|
|
22
22
|
# Migrations/seed data
|
23
23
|
'db/migrate/20080815014337_browsercms_3_0_0.rb',
|
24
24
|
'db/migrate/20091109175123_browsercms_3_0_5.rb',
|
25
|
+
'db/migrate/20100117144039_browsercms315.rb',
|
25
26
|
'db/migrate/20100705083859_browsercms_3_3_0.rb',
|
26
27
|
'db/seeds.rb'
|
27
28
|
]
|
@@ -671,4 +671,4 @@ into proprietary programs. If your program is a subroutine library, you
|
|
671
671
|
may consider it more useful to permit linking proprietary applications with
|
672
672
|
the library. If this is what you want to do, use the GNU Lesser General
|
673
673
|
Public License instead of this License. But first, please read
|
674
|
-
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
674
|
+
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
data/lib/tasks/data.rake
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
def create_pages(root)
|
2
|
+
(1..20).each do |i|
|
3
|
+
Page.create!(:name=>"Page #{i}", :path=>"#{root.path}/page-#{i}", :section=>root, :template_file_name=>"default.html.erb", :publish_on_save=>true)
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
def reset_root_user_password
|
8
|
+
u = User.find(1)
|
9
|
+
u.password= "cmsadmin"
|
10
|
+
u.password_confirmation = "cmsadmin"
|
11
|
+
u.save!
|
12
|
+
|
13
|
+
puts "Reset #{u.login}'s password to '#{u.password}'."
|
14
|
+
end
|
15
|
+
|
16
|
+
namespace :cms do
|
17
|
+
|
18
|
+
task "load" do
|
19
|
+
sh "mysql -u root browsercms_development --password= < db/backups/backup.sql"
|
20
|
+
end
|
21
|
+
|
22
|
+
task "correct" => :environment do
|
23
|
+
ActiveRecord::Base.connection.execute("UPDATE portlets SET type = 'DynamicPortlet' where type != 'DynamicPortlet'")
|
24
|
+
ct = ["'CategoryType'", "'Category'", "'HtmlBlock'", "'Portlet'", "'FileBlock'", "'ImageBlock'", "'Tag'"].join(",")
|
25
|
+
ActiveRecord::Base.connection.execute("DELETE FROM content_types where name not in (#{ct})")
|
26
|
+
reset_root_user_password
|
27
|
+
end
|
28
|
+
|
29
|
+
desc "Load a CMS site backup (a .sql file must be called db/backups/backup.sql) for testing."
|
30
|
+
task "load:backup" => ['db:drop','db:create', 'cms:load', 'cms:correct', 'db:migrate']
|
31
|
+
|
32
|
+
desc "Load some sample pages for performance tuning"
|
33
|
+
task "load:pages" => :environment do
|
34
|
+
root = Section.root.first
|
35
|
+
create_pages(root)
|
36
|
+
(21..40).each do |i|
|
37
|
+
sec = Section.create! :name=>"Section #{i}", :path=>"/section-#{i}/", :parent=>root
|
38
|
+
create_pages(sec)
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|