Changesets can be listed by changeset number.
The Git repository is here.
- Revision:
- 193
- Log:
First stage commit of Typo 4.1, modified for the ROOL site.
Includes all local modifications but a final pass needs to be
made to delete any files left over from earlier Typo versions
that shouldn't be here anymore. See the 'tags' section of the
repository for a clean Typo 4.1 tree.Note that symlinks to shared files in the RISC OS Open theme
directory have been deliberately included this time around; I
decided that on balance it was better to leave them in as
placeholders, since unlike symlinks in app/views/shared, the
Typo theme structure is not a standard Rails concept.
- Author:
- rool
- Date:
- Wed Apr 04 18:51:02 +0100 2007
- Size:
- 6889 Bytes
1 | require 'observer' |
2 | require 'set' |
3 | |
4 | class Content < ActiveRecord::Base |
5 | include Observable |
6 | |
7 | belongs_to :text_filter |
8 | belongs_to :blog |
9 | validates_presence_of :blog_id |
10 | |
11 | composed_of :state, :class_name => 'ContentState::Factory', |
12 | :mapping => %w{ state memento } |
13 | |
14 | has_many :notifications, :foreign_key => 'content_id' |
15 | has_many :notify_users, :through => :notifications, |
16 | :source => 'notify_user', |
17 | :uniq => true |
18 | |
19 | def notify_users=(collection) |
20 | return notify_users.clear if collection.empty? |
21 | self.class.transaction do |
22 | self.notifications.clear |
23 | collection.uniq.each do |u| |
24 | self.notifications.build(:notify_user => u) |
25 | end |
26 | notify_users.target = collection |
27 | end |
28 | end |
29 | |
30 | has_many :triggers, :as => :pending_item, :dependent => :delete_all |
31 | |
32 | before_save :state_before_save |
33 | after_save :post_trigger, :state_after_save |
34 | |
35 | serialize :whiteboard |
36 | |
37 | @@content_fields = Hash.new |
38 | @@html_map = Hash.new |
39 | |
40 | def initialize(*args, &block) |
41 | super(*args, &block) |
42 | set_default_blog |
43 | end |
44 | |
45 | def invalidates_cache?(on_destruction = false) |
46 | if on_destruction |
47 | just_changed_published_status? || published? |
48 | else |
49 | changed? && published? || just_changed_published_status? |
50 | end |
51 | end |
52 | |
53 | def set_default_blog |
54 | if self.blog_id == nil or self.blog_id == 0 |
55 | self.blog = Blog.default |
56 | end |
57 | end |
58 | |
59 | class << self |
60 | # Quite a bit of this isn't needed anymore. |
61 | def content_fields(*attribs) |
62 | @@content_fields[self] = ((@@content_fields[self]||[]) + attribs).uniq |
63 | @@html_map[self] = nil |
64 | attribs.each do | field | |
65 | define_method("#{field}=") do | newval | |
66 | if self[field] != newval |
67 | changed |
68 | self[field] = newval |
69 | end |
70 | self[field] |
71 | end |
72 | unless self.method_defined?("#{field}_html") |
73 | define_method("#{field}_html") do |
74 | typo_deprecated "Use html(:#{field})" |
75 | html(field.to_sym) |
76 | end |
77 | end |
78 | end |
79 | end |
80 | |
81 | def html_map(field=nil) |
82 | unless @@html_map[self] |
83 | @@html_map[self] = Hash.new |
84 | instance = self.new |
85 | @@content_fields[self].each do |attrib| |
86 | @@html_map[self][attrib] = true |
87 | end |
88 | end |
89 | if field |
90 | @@html_map[self][field] |
91 | else |
92 | @@html_map[self] |
93 | end |
94 | end |
95 | |
96 | def find_published(what = :all, options = {}) |
97 | with_scope(:find => {:order => default_order, :conditions => {:published => true}}) do |
98 | find what, options |
99 | end |
100 | end |
101 | |
102 | def default_order |
103 | 'published_at DESC' |
104 | end |
105 | |
106 | def find_already_published(what = :all, at = nil, options = { }) |
107 | if what.respond_to?(:has_key?) |
108 | what, options = :all, what |
109 | elsif at.respond_to?(:has_key?) |
110 | options, at = at, nil |
111 | end |
112 | at ||= options.delete(:at) || Time.now |
113 | with_scope(:find => { :conditions => ['published_at < ?', at]}) do |
114 | find_published(what, options) |
115 | end |
116 | end |
117 | end |
118 | |
119 | def content_fields |
120 | @@content_fields[self.class] |
121 | end |
122 | |
123 | def state_before_save |
124 | state.before_save(self) |
125 | end |
126 | |
127 | def state_after_save |
128 | state.after_save(self) |
129 | end |
130 | |
131 | def html_map(field=nil) |
132 | self.class.html_map(field) |
133 | end |
134 | |
135 | def cache_key(field) |
136 | id ? "contents_html/#{id}/#{field}" : nil |
137 | end |
138 | |
139 | # Return HTML for some part of this object. It will be fetched from the |
140 | # cache if possible, or regenerated if needed. |
141 | def html(field = :all) |
142 | if field == :all |
143 | generate_html(:all, content_fields.map{|f| self[f].to_s}.join("\n\n")) |
144 | elsif self.class.html_map(field) |
145 | generate_html(field) |
146 | else |
147 | raise "Unknown field: #{field.inspect} in article.html" |
148 | end |
149 | end |
150 | |
151 | # Generate HTML for a specific field using the text_filter in use for this |
152 | # object. The HTML is cached in the fragment cache, using the +ContentCache+ |
153 | # object in @@cache. |
154 | def generate_html(field, text = nil) |
155 | text ||= self[field].to_s |
156 | html = text_filter.filter_text_for_content(blog, text, self) |
157 | html ||= text # just in case the filter puked |
158 | html_postprocess(field,html).to_s |
159 | end |
160 | |
161 | # Post-process the HTML. This is a noop by default, but Comment overrides it |
162 | # to enforce HTML sanity. |
163 | def html_postprocess(field,html) |
164 | html |
165 | end |
166 | |
167 | def whiteboard |
168 | self[:whiteboard] ||= Hash.new |
169 | end |
170 | |
171 | # The default text filter. Generally, this is the filter specified by blog.text_filter, |
172 | # but comments may use a different default. |
173 | def default_text_filter |
174 | blog.text_filter.to_text_filter |
175 | end |
176 | |
177 | # Grab the text filter for this object. It's either the filter specified by |
178 | # self.text_filter_id, or the default specified in the blog object. |
179 | def text_filter |
180 | if self[:text_filter_id] && !self[:text_filter_id].zero? |
181 | TextFilter.find(self[:text_filter_id]) |
182 | else |
183 | default_text_filter |
184 | end |
185 | end |
186 | |
187 | # Set the text filter for this object. |
188 | def text_filter=(filter) |
189 | returning(filter.to_text_filter) { |tf| self.text_filter_id = tf.id } |
190 | end |
191 | |
192 | # Changing the title flags the object as changed |
193 | def title=(new_title) |
194 | if new_title == self[:title] |
195 | self[:title] |
196 | else |
197 | self.changed |
198 | self[:title] = new_title |
199 | end |
200 | end |
201 | |
202 | # FIXME -- this feels wrong. |
203 | def blog |
204 | self[:blog] ||= blog_id.to_i.zero? ? Blog.default : Blog.find(blog_id) |
205 | end |
206 | |
207 | def state=(newstate) |
208 | if state |
209 | state.exit_hook(self, newstate) |
210 | end |
211 | @state = newstate |
212 | self[:state] = newstate.memento |
213 | newstate.enter_hook(self) |
214 | @state |
215 | end |
216 | |
217 | def publish! |
218 | self.published = true |
219 | self.save! |
220 | end |
221 | |
222 | def withdraw |
223 | state.withdraw(self) |
224 | end |
225 | |
226 | def withdraw! |
227 | self.withdraw |
228 | self.save! |
229 | end |
230 | |
231 | def published=(a_boolean) |
232 | self[:published] = a_boolean |
233 | state.change_published_state(self, a_boolean) |
234 | end |
235 | |
236 | def published_at=(a_time) |
237 | state.set_published_at(self, (a_time.to_time rescue nil)) |
238 | end |
239 | |
240 | def published_at |
241 | self[:published_at] || self[:created_at] |
242 | end |
243 | |
244 | def published? |
245 | state.published?(self) |
246 | end |
247 | |
248 | def just_published? |
249 | state.just_published? |
250 | end |
251 | |
252 | def just_changed_published_status? |
253 | state.just_changed_published_status? |
254 | end |
255 | |
256 | def withdrawn? |
257 | state.withdrawn? |
258 | end |
259 | |
260 | def publication_pending? |
261 | state.publication_pending? |
262 | end |
263 | |
264 | def post_trigger |
265 | state.post_trigger(self) |
266 | end |
267 | |
268 | def after_save |
269 | state.after_save(self) |
270 | end |
271 | |
272 | def send_notification_to_user(user) |
273 | notify_user_via_email(user) |
274 | notify_user_via_jabber(user) |
275 | end |
276 | |
277 | def send_notifications() |
278 | state.send_notifications(self) |
279 | end |
280 | |
281 | # deprecated |
282 | def full_html |
283 | typo_deprecated "use .html instead" |
284 | html |
285 | end |
286 | |
287 | end |
288 | |
289 | class Object |
290 | def to_text_filter |
291 | TextFilter.find_by_name(self.to_s) || TextFilter.find_by_name('none') |
292 | end |
293 | end |
294 | |
295 | class ContentTextHelpers |
296 | include ActionView::Helpers::TagHelper |
297 | include ActionView::Helpers::TextHelper |
298 | end |
299 |