Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

580 lines (470 sloc) 15.527 kB
require 'uri'
require 'net/http'
class Article < Content
include TypoGuid
content_fields :body, :extended
has_many :pings, :dependent => :destroy, :order => "created_at ASC"
has_many :comments, :dependent => :destroy, :order => "created_at ASC" do
# Get only ham or presumed_ham comments
def ham
find :all, :conditions => {:state => ["presumed_ham", "ham"]}
end
# Get only spam or presumed_spam comments
def spam
find :all, :conditions => {:state => ["presumed_spam", "spam"]}
end
end
with_options(:conditions => { :published => true }, :order => 'created_at DESC') do |this|
this.has_many :published_comments, :class_name => "Comment", :order => "created_at ASC"
this.has_many :published_trackbacks, :class_name => "Trackback", :order => "created_at ASC"
this.has_many :published_feedback, :class_name => "Feedback", :order => "created_at ASC"
end
has_many :trackbacks, :dependent => :destroy, :order => "created_at ASC"
#TODO: change it because more logical with s in end : feedbacks
has_many :feedback, :order => "created_at DESC"
has_many :resources, :order => "created_at DESC",
:class_name => "Resource", :foreign_key => 'article_id'
after_destroy :fix_resources
has_many :categorizations
has_many :categories, \
:through => :categorizations, \
:include => :categorizations, \
:uniq => true, \
:order => 'categorizations.is_primary DESC, categories.position '
has_and_belongs_to_many :tags, :foreign_key => 'article_id'
named_scope :category, lambda {|category_id| {:conditions => ['categorizations.category_id = ?', category_id], :include => 'categorizations'}}
belongs_to :user
has_many :triggers, :as => :pending_item
after_save :post_trigger
attr_accessor :draft
has_state(:state,
:valid_states => [:new, :draft,
:publication_pending, :just_published, :published,
:just_withdrawn, :withdrawn],
:initial_state => :new,
:handles => [:withdraw,
:post_trigger,
:after_save, :send_pings, :send_notifications,
:published_at=, :just_published?])
include Article::States
class << self
def published_articles
find(:conditions => { :published => true }, :order => 'published_at DESC')
end
def count_published_articles
count(:conditions => { :published => true })
end
def search_no_draft_paginate(search_hash, paginate_hash)
list_function = ["Article.no_draft"] + function_search_no_draft(search_hash)
if search_hash[:category] and search_hash[:category].to_i > 0
list_function << 'category(search_hash[:category])'
end
paginate_hash[:order] = 'created_at DESC'
list_function << "paginate(paginate_hash)"
eval(list_function.join('.'))
end
end
accents = { ['á','à','â','ä','ã','Ã','Ä','Â','À'] => 'a',
['é','è','ê','ë','Ë','É','È','Ê'] => 'e',
['í','ì','î','ï','I','Î','Ì'] => 'i',
['ó','ò','ô','ö','õ','Õ','Ö','Ô','Ò'] => 'o',
['œ'] => 'oe',
['ß'] => 'ss',
['ú','ù','û','ü','U','Û','Ù'] => 'u',
['ç','Ç'] => 'c'
}
FROM, TO = accents.inject(['','']) { |o,(k,v)|
o[0] << k * '';
o[1] << v * k.size
o
}
def stripped_title
self.title.tr(FROM, TO).gsub(/<[^>]*>/, '').to_url
end
def year_url
published_at.year.to_s
end
def month_url
sprintf("%.2d", published_at.month)
end
def day_url
sprintf("%.2d", published_at.day)
end
def title_url
permalink.to_s
end
def permalink_url_options(nesting = false)
format_url = blog.permalink_format
format_url.gsub!('%year%', year_url)
format_url.gsub!('%month%', month_url)
format_url.gsub!('%day%', day_url)
format_url.gsub!('%title%', title_url)
if format_url[0,1] == '/'
format_url[1..-1]
else
format_url
end
end
def permalink_url(anchor=nil, only_path=true)
@cached_permalink_url ||= {}
@cached_permalink_url["#{anchor}#{only_path}"] ||= \
blog.url_for(permalink_url_options, :anchor => anchor, :only_path => only_path)
end
def param_array
@param_array ||=
returning([published_at.year,
sprintf('%.2d', published_at.month),
sprintf('%.2d', published_at.day),
permalink]) \
do |params|
this = self
k = class << params; self; end
k.send(:define_method, :to_s) { params[-1] }
end
end
def to_param
param_array
end
def trackback_url
blog.url_for("trackbacks?article_id=#{self.id}", :only_path => true)
end
def permalink_by_format(format=nil)
if format.nil?
permalink_url
elsif format.to_sym == :rss
feed_url(:rss)
elsif format.to_sym == :atom
feed_url(:atom)
else
raise UnSupportedFormat
end
end
def comment_url
blog.url_for("comments?article_id=#{self.id}")
end
def preview_comment_url
blog.url_for("comments/preview?article_id=#{self.id}")
end
def feed_url(format = :rss20)
blog.url_for(:controller => 'xml', :action => 'feed', :type => 'article', :format => format, :id => id)
end
def edit_url
blog.url_for(:controller => "/admin/content", :action =>"edit", :id => id)
end
def delete_url
blog.url_for(:controller => "/admin/content", :action =>"destroy", :id => id)
end
def html_urls
urls = Array.new
html.gsub(/<a [^>]*>/) do |tag|
if(tag =~ /href="([^"]+)"/)
urls.push($1)
end
end
urls.uniq
end
def really_send_pings(serverurl = blog.base_url, articleurl = nil)
return unless blog.send_outbound_pings
articleurl ||= permalink_url(nil, false)
weblogupdatesping_urls = blog.ping_urls.gsub(/ +/,'').split(/[\n\r]+/)
pingback_or_trackback_urls = self.html_urls
ping_urls = weblogupdatesping_urls + pingback_or_trackback_urls
ping_urls.uniq.each do |url|
begin
unless pings.collect { |p| p.url }.include?(url.strip)
ping = pings.build("url" => url)
if weblogupdatesping_urls.include?(url)
ping.send_weblogupdatesping(serverurl, articleurl)
elsif pingback_or_trackback_urls.include?(url)
ping.send_pingback_or_trackback(articleurl)
end
end
rescue Exception => e
logger.error(e)
# in case the remote server doesn't respond or gives an error,
# we should throw an xmlrpc error here.
end
end
end
def next
self.class.find(:first, :conditions => ['published_at > ?', published_at],
:order => 'published_at asc')
end
def previous
self.class.find(:first, :conditions => ['published_at < ?', published_at],
:order => 'published_at desc')
end
# Count articles on a certain date
def self.count_by_date(year, month = nil, day = nil, limit = nil)
if !year.blank?
count(:conditions => { :published_at => time_delta(year, month, day),
:published => true })
else
count(:conditions => { :published => true })
end
end
def self.find_by_published_at
super(:published_at)
end
# Finds one article which was posted on a certain date and matches the supplied dashed-title
# params is a Hash
def self.find_by_permalink(params)
date_range = self.time_delta(params[:year], params[:month], params[:day])
req_params = {}
if params[:title]
req_params[:permalink] = params[:title]
end
if date_range
req_params[:published_at] = date_range
end
return nil if req_params.empty? # no search if no params send
find_published(:first, :conditions => req_params) or raise ActiveRecord::RecordNotFound
end
def self.find_by_params_hash(params = {})
params[:title] ||= params[:article_id]
find_by_permalink(params)
end
# Fulltext searches the body of published articles
def self.search(query, args={})
query_s = query.to_s.strip
if !query_s.empty? && args.empty?
Article.searchstring(query)
elsif !query_s.empty? && !args.empty?
Article.searchstring(query).paginate(args)
else
[]
end
end
def keywords_to_tags
Article.transaction do
tags.clear
keywords.to_s.scan(/((['"]).*?\2|[\.\w]+)/).collect do |x|
x.first.tr("\"'", '')
end.uniq.each do |tagword|
tags << Tag.get(tagword)
end
end
end
def interested_users
User.find_boolean(:all, :notify_on_new_articles)
end
def notify_user_via_email(user)
if user.notify_via_email?
EmailNotify.send_article(self, user)
end
end
def comments_closed?
!(allow_comments? && in_feedback_window?)
end
def pings_closed?
!(allow_pings? && in_feedback_window?)
end
# check if time to comment is open or not
def in_feedback_window?
self.blog.sp_article_auto_close.zero? ||
self.created_at.to_i > self.blog.sp_article_auto_close.days.ago.to_i
end
def cast_to_boolean(value)
ActiveRecord::ConnectionAdapters::Column.value_to_boolean(value)
end
# Cast the input value for published= before passing it to the state.
def published=(newval)
state.published = cast_to_boolean(newval)
end
# Bloody rails reloading. Nasty workaround.
def allow_comments=(newval)
returning(cast_to_boolean(newval)) do |val|
if self[:allow_comments] != val
changed if published?
self[:allow_comments] = val
end
end
end
def allow_pings=(newval)
returning(cast_to_boolean(newval)) do |val|
if self[:allow_pings] != val
changed if published?
self[:allow_pings] = val
end
end
end
def body=(newval)
if self[:body] != newval
changed if published?
self[:body] = newval
end
self[:body]
end
def body_html
typo_deprecated "Use html(:body)"
html(:body)
end
def extended=(newval)
if self[:extended] != newval
changed if published?
self[:extended] = newval
end
self[:extended]
end
def extended_html
typo_deprecated "Use html(:extended)"
html(:extended)
end
def self.html_map(field=nil)
html_map = { :body => true, :extended => true }
if field
html_map[field.to_sym]
else
html_map
end
end
def content_fields
[:body, :extended]
end
# The web interface no longer distinguishes between separate "body" and
# "extended" fields, and instead edits everything in a single edit field,
# separating the extended content using "<!--more-->".
def body_and_extended
if extended.nil? || extended.empty?
body
else
body + "\n<!--more-->\n" + extended
end
end
# Split apart value around a "<!--more-->" comment and assign it to our
# #body and #extended fields.
def body_and_extended= value
parts = value.split(/\n?<!--more-->\n?/, 2)
self.body = parts[0]
self.extended = parts[1] || ''
end
## Feed Stuff
def rss_trackback(xml)
return unless allow_pings?
xml.trackback :ping, trackback_url
end
def rss_enclosure(xml)
return if resources.empty?
res = resources.first
xml.enclosure(:url => blog.file_url(res.filename),
:length => res.size,
:type => res.mime)
end
def rss_groupings(xml)
categories.each { |v| v.to_rss(xml) }
tags.each { |v| v.to_rss(xml) }
end
def rss_author(xml)
if link_to_author?
xml.author("#{user.email} (#{user.name})")
end
end
def rss_comments(xml)
xml.comments(permalink_url + "#comments")
end
def link_to_author?
!user.email.blank? && blog.link_to_author
end
def rss_title(xml)
xml.title title
end
def atom_author(xml)
xml.author { xml.name user.name }
end
def atom_title(xml)
xml.title title, "type" => "html"
end
def atom_groupings(xml)
categories.each {|v| v.to_atom(xml) }
tags.each { |v| v.to_atom(xml) }
end
def atom_enclosures(xml)
resources.each do |value|
xml.with_options(value.size > 0 ? { :length => value.size } : { }) do |xm|
xm.link "rel" => "enclosure",
:type => value.mime,
:title => title,
:href => blog.file_url(value.filename)
end
end
end
def atom_content(xml)
if self.user && self.user.name
rss_desc = "<hr /><p><small>#{_('Original article writen by')} #{self.user.name} #{_('and published on')} <a href='#{blog.base_url}'>#{blog.blog_name}</a> | <a href='#{self.permalink_url}'>#{_('direct link to this article')}</a> | #{_('If you are reading this article elsewhere than')} <a href='#{blog.base_url}'>#{blog.blog_name}</a>, #{_('it has been illegally reproduced and without proper authorization')}.</small></p>"
else
rss_desc = ""
end
# This HTMLEntities is use to convert bad entities on dabase. We can check
# some bad data insert by FCKEditor. We can found to &eacute; by exemple.
# If we doesn't change that, the atom feed is invalid
coder = HTMLEntities.new
post = coder.decode(html(blog.show_extended_on_rss ? :all : :body))
content = blog.rss_description ? post + rss_desc : post
xml.summary "type" => "xhtml" do
xml.div(:xmlns => "http://www.w3.org/1999/xhtml") {xml << coder.decode(html(:body)) }
end
if blog.show_extended_on_rss
xml.content(:type => "xhtml") do
xml.div(:xmlns => 'http://www.w3.org/1999/xhtml') { xml << content }
end
end
end
def add_comment(params)
comments.build(params)
end
def add_category(category, is_primary = false)
self.categorizations.build(:category => category, :is_primary => is_primary)
end
def access_by?(user)
user.admin? || user_id == user.id
end
protected
before_create :set_defaults, :create_guid
before_save :set_published_at
after_save :keywords_to_tags
after_create :add_notifications
def set_published_at
if self.published and self[:published_at].nil?
self[:published_at] = self.created_at || Time.now
end
end
def set_defaults
if self.attributes.include?("permalink") and
(self.permalink.blank? or
self.permalink.to_s =~ /article-draft/ or
self.state == "draft"
)
self.permalink = self.stripped_title
end
if blog && self.allow_comments.nil?
self.allow_comments = blog.default_allow_comments
end
if blog && self.allow_pings.nil?
self.allow_pings = blog.default_allow_pings
end
true
end
def add_notifications
self.notify_users = User.find_boolean(:all, :notify_on_new_articles)
self.notify_users << self.user if (self.user.notify_watch_my_articles? rescue false)
self.notify_users.uniq!
end
def self.time_delta(year = nil, month = nil, day = nil)
return nil if year.nil? && month.nil? && day.nil?
from = Time.mktime(year, month || 1, day || 1)
to = from.next_year
to = from.next_month unless month.blank?
to = from + 1.day unless day.blank?
to = to - 1 # pull off 1 second so we don't overlap onto the next day
return from..to
end
validates_uniqueness_of :guid
validates_presence_of :title
private
def fix_resources
Resource.find(:all, :conditions => "article_id = #{id}").each do |fu|
fu.article_id = nil
fu.save
end
end
end
Jump to Line
Something went wrong with that request. Please try again.