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

Ajax

开发平台:

Others

  1. require 'set'
  2. class Content < ActiveRecord::Base
  3.   belongs_to :text_filter
  4.   has_many :notifications, :foreign_key => 'content_id'
  5.   has_many :notify_users, :through => :notifications,
  6.     :source => 'notify_user',
  7.     :uniq => true
  8.   def notify_users=(collection)
  9.     return notify_users.clear if collection.empty?
  10.     self.class.transaction do
  11.       self.notifications.clear
  12.       collection.uniq.each do |u|
  13.         self.notifications.build(:notify_user => u)
  14.       end
  15.       notify_users.target = collection
  16.     end
  17.   end
  18.   has_many :triggers, :as => :pending_item, :dependent => :delete_all
  19.   named_scope :published_at_like, lambda {|date_at| {:conditions => {
  20.     :published_at => (
  21.       if date_at =~ /d{4}-d{2}-d{2}/
  22.         DateTime.strptime(date_at, '%Y-%m-%d').beginning_of_day..DateTime.strptime(date_at, '%Y-%m-%d').end_of_day
  23.       elsif date_at =~ /d{4}-d{2}/
  24.         DateTime.strptime(date_at, '%Y-%m').beginning_of_month..DateTime.strptime(date_at, '%Y-%m').end_of_month
  25.       elsif date_at =~ /d{4}/
  26.         DateTime.strptime(date_at, '%Y').beginning_of_year..DateTime.strptime(date_at, '%Y').end_of_year
  27.       else
  28.         date_at
  29.       end
  30.     )}
  31.   }
  32.   }
  33.   named_scope :user_id, lambda {|user_id| {:conditions => ['user_id = ?', user_id]}}
  34.   named_scope :published, {:conditions => ['published = ?', true]}
  35.   named_scope :order, lambda {|order_by| {:order => order_by}}
  36.   named_scope :not_published, {:conditions => ['published = ?', false]}
  37.   named_scope :draft, {:conditions => ['state = ?', 'draft']}
  38.   named_scope :no_draft, {:conditions => ['state <> ?', 'draft'], :order => 'created_at DESC'}
  39.   named_scope :searchstring, lambda {|search_string|
  40.     tokens = search_string.split(' ').collect {|c| "%#{c.downcase}%"}
  41.     {:conditions => ['state = ? AND ' + (['(LOWER(body) LIKE ? OR LOWER(extended) LIKE ? OR LOWER(title) LIKE ?)']*tokens.size).join(' AND '),
  42.                         "published", *tokens.collect{ |token| [token] * 3 }.flatten]}
  43.   }
  44.   named_scope :already_published, lambda { {:conditions => ['published = ? AND published_at < ?', true, Time.now],
  45.     :order => default_order,
  46.     }}
  47.   serialize :whiteboard
  48.   attr_accessor :just_changed_published_status
  49.   alias_method :just_changed_published_status?, :just_changed_published_status
  50.   include Stateful
  51.   @@content_fields = Hash.new
  52.   @@html_map       = Hash.new
  53.   def invalidates_cache?(on_destruction = false)
  54.     if on_destruction
  55.       just_changed_published_status? || published?
  56.     else
  57.       changed? && published? || just_changed_published_status?
  58.     end
  59.   end
  60.   class << self
  61.     # FIXME: Quite a bit of this isn't needed anymore.
  62.     def content_fields(*attribs)
  63.       @@content_fields[self] = ((@@content_fields[self]||[]) + attribs).uniq
  64.       @@html_map[self] = nil
  65.       attribs.each do | field |
  66.         define_method("#{field}=") do | newval |
  67.           if self[field] != newval
  68.             changed
  69.             self[field] = newval
  70.           end
  71.           self[field]
  72.         end
  73.         unless self.method_defined?("#{field}_html")
  74.           define_method("#{field}_html") do
  75.             html(field.to_sym)
  76.           end
  77.         end
  78.       end
  79.     end
  80.     def html_map(field=nil)
  81.       unless @@html_map[self]
  82.         @@html_map[self] = Hash.new
  83.         instance = self.new
  84.         @@content_fields[self].each do |attrib|
  85.           @@html_map[self][attrib] = true
  86.         end
  87.       end
  88.       if field
  89.         @@html_map[self][field]
  90.       else
  91.         @@html_map[self]
  92.       end
  93.     end
  94.     def find_published(what = :all, options = {})
  95.       with_scope(:find => {:order => default_order, :conditions => {:published => true}}) do
  96.         find what, options
  97.       end
  98.     end
  99.     def default_order
  100.       'published_at DESC'
  101.     end
  102.     def find_already_published(what = :all, at = nil, options = { })
  103.       if what.respond_to?(:has_key?)
  104.         what, options = :all, what
  105.       elsif at.respond_to?(:has_key?)
  106.         options, at = at, nil
  107.       end
  108.       at ||= options.delete(:at) || Time.now
  109.       with_scope(:find => { :conditions => ['published_at < ?', at]}) do
  110.         find_published(what, options)
  111.       end
  112.     end
  113.     def find_by_published_at(column_name = :published_at)
  114.       from_where = "FROM #{self.table_name} WHERE #{column_name} is not NULL AND type='#{self.name}'"
  115.       # Implement adapter-specific groupings below, or allow us to fall through to the generic ruby-side grouping
  116.       if defined?(ActiveRecord::ConnectionAdapters::MysqlAdapter) && self.connection.is_a?(ActiveRecord::ConnectionAdapters::MysqlAdapter)
  117.         # MySQL uses date_format
  118.         find_by_sql("SELECT date_format(#{column_name}, '%Y-%m') AS publication #{from_where} GROUP BY publication ORDER BY publication DESC")
  119.       elsif defined?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter) && self.connection.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
  120.         # PostgreSQL uses to_char
  121.         find_by_sql("SELECT to_char(#{column_name}, 'YYYY-MM') AS publication #{from_where} GROUP BY publication ORDER BY publication DESC")
  122.       else
  123.         # If we don't have an adapter-safe conversion from date -> YYYY-MM,
  124.         # we'll do the GROUP BY server-side. There won't be very many objects
  125.         # in this array anyway.
  126.         date_map = {}
  127.         dates = find_by_sql("SELECT #{column_name} AS publication #{from_where}")
  128.         dates.map! do |d|
  129.           d.publication = Time.parse(d.publication).strftime('%Y-%m')
  130.           d.freeze
  131.           if !date_map.has_key?(d.publication)
  132.             date_map[d.publication] = true
  133.             d
  134.           end
  135.         end
  136.         dates.reject!{|d| d.blank? || d.publication.blank?}
  137.         dates.sort!{|a,b| b.publication <=> a.publication}
  138.         dates
  139.       end
  140.     end
  141.     def function_search_no_draft(search_hash)
  142.       list_function = []
  143.       if search_hash.nil?
  144.         search_hash = {}
  145.       end
  146.       if search_hash[:searchstring] 
  147.         list_function << 'searchstring(search_hash[:searchstring])' unless search_hash[:searchstring].to_s.empty?
  148.       end
  149.       if search_hash[:published_at] and %r{(dddd)-(dd)} =~ search_hash[:published_at]
  150.         list_function << 'published_at_like(search_hash[:published_at])'
  151.       end
  152.       if search_hash[:user_id] && search_hash[:user_id].to_i > 0
  153.         list_function << 'user_id(search_hash[:user_id])'
  154.       end
  155.       if search_hash[:published]      
  156.         list_function << 'published' if search_hash[:published].to_s == '1'
  157.         list_function << 'not_published' if search_hash[:published].to_s == '0'
  158.       end
  159.       
  160.       list_function
  161.     end
  162.   end
  163.   def content_fields
  164.     @@content_fields[self.class]
  165.   end
  166.   def html_map(field=nil)
  167.     self.class.html_map(field)
  168.   end
  169.   def cache_key(field)
  170.     id ? "contents_html/#{id}/#{field}" : nil
  171.   end
  172.   # Return HTML for some part of this object.  It will be fetched from the
  173.   # cache if possible, or regenerated if needed.
  174.   def html(field = :all)
  175.     if field == :all
  176.       generate_html(:all, content_fields.map{|f| self[f].to_s}.join("nn"))
  177.     elsif self.class.html_map(field)
  178.       generate_html(field)
  179.     else
  180.       raise "Unknown field: #{field.inspect} in content.html"
  181.     end
  182.   end
  183.   # Generate HTML for a specific field using the text_filter in use for this
  184.   # object.  The HTML is cached in the fragment cache, using the +ContentCache+
  185.   # object in @@cache.
  186.   def generate_html(field, text = nil)
  187.     text ||= self[field].to_s
  188.     html = text_filter.filter_text_for_content(blog, text, self) || text
  189.     html_postprocess(field,html).to_s
  190.   end
  191.   # Post-process the HTML.  This is a noop by default, but Comment overrides it
  192.   # to enforce HTML sanity.
  193.   def html_postprocess(field,html)
  194.     html
  195.   end
  196.   def whiteboard
  197.     self[:whiteboard] ||= Hash.new
  198.   end
  199.   # The default text filter.  Generally, this is the filter specified by blog.text_filter,
  200.   # but comments may use a different default.
  201.   def default_text_filter
  202.     blog.text_filter.to_text_filter
  203.   end
  204.   # Grab the text filter for this object.  It's either the filter specified by
  205.   # self.text_filter_id, or the default specified in the default blog object.
  206.   def text_filter
  207.     if self[:text_filter_id] && !self[:text_filter_id].zero?
  208.       TextFilter.find(self[:text_filter_id])
  209.     else
  210.       default_text_filter
  211.     end
  212.   end
  213.   # Set the text filter for this object.
  214.   def text_filter=(filter)
  215.     returning(filter.to_text_filter) do |tf|
  216.       if tf.id != text_filter_id
  217.         changed if !new_record? && published?
  218.       end
  219.       self.text_filter_id = tf.id
  220.     end
  221.   end
  222.   # Changing the title flags the object as changed
  223.   def title=(new_title)
  224.     if new_title == self[:title]
  225.       self[:title]
  226.     else
  227.       changed if !new_record? && published?
  228.       self[:title] = new_title
  229.     end
  230.   end
  231.   def blog
  232.     @blog ||= Blog.default
  233.   end
  234.   def publish!
  235.     self.published = true
  236.     self.save!
  237.   end
  238.   def withdraw!
  239.     self.withdraw
  240.     self.save!
  241.   end
  242.   def published_at
  243.     self[:published_at] || self[:created_at]
  244.   end
  245.   def send_notification_to_user(user)
  246.     notify_user_via_email(user)
  247.   end
  248.   def really_send_notifications
  249.     returning true do
  250.       interested_users.each do |value|
  251.         send_notification_to_user(value)
  252.       end
  253.     end
  254.   end
  255.   def to_atom xml
  256.     xml.entry self, :url => permalink_url do |entry|
  257.       atom_author(entry)
  258.       atom_title(entry)
  259.       atom_groupings(entry)
  260.       atom_enclosures(entry)
  261.       atom_content(entry)
  262.     end
  263.   end
  264.   def to_rss(xml)
  265.     xml.item do
  266.       rss_title(xml)
  267.       rss_description(xml)
  268.       xml.pubDate published_at.rfc822
  269.       xml.guid "urn:uuid:#{guid}", :isPermaLink => "false"
  270.       rss_author(xml)
  271.       rss_comments(xml)
  272.       rss_groupings(xml)
  273.       rss_enclosure(xml)
  274.       rss_trackback(xml)
  275.       xml.link normalized_permalink_url
  276.     end
  277.   end
  278.   def rss_comments(xml)
  279.   end
  280.   def rss_description(xml)
  281.     post = html(blog.show_extended_on_rss ? :all : :body)
  282.     if blog.rss_description
  283.       if respond_to?(:user) && self.user && self.user.name
  284.         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>"
  285.       else
  286.         rss_desc = ""
  287.       end
  288.       post = post + rss_desc
  289.     end
  290.     xml.description(post)
  291.   end
  292.   def rss_groupings(xml)
  293.   end
  294.   def rss_enclosure(xml)
  295.   end
  296.   def rss_trackback(xml)
  297.   end
  298.   def atom_groupings(xml)
  299.   end
  300.   def atom_enclosures(xml)
  301.   end
  302.   def atom_content(entry)
  303.     entry.content(html(:all), :type => 'html')
  304.   end
  305.   # TODO: Perhaps permalink_url should produce valid URI's instead of IRI's
  306.   def normalized_permalink_url
  307.     @normalized_permalink_url ||= Addressable::URI.parse(permalink_url).normalize
  308.   end
  309. end
  310. class Object
  311.   def to_text_filter
  312.     TextFilter.find_by_name(self.to_s) || TextFilter.find_by_name('none')
  313.   end
  314. end
  315. class ContentTextHelpers
  316.   include ActionView::Helpers::UrlHelper
  317.   include ActionView::Helpers::TagHelper
  318.   include ActionView::Helpers::SanitizeHelper
  319.   include ActionView::Helpers::TextHelper
  320.   extend ActionView::Helpers::SanitizeHelper::ClassMethods
  321. end