canvas-lms/app/helpers/stream_items_helper.rb

182 lines
6.4 KiB
Ruby

#
# Copyright (C) 2012 - 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 StreamItemsHelper
include TextHelper
class StreamItemPresenter
attr_accessor :stream_item_id, :updated_at, :unread, :path, :context, :state, :summary
end
class ContextPresenter
attr_accessor :id, :type, :name, :linked_to, :time_zone
end
def categorize_stream_items(stream_items, user = @current_user)
categorized_items = {}
return categorized_items unless stream_items.present? # if we have no items (possibly because we have no user), don't try to activate the user's shard
supported_categories = %w(Announcement Conversation Assignment DiscussionTopic AssessmentRequest)
supported_categories.each { |category| categorized_items[category] = [] }
topic_types = %w{DiscussionTopic Announcement}
ActiveRecord::Associations::Preloader.new.preload(
stream_items.select{|i| topic_types.include?(i.asset_type)}.map{|item| item.data }, :context)
stream_items.each do |item|
category = item.data.class.name
category = category_for_message(item.data.notification_name) if category == "Message"
next unless supported_categories.include?(category)
if category == "Conversation"
participant = user.conversation_participant(item.asset_id)
next if participant.nil? || participant.last_message.nil? || participant.last_author?
item.participant = participant
# because we're cheating and just checking unread here instead of using
# the workflow_state on the stream_item_instance, that workflow_state
# may be out of sync with the underlying conversation.
item.unread = participant.unread?
elsif category == "AssessmentRequest"
next unless item.data.asset.grants_right?(user, :read)
end
if topic_types.include? category
next if item.data.try(:visible_for?, user) == false
end
categorized_items[category] << generate_presenter(category, item, user)
end
categorized_items
end
def generate_presenter(category, item, user = @current_user)
presenter = StreamItemPresenter.new
# need to store stream item id relative to the user's shard, since we'll
# use it later to look up the user's StreamItemInstances for deletion
presenter.stream_item_id = user.shard.activate{ item.id }
presenter.updated_at = extract_updated_at(category, item, user)
presenter.updated_at ||= item.updated_at
presenter.unread = item.unread
presenter.path = extract_path(category, item, user)
presenter.context = extract_context(category, item)
presenter.summary = extract_summary(category, item, user)
presenter
end
def extract_updated_at(category, item, user)
case category
when "Conversation"
item.data.conversation_participants.find_by(user: user)&.last_message_at
else
item.data.respond_to?(:updated_at) ? item.data.updated_at : nil
end
end
def extract_path(category, item, user)
case category
when "Announcement", "DiscussionTopic"
polymorphic_path([item.context_type.underscore, category.underscore], :"#{item.context_type.underscore}_id"\
=> Shard.short_id_for(item.context_id), :id => Shard.short_id_for(item.asset_id))
when "Conversation"
conversation_path(Shard.short_id_for(item.asset_id))
when "Assignment"
polymorphic_path([item.context_type.underscore, category.underscore], :"#{item.context_type.underscore}_id"\
=> Shard.short_id_for(item.context_id), :id => Shard.short_id_for(item.data.context_id))
when "AssessmentRequest"
submission = item.data.asset
Submission::ShowPresenter.new(
submission: submission,
current_user: user,
assessment_request: item.data
).submission_data_url
end
end
def extract_context(category, item)
context = ContextPresenter.new
asset = item.data
case category
when "Announcement", "DiscussionTopic", "Assignment"
context.type = item.context_type
context.id = item.context_id
context.name = asset.context_short_name
context.linked_to = polymorphic_path([context.type.underscore, category.underscore.pluralize], :"#{context.type.underscore}_id" => Shard.short_id_for(context.id))
when "Conversation"
context.type = "User"
last_author = item.participant.last_message.author
context.id = last_author.id
context.name = last_author.short_name
context.linked_to = user_path(last_author)
when "AssessmentRequest"
context.type = item.context_type
context.id = item.context_id
context.name = asset.context_short_name
end
context.time_zone = item.context.try(:time_zone)
context
end
def extract_summary(category, item, user = @current_user)
asset = item.data
case category
when "Announcement", "DiscussionTopic"
asset.title
when "Conversation"
CanvasTextHelper.truncate_text(item.participant.last_message.body, :max_length => 250)
when "Assignment"
asset.subject
when "AssessmentRequest"
# TODO I18N should use placeholders, not concatenation
asset.asset.assignment.title + " " + I18n.t('for', "for") + " " + assessment_author_name(asset, user)
else
nil
end
end
def category_for_message(name)
case name
when "Assignment Created",
"Assignment Changed",
"Assignment Due Date Changed"
"Assignment"
when "Assignment Graded",
"Assignment Submitted Late",
"Grade Weight Changed",
"Group Assignment Submitted Late"
"Submission"
else
"Ignore"
end
end
def assessment_author_name(asset, user = @current_user)
if can_do(asset, user, :read_assessment_user)
asset.asset.user.name
else
I18n.t(:anonymous_user, "Anonymous User")
end
end
private :category_for_message, :assessment_author_name
end