bcms_ancestry 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,193 @@
1
+ require 'digest/sha1'
2
+ require 'fileutils'
3
+
4
+ class LegacyBcms::Attachment < ActiveRecord::Base
5
+
6
+ #----- Macros ----------------------------------------------------------------
7
+
8
+ is_archivable
9
+ is_publishable
10
+ uses_soft_delete
11
+ is_userstamped
12
+ is_versioned
13
+ attr_accessor :temp_file
14
+
15
+ #----- Callbacks -------------------------------------------------------------
16
+
17
+ before_validation :make_dirty_if_temp_file
18
+ before_validation :prepend_file_path_with_slash
19
+ before_validation :extract_file_extension_from_file_name
20
+ before_validation :extract_file_type_from_temp_file
21
+ before_validation :extract_file_size_from_temp_file
22
+ before_validation :set_file_location
23
+ before_save :process_section
24
+
25
+ after_save :write_temp_file_to_storage_location
26
+ after_save :clear_ivars
27
+
28
+ #----- Associations ----------------------------------------------------------
29
+
30
+ has_one :section_node, :as => :node
31
+
32
+ #----- Validations -----------------------------------------------------------
33
+
34
+ validates_presence_of :temp_file,
35
+ :message => "You must upload a file", :on => :create
36
+ validates_presence_of :file_path
37
+ 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
+
57
+ def section=(section)
58
+ if @section != section
59
+ dirty!
60
+ @section_id = section ? section.id : nil
61
+ @section = section
62
+ end
63
+ end
64
+
65
+ #----- Callbacks Methods -----------------------------------------------------
66
+
67
+ def make_dirty_if_temp_file
68
+ dirty! if temp_file
69
+ end
70
+
71
+ def prepend_file_path_with_slash
72
+ unless file_path.blank?
73
+ self.file_path = "/#{file_path}" unless file_path =~ /^\//
74
+ end
75
+ end
76
+
77
+ def extract_file_extension_from_file_name
78
+ if file_name && file_name['.']
79
+ self.file_extension = file_name.split('.').last.to_s.downcase
80
+ end
81
+ end
82
+
83
+ def extract_file_type_from_temp_file
84
+ unless temp_file.blank?
85
+ self.file_type = temp_file.content_type
86
+ end
87
+ end
88
+
89
+ def extract_file_size_from_temp_file
90
+ unless temp_file.blank?
91
+ self.file_size = temp_file.size
92
+ end
93
+ end
94
+
95
+ # The file will be stored on disk at
96
+ # Attachment.storage_location/year/month/day/sha1
97
+ # The sha1 is a 40 character hash based on the original_filename
98
+ # of the file uploaded and the current time
99
+ def set_file_location
100
+ unless temp_file.blank?
101
+ sha1 = Digest::SHA1.hexdigest("#{temp_file.original_filename}#{Time.now.to_f}")
102
+ self.file_location = "#{Time.now.strftime("%Y/%m/%d")}/#{sha1}"
103
+ end
104
+ end
105
+
106
+ def process_section
107
+ #logger.info "processing section, section_id => #{section_id}, section_node => #{section_node.inspect}"
108
+ if section_node && !section_node.new_record? && section_node.section_id != section_id
109
+ section_node.move_to_end(Section.find(section_id))
110
+ else
111
+ build_section_node(:node => self, :section_id => section_id)
112
+ end
113
+ end
114
+
115
+ def write_temp_file_to_storage_location
116
+ unless temp_file.blank?
117
+ FileUtils.mkdir_p File.dirname(full_file_location)
118
+ if temp_file.local_path
119
+ FileUtils.copy temp_file.local_path, full_file_location
120
+ else
121
+ open(full_file_location, 'w') {|f| f << temp_file.read }
122
+ end
123
+
124
+ if Cms.attachment_file_permission
125
+ FileUtils.chmod Cms.attachment_file_permission, full_file_location
126
+ end
127
+ end
128
+ end
129
+
130
+ def clear_ivars
131
+ @temp_file = nil
132
+ @section = nil
133
+ @section_id = nil
134
+ end
135
+
136
+ #----- Class Methods ---------------------------------------------------------
137
+
138
+ def self.storage_location
139
+ @storage_location ||= File.join(Rails.root, "/tmp/uploads")
140
+ end
141
+
142
+ def self.storage_location=(storage_location)
143
+ @storage_location = storage_location
144
+ end
145
+
146
+ def self.find_live_by_file_path(file_path)
147
+ Attachment.published.not_archived.first(:conditions => {:file_path => file_path})
148
+ end
149
+
150
+ #----- Instance Methods ------------------------------------------------------
151
+
152
+ def file_name
153
+ file_path ? file_path.split('/').last : nil
154
+ end
155
+
156
+ def name
157
+ file_name
158
+ end
159
+
160
+ def icon
161
+ {
162
+ :doc => %w[doc],
163
+ :gif => %w[gif jpg jpeg png tiff bmp],
164
+ :htm => %w[htm html],
165
+ :pdf => %w[pdf],
166
+ :ppt => %w[ppt],
167
+ :swf => %w[swf],
168
+ :txt => %w[txt],
169
+ :xls => %w[xls],
170
+ :xml => %w[xml],
171
+ :zip => %w[zip rar tar gz tgz]
172
+ }.each do |icon, extensions|
173
+ return icon if extensions.include?(file_extension.to_s)
174
+ end
175
+ :file
176
+ end
177
+
178
+ def public?
179
+ section ? section.public? : false
180
+ end
181
+
182
+ def full_file_location
183
+ File.join(Attachment.storage_location, file_location)
184
+ end
185
+
186
+ # Forces this record to be changed, even if nothing has changed
187
+ # This is necessary if just the section.id has changed, for example
188
+ def dirty!
189
+ # Seems like a hack, is there a better way?
190
+ self.updated_at = Time.now
191
+ end
192
+
193
+ end
@@ -0,0 +1,35 @@
1
+ class LegacyBcms::Link < ActiveRecord::Base
2
+ acts_as_content_block :connectable => false
3
+
4
+ named_scope :named, lambda{|name| {:conditions => ['links.name = ?', name]}}
5
+
6
+ has_one :section_node, :as => :node, :dependent => :destroy
7
+
8
+ validates_presence_of :name
9
+
10
+ def section_id
11
+ section ? section.id : nil
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
29
+
30
+ #needed by menu_helper
31
+ def path
32
+ url
33
+ end
34
+
35
+ end
@@ -0,0 +1,275 @@
1
+ class LegacyBcms::Page < ActiveRecord::Base
2
+
3
+ is_archivable
4
+ flush_cache_on_change
5
+ is_hideable
6
+ is_publishable
7
+ uses_soft_delete
8
+ is_userstamped
9
+ is_versioned
10
+
11
+ has_many :connectors, :order => "connectors.container, connectors.position"
12
+ has_many :page_routes
13
+
14
+ named_scope :named, lambda{|name| {:conditions => ['pages.name = ?', name]}}
15
+ named_scope :with_path, lambda{|path| {:conditions => ['pages.path = ?', path]}}
16
+
17
+ # This scope will accept a connectable object or a Hash. The Hash is expect to have
18
+ # a value for the key :connectable, which is the connectable object, and possibly
19
+ # a value for the key :version. The Hash contains a versioned connectable object,
20
+ # it will use the value in :version if present, otherwise it will use the version
21
+ # of the object. In either case of a connectable object or a Hash, if the object
22
+ # is not versioned, no version will be used
23
+ named_scope :connected_to, lambda { |b|
24
+ if b.is_a?(Hash)
25
+ obj = b[:connectable]
26
+ if obj.class.versioned?
27
+ ver = b[:version] ? b[:version] : obj.version
28
+ else
29
+ ver = nil
30
+ end
31
+ else
32
+ obj = b
33
+ ver = obj.class.versioned? ? obj.version : nil
34
+ end
35
+
36
+ if ver
37
+ { :include => :connectors,
38
+ :conditions => ['connectors.connectable_id = ? and connectors.connectable_type = ? and connectors.connectable_version = ?', obj.id, obj.class.base_class.name, ver] }
39
+ else
40
+ { :include => :connectors,
41
+ :conditions => ['connectors.connectable_id = ? and connectors.connectable_type = ?', obj.id, obj.class.base_class.name] }
42
+ end
43
+ }
44
+
45
+ has_one :section_node, :as => :node, :dependent => :destroy
46
+
47
+ has_many :tasks
48
+
49
+ before_validation :append_leading_slash_to_path
50
+ before_destroy :delete_connectors
51
+
52
+ validates_presence_of :name, :path
53
+ validates_uniqueness_of :path
54
+ validate :path_not_reserved
55
+
56
+ def after_build_new_version(new_version)
57
+ copy_connectors(
58
+ :from_version_number => @copy_connectors_from_version || (new_version.version - 1),
59
+ :to_version_number => new_version.version
60
+ )
61
+ @copy_connectors_from_version = nil
62
+ true
63
+ end
64
+
65
+ # Publish all
66
+ def after_publish
67
+ self.reload # Get's the correct version number loaded
68
+ self.connectors.for_page_version(self.version).all(:order => "position").each do |c|
69
+ if c.connectable_type.constantize.publishable? && con = c.connectable
70
+ con.publish
71
+ end
72
+ end
73
+ end
74
+
75
+ def copy_connectors(options={})
76
+ connectors.for_page_version(options[:from_version_number]).all(:order => "connectors.container, connectors.position").each do |c|
77
+ # The connector won't have a connectable if it has been deleted
78
+ # Also need to see if the draft has been deleted,
79
+ # in which case we are in the process of deleting it
80
+ if c.should_be_copied?
81
+ connectable = c.connectable_type.constantize.versioned? ? c.connectable.as_of_version(c.connectable_version) : c.connectable
82
+
83
+ #If we are copying connectors from a previous version, that means we are reverting this page,
84
+ #in which case we should create a new version of the block, and connect this page to that block
85
+ if @copy_connectors_from_version && connectable.class.versioned? && (connectable.version != connectable.draft.version)
86
+ connectable = connectable.class.find(connectable.id)
87
+ connectable.updated_by_page = self
88
+ connectable.revert_to(c.connectable_version)
89
+ end
90
+
91
+ new_connector = connectors.build(
92
+ :page_version => options[:to_version_number],
93
+ :connectable => connectable,
94
+ :connectable_version => connectable.class.versioned? ? connectable.version : nil,
95
+ :container => c.container,
96
+ :position => c.position
97
+ )
98
+ end
99
+ end
100
+ true
101
+ end
102
+
103
+ def create_connector(connectable, container)
104
+ transaction do
105
+ raise "Connectable is nil" unless connectable
106
+ raise "Container is required" if container.blank?
107
+ update_attributes(
108
+ :version_comment => "#{connectable} was added to the '#{container}' container",
109
+ :publish_on_save => (
110
+ published? &&
111
+ connectable.connected_page &&
112
+ (connectable.class.publishable? ? connectable.published? : true)))
113
+ connectors.create(
114
+ :page_version => draft.version,
115
+ :connectable => connectable,
116
+ :connectable_version => connectable.class.versioned? ? connectable.version : nil,
117
+ :container => container)
118
+ end
119
+ end
120
+
121
+ def move_connector(connector, direction)
122
+ transaction do
123
+ raise "Connector is nil" unless connector
124
+ raise "Direction is nil" unless direction
125
+ orientation = direction[/_/] ? "#{direction.sub('_', ' the ')} of" : "#{direction} within"
126
+ update_attributes(:version_comment => "#{connector.connectable} was moved #{orientation} the '#{connector.container}' container")
127
+ connectors.for_page_version(draft.version).like(connector).first.send("move_#{direction}")
128
+ end
129
+ end
130
+
131
+ %w(up down to_top to_bottom).each do |d|
132
+ define_method("move_connector_#{d}") do |connector|
133
+ move_connector(connector, d)
134
+ end
135
+ end
136
+
137
+ def remove_connector(connector)
138
+ transaction do
139
+ raise "Connector is nil" unless connector
140
+ update_attributes(:version_comment => "#{connector.connectable} was removed from the '#{connector.container}' container")
141
+
142
+ #The logic of this is to go ahead and let the container get copied forward, then delete the new connector
143
+ if new_connector = connectors.for_page_version(draft.version).like(connector).first
144
+ new_connector.destroy
145
+ else
146
+ raise "Error occurred while trying to remove connector #{connector.id}"
147
+ end
148
+ end
149
+ end
150
+
151
+ def delete_connectors
152
+ connectors.for_page_version(version).all.each{|c| c.destroy }
153
+ end
154
+
155
+ #This is done to let copy_connectors know which version to pull from
156
+ #copy_connectors will get called later as an after_update callback
157
+ def revert_to(version)
158
+ @copy_connectors_from_version = version
159
+ super(version)
160
+ end
161
+
162
+ def file_size
163
+ "?"
164
+ end
165
+
166
+ def section_id
167
+ section ? section.id : nil
168
+ end
169
+
170
+ def section
171
+ section_node ? section_node.section : nil
172
+ end
173
+
174
+ def section_id=(sec_id)
175
+ self.section = Section.find(sec_id)
176
+ end
177
+
178
+ def section=(sec)
179
+ if section_node
180
+ section_node.move_to_end(sec)
181
+ else
182
+ build_section_node(:node => self, :section => sec)
183
+ end
184
+ end
185
+
186
+ def public?
187
+ section ? section.public? : false
188
+ end
189
+
190
+ def page_title
191
+ title.blank? ? name : title
192
+ end
193
+
194
+ def append_leading_slash_to_path
195
+ if path.blank?
196
+ self.path = "/"
197
+ elsif path[0,1] != "/"
198
+ self.path = "/#{path}"
199
+ end
200
+ end
201
+
202
+ def path_not_reserved
203
+ if Cms.reserved_paths.include?(path)
204
+ errors.add(:path, "is invalid, '#{path}' a reserved path")
205
+ end
206
+ end
207
+
208
+ def layout
209
+ template_file_name && "templates/#{template_file_name.split('.').first}"
210
+ end
211
+
212
+ # This will be nil if it is a file system based template
213
+ def template
214
+ PageTemplate.find_by_file_name(template_file_name)
215
+ end
216
+
217
+ def template_name
218
+ template_file_name && PageTemplate.display_name(template_file_name)
219
+ end
220
+
221
+ def ancestors
222
+ section_node.ancestors
223
+ end
224
+
225
+ def in_section?(section_or_section_name)
226
+ sec = section_or_section_name.is_a?(String) ?
227
+ Section.first(:conditions => {:name => section_or_section_name}) :
228
+ section_or_section_name
229
+ fn = lambda{|s| s ? (s == sec || fn.call(s.parent)) : false}
230
+ fn.call(section)
231
+ end
232
+
233
+ #Returns true if the block attached to each connector in the given container are published
234
+ def container_published?(container)
235
+ connectors.for_page_version(draft.version).in_container(container.to_s).all? do |c|
236
+ c.connectable_type.constantize.publishable? ? c.connectable.live? : true
237
+ end
238
+ end
239
+
240
+ # Returns the number of connectables in the given container for this version of this page
241
+ def connectable_count_for_container(container)
242
+ connectors.for_page_version(version).in_container(container.to_s).count
243
+ end
244
+
245
+ def self.find_live_by_path(path)
246
+ published.not_archived.first(:conditions => {:path => path})
247
+ end
248
+
249
+ def name_with_section_path
250
+ a = ancestors
251
+ (a[1..a.size].map{|a| a.name} + [name]).join(" / ")
252
+ end
253
+
254
+ # This will return the "top level section" for a page, which is the section directly
255
+ # below the root (a.k.a My Site) that this page is in. If this page is in root,
256
+ # then this will return root.
257
+ def top_level_section
258
+ a = ancestors
259
+ (a.size > 0 && ancestors[1]) ? ancestors[1] : Section.root.first
260
+ end
261
+
262
+ def current_task
263
+ tasks.incomplete.first
264
+ end
265
+
266
+ def assigned_to
267
+ current_task ? current_task.assigned_to : nil
268
+ end
269
+
270
+ def assigned_to?(user)
271
+ assigned_to == user
272
+ end
273
+
274
+ end
275
+