Changesets can be listed by changeset number.
The Git repository is here.
- Revision:
- 373
- Log:
Initial import of Radiant 0.9.1, which is now packaged as a gem. This is an
import of the tagged 0.9.1 source checked out from GitHub, which isn't quite
the same as the gem distribution - but it doesn't seem to be available in an
archived form and the installed gem already has modifications, so this is
the closest I can get.
- Author:
- rool
- Date:
- Mon Mar 21 13:40:05 +0000 2011
- Size:
- 8113 Bytes
1 | class Page < ActiveRecord::Base |
2 | |
3 | class MissingRootPageError < StandardError |
4 | def initialize(message = 'Database missing root page'); super end |
5 | end |
6 | |
7 | # Callbacks |
8 | before_save :update_virtual, :update_status |
9 | |
10 | # Associations |
11 | acts_as_tree :order => 'virtual DESC, title ASC' |
12 | has_many :parts, :class_name => 'PagePart', :order => 'id', :dependent => :destroy |
13 | accepts_nested_attributes_for :parts, :allow_destroy => true |
14 | belongs_to :layout |
15 | belongs_to :created_by, :class_name => 'User' |
16 | belongs_to :updated_by, :class_name => 'User' |
17 | |
18 | # Validations |
19 | validates_presence_of :title, :slug, :breadcrumb, :status_id |
20 | |
21 | validates_length_of :title, :maximum => 255 |
22 | validates_length_of :slug, :maximum => 100 |
23 | validates_length_of :breadcrumb, :maximum => 160 |
24 | |
25 | validates_format_of :slug, :with => %r{^([-_.A-Za-z0-9]*|/)$} |
26 | validates_uniqueness_of :slug, :scope => :parent_id |
27 | validates_numericality_of :id, :status_id, :parent_id, :allow_nil => true, :only_integer => true |
28 | |
29 | validate :valid_class_name |
30 | |
31 | include Radiant::Taggable |
32 | include StandardTags |
33 | include Annotatable |
34 | |
35 | annotate :description |
36 | attr_accessor :request, :response, :pagination_parameters |
37 | |
38 | set_inheritance_column :class_name |
39 | |
40 | def layout_with_inheritance |
41 | unless layout_without_inheritance |
42 | parent.layout if parent? |
43 | else |
44 | layout_without_inheritance |
45 | end |
46 | end |
47 | alias_method_chain :layout, :inheritance |
48 | |
49 | def description |
50 | self["description"] |
51 | end |
52 | |
53 | def description=(value) |
54 | self["description"] = value |
55 | end |
56 | |
57 | def cache? |
58 | true |
59 | end |
60 | |
61 | def child_url(child) |
62 | clean_url(url + '/' + child.slug) |
63 | end |
64 | |
65 | def headers |
66 | # Return a blank hash that child classes can override or merge |
67 | { } |
68 | end |
69 | |
70 | def part(name) |
71 | if new_record? or parts.to_a.any?(&:new_record?) |
72 | parts.to_a.find {|p| p.name == name.to_s } |
73 | else |
74 | parts.find_by_name name.to_s |
75 | end |
76 | end |
77 | |
78 | def has_part?(name) |
79 | !part(name).nil? |
80 | end |
81 | |
82 | def has_or_inherits_part?(name) |
83 | has_part?(name) || inherits_part?(name) |
84 | end |
85 | |
86 | def inherits_part?(name) |
87 | !has_part?(name) && self.ancestors.any? { |page| page.has_part?(name) } |
88 | end |
89 | |
90 | def published? |
91 | status == Status[:published] |
92 | end |
93 | |
94 | def status |
95 | Status.find(self.status_id) |
96 | end |
97 | |
98 | def status=(value) |
99 | self.status_id = value.id |
100 | end |
101 | |
102 | def url |
103 | if parent? |
104 | parent.child_url(self) |
105 | else |
106 | clean_url(slug) |
107 | end |
108 | end |
109 | |
110 | def process(request, response) |
111 | @request, @response = request, response |
112 | if layout |
113 | content_type = layout.content_type.to_s.strip |
114 | @response.headers['Content-Type'] = content_type unless content_type.empty? |
115 | end |
116 | headers.each { |k,v| @response.headers[k] = v } |
117 | @response.body = render |
118 | @response.status = response_code |
119 | end |
120 | |
121 | def response_code |
122 | 200 |
123 | end |
124 | |
125 | def render |
126 | if layout |
127 | parse_object(layout) |
128 | else |
129 | render_part(:body) |
130 | end |
131 | end |
132 | |
133 | def render_part(part_name) |
134 | part = part(part_name) |
135 | if part |
136 | parse_object(part) |
137 | else |
138 | '' |
139 | end |
140 | end |
141 | |
142 | def render_snippet(snippet) |
143 | parse_object(snippet) |
144 | end |
145 | |
146 | def find_by_url(url, live = true, clean = true) |
147 | return nil if virtual? |
148 | url = clean_url(url) if clean |
149 | my_url = self.url |
150 | if (my_url == url) && (not live or published?) |
151 | self |
152 | elsif (url =~ /^#{Regexp.quote(my_url)}([^\/]*)/) |
153 | slug_child = children.find_by_slug($1) |
154 | if slug_child |
155 | found = slug_child.find_by_url(url, live, clean) |
156 | return found if found |
157 | end |
158 | children.each do |child| |
159 | found = child.find_by_url(url, live, clean) |
160 | return found if found |
161 | end |
162 | file_not_found_types = ([FileNotFoundPage] + FileNotFoundPage.descendants) |
163 | file_not_found_names = file_not_found_types.collect { |x| x.name } |
164 | condition = (['class_name = ?'] * file_not_found_names.length).join(' or ') |
165 | condition = "status_id = #{Status[:published].id} and (#{condition})" if live |
166 | children.find(:first, :conditions => [condition] + file_not_found_names) |
167 | end |
168 | end |
169 | |
170 | def update_status |
171 | self[:published_at] = Time.now if self[:status_id] == Status[:published].id && self[:published_at] == nil |
172 | |
173 | if self[:published_at] != nil && (self[:status_id] == Status[:published].id || self[:status_id] == Status[:scheduled].id) |
174 | self[:status_id] = Status[:scheduled].id if self[:published_at] > Time.now |
175 | self[:status_id] = Status[:published].id if self[:published_at] <= Time.now |
176 | end |
177 | |
178 | true |
179 | end |
180 | |
181 | |
182 | def to_xml(options={}, &block) |
183 | super(options.reverse_merge(:include => :parts), &block) |
184 | end |
185 | |
186 | class << self |
187 | def find_by_url(url, live = true) |
188 | root = find_by_parent_id(nil) |
189 | raise MissingRootPageError unless root |
190 | root.find_by_url(url, live) |
191 | end |
192 | |
193 | def display_name(string = nil) |
194 | if string |
195 | @display_name = string |
196 | else |
197 | @display_name ||= begin |
198 | n = name.to_s |
199 | n.sub!(/^(.+?)Page$/, '\1') |
200 | n.gsub!(/([A-Z])/, ' \1') |
201 | n.strip |
202 | end |
203 | end |
204 | @display_name = @display_name + " - not installed" if missing? && @display_name !~ /not installed/ |
205 | @display_name |
206 | end |
207 | def display_name=(string) |
208 | display_name(string) |
209 | end |
210 | |
211 | def load_subclasses |
212 | ([RADIANT_ROOT] + Radiant::Extension.descendants.map(&:root)).each do |path| |
213 | Dir["#{path}/app/models/*_page.rb"].each do |page| |
214 | $1.camelize.constantize if page =~ %r{/([^/]+)\.rb} |
215 | end |
216 | end |
217 | if ActiveRecord::Base.connection.tables.include?('pages') && Page.column_names.include?('class_name') # Assume that we have bootstrapped |
218 | Page.connection.select_values("SELECT DISTINCT class_name FROM pages WHERE class_name <> '' AND class_name IS NOT NULL").each do |p| |
219 | begin |
220 | p.constantize |
221 | rescue NameError, LoadError |
222 | eval(%Q{class #{p} < Page; def self.missing?; true end end}, TOPLEVEL_BINDING) |
223 | end |
224 | end |
225 | end |
226 | end |
227 | |
228 | def new_with_defaults(config = Radiant::Config) |
229 | page = new |
230 | page.parts.concat default_page_parts(config) |
231 | default_status = config['defaults.page.status'] |
232 | page.status = Status[default_status] if default_status |
233 | page |
234 | end |
235 | |
236 | def is_descendant_class_name?(class_name) |
237 | (Page.descendants.map(&:to_s) + [nil, "", "Page"]).include?(class_name) |
238 | end |
239 | |
240 | def descendant_class(class_name) |
241 | raise ArgumentError.new("argument must be a valid descendant of Page") unless is_descendant_class_name?(class_name) |
242 | if ["", nil, "Page"].include?(class_name) |
243 | Page |
244 | else |
245 | class_name.constantize |
246 | end |
247 | end |
248 | |
249 | def missing? |
250 | false |
251 | end |
252 | |
253 | private |
254 | |
255 | def default_page_parts(config = Radiant::Config) |
256 | default_parts = config['defaults.page.parts'].to_s.strip.split(/\s*,\s*/) |
257 | default_parts.map do |name| |
258 | PagePart.new(:name => name, :filter_id => config['defaults.page.filter']) |
259 | end |
260 | end |
261 | end |
262 | |
263 | private |
264 | |
265 | def valid_class_name |
266 | unless Page.is_descendant_class_name?(class_name) |
267 | errors.add :class_name, "must be set to a valid descendant of Page" |
268 | end |
269 | end |
270 | |
271 | def attributes_protected_by_default |
272 | super - [self.class.inheritance_column] |
273 | end |
274 | |
275 | |
276 | def update_virtual |
277 | unless self.class == Page.descendant_class(class_name) |
278 | self.virtual = Page.descendant_class(class_name).new.virtual? |
279 | else |
280 | self.virtual = virtual? |
281 | end |
282 | true |
283 | end |
284 | |
285 | def clean_url(url) |
286 | "/#{ url.strip }/".gsub(%r{//+}, '/') |
287 | end |
288 | |
289 | def parent? |
290 | !parent.nil? |
291 | end |
292 | |
293 | def lazy_initialize_parser_and_context |
294 | unless @parser and @context |
295 | @context = PageContext.new(self) |
296 | @parser = Radius::Parser.new(@context, :tag_prefix => 'r') |
297 | end |
298 | @parser |
299 | end |
300 | |
301 | def parse(text) |
302 | lazy_initialize_parser_and_context.parse(text) |
303 | end |
304 | |
305 | def parse_object(object) |
306 | text = object.content |
307 | text = parse(text) |
308 | text = object.filter.filter(text) if object.respond_to? :filter_id |
309 | text |
310 | end |
311 | |
312 | end |