#encoding: utf-8
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class Message < ActiveRecord :: Base
require 'net/http'
require 'json'
include Redmine :: SafeAttributes
include UserScoreHelper
include ApplicationHelper
has_many_kindeditor_assets :assets , :dependent = > :destroy
belongs_to :board , :touch = > true
belongs_to :author , :class_name = > 'User' , :foreign_key = > 'author_id'
has_many :praise_tread , as : :praise_tread_object , dependent : :destroy
has_one :praise_tread_cache , as : :object , dependent : :destroy
# has_many :org_subfield_messages, :dependent => :destroy
# has_many :org_subfields, :through => :org_subfield_messages
acts_as_tree :counter_cache = > :replies_count , :order = > " #{ Message . table_name } .created_on ASC "
acts_as_attachable
belongs_to :last_reply , :class_name = > 'Message' , :foreign_key = > 'last_reply_id'
# added by fq
has_many :acts , :class_name = > 'Activity' , :as = > :act , :dependent = > :destroy
# 被ForgeActivity虚拟关联
has_many :forge_acts , :class_name = > 'ForgeActivity' , :as = > :forge_act , :dependent = > :destroy
# 课程动态
has_many :course_acts , :class_name = > 'CourseActivity' , :as = > :course_act , :dependent = > :destroy
# 竞赛动态
has_many :contest_acts , :class_name = > 'ContestActivity' , :as = > :contest_act , :dependent = > :destroy
# end
# 课程/项目 消息
has_many :course_messages , :class_name = > 'CourseMessage' , :as = > :course_message , :dependent = > :destroy
has_many :forge_messages , :class_name = > 'ForgeMessage' , :as = > :forge_message , :dependent = > :destroy
has_many :contest_messages , :class_name = > 'ContestMessage' , :as = > :contest_message , :dependent = > :destroy
has_many :at_messages , as : :at_message , dependent : :destroy
has_many :ActivityNotifies , :as = > :activity , :dependent = > :destroy
has_many :tidings , :as = > :container , :dependent = > :destroy
belongs_to :reply , :class_name = > 'Message' , :foreign_key = > 'reply_id'
has_one :message_detail , :dependent = > :destroy
#转发表
has_many :forwards , :as = > :from , :dependent = > :destroy
after_destroy :delete_org_activities
acts_as_searchable :columns = > [ 'subject' , 'content' ] ,
:include = > { :board = > :project } ,
:project_key = > " #{ Board . table_name } .project_id " ,
:date_column = > " #{ table_name } .created_on "
acts_as_searchable :columns = > [ 'subject' , 'content' ] ,
:include = > { :board = > :course } ,
:course_key = > " #{ Board . table_name } .course_id " ,
:date_column = > " #{ table_name } .created_at "
acts_as_event :title = > Proc . new { | o | " #{ o . board . name } : #{ o . subject } " } ,
:description = > :content ,
:datetime = > :updated_on ,
# :datetime => "#{Message.table_name}.created_on",
:group = > :parent ,
:type = > Proc . new { | o | o . parent_id . nil? ? 'message' : 'reply' } ,
:url = > Proc . new { | o | { :controller = > 'messages' , :action = > 'show' , :board_id = > o . board_id } . merge ( o . parent_id . nil? ? { :id = > o . id } :
{ :id = > o . parent_id , :r = > o . id , :anchor = > " message- #{ o . id } " } ) }
acts_as_activity_provider :find_options = > { :include = > [ { :board = > :project } , :author ] } ,
:author_key = > :author_id
acts_as_activity_provider :find_options = > { :include = > [ { :board = > :course } , :author ] } ,
:type = > 'course_messages' ,
:author_key = > :author_id
acts_as_watchable
validates_presence_of :board , :subject , :content
validates_length_of :subject , :maximum = > 255
validate :cannot_reply_to_locked_topic , :on = > :create
# after_create :add_author_as_watcher, :reset_counters!, :add_boards_count
after_update :update_messages_board , :update_activity
after_destroy :reset_counters! , :down_user_score , :delete_kindeditor_assets , :decrease_boards_count , :down_course_score
after_create :act_as_course_activity , :act_as_student_score , act_as_at_message ( :content , :author_id ) , :add_author_as_watcher , :reset_counters! , :add_boards_count , :send_tiding
#before_save :be_user_score
scope :visible , lambda { | * args |
includes ( :board = > :project ) . where ( Project . allowed_to_condition ( args . shift || User . current , :view_messages , * args ) )
}
scope :course_visible , lambda { | * args |
includes ( :board = > :course ) . where ( Course . allowed_to_condition ( args . shift || User . current , :view_course_messages , * args ) )
}
safe_attributes 'subject' , 'content' , 'reply_id' , 'root_id' , 'visits'
safe_attributes 'board_id' , 'locked' , 'sticky' ,
:if = > lambda { | message , user |
if message . project
user . allowed_to? ( :edit_messages , message . project )
elsif message . course
user . allowed_to? ( :edit_messages , message . course )
elsif message . contest
user . admin_of_contest? ( message . contest )
end
}
def description
self . content
end
def content
self . try ( :content )
end
def topic?
parent_id . nil?
end
def visible? ( user = User . current )
if project
! user . nil? && user . allowed_to? ( :view_messages , project )
elsif course
! user . nil? && user . allowed_to? ( :view_messages , course )
end
end
def cannot_reply_to_locked_topic
# Can not reply to a locked topic
errors . add :base , 'Topic is locked' if root . locked? && self != root
end
def update_messages_board
if board_id_changed?
Message . update_all ( { :board_id = > board_id } , [ " id = ? OR parent_id = ? " , root . id , root . id ] )
Board . reset_counters! ( board_id_was )
Board . reset_counters! ( board_id )
end
end
# 发帖精辟更新发帖总数
def add_boards_count
if self . project && ! project . project_score . nil?
# 讨论区
if self . parent_id . nil?
count = self . project . project_score . board_num . to_i + 1
self . project . project_score . update_column ( :board_num , count )
else # 回复
count = self . project . project_score . board_message_num . to_i + 1
self . project . project_score . update_column ( :board_message_num , count )
end
end
end
# 删除帖子的时候更新帖子总数, 删除回复的时候总数不减少
def decrease_boards_count
if self . project && ! project . project_score . nil?
# 讨论区
if self . parent_id . nil?
count = self . project . project_score . board_num - 1
self . project . project_score . update_attribute ( :board_num , count < 0 ? 0 : count )
else # 回复
count = self . project . project_score . board_message_num - 1
self . project . project_score . update_attribute ( :board_message_num , count < 0 ? 0 : count )
end
end
end
# 有很大的性能隐患, 没做注释? 为什么要更新为最大ID?
def reset_counters!
if parent && parent . id
parent . update_column ( :last_reply_id , parent . children . maximum ( :id ) )
end
if root
root . update_attribute ( :last_reply_id , Message . where ( :root_id = > root . id ) . maximum ( :id ) ) unless root . destroyed?
end
# board.reset_counters!
end
def sticky = ( arg )
write_attribute :sticky , ( arg == true || arg . to_s == '1' ? 1 : 0 )
end
def sticky?
sticky == 1
end
def project
board . project
end
def course
board . course
end
def contest
board . contest
end
def contest_editable_by? ( usr )
usr && usr . logged? && ( usr . admin_of_contest? ( contest ) || self . author == usr || usr . admin? )
end
def contest_destroyable_by? ( usr )
usr && usr . logged? && ( usr . admin_of_contest? ( contest ) || self . author == usr || usr . admin? )
end
def course_editable_by? ( usr )
usr && usr . logged? && ( usr . allowed_to? ( :edit_messages , course ) || ( self . author == usr && usr . allowed_to? ( :edit_own_messages , course ) ) )
end
def org_subfield_editable_by? ( usr )
usr && usr . logged? && ( usr . admin? || self . author == usr || usr . admin_of_org? ( self . board . org_subfield . organization ) )
end
def course_destroyable_by? ( usr )
usr && usr . logged? && ( usr . allowed_to? ( :delete_messages , course ) || ( self . author == usr && usr . allowed_to? ( :delete_own_messages , course ) ) )
end
def editable_by? ( usr )
usr && usr . logged? && ( usr . allowed_to? ( :edit_messages , project ) || ( self . author == usr && usr . allowed_to? ( :edit_own_messages , project ) ) )
end
def destroyable_by? ( usr )
usr && usr . logged? && ( usr . allowed_to? ( :delete_messages , project ) || ( self . author == usr && usr . allowed_to? ( :delete_own_messages , project ) ) )
end
def set_notify_id ( notify_id )
@notify_id = notify_id
end
def get_notify_id ( )
return @notify_id
end
def set_notify_is_read ( notify_is_read )
@notify_is_read = notify_is_read
end
def get_notify_is_read ( )
return @notify_is_read
end
#动态的更新
def update_activity
update_course_activity ( self . class , self . id )
update_contest_activity ( self . class , self . id )
update_user_activity ( self . class , self . id )
update_forge_activity ( self . class , self . id )
update_org_activity ( self . class , self . id )
end
def creator_user
self . author
end
def created_time
self . created_on
end
def content_detail
self . content
end
private
def add_author_as_watcher
Watcher . create ( :watchable = > self . root , :user = > author )
end
# fq
def act_as_activity
self . acts << Activity . new ( :user_id = > self . author_id )
end
# end
# Time 2015-02-27 14:32:25
# Author lizanle
# Description
def act_as_forge_activity
# 如果project为空, 那么就是课程相关的消息
if self . board . project_id != - 1 && self . parent_id . nil?
self . forge_acts << ForgeActivity . new ( :user_id = > self . author_id , :project_id = > self . board . project . id )
end
end
#课程动态公共表记录
def act_as_course_activity
if self . course && self . parent_id . nil?
self . course_acts << CourseActivity . new ( :user_id = > self . author_id , :course_id = > self . board . course_id )
end
end
#竞赛动态公共表记录
def act_as_contest_activity
if self . contest && self . parent_id . nil?
self . contest_acts << ContestActivity . new ( :user_id = > self . author_id , :contest_id = > self . board . contest_id )
end
end
# 课程讨论区添加消息:
# 老师或助教发帖所有人都能收到消息
# 直接回复帖子,给帖子发布人发送
# 多级回复帖子,给被回复人发送
# 回复若使用了@,同时给被@人发消息
# 项目同课堂
def send_tiding
if self . course
if self . parent_id . nil? || self . parent_id == 0 # 主贴
if self . author . allowed_to? ( :as_teacher , self . course )
str = " "
self . course . members . select { | m | m . user_id != self . author_id } . each do | m |
str += " , " if str != " "
str += " (' #{ m . user_id } ', #{ self . author_id } , #{ self . id } , ' #{ self . class . to_s } ', #{ self . id } , 'Message', #{ self . course . id } ,'Course', '0', 'Comment', ' #{ Time . now . strftime ( '%Y-%m-%d %H:%M:%S' ) } ', ' #{ Time . now . strftime ( '%Y-%m-%d %H:%M:%S' ) } ') "
end
if str != " "
sql = " insert into tidings (user_id, trigger_user_id, container_id, container_type, parent_container_id, parent_container_type, belong_container_id, belong_container_type, viewed, tiding_type, created_at, updated_at) values " + str
ActiveRecord :: Base . connection . execute sql
end
end
else # 回帖
parent_author_id = self . parent . author_id
if parent_author_id != self . author_id && ! self . tidings . where ( :user_id = > parent_author_id , :trigger_user_id = > self . author_id , :tiding_type = > " Mentioned " ) . first . present? # 只针对主贴回复,回复自己的帖子不发消息
# self.course_messages << CourseMessage.new(:user_id => parent_author_id, :course_id => self.board.course_id, :viewed => false)
self . tidings << Tiding . new ( :user_id = > parent_author_id , :trigger_user_id = > self . author_id , :parent_container_id = > self . root_id , :parent_container_type = > " Message " ,
:belong_container_id = > self . course . id , :belong_container_type = > " Course " , :viewed = > 0 , :tiding_type = > " Comment " )
end
end
elsif self . project # 项目相关
if self . parent_id . nil? # 主贴
if self . author . manager_of_project? ( self . project . id )
str = " "
self . project . members . select { | m | m . user_id != self . author_id } . each do | m |
str += " , " if str != " "
str += " (' #{ m . user_id } ', #{ self . author_id } , #{ self . id } , ' #{ self . class . to_s } ', #{ self . id } , 'Message', #{ self . project . id } ,'Project', '0', 'Comment', ' #{ Time . now . strftime ( '%Y-%m-%d %H:%M:%S' ) } ', ' #{ Time . now . strftime ( '%Y-%m-%d %H:%M:%S' ) } ') "
end
if str != " "
sql = " insert into tidings (user_id, trigger_user_id, container_id, container_type, parent_container_id, parent_container_type, belong_container_id, belong_container_type, viewed, tiding_type, created_at, updated_at) values " + str
ActiveRecord :: Base . connection . execute sql
end
end
else # 回帖
parent_author_id = self . parent . author_id
if parent_author_id != self . author_id && ! self . tidings . where ( :user_id = > parent_author_id , :trigger_user_id = > self . author_id , :tiding_type = > " Mentioned " ) . first . present? # 只针对主贴回复,回复自己的帖子不发消息
# self.forge_messages << ForgeMessage.new(:user_id => parent_author_id, :project_id => self.board.project_id, :viewed => false)
self . tidings << Tiding . new ( :user_id = > parent_author_id , :trigger_user_id = > self . author_id , :parent_container_id = > self . root_id , :parent_container_type = > " Message " ,
:belong_container_id = > self . course . id , :belong_container_type = > " Project " , :viewed = > 0 , :tiding_type = > " Comment " )
end
end
elsif self . contest
if self . parent_id . nil? # 主贴
self . contest . contest_members . includes ( :user ) . each do | m |
if self . author . admin_of_contest? ( self . contest ) && m . user_id != self . author_id # 老师 自己的帖子不给自己发送消息
#self.course_messages << CourseMessage.new(:user_id => m.user_id, :course_id => self.board.course_id, :viewed => false)
# count = ShieldWechatMessage.where("container_type='User' and container_id=#{m.user_id} and shield_type='Course' and shield_id=#{self.course.id}").count
# if count == 0
# content = strip_html self.subject, 200
# ws.topic_publish_template m.user_id, "course_discussion", self.id, "#{l(:label_course_topic_template)}", content, self.author.try(:realname), format_time(self.created_on)
# end
end
end
else # 回帖
parent_author_id = Message . find ( self . parent_id ) . author_id
if parent_author_id != self . author_id # 只针对主贴回复,回复自己的帖子不发消息
self . contest_messages << ContestMessage . new ( :user_id = > parent_author_id , :contest_id = > self . board . contest_id , :viewed = > false )
# count = ShieldWechatMessage.where("container_type='User' and container_id=#{parent_author_id} and shield_type='Course' and shield_id=#{self.board.course_id}").count
# if count == 0
# content = strip_html self.content.html_safe, 200
# ws.comment_template parent_author_id, "course_discussion", self.parent_id, "#{l(:label_topic_comment_template)}", self.author.try(:realname), format_time(self.created_on), content
# end
end
end
end
end
def delay_message_send
if self . course
if self . parent_id . nil? # 发帖
dm = [ ]
self . course . members . includes ( :user ) . each do | m |
if self . author . allowed_to? ( :as_teacher , self . course ) && m . user_id != self . author_id # 老师 自己的帖子不给自己发送消息
dm << { course_message_type : 'Message' , course_message_id : self . id , :user_id = > m . user_id ,
:course_id = > self . board . course_id , :viewed = > false }
if dm . size > = 30
self . delay . contain_messages_message ( dm )
dm . clear
end
end
end
unless dm . empty?
self . delay . contain_messages_message ( dm )
end
end
elsif self . contest
if self . parent_id . nil? # 发帖
dm = [ ]
self . contest . contest_members . includes ( :user ) . each do | m |
if self . author . admin_of_contest? ( self . contest ) && m . user_id != self . author_id # 老师 自己的帖子不给自己发送消息
dm << { contest_message_type : 'Message' , contest_message_id : self . id , :user_id = > m . user_id ,
:contest_id = > self . board . contest_id , :viewed = > false }
if dm . size > = 30
self . delay . contain_contest_messages_message ( dm )
dm . clear
end
end
end
unless dm . empty?
self . delay . contain_contest_messages_message ( dm )
end
end
end
end
def contain_messages_message vs
CourseMessage . create ( vs )
end
def contain_contest_messages_message vs
ContestMessage . create ( vs )
end
#更新用户分数 -by zjc
def be_user_score
#新建message且无parent的为发帖
if self . parent_id . nil? && ! self . board . project . nil?
UserScore . joint ( :post_message , self . author , nil , self , { message_id : self . id } )
update_memo_number ( self . author , 1 )
if self . board . project_id != - 1
update_memo_number ( self . author , 2 , self . board . project )
end
#新建message且有parent的为回帖
elsif ! self . parent_id . nil? && ! self . board . project . nil?
UserScore . joint ( :reply_posting , self . author , self . parent . author , self , { message_id : self . id } )
update_replay_for_memo ( self . author , 1 )
if self . board . project_id != - 1
update_replay_for_memo ( self . author , 2 , self . board . project )
end
end
end
#减少用户分数
def down_user_score
if self . parent_id . nil? && ! self . board . project . nil?
UserScore . joint ( :delete_message , self . author , nil , self , { message_id : self . id } )
update_memo_number ( User . current , 1 )
if self . board . project_id != - 1
update_memo_number ( self . author , 2 , self . board . project )
end
elsif ! self . parent_id . nil? && ! self . board . project . nil?
UserScore . joint ( :reply_deleting , self . author , self . parent . author , self , { message_id : self . id } )
update_replay_for_memo ( User . current , 1 )
if self . board . project_id != - 1
update_replay_for_memo ( self . author , 2 , self . board . project )
end
end
end
def send_mail
# Mailer.run.message_posted(self) if Setting.notified_events.include?('message_posted')
end
# Time 2015-03-31 09:15:06
# Author lizanle
# Description 删除对应消息的图片资源
def delete_kindeditor_assets
delete_kindeditor_assets_from_disk self . id , OwnerTypeHelper :: MESSAGE
end
# 课程成员得分(活跃度)
def act_as_student_score
if self . course
if self . parent_id . nil?
# 发帖
course_member_score ( self . course . id , self . author_id , " Message " )
else
# 回帖
course_member_score ( self . course . id , self . author_id , " MessageReply " )
end
end
end
# 删除帖子的时候更新课程帖子总数, 删除回复的时候减少总数
def down_course_score
if self . course
if self . parent_id . nil? # 发帖
down_course_score_num ( self . course . id , self . author_id , " Message " )
else
# 回帖
down_course_score_num ( self . course . id , self . author_id , " MessageReply " )
end
end
end
def delete_org_activities
OrgActivity . where ( " org_act_type='Message' and org_act_id = #{ self . id } and container_type='OrgSubfield' " ) . destroy_all
end
end