You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
pgfqe6ch8/app/models/message.rb

507 lines
20 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#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.message_detail.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