article.rb
上传用户:netsea168
上传日期:2022-07-22
资源大小:4652k
文件大小:15k
源码类别:

Ajax

开发平台:

Others

  1. require 'uri'
  2. require 'net/http'
  3. class Article < Content
  4.   include TypoGuid
  5.   content_fields :body, :extended
  6.   has_many :pings,      :dependent => :destroy, :order => "created_at ASC"
  7.   has_many :comments,   :dependent => :destroy, :order => "created_at ASC" do
  8.     # Get only ham or presumed_ham comments
  9.     def ham
  10.       find :all, :conditions => {:state => ["presumed_ham", "ham"]}
  11.     end
  12.     # Get only spam or presumed_spam comments
  13.     def spam
  14.       find :all, :conditions => {:state => ["presumed_spam", "spam"]}
  15.     end
  16.   end
  17.   with_options(:conditions => { :published => true }, :order => 'created_at DESC') do |this|
  18.     this.has_many :published_comments,   :class_name => "Comment", :order => "created_at ASC"
  19.     this.has_many :published_trackbacks, :class_name => "Trackback", :order => "created_at ASC"
  20.     this.has_many :published_feedback,   :class_name => "Feedback", :order => "created_at ASC"
  21.   end
  22.   has_many :trackbacks, :dependent => :destroy, :order => "created_at ASC"
  23.   #TODO: change it because more logical with s in end : feedbacks
  24.   has_many :feedback, :order => "created_at DESC"
  25.   has_many :resources, :order => "created_at DESC",
  26.            :class_name => "Resource", :foreign_key => 'article_id'
  27.   after_destroy :fix_resources
  28.   has_many :categorizations
  29.   has_many :categories, 
  30.     :through => :categorizations, 
  31.     :include => :categorizations, 
  32.     :select => 'categories.*', 
  33.     :uniq => true, 
  34.     :order => 'categorizations.is_primary DESC'
  35.   has_and_belongs_to_many :tags, :foreign_key => 'article_id'
  36.   named_scope :category, lambda {|category_id| {:conditions => ['categorizations.category_id = ?', category_id], :include => 'categorizations'}}
  37.   named_scope :drafts, :conditions => ['state = ?', 'draft']
  38.   named_scope :without_parent, {:conditions => {:parent_id => nil}}
  39.   named_scope :child_of, lambda { |article_id| {:conditions => {:parent_id => article_id}} }
  40.   def has_child?
  41.     Article.exists?({:parent_id => self.id})
  42.   end
  43.   belongs_to :user
  44.   has_many :triggers, :as => :pending_item
  45.   after_save :post_trigger
  46.   attr_accessor :draft
  47.   has_state(:state,
  48.             :valid_states  => [:new, :draft,
  49.                                :publication_pending, :just_published, :published,
  50.                                :just_withdrawn, :withdrawn],
  51.             :initial_state =>  :new,
  52.             :handles       => [:withdraw,
  53.                                :post_trigger,
  54.                                :after_save, :send_pings, :send_notifications,
  55.                                :published_at=, :just_published?])
  56.   include Article::States
  57.   class << self
  58.     def last_draft(article_id)
  59.       article = Article.find(article_id)
  60.       while article.has_child?
  61.         article = Article.child_of(article.id).first
  62.       end
  63.       article
  64.     end
  65.     def published_articles
  66.       find(:conditions => { :published => true }, :order => 'published_at DESC')
  67.     end
  68.     def count_published_articles
  69.       count(:conditions => { :published => true })
  70.     end
  71.     def search_no_draft_paginate(search_hash, paginate_hash)
  72.       list_function  = ["Article.no_draft"] + function_search_no_draft(search_hash)
  73.       if search_hash[:category] and search_hash[:category].to_i > 0
  74.         list_function << 'category(search_hash[:category])'
  75.       end
  76.       paginate_hash[:order] = 'published_at DESC'
  77.       list_function << "paginate(paginate_hash)"
  78.       eval(list_function.join('.'))
  79.     end
  80.   end
  81.   accents = { ['á','à','â','ä','ã','Ã','Ä','Â','À'] => 'a',
  82.     ['é','è','ê','ë','Ë','É','È','Ê'] => 'e',
  83.     ['í','ì','î','ï','I','Î','Ì'] => 'i',
  84.     ['ó','ò','ô','ö','õ','Õ','Ö','Ô','Ò'] => 'o',
  85.     ['œ'] => 'oe',
  86.     ['ß'] => 'ss',
  87.     ['ú','ù','û','ü','U','Û','Ù'] => 'u',
  88.     ['ç','Ç'] => 'c'
  89.   }
  90.   FROM, TO = accents.inject(['','']) { |o,(k,v)|
  91.     o[0] << k * '';
  92.     o[1] << v * k.size
  93.     o
  94.   }
  95.   def stripped_title
  96.     CGI.escape(self.title.tr(FROM, TO).gsub(/<[^>]*>/, '').to_url)
  97.   end
  98.   def year_url
  99.     published_at.year.to_s
  100.   end
  101.   def month_url
  102.     sprintf("%.2d", published_at.month)
  103.   end
  104.   def day_url
  105.     sprintf("%.2d", published_at.day)
  106.   end
  107.   def title_url
  108.     permalink.to_s
  109.   end
  110.   def permalink_url_options(nesting = false)
  111.     format_url = blog.permalink_format.dup
  112.     format_url.gsub!('%year%', year_url)
  113.     format_url.gsub!('%month%', month_url)
  114.     format_url.gsub!('%day%', day_url)
  115.     format_url.gsub!('%title%', title_url)
  116.     if format_url[0,1] == '/'
  117.       format_url[1..-1]
  118.     else
  119.       format_url
  120.     end
  121.   end
  122.   def permalink_url(anchor=nil, only_path=false)
  123.     @cached_permalink_url ||= {}
  124.     @cached_permalink_url["#{anchor}#{only_path}"] ||= 
  125.       blog.url_for(permalink_url_options, :anchor => anchor, :only_path => only_path)
  126.   end
  127.   def param_array
  128.     @param_array ||=
  129.       returning([published_at.year,
  130.                  sprintf('%.2d', published_at.month),
  131.                  sprintf('%.2d', published_at.day),
  132.                  permalink]) 
  133.       do |params|
  134.         this = self
  135.         k = class << params; self; end
  136.         k.send(:define_method, :to_s) { params[-1] }
  137.       end
  138.   end
  139.   def to_param
  140.     param_array
  141.   end
  142.   def trackback_url
  143.     blog.url_for("trackbacks?article_id=#{self.id}", :only_path => false)
  144.   end
  145.   def permalink_by_format(format=nil)
  146.     if format.nil?
  147.       permalink_url
  148.     elsif format.to_sym == :rss
  149.       feed_url(:rss)
  150.     elsif format.to_sym == :atom
  151.       feed_url(:atom)
  152.     else
  153.       raise UnSupportedFormat
  154.     end
  155.   end
  156.   def comment_url
  157.     blog.url_for("comments?article_id=#{self.id}", :only_path => false)
  158.   end
  159.   def preview_comment_url
  160.     blog.url_for("comments/preview?article_id=#{self.id}", :only_path => false)
  161.   end
  162.   def feed_url(format = :rss20)
  163.     format_extension = format.to_s.gsub(/d/,'')
  164.     permalink_url + ".#{format_extension}"
  165.   end
  166.   def edit_url
  167.     blog.url_for(:controller => "/admin/content", :action =>"edit", :id => id)
  168.   end
  169.   def delete_url
  170.     blog.url_for(:controller => "/admin/content", :action =>"destroy", :id => id)
  171.   end
  172.   def html_urls
  173.     urls = Array.new
  174.     html.gsub(/<as+[^>]*>/) do |tag|
  175.       if(tag =~ /bhref=(["']?)([^ >"]+)1/)
  176.         urls.push($2)
  177.       end
  178.     end
  179.     urls.uniq
  180.   end
  181.   def really_send_pings(serverurl = blog.base_url, articleurl = nil)
  182.     return unless blog.send_outbound_pings
  183.     articleurl ||= permalink_url(nil)
  184.     weblogupdatesping_urls = blog.ping_urls.gsub(/ +/,'').split(/[nr]+/)
  185.     pingback_or_trackback_urls = self.html_urls
  186.     ping_urls = weblogupdatesping_urls + pingback_or_trackback_urls
  187.     ping_urls.uniq.each do |url|
  188.       begin
  189.         unless pings.collect { |p| p.url }.include?(url.strip)
  190.           ping = pings.build("url" => url)
  191.           if weblogupdatesping_urls.include?(url)
  192.             ping.send_weblogupdatesping(serverurl, articleurl)
  193.           elsif pingback_or_trackback_urls.include?(url)
  194.             ping.send_pingback_or_trackback(articleurl)
  195.           end
  196.         end
  197.       rescue Exception => e
  198.         logger.error(e)
  199.         # in case the remote server doesn't respond or gives an error,
  200.         # we should throw an xmlrpc error here.
  201.       end
  202.     end
  203.   end
  204.   def next
  205.     self.class.find(:first, :conditions => ['published_at > ?', published_at],
  206.                     :order => 'published_at asc')
  207.   end
  208.   def previous
  209.     self.class.find(:first, :conditions => ['published_at < ?', published_at],
  210.                     :order => 'published_at desc')
  211.   end
  212.   # Count articles on a certain date
  213.   def self.count_by_date(year, month = nil, day = nil, limit = nil)
  214.     if !year.blank?
  215.       count(:conditions => { :published_at => time_delta(year, month, day),
  216.               :published => true })
  217.     else
  218.       count(:conditions => { :published => true })
  219.     end
  220.   end
  221.   def self.find_by_published_at
  222.     super(:published_at)
  223.   end
  224.   # Finds one article which was posted on a certain date and matches the supplied dashed-title
  225.   # params is a Hash
  226.   def self.find_by_permalink(params)
  227.     date_range = self.time_delta(params[:year], params[:month], params[:day])
  228.     req_params = {}
  229.     if params[:title]
  230.       req_params[:permalink] = params[:title]
  231.     end
  232.     if date_range
  233.       req_params[:published_at] = date_range
  234.     end
  235.     return nil if req_params.empty? # no search if no params send
  236.     find_published(:first, :conditions => req_params) or raise ActiveRecord::RecordNotFound
  237.   end
  238.   def self.find_by_params_hash(params = {})
  239.     params[:title] ||= params[:article_id]
  240.     find_by_permalink(params)
  241.   end
  242.   # Fulltext searches the body of published articles
  243.   def self.search(query, args={})
  244.     query_s = query.to_s.strip
  245.     if !query_s.empty? && args.empty?
  246.       Article.searchstring(query)
  247.     elsif !query_s.empty? && !args.empty?
  248.       Article.searchstring(query).paginate(args)
  249.     else
  250.       []
  251.     end
  252.   end
  253.   def keywords_to_tags
  254.     Article.transaction do
  255.       tags.clear
  256.       keywords.to_s.scan(/((['"]).*?2|[.w]+)/).collect do |x|
  257.         x.first.tr(""'", '')
  258.       end.uniq.each do |tagword|
  259.         tags << Tag.get(tagword)
  260.       end
  261.     end
  262.   end
  263.   def interested_users
  264.     User.find_boolean(:all, :notify_on_new_articles)
  265.   end
  266.   def notify_user_via_email(user)
  267.     if user.notify_via_email?
  268.       EmailNotify.send_article(self, user)
  269.     end
  270.   end
  271.   def comments_closed?
  272.     !(allow_comments? && in_feedback_window?)
  273.   end
  274.   def pings_closed?
  275.     !(allow_pings? && in_feedback_window?)
  276.   end
  277.   # check if time to comment is open or not
  278.   def in_feedback_window?
  279.     self.blog.sp_article_auto_close.zero? ||
  280.       self.created_at.to_i > self.blog.sp_article_auto_close.days.ago.to_i
  281.   end
  282.   def cast_to_boolean(value)
  283.     ActiveRecord::ConnectionAdapters::Column.value_to_boolean(value)
  284.   end
  285.   # Cast the input value for published= before passing it to the state.
  286.   def published=(newval)
  287.     state.published = cast_to_boolean(newval)
  288.   end
  289.   # Bloody rails reloading. Nasty workaround.
  290.   def allow_comments=(newval)
  291.     returning(cast_to_boolean(newval)) do |val|
  292.       if self[:allow_comments] != val
  293.         changed if published?
  294.         self[:allow_comments] = val
  295.       end
  296.     end
  297.   end
  298.   def allow_pings=(newval)
  299.     returning(cast_to_boolean(newval)) do |val|
  300.       if self[:allow_pings] != val
  301.         changed if published?
  302.         self[:allow_pings] = val
  303.       end
  304.     end
  305.   end
  306.   def body=(newval)
  307.     if self[:body] != newval
  308.       changed if published?
  309.       self[:body] = newval
  310.     end
  311.     self[:body]
  312.   end
  313.   def extended=(newval)
  314.     if self[:extended] != newval
  315.       changed if published?
  316.       self[:extended] = newval
  317.     end
  318.     self[:extended]
  319.   end
  320.   def self.html_map(field=nil)
  321.     html_map = { :body => true, :extended => true }
  322.     if field
  323.       html_map[field.to_sym]
  324.     else
  325.       html_map
  326.     end
  327.   end
  328.   def content_fields
  329.     [:body, :extended]
  330.   end
  331.   # The web interface no longer distinguishes between separate "body" and
  332.   # "extended" fields, and instead edits everything in a single edit field,
  333.   # separating the extended content using "<!--more-->".
  334.   def body_and_extended
  335.     if extended.nil? || extended.empty?
  336.       body
  337.     else
  338.       body + "n<!--more-->n" + extended
  339.     end
  340.   end
  341.   # Split apart value around a "<!--more-->" comment and assign it to our
  342.   # #body and #extended fields.
  343.   def body_and_extended= value
  344.     parts = value.split(/n?<!--more-->n?/, 2)
  345.     self.body = parts[0]
  346.     self.extended = parts[1] || ''
  347.   end
  348.   ## Feed Stuff
  349.   def rss_trackback(xml)
  350.     return unless allow_pings?
  351.     xml.trackback :ping, trackback_url
  352.   end
  353.   def rss_enclosure(xml)
  354.     return if resources.empty?
  355.     res = resources.first
  356.     xml.enclosure(:url    => blog.file_url(res.filename),
  357.                   :length => res.size,
  358.                   :type   => res.mime)
  359.   end
  360.   def rss_groupings(xml)
  361.     categories.each { |v| v.to_rss(xml) }
  362.     tags.each       { |v| v.to_rss(xml) }
  363.   end
  364.   def rss_author(xml)
  365.     if link_to_author?
  366.       xml.author("#{user.email} (#{user.name})")
  367.     end
  368.   end
  369.   def rss_comments(xml)
  370.     xml.comments(normalized_permalink_url + "#comments")
  371.   end
  372.   def link_to_author?
  373.     !user.email.blank? && blog.link_to_author
  374.   end
  375.   def rss_title(xml)
  376.     xml.title title
  377.   end
  378.   def atom_author(xml)
  379.     xml.author { xml.name user.name }
  380.   end
  381.   def atom_title(xml)
  382.     xml.title title, "type" => "html"
  383.   end
  384.   def atom_groupings(xml)
  385.     categories.each {|v| v.to_atom(xml) }
  386.     tags.each { |v| v.to_atom(xml) }
  387.   end
  388.   def atom_enclosures(xml)
  389.     resources.each do |value|
  390.       xml.with_options(value.size > 0 ? { :length => value.size } : { }) do |xm|
  391.         xm.link "rel" => "enclosure",
  392.         :type => value.mime,
  393.         :title => title,
  394.         :href => blog.file_url(value.filename)
  395.       end
  396.     end
  397.   end
  398.   def atom_content(entry)
  399.     if self.user && self.user.name
  400.       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>"
  401.     else
  402.       rss_desc = ""
  403.     end
  404.     post = blog.show_extended_on_rss ? post = html(:all) : post = html(:body)
  405.     content = blog.rss_description ? post + rss_desc : post
  406.     entry.content(content, :type => "html")
  407.   end
  408.   def add_comment(params)
  409.     comments.build(params)
  410.   end
  411.   def add_category(category, is_primary = false)
  412.     self.categorizations.build(:category => category, :is_primary => is_primary)
  413.   end
  414.   def access_by?(user)
  415.     user.admin? || user_id == user.id
  416.   end
  417.   protected
  418.   before_create :set_defaults, :create_guid
  419.   before_save :set_published_at
  420.   after_save :keywords_to_tags
  421.   after_create :add_notifications
  422.   def set_published_at
  423.     if self.published and self[:published_at].nil?
  424.       self[:published_at] = self.created_at || Time.now
  425.     end
  426.   end
  427.   def set_defaults
  428.     if self.attributes.include?("permalink") and
  429.           (self.permalink.blank? or
  430.           self.permalink.to_s =~ /article-draft/ or
  431.           self.state == "draft"
  432.     )
  433.       self.permalink = self.stripped_title
  434.     end
  435.     if blog && self.allow_comments.nil?
  436.       self.allow_comments = blog.default_allow_comments
  437.     end
  438.     if blog && self.allow_pings.nil?
  439.       self.allow_pings = blog.default_allow_pings
  440.     end
  441.     true
  442.   end
  443.   def add_notifications
  444.     self.notify_users = User.find_boolean(:all, :notify_on_new_articles)
  445.     self.notify_users << self.user if (self.user.notify_watch_my_articles? rescue false)
  446.     self.notify_users.uniq!
  447.   end
  448.   def self.time_delta(year = nil, month = nil, day = nil)
  449.     return nil if year.nil? && month.nil? && day.nil?
  450.     from = Time.mktime(year, month || 1, day || 1)
  451.     to = from.next_year
  452.     to = from.next_month unless month.blank?
  453.     to = from + 1.day unless day.blank?
  454.     to = to - 1 # pull off 1 second so we don't overlap onto the next day
  455.     return from..to
  456.   end
  457.   validates_uniqueness_of :guid
  458.   validates_presence_of :title
  459.   private
  460.   def fix_resources
  461.     Resource.find(:all, :conditions => "article_id = #{id}").each do |fu|
  462.       fu.article_id = nil
  463.       fu.save
  464.     end
  465.   end
  466. end