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:
- 8148 Bytes
1 | require 'uri' |
2 | require 'net/http' |
3 | |
4 | class Article < Content |
5 | include TypoGuid |
6 | |
7 | content_fields :body, :extended |
8 | |
9 | has_many :pings, :dependent => :destroy, :order => "created_at ASC" |
10 | has_many :comments, :dependent => :destroy, :order => "created_at ASC" |
11 | has_many :trackbacks, :dependent => :destroy, :order => "created_at ASC" |
12 | has_many :resources, :order => "created_at DESC", |
13 | :class_name => "Resource", :foreign_key => 'article_id' |
14 | has_many :categorizations |
15 | has_many :categories, :through => :categorizations, :uniq => true do |
16 | def push_with_attributes(cat, join_attrs = { :is_primary => false }) |
17 | Categorization.with_scope(:create => join_attrs) { self << cat } |
18 | end |
19 | end |
20 | has_and_belongs_to_many :tags, :foreign_key => 'article_id' |
21 | belongs_to :user |
22 | has_many :triggers, :as => :pending_item |
23 | |
24 | after_destroy :fix_resources |
25 | |
26 | def stripped_title |
27 | self.title.gsub(/<[^>]*>/,'').to_url |
28 | end |
29 | |
30 | def permalink_url(anchor=nil, only_path=true) |
31 | @cached_permalink_url ||= {} |
32 | @cached_permalink_url["#{anchor}#{only_path}"] ||= blog.url_for( |
33 | :year => published_at.year, |
34 | :month => sprintf("%.2d", published_at.month), |
35 | :day => sprintf("%.2d", published_at.day), |
36 | :title => permalink, |
37 | :anchor => anchor, |
38 | :only_path => only_path, |
39 | :controller => '/articles' |
40 | ) |
41 | end |
42 | |
43 | def trackback_url |
44 | blog.url_for(:controller => "articles", :action =>"trackback", :id => id) |
45 | end |
46 | |
47 | def feed_url(format = :rss20) |
48 | blog.url_for(:controller => 'xml', :action => 'feed', :type => 'article', :format => format, :id => id) |
49 | end |
50 | |
51 | def edit_url |
52 | blog.url_for(:controller => "/admin/content", :action =>"edit", :id => id) |
53 | end |
54 | |
55 | def delete_url |
56 | blog.url_for(:controller => "/admin/content", :action =>"destroy", :id => id) |
57 | end |
58 | |
59 | def html_urls |
60 | urls = Array.new |
61 | html.gsub(/<a [^>]*>/) do |tag| |
62 | if(tag =~ /href="([^"]+)"/) |
63 | urls.push($1) |
64 | end |
65 | end |
66 | |
67 | urls.uniq |
68 | end |
69 | |
70 | def really_send_pings(serverurl = blog.base_url, articleurl = nil) |
71 | return unless blog.send_outbound_pings |
72 | |
73 | articleurl ||= permalink_url(nil, false) |
74 | |
75 | weblogupdatesping_urls = blog.ping_urls.gsub(/ +/,'').split(/[\n\r]+/) |
76 | pingback_or_trackback_urls = self.html_urls |
77 | |
78 | ping_urls = weblogupdatesping_urls + pingback_or_trackback_urls |
79 | |
80 | ping_urls.uniq.each do |url| |
81 | begin |
82 | unless pings.collect { |p| p.url }.include?(url.strip) |
83 | ping = pings.build("url" => url) |
84 | |
85 | if weblogupdatesping_urls.include?(url) |
86 | ping.send_weblogupdatesping(serverurl, articleurl) |
87 | elsif pingback_or_trackback_urls.include?(url) |
88 | ping.send_pingback_or_trackback(articleurl) |
89 | end |
90 | end |
91 | rescue Exception => e |
92 | logger.error(e) |
93 | # in case the remote server doesn't respond or gives an error, |
94 | # we should throw an xmlrpc error here. |
95 | end |
96 | end |
97 | end |
98 | |
99 | def send_pings |
100 | state.send_pings(self) |
101 | end |
102 | |
103 | def next |
104 | blog.articles.find(:first, :conditions => ['published_at > ?', published_at], |
105 | :order => 'published_at asc') |
106 | end |
107 | |
108 | def previous |
109 | blog.articles.find(:first, :conditions => ['published_at < ?', published_at], |
110 | :order => 'published_at desc') |
111 | end |
112 | |
113 | # Count articles on a certain date |
114 | def self.count_by_date(year, month = nil, day = nil, limit = nil) |
115 | from, to = self.time_delta(year, month, day) |
116 | Article.count(["published_at BETWEEN ? AND ? AND published = ?", |
117 | from, to, true]) |
118 | end |
119 | |
120 | # Find all articles on a certain date |
121 | def self.find_all_by_date(year, month = nil, day = nil) |
122 | from, to = self.time_delta(year, month, day) |
123 | Article.find_published(:all, :conditions => ["published_at BETWEEN ? AND ?", |
124 | from, to]) |
125 | end |
126 | |
127 | # Find one article on a certain date |
128 | |
129 | def self.find_by_date(year, month, day) |
130 | find_all_by_date(year, month, day).first |
131 | end |
132 | |
133 | # Finds one article which was posted on a certain date and matches the supplied dashed-title |
134 | def self.find_by_permalink(year, month, day, title) |
135 | from, to = self.time_delta(year, month, day) |
136 | find_published(:first, |
137 | :conditions => ['permalink = ? AND ' + |
138 | 'published_at BETWEEN ? AND ?', |
139 | title, from, to ]) |
140 | end |
141 | |
142 | # Fulltext searches the body of published articles |
143 | def self.search(query) |
144 | if !query.to_s.strip.empty? |
145 | tokens = query.split.collect {|c| "%#{c.downcase}%"} |
146 | find_published(:all, |
147 | :conditions => [(["(LOWER(body) LIKE ? OR LOWER(extended) LIKE ? OR LOWER(title) LIKE ?)"] * tokens.size).join(" AND "), *tokens.collect { |token| [token] * 3 }.flatten]) |
148 | else |
149 | [] |
150 | end |
151 | end |
152 | |
153 | def keywords_to_tags |
154 | Article.transaction do |
155 | tags.clear |
156 | keywords.to_s.scan(/((['"]).*?\2|[\.\w]+)/).collect do |x| |
157 | x.first.tr("\"'", '') |
158 | end.uniq.each do |tagword| |
159 | tags << Tag.get(tagword) |
160 | end |
161 | end |
162 | end |
163 | |
164 | def interested_users |
165 | User.find_boolean(:all, :notify_on_new_articles) |
166 | end |
167 | |
168 | def notify_user_via_email(user) |
169 | if user.notify_via_email? |
170 | EmailNotify.send_article(self, user) |
171 | end |
172 | end |
173 | |
174 | def notify_user_via_jabber(user) |
175 | if user.notify_via_jabber? |
176 | JabberNotify.send_message(user, "New post", |
177 | "A new message was posted to #{blog.blog_name}", |
178 | html(:body)) |
179 | end |
180 | end |
181 | |
182 | def comments_closed? |
183 | if self.allow_comments? |
184 | if !self.blog.sp_article_auto_close.zero? and self.created_at.to_i < self.blog.sp_article_auto_close.days.ago.to_i |
185 | return true |
186 | else |
187 | return false |
188 | end |
189 | else |
190 | return true |
191 | end |
192 | end |
193 | |
194 | def published_comments |
195 | comments.select {|c| c.published?} |
196 | end |
197 | |
198 | def published_trackbacks |
199 | trackbacks.select {|c| c.published?} |
200 | end |
201 | |
202 | # Bloody rails reloading. Nasty workaround. |
203 | def body=(newval) |
204 | if self[:body] != newval |
205 | changed |
206 | self[:body] = newval |
207 | end |
208 | self[:body] |
209 | end |
210 | |
211 | def body_html |
212 | typo_deprecated "Use html(:body)" |
213 | html(:body) |
214 | end |
215 | |
216 | def extended=(newval) |
217 | if self[:extended] != newval |
218 | changed |
219 | self[:extended] = newval |
220 | end |
221 | self[:extended] |
222 | end |
223 | |
224 | def extended_html |
225 | typo_deprecated "Use html(:extended)" |
226 | html(:extended) |
227 | end |
228 | |
229 | def self.html_map(field=nil) |
230 | html_map = { :body => true, :extended => true } |
231 | if field |
232 | html_map[field.to_sym] |
233 | else |
234 | html_map |
235 | end |
236 | end |
237 | |
238 | def content_fields |
239 | [:body, :extended] |
240 | end |
241 | |
242 | protected |
243 | |
244 | before_create :set_defaults, :create_guid |
245 | before_save :set_published_at |
246 | after_save :keywords_to_tags |
247 | after_create :add_notifications |
248 | |
249 | def set_published_at |
250 | if self.published and self[:published_at].nil? |
251 | self[:published_at] = self.created_at || Time.now |
252 | end |
253 | end |
254 | |
255 | def set_defaults |
256 | if self.attributes.include?("permalink") and self.permalink.blank? |
257 | self.permalink = self.stripped_title |
258 | end |
259 | if blog && self.allow_comments.nil? |
260 | self.allow_comments = blog.default_allow_comments |
261 | end |
262 | |
263 | if blog && self.allow_pings.nil? |
264 | self.allow_pings = blog.default_allow_pings |
265 | end |
266 | |
267 | true |
268 | end |
269 | |
270 | def add_notifications |
271 | self.notify_users = User.find_boolean(:all, :notify_on_new_articles) |
272 | self.notify_users << self.user if (self.user.notify_watch_my_articles? rescue false) |
273 | self.notify_users.uniq! |
274 | end |
275 | |
276 | def self.time_delta(year, month = nil, day = nil) |
277 | from = Time.mktime(year, month || 1, day || 1) |
278 | |
279 | to = from.next_year |
280 | to = from.next_month unless month.blank? |
281 | to = from + 1.day unless day.blank? |
282 | to = to - 1 # pull off 1 second so we don't overlap onto the next day |
283 | return [from, to] |
284 | end |
285 | |
286 | def find_published(what = :all, options = {}) |
287 | super(what, options) |
288 | end |
289 | |
290 | validates_uniqueness_of :guid |
291 | validates_presence_of :title |
292 | |
293 | private |
294 | |
295 | def fix_resources |
296 | Resource.find(:all, :conditions => "article_id = #{id}").each do |fu| |
297 | fu.article_id = nil |
298 | fu.save |
299 | end |
300 | end |
301 | end |