canvas-lms/lib/mutable.rb

142 lines
5.5 KiB
Ruby

#
# Copyright (C) 2011 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas 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 Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
module Mutable
attr_accessor :recently_unmuted
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def declare_mutable_broadcast_policy(options)
policy = options[:policy]
participants = options[:participants]
policy.dispatch :assignment_unmuted
policy.to { participants }
policy.whenever do |record|
@recently_unmuted
end
end
end
def mute!
return if muted?
self.update_attribute(:muted, true)
clear_sent_messages
hide_submissions if respond_to?(:hide_submissions)
ensure_post_policy(post_manually: true) if respond_to?(:ensure_post_policy)
true
end
def unmute!
return unless muted?
self.update_attribute(:muted, false)
broadcast_unmute_event
post_submissions if respond_to?(:post_submissions)
ensure_post_policy(post_manually: false) if respond_to?(:ensure_post_policy)
true
end
def broadcast_unmute_event
@recently_unmuted = true
self.save!
@recently_unmuted = false
end
protected
def clear_sent_messages
self.clear_broadcast_messages if self.respond_to? :clear_broadcast_messages
end
def hide_stream_items(submissions:)
if submissions.present?
submission_ids = submissions.pluck(:id)
stream_items = StreamItem.select([:id, :context_type, :context_id]).
where(asset_type: 'Submission', asset_id: submission_ids).
preload(:context).to_a
stream_item_contexts = stream_items.map { |si| [si.context_type, si.context_id] }
user_ids = submissions.map(&:user_id).uniq # hide stream items for submission owners, not instructors
# note: unfortunately this will hide items for an instructor if instructor (somehow) has a submission too
Shard.partition_by_shard(user_ids) do |user_ids_subset|
StreamItemInstance.where(:stream_item_id => stream_items, :user_id => user_ids_subset).
update_all_with_invalidation(stream_item_contexts, :hidden => true)
end
# Teachers want to hide their submission comments if they mute
# the assignment after leaving them.
instructor_ids = self.context.instructors.pluck(:id)
visible_comment_sub_ids =
SubmissionComment.where(hidden: false, submission_id: submission_ids, author_id: instructor_ids).
pluck(:submission_id)
update_submission_comments_and_count(visible_comment_sub_ids, hidden: true, instructor_ids: instructor_ids) if visible_comment_sub_ids.any?
end
end
def show_stream_items(submissions:)
if submissions.present?
submission_ids = submissions.pluck(:id)
stream_items = StreamItem.select([:id, :context_type, :context_id]).
where(asset_type: 'Submission', asset_id: submission_ids).
preload(:context).to_a
stream_item_contexts = stream_items.map { |si| [si.context_type, si.context_id] }
associated_shards = stream_items.inject([]) { |result, si| result | si.associated_shards }
Shard.with_each_shard(associated_shards) do
StreamItemInstance.where(:hidden => true, :stream_item_id => stream_items).
update_all_with_invalidation(stream_item_contexts, :hidden => false)
end
hidden_comment_sub_ids = SubmissionComment.where(hidden: true, submission_id: submission_ids).pluck(:submission_id)
update_submission_comments_and_count(hidden_comment_sub_ids, hidden: false) if hidden_comment_sub_ids.any?
end
end
def update_submission_comments_and_count(submission_ids, hidden: false, instructor_ids: nil)
update_time = Time.zone.now
submission_ids.each_slice(100) do |submission_id_slice|
submission_comment_scope = SubmissionComment.where(hidden: !hidden, submission_id: submission_id_slice)
submission_comment_scope = submission_comment_scope.where(author_id: instructor_ids) if instructor_ids.present?
submission_comment_scope.update_all(hidden: hidden, updated_at: update_time)
Submission.where(:id => submission_id_slice).
update_all(["submission_comments_count = (SELECT COUNT(*) FROM #{SubmissionComment.quoted_table_name} WHERE
submissions.id = submission_comments.submission_id AND submission_comments.hidden = ? AND
submission_comments.draft IS NOT TRUE AND submission_comments.provisional_grade_id IS NULL)", false])
end
end
private
def update_muted_status!
# With post policies active, an assignment is considered "muted" if it has any
# unposted submissions.
has_unposted_submissions = submissions.active.unposted.exists?
if muted? != has_unposted_submissions
# Set grade_posting_in_progress so we don't attempt to save changes to
# this assignment's associated quiz/discussion topic/whatever in the
# process of muting
self.grade_posting_in_progress = true
update!(muted: has_unposted_submissions)
self.grade_posting_in_progress = false
end
end
end