# root_id:nil => 主贴; 有值 => 主帖的ID(值为帖子的ID) # parent_id: 回复(对这个id的回复) # sticky 顶置 1 # replies_count 表示帖子的主回复(一级回复数) # all_replies_count 表示帖子的所有回复(一级回复数 + 二级回复数) require 'elasticsearch/model' class Memo < ActiveRecord::Base include Redmine::SafeAttributes include UserScoreHelper include ApplicationHelper include Elasticsearch::Model belongs_to :forum has_many_kindeditor_assets :assets, :dependent => :destroy has_many :tidings, :as => :container , :dependent => :destroy belongs_to :author, :class_name => "User", :foreign_key => 'author_id' has_many :tag_repertoires, :through => :memo_tag_repertoires has_many :memo_tag_repertoires, :dependent => :destroy validates_presence_of :author_id, :forum_id, :subject,:content # 若是主题帖,则内容可以是空 #validates :content, presence: true, if: Proc.new{|o| !o.parent_id.nil? } validates_length_of :subject, maximum: 100 validate :cannot_reply_to_locked_topic, :on => :create #elasticsearch kaminari init Kaminari::Hooks.init Elasticsearch::Model::Response::Response.__send__ :include, Elasticsearch::Model::Response::Pagination::Kaminari settings index: { number_of_shards: 5 , analysis: { char_filter: { and_filter: { type: "mapping", mappings: [ "&=> and "] } }, analyzer: { my_analyzer: { type: 'custom', tokenizer: 'standard', filter: ['classic'], char_filter: ['html_strip'] } } } } do mappings dynamic: 'false' do indexes :subject, analyzer: 'smartcn',index_options: 'offsets'#, char_filter: 'html_strip' indexes :content, analyzer:'my_analyzer',index_options: 'offsets',search_analyzer: 'smartcn' indexes :updated_at,index:"not_analyzed" ,type:'date' end end acts_as_tree :counter_cache => :replies_count, :order => "#{Memo.table_name}.created_at ASC" acts_as_attachable has_many :user_score_details, :class_name => 'UserScoreDetails',:as => :score_changeable_obj has_many :praise_tread, as: :praise_tread_object, dependent: :destroy has_one :praise_tread_cache, as: :object, dependent: :destroy # 消息 has_many :memo_messages, :class_name =>'MemoMessage', :dependent => :destroy # end belongs_to :last_reply, :class_name => 'Memo', :foreign_key => 'last_reply_id' # acts_as_searchable :column => ['subject', 'content'], # #:include => { :forum => :p} # #:project_key => "#{Forum.table_name}.project_id" # :date_column => "#{table_name}.created_at" acts_as_event :title => Proc.new {|o| "#{o.forum.name}: #{o.subject}"}, :datetime => :updated_at, # :datetime => :created_at, :description => :content, :author => :author, :type => Proc.new {|o| o.parent_id.nil? ? 'Memo' : 'Reply'}, :url => Proc.new {|o| {:controller => 'memos', :action => 'show', :forum_id => o.forum_id}.merge(o.parent_id.nil? ? {:id => o.id} : {:id => o.parent_id, :r => o.id, :anchor => "reply-#{o.id}"})} acts_as_activity_provider :author_key => :author_id, :func => 'memos', :timestamp => 'created_at' # :find_options => {:type => 'memos'} # acts_as_watchable safe_attributes "author_id", "subject", "content", "forum_id", "last_memo_id", "lock", "sticky", "parent_id", "replies_count", "all_replies_count", "root_id", "language" after_create :add_author_as_watcher, :reset_counters!, :send_tiding after_update after_destroy :reset_counters!,:delete_kindeditor_assets #,:down_user_score -- 公共区发帖暂不计入得分, # after_create :send_notification # after_save :plusParentAndForum # after_destroy :minusParentAndForum #before_save :be_user_score # scope :visible, lambda { |*args| # includes(:forum => ).where() # } scope :indexable,lambda { where('parent_id is null') } scope :visible, lambda{where(hidden: 0)} scope :field_for_list, lambda{ select([:id, :subject, :author_id, :sticky, :updated_at, :language, :reward, :all_replies_count, :viewed_count, :forum_id]) } scope :field_for_recommend, lambda{select([:id, :subject, :language, :forum_id, :all_replies_count])} scope :user_posts, ->(user_id){where(root_id: nil, author_id: user_id, forum_id: [3,5])} # 只取技术分享和操作指南 scope :memo_replies, ->(id){where(:root_id => id)} scope :hot, lambda{order("all_replies_count desc, updated_at desc")} scope :posts, lambda{ where(root_id: nil, forum_id: [3,5]) }# 只取技术分享和操作指南 def self.search(query) __elasticsearch__.search( { query: { multi_match: { query: query, type:"most_fields", operator: "or", fields: ['subject','content^0.5'] } }, sort: { _score:{order: "desc" }, updated_at:{order: "desc" } }, highlight: { pre_tags: [''], post_tags: [''], fields: { subject: {}, content: {} } } } ) end def reply_for_memo Memo.where(:parent_id => self.id) end def all_replies self.all_replies_count end def send_mail # Mailer.run.forum_message_added(self) if Setting.notified_events.include?('forum_message_added') end def creator_user self.author end def created_time self.created_on end def content_detail self.content end # 公共贴吧消息记录 # 原则:新帖子给超级管理员发消息 def send_tiding if self.parent_id.present? self.tidings << Tiding.new(:user_id => self.parent.author_id, :trigger_user_id => self.author_id, :parent_container_id => self.root_id, :parent_container_type => "Memo", :belong_container_id => self.forum_id, :belong_container_type => "Forum", :viewed => 0, :tiding_type => "Comment") else self.tidings << Tiding.new(:user_id => 1, :trigger_user_id => self.author_id, :parent_container_id => self.id, :parent_container_type => "Memo", :belong_container_id => self.forum_id, :belong_container_type => "Forum", :viewed => 0, :tiding_type => "Comment") end end def cannot_reply_to_locked_topic errors.add :base, l(:label_memo_locked) if root.locked? && self != root end # def update_memos_forum # if forum_id_changed? # Message.update_all({:board_id => board_id}, ["id = ? OR parent_id = ?", root.id, root.id ]) # Forum.reset_counters!(forum_id_was) # Forum.reset_counters!(forum_id) # end # end def reset_counters! if parent && parent.id parent.update_attribute(:last_reply_id, parent.children.maximum(:id)) end if root root.update_attribute(:last_reply_id, Memo.where(:root_id => root.id).maximum(:id)) unless root.destroyed? end forum.reset_counters! end def sticky? sticky == 1 end def replies Memo.where("parent_id = ?", id) end def locked? self.lock end def editable_by? user # user && user.logged? || (self.author == usr && usr.allowed_to?(:edit_own_messages, project)) user.admin? || self.author == user end def destroyable_by? user (user && self.author == user) || user.admin? || self.forum.creator == user || Memo.find(self.root_id).author == user #self.author == user || user.admin? end def deleted_attach_able_by? user (user && user.logged? && (self.author == user) ) || user.admin? end private def add_author_as_watcher Watcher.create(:watchable => self.root, :user => author) end def send_notification # if Setting.notified_events.include?('message_posted') # Mailer.run.message_posted(self) # end end # def update_reply_count # if self.root_id # self.root.increment!(:replies_count) # end # end def plusParentAndForum @forum = Forum.find(self.forum_id) @forum.memo_count = @forum.memo_count.to_int + 1 @forum.last_memo_id = self.id if self.parent_id @parent_memo = Memo.find_by_id(self.parent_id) @parent_memo.last_reply_id = self @parent_memo.replies_count = @parent_memo.replies_count.to_int + 1 @parent_memo.save else @forum.topic_count = @forum.topic_count.to_int + 1 end @forum.save end def minusParentAndForum @forum = Forum.find(self.forum_id) @forum.memo_count = @forum.memo_count.to_int - 1 @forum.memo_count = 0 if @forum.memo_count.to_int < 0 # @forum.last_memo_id = Memo.reorder('created_at ASC').find_all_by_forum_id(self.forum_id).last.id if self.parent_id @parent_memo = Memo.find_by_id(self.parent_id) # @parent_memo.last_reply_id = Memo.reorder('created_at ASC').find_all_by_parent_id(self.parent_id).last.id @parent_memo.replies_count = @parent_memo.replies_count.to_int - 1 @parent_memo.replies_count = 0 if @parent_memo.replies_count.to_int < 0 @parent_memo.save else @forum.topic_count = @forum.topic_count.to_int - 1 @forum.topic_count = 0 if @forum.topic_count.to_int < 0 end @forum.save end #更新用户分数 -by zjc def be_user_score #新建memo且无parent的为发帖 if self.parent_id.nil? UserScore.joint(:post_message, User.current,nil,self ,{ memo_id: self.id }) update_memo_number(User.current,1) #新建memo且有parent的为回帖 elsif !self.parent_id.nil? UserScore.joint(:reply_posting, User.current,self.parent.author,self, { memo_id: self.id }) update_replay_for_memo(User.current,1) end end #被删除时更新用户分数 def down_user_score update_memo_number(User.current,1) update_replay_for_memo(User.current,1) end # Time 2015-03-26 15:20:24 # Author lizanle # Description 从硬盘上删除资源 def delete_kindeditor_assets delete_kindeditor_assets_from_disk self.id,OwnerTypeHelper::MEMO end def create_memo_ealasticsearch_index if self.parent_id.nil? self.__elasticsearch__.index_document end end def update_memo_ealasticsearch_index if self.parent_id.nil? self.__elasticsearch__.update_document end end def delete_memo_ealasticsearch_index if self.parent_id.nil? self.__elasticsearch__.delete_document end end end