2020-10-27 00:46:40 +08:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2011-02-01 09:57:29 +08:00
|
|
|
#
|
2017-04-28 10:30:08 +08:00
|
|
|
# Copyright (C) 2011 - present Instructure, Inc.
|
2011-02-01 09:57:29 +08:00
|
|
|
#
|
|
|
|
# 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/>.
|
|
|
|
#
|
|
|
|
|
|
|
|
# These methods are mixed into the classes that can be considered a "context".
|
2015-04-08 05:58:15 +08:00
|
|
|
# See Context::CONTEXT_TYPES below.
|
2011-02-01 09:57:29 +08:00
|
|
|
module Context
|
2020-07-22 06:50:25 +08:00
|
|
|
CONTEXT_TYPES = %i[Account Course CourseSection User Group].freeze
|
2012-12-12 21:50:15 +08:00
|
|
|
|
2015-04-08 05:58:15 +08:00
|
|
|
ASSET_TYPES = {
|
|
|
|
Announcement: :Announcement,
|
|
|
|
AssessmentQuestion: :AssessmentQuestion,
|
|
|
|
AssessmentQuestionBank: :AssessmentQuestionBank,
|
|
|
|
Assignment: :Assignment,
|
|
|
|
AssignmentGroup: :AssignmentGroup,
|
|
|
|
Attachment: :Attachment,
|
|
|
|
CalendarEvent: :CalendarEvent,
|
|
|
|
Collaboration: :Collaboration,
|
|
|
|
ContentTag: :ContentTag,
|
|
|
|
ContextExternalTool: :ContextExternalTool,
|
|
|
|
ContextModule: :ContextModule,
|
|
|
|
DiscussionEntry: :DiscussionEntry,
|
|
|
|
DiscussionTopic: :DiscussionTopic,
|
|
|
|
Folder: :Folder,
|
|
|
|
LearningOutcome: :LearningOutcome,
|
|
|
|
LearningOutcomeGroup: :LearningOutcomeGroup,
|
|
|
|
MediaObject: :MediaObject,
|
|
|
|
Progress: :Progress,
|
|
|
|
Quiz: :"Quizzes::Quiz",
|
|
|
|
QuizGroup: :"Quizzes::QuizGroup",
|
|
|
|
QuizQuestion: :"Quizzes::QuizQuestion",
|
|
|
|
QuizSubmission: :"Quizzes::QuizSubmission",
|
|
|
|
Rubric: :Rubric,
|
|
|
|
RubricAssociation: :RubricAssociation,
|
|
|
|
Submission: :Submission,
|
|
|
|
WebConference: :WebConference,
|
|
|
|
Wiki: :Wiki,
|
2015-04-14 23:37:49 +08:00
|
|
|
WikiPage: :WikiPage,
|
|
|
|
Eportfolio: :Eportfolio
|
2015-04-08 05:58:15 +08:00
|
|
|
}.freeze
|
2011-02-01 09:57:29 +08:00
|
|
|
|
2017-12-29 00:41:08 +08:00
|
|
|
def clear_cached_short_name
|
|
|
|
self.class.connection.after_transaction_commit do
|
|
|
|
Rails.cache.delete(["short_name_lookup", asset_string].cache_key)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2011-02-01 09:57:29 +08:00
|
|
|
def add_aggregate_entries(entries, feed)
|
2014-12-17 04:45:12 +08:00
|
|
|
entries.each do |entry|
|
|
|
|
user = entry.user || feed.user
|
|
|
|
# If already existed and has been updated
|
|
|
|
if entry.entry_changed? && entry.asset
|
2020-01-09 07:39:02 +08:00
|
|
|
entry.asset.update(
|
2014-12-17 04:45:12 +08:00
|
|
|
title: entry.title,
|
|
|
|
message: entry.message
|
|
|
|
)
|
|
|
|
elsif !entry.asset
|
|
|
|
announcement = announcements.build(
|
|
|
|
title: entry.title,
|
|
|
|
message: entry.message
|
|
|
|
)
|
|
|
|
announcement.external_feed_id = feed.id
|
|
|
|
announcement.user = user
|
|
|
|
announcement.save
|
2020-01-09 07:39:02 +08:00
|
|
|
entry.update(asset: announcement)
|
2011-02-01 09:57:29 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2012-12-12 21:50:15 +08:00
|
|
|
|
2021-10-22 00:18:16 +08:00
|
|
|
def self.sorted_rubrics(context)
|
2020-11-25 02:46:25 +08:00
|
|
|
associations = RubricAssociation.active.bookmarked.for_context_codes(context.asset_string).preload(rubric: :context)
|
2014-03-18 04:54:26 +08:00
|
|
|
Canvas::ICU.collate_by(associations.to_a.uniq(&:rubric_id).select(&:rubric)) { |r| r.rubric.title || CanvasSort::Last }
|
2011-02-01 09:57:29 +08:00
|
|
|
end
|
2012-12-12 21:50:15 +08:00
|
|
|
|
2011-02-01 09:57:29 +08:00
|
|
|
def rubric_contexts(user)
|
2018-09-27 04:49:21 +08:00
|
|
|
associations = []
|
|
|
|
course_ids = [id]
|
2019-03-07 09:12:13 +08:00
|
|
|
course_ids = (course_ids + user.participating_instructor_course_with_concluded_ids.map { |id| Shard.relative_id_for(id, user.shard, Shard.current) }).uniq if user
|
2018-09-27 04:49:21 +08:00
|
|
|
Shard.partition_by_shard(course_ids) do |sharded_course_ids|
|
|
|
|
context_codes = sharded_course_ids.map { |id| "course_#{id}" }
|
|
|
|
if Shard.current == shard
|
|
|
|
context = self
|
2021-09-27 23:58:39 +08:00
|
|
|
while context.respond_to?(:account) || context.respond_to?(:parent_account)
|
2018-09-27 04:49:21 +08:00
|
|
|
context = context.respond_to?(:account) ? context.account : context.parent_account
|
|
|
|
context_codes << context.asset_string if context
|
|
|
|
end
|
|
|
|
end
|
2020-11-25 02:46:25 +08:00
|
|
|
associations += RubricAssociation.active.bookmarked.for_context_codes(context_codes).include_rubric.preload(:context).to_a
|
2011-02-01 09:57:29 +08:00
|
|
|
end
|
2018-09-27 04:49:21 +08:00
|
|
|
|
|
|
|
associations = associations.select(&:rubric).uniq { |a| [a.rubric_id, a.context.asset_string] }
|
|
|
|
contexts = associations.group_by { |a| a.context.asset_string }.map do |code, code_associations|
|
2018-04-21 06:23:58 +08:00
|
|
|
{
|
2018-05-30 03:33:59 +08:00
|
|
|
rubrics: code_associations.length,
|
2011-02-01 09:57:29 +08:00
|
|
|
context_code: code,
|
2018-09-27 04:49:21 +08:00
|
|
|
name: code_associations.first.context_name
|
2011-02-01 09:57:29 +08:00
|
|
|
}
|
|
|
|
end
|
2018-04-21 06:23:58 +08:00
|
|
|
Canvas::ICU.collate_by(contexts) { |r| r[:name] }
|
2011-02-01 09:57:29 +08:00
|
|
|
end
|
2012-12-12 21:50:15 +08:00
|
|
|
|
restore "speed up dashcards by only loading tabs shown" & fix bug
Fixes: CORE-1964
This reverts commit 72d558f09554f4e45130c4a508311a1bc0d007f3
this restores the commit (aka, reverts that ^ revert) that was causing
us bugs on caturday. the root cause of the bug was that
["active_record_types", [], self].cache_key
and
["active_record_types", nil, self].cache_key
return the same string.
how to replay the buggy scenerio:
(as a student, in a course with annnouncements):
* right after having "touched" the course (so there's nothing cached),
* have one person go to /courses/:course_id/assignments/syllabus
* in the controller action for that, it will do:
`return unless tab_enabled?(@context.class::TAB_SYLLABUS)`
that will call Course::uncached_tabs_available(only_check: @context.class::TAB_SYLLABUS)
which would have called @course.active_record_types(only_check: [])
(because @context.class::TAB_SYLLABUS is not one of the `tabs_that_can_be_marked_hidden_unused`)
which woud have written `{}` to redis at ['active_record_types', [], self].cache_key
* now, as a different student, go to /courses/:course_id/annnouncements
it will call `tab_enabled?(@context.class::TAB_ANNOUNCEMENTS)`
that will call Course::uncached_tabs_available(only_check: @context.class::TAB_ANNOUNCEMENTS)
which will call @course.active_record_types(only_check: [:announcements])
it will do a cache read for ['active_record_types', [:announcements], self].cache_key
since it it a fresh cach, that will not be found
then it would have done a cache read for the "everything" cache at
['active_record_types', nil, self].cache_key
THAT WOULD HAVE RETURNED THE CACHED `{}` SINCE `nil.cache_key` and `[].cache_key` ARE THE SAME!
* the user would be told the announcement page is not enabled for that course
the fix is to explicitly not allow Context::active_record_types to ever
be called with `only_check: []`
and for good measure, we don't allow the implicit conversion of
nil.cache_key to "" and instead use "everything" for the cache cache_key
I added specs to spefically catch this bug so that it doesn't happen again.
To see the difference, compare the latest patchset of this commit against
patchset 1. patchset 1 is the original version of this code without this
fix.
Change-Id: I513104b90dd94227a04c151ee02a22f4a4ac2832
Reviewed-on: https://gerrit.instructure.com/167400
Tested-by: Jenkins
Reviewed-by: Jeremy Stanley <jeremy@instructure.com>
QA-Review: Jeremy Stanley <jeremy@instructure.com>
Product-Review: Ryan Shaw <ryan@instructure.com>
2018-10-08 23:36:54 +08:00
|
|
|
def active_record_types(only_check: nil)
|
|
|
|
only_check = only_check.sort if only_check.present? # so that we always have consistent cache keys
|
|
|
|
@active_record_types ||= {}
|
|
|
|
return @active_record_types[only_check] if @active_record_types[only_check]
|
|
|
|
|
|
|
|
possible_types = {
|
|
|
|
files: -> { respond_to?(:attachments) && attachments.active.exists? },
|
|
|
|
modules: -> { respond_to?(:context_modules) && context_modules.active.exists? },
|
2020-02-22 02:08:16 +08:00
|
|
|
quizzes: lambda do
|
|
|
|
(respond_to?(:quizzes) && quizzes.active.exists?) ||
|
|
|
|
(respond_to?(:assignments) && assignments.active.quiz_lti.exists?)
|
|
|
|
end,
|
restore "speed up dashcards by only loading tabs shown" & fix bug
Fixes: CORE-1964
This reverts commit 72d558f09554f4e45130c4a508311a1bc0d007f3
this restores the commit (aka, reverts that ^ revert) that was causing
us bugs on caturday. the root cause of the bug was that
["active_record_types", [], self].cache_key
and
["active_record_types", nil, self].cache_key
return the same string.
how to replay the buggy scenerio:
(as a student, in a course with annnouncements):
* right after having "touched" the course (so there's nothing cached),
* have one person go to /courses/:course_id/assignments/syllabus
* in the controller action for that, it will do:
`return unless tab_enabled?(@context.class::TAB_SYLLABUS)`
that will call Course::uncached_tabs_available(only_check: @context.class::TAB_SYLLABUS)
which would have called @course.active_record_types(only_check: [])
(because @context.class::TAB_SYLLABUS is not one of the `tabs_that_can_be_marked_hidden_unused`)
which woud have written `{}` to redis at ['active_record_types', [], self].cache_key
* now, as a different student, go to /courses/:course_id/annnouncements
it will call `tab_enabled?(@context.class::TAB_ANNOUNCEMENTS)`
that will call Course::uncached_tabs_available(only_check: @context.class::TAB_ANNOUNCEMENTS)
which will call @course.active_record_types(only_check: [:announcements])
it will do a cache read for ['active_record_types', [:announcements], self].cache_key
since it it a fresh cach, that will not be found
then it would have done a cache read for the "everything" cache at
['active_record_types', nil, self].cache_key
THAT WOULD HAVE RETURNED THE CACHED `{}` SINCE `nil.cache_key` and `[].cache_key` ARE THE SAME!
* the user would be told the announcement page is not enabled for that course
the fix is to explicitly not allow Context::active_record_types to ever
be called with `only_check: []`
and for good measure, we don't allow the implicit conversion of
nil.cache_key to "" and instead use "everything" for the cache cache_key
I added specs to spefically catch this bug so that it doesn't happen again.
To see the difference, compare the latest patchset of this commit against
patchset 1. patchset 1 is the original version of this code without this
fix.
Change-Id: I513104b90dd94227a04c151ee02a22f4a4ac2832
Reviewed-on: https://gerrit.instructure.com/167400
Tested-by: Jenkins
Reviewed-by: Jeremy Stanley <jeremy@instructure.com>
QA-Review: Jeremy Stanley <jeremy@instructure.com>
Product-Review: Ryan Shaw <ryan@instructure.com>
2018-10-08 23:36:54 +08:00
|
|
|
assignments: -> { respond_to?(:assignments) && assignments.active.exists? },
|
|
|
|
pages: -> { respond_to?(:wiki_pages) && wiki_pages.active.exists? },
|
|
|
|
conferences: -> { respond_to?(:web_conferences) && web_conferences.active.exists? },
|
|
|
|
announcements: -> { respond_to?(:announcements) && announcements.active.exists? },
|
|
|
|
outcomes: -> { respond_to?(:has_outcomes?) && has_outcomes? },
|
|
|
|
discussions: -> { respond_to?(:discussion_topics) && discussion_topics.only_discussion_topics.except(:preload).exists? }
|
|
|
|
}
|
|
|
|
|
|
|
|
types_to_check = if only_check
|
|
|
|
possible_types.select { |k| only_check.include?(k) }
|
|
|
|
else
|
|
|
|
possible_types
|
|
|
|
end
|
|
|
|
|
|
|
|
raise ArgumentError, "only_check is either an empty array or you are aking for invalid types" if types_to_check.empty?
|
|
|
|
|
|
|
|
base_cache_key = "active_record_types3"
|
2023-12-02 05:46:58 +08:00
|
|
|
cache_key = [base_cache_key, only_check.presence || "everything", self].cache_key
|
restore "speed up dashcards by only loading tabs shown" & fix bug
Fixes: CORE-1964
This reverts commit 72d558f09554f4e45130c4a508311a1bc0d007f3
this restores the commit (aka, reverts that ^ revert) that was causing
us bugs on caturday. the root cause of the bug was that
["active_record_types", [], self].cache_key
and
["active_record_types", nil, self].cache_key
return the same string.
how to replay the buggy scenerio:
(as a student, in a course with annnouncements):
* right after having "touched" the course (so there's nothing cached),
* have one person go to /courses/:course_id/assignments/syllabus
* in the controller action for that, it will do:
`return unless tab_enabled?(@context.class::TAB_SYLLABUS)`
that will call Course::uncached_tabs_available(only_check: @context.class::TAB_SYLLABUS)
which would have called @course.active_record_types(only_check: [])
(because @context.class::TAB_SYLLABUS is not one of the `tabs_that_can_be_marked_hidden_unused`)
which woud have written `{}` to redis at ['active_record_types', [], self].cache_key
* now, as a different student, go to /courses/:course_id/annnouncements
it will call `tab_enabled?(@context.class::TAB_ANNOUNCEMENTS)`
that will call Course::uncached_tabs_available(only_check: @context.class::TAB_ANNOUNCEMENTS)
which will call @course.active_record_types(only_check: [:announcements])
it will do a cache read for ['active_record_types', [:announcements], self].cache_key
since it it a fresh cach, that will not be found
then it would have done a cache read for the "everything" cache at
['active_record_types', nil, self].cache_key
THAT WOULD HAVE RETURNED THE CACHED `{}` SINCE `nil.cache_key` and `[].cache_key` ARE THE SAME!
* the user would be told the announcement page is not enabled for that course
the fix is to explicitly not allow Context::active_record_types to ever
be called with `only_check: []`
and for good measure, we don't allow the implicit conversion of
nil.cache_key to "" and instead use "everything" for the cache cache_key
I added specs to spefically catch this bug so that it doesn't happen again.
To see the difference, compare the latest patchset of this commit against
patchset 1. patchset 1 is the original version of this code without this
fix.
Change-Id: I513104b90dd94227a04c151ee02a22f4a4ac2832
Reviewed-on: https://gerrit.instructure.com/167400
Tested-by: Jenkins
Reviewed-by: Jeremy Stanley <jeremy@instructure.com>
QA-Review: Jeremy Stanley <jeremy@instructure.com>
Product-Review: Ryan Shaw <ryan@instructure.com>
2018-10-08 23:36:54 +08:00
|
|
|
|
|
|
|
# if it exists in redis, return that
|
|
|
|
if (cached = Rails.cache.read(cache_key))
|
|
|
|
return @active_record_types[only_check] = cached
|
|
|
|
end
|
|
|
|
|
|
|
|
# if we're only asking for a subset but the full set is cached return that, but filtered with just what we want
|
|
|
|
if only_check.present? && (cache_with_everything = Rails.cache.read([base_cache_key, "everything", self].cache_key))
|
|
|
|
return @active_record_types[only_check] = cache_with_everything.select { |k, _v| only_check.include?(k) }
|
|
|
|
end
|
|
|
|
|
|
|
|
# otherwise compute it and store it in the cache
|
|
|
|
value_to_cache = nil
|
|
|
|
ActiveRecord::Base.uncached do
|
|
|
|
value_to_cache = types_to_check.transform_values(&:call)
|
2011-02-01 09:57:29 +08:00
|
|
|
end
|
restore "speed up dashcards by only loading tabs shown" & fix bug
Fixes: CORE-1964
This reverts commit 72d558f09554f4e45130c4a508311a1bc0d007f3
this restores the commit (aka, reverts that ^ revert) that was causing
us bugs on caturday. the root cause of the bug was that
["active_record_types", [], self].cache_key
and
["active_record_types", nil, self].cache_key
return the same string.
how to replay the buggy scenerio:
(as a student, in a course with annnouncements):
* right after having "touched" the course (so there's nothing cached),
* have one person go to /courses/:course_id/assignments/syllabus
* in the controller action for that, it will do:
`return unless tab_enabled?(@context.class::TAB_SYLLABUS)`
that will call Course::uncached_tabs_available(only_check: @context.class::TAB_SYLLABUS)
which would have called @course.active_record_types(only_check: [])
(because @context.class::TAB_SYLLABUS is not one of the `tabs_that_can_be_marked_hidden_unused`)
which woud have written `{}` to redis at ['active_record_types', [], self].cache_key
* now, as a different student, go to /courses/:course_id/annnouncements
it will call `tab_enabled?(@context.class::TAB_ANNOUNCEMENTS)`
that will call Course::uncached_tabs_available(only_check: @context.class::TAB_ANNOUNCEMENTS)
which will call @course.active_record_types(only_check: [:announcements])
it will do a cache read for ['active_record_types', [:announcements], self].cache_key
since it it a fresh cach, that will not be found
then it would have done a cache read for the "everything" cache at
['active_record_types', nil, self].cache_key
THAT WOULD HAVE RETURNED THE CACHED `{}` SINCE `nil.cache_key` and `[].cache_key` ARE THE SAME!
* the user would be told the announcement page is not enabled for that course
the fix is to explicitly not allow Context::active_record_types to ever
be called with `only_check: []`
and for good measure, we don't allow the implicit conversion of
nil.cache_key to "" and instead use "everything" for the cache cache_key
I added specs to spefically catch this bug so that it doesn't happen again.
To see the difference, compare the latest patchset of this commit against
patchset 1. patchset 1 is the original version of this code without this
fix.
Change-Id: I513104b90dd94227a04c151ee02a22f4a4ac2832
Reviewed-on: https://gerrit.instructure.com/167400
Tested-by: Jenkins
Reviewed-by: Jeremy Stanley <jeremy@instructure.com>
QA-Review: Jeremy Stanley <jeremy@instructure.com>
Product-Review: Ryan Shaw <ryan@instructure.com>
2018-10-08 23:36:54 +08:00
|
|
|
Rails.cache.write(cache_key, value_to_cache)
|
|
|
|
@active_record_types[only_check] = value_to_cache
|
2011-02-01 09:57:29 +08:00
|
|
|
end
|
2012-12-12 21:50:15 +08:00
|
|
|
|
2011-02-01 09:57:29 +08:00
|
|
|
def allow_wiki_comments
|
|
|
|
false
|
|
|
|
end
|
|
|
|
|
|
|
|
def find_asset(asset_string, allowed_types = nil)
|
|
|
|
return nil unless asset_string
|
2021-09-23 00:20:17 +08:00
|
|
|
|
2011-02-01 09:57:29 +08:00
|
|
|
res = Context.find_asset_by_asset_string(asset_string, self, allowed_types)
|
|
|
|
res = nil if res.respond_to?(:deleted?) && res.deleted?
|
|
|
|
res
|
|
|
|
end
|
2012-12-12 21:50:15 +08:00
|
|
|
|
2013-08-10 06:12:48 +08:00
|
|
|
# [[context_type, context_id], ...] -> {[context_type, context_id] => name, ...}
|
|
|
|
def self.names_by_context_types_and_ids(context_types_and_ids)
|
|
|
|
ids_by_type = Hash.new([])
|
|
|
|
context_types_and_ids.each do |type, id|
|
2015-04-08 05:58:15 +08:00
|
|
|
next unless type && CONTEXT_TYPES.include?(type.to_sym)
|
2021-09-23 00:20:17 +08:00
|
|
|
|
2013-08-10 06:12:48 +08:00
|
|
|
ids_by_type[type] += [id]
|
|
|
|
end
|
|
|
|
|
2015-04-28 01:48:35 +08:00
|
|
|
result = {}
|
2013-08-10 06:12:48 +08:00
|
|
|
ids_by_type.each do |type, ids|
|
2015-04-08 05:58:15 +08:00
|
|
|
klass = Object.const_get(type, false)
|
2016-08-16 23:07:06 +08:00
|
|
|
klass.where(id: ids).pluck(:id, :name).map { |id, name| result[[type, id]] = name }
|
2013-08-10 06:12:48 +08:00
|
|
|
end
|
|
|
|
result
|
|
|
|
end
|
|
|
|
|
2017-08-22 22:15:37 +08:00
|
|
|
def self.context_code_for(record)
|
|
|
|
raise ArgumentError unless record.respond_to?(:context_type) && record.respond_to?(:context_id)
|
2021-08-18 05:23:58 +08:00
|
|
|
|
2017-08-22 22:15:37 +08:00
|
|
|
"#{record.context_type.underscore}_#{record.context_id}"
|
|
|
|
end
|
|
|
|
|
2018-04-21 14:43:08 +08:00
|
|
|
def self.find_by_asset_string(string)
|
2021-08-18 05:23:58 +08:00
|
|
|
ActiveRecord::Base.find_by_asset_string(string, CONTEXT_TYPES.map(&:to_s))
|
2011-02-01 09:57:29 +08:00
|
|
|
end
|
2012-12-12 21:50:15 +08:00
|
|
|
|
2021-08-18 05:23:58 +08:00
|
|
|
def self.find_all_by_asset_string(strings)
|
|
|
|
ActiveRecord::Base.find_all_by_asset_string(strings, CONTEXT_TYPES.map(&:to_s))
|
2017-08-19 06:36:41 +08:00
|
|
|
end
|
|
|
|
|
2015-04-08 05:58:15 +08:00
|
|
|
def self.asset_type_for_string(string)
|
|
|
|
ASSET_TYPES[string.to_sym].to_s.constantize if ASSET_TYPES.key?(string.to_sym)
|
|
|
|
end
|
|
|
|
|
2011-02-01 09:57:29 +08:00
|
|
|
def self.find_asset_by_asset_string(string, context = nil, allowed_types = nil)
|
2021-08-18 05:23:58 +08:00
|
|
|
type, id = ActiveRecord::Base.parse_asset_string(string)
|
2015-04-08 05:58:15 +08:00
|
|
|
klass = asset_type_for_string(type)
|
2011-02-01 09:57:29 +08:00
|
|
|
klass = nil if allowed_types && !allowed_types.include?(klass.to_s.underscore.to_sym)
|
|
|
|
return nil unless klass
|
2021-08-18 05:23:58 +08:00
|
|
|
|
2011-02-01 09:57:29 +08:00
|
|
|
res = nil
|
2015-04-14 23:37:49 +08:00
|
|
|
if context && klass == ContextExternalTool
|
2014-10-01 21:23:18 +08:00
|
|
|
res = klass.find_external_tool_by_id(id, context)
|
2015-04-14 23:37:49 +08:00
|
|
|
elsif context && (klass.column_names & ["context_id", "context_type"]).length == 2
|
2023-06-02 06:06:09 +08:00
|
|
|
res = klass.where(context:, id:).first
|
2014-09-12 03:44:34 +08:00
|
|
|
else
|
2023-06-02 06:06:09 +08:00
|
|
|
res = klass.find_by(id:)
|
2021-08-18 05:23:58 +08:00
|
|
|
res = nil if context && res.respond_to?(:context_id) && res.context_id != context.id
|
2011-02-01 09:57:29 +08:00
|
|
|
end
|
|
|
|
res
|
2021-08-18 05:23:58 +08:00
|
|
|
rescue
|
2011-02-01 09:57:29 +08:00
|
|
|
nil
|
|
|
|
end
|
2012-12-12 21:50:15 +08:00
|
|
|
|
2019-12-21 01:07:26 +08:00
|
|
|
def self.get_front_wiki_page_for_course_from_url(url)
|
|
|
|
params = Rails.application.routes.recognize_path(url)
|
|
|
|
if params[:controller] == "courses" && params[:action] == "show"
|
|
|
|
course = Course.find(params[:id])
|
|
|
|
if course.default_view == "wiki"
|
|
|
|
course.wiki.front_page
|
|
|
|
end
|
|
|
|
end
|
|
|
|
rescue
|
|
|
|
nil
|
|
|
|
end
|
|
|
|
|
2019-01-19 05:11:28 +08:00
|
|
|
def self.find_asset_by_url(url)
|
|
|
|
object = nil
|
2021-04-14 01:16:56 +08:00
|
|
|
uri = URI.parse(url)
|
|
|
|
params = Rails.application.routes.recognize_path(uri.path)
|
2019-01-19 05:11:28 +08:00
|
|
|
course = Course.find(params[:course_id]) if params[:course_id]
|
|
|
|
group = Group.find(params[:group_id]) if params[:group_id]
|
|
|
|
user = User.find(params[:user_id]) if params[:user_id]
|
|
|
|
context = course || group || user
|
2019-12-21 01:07:26 +08:00
|
|
|
|
2023-11-22 09:01:12 +08:00
|
|
|
return nil unless context || params[:controller] == "media_objects"
|
2021-09-23 00:20:17 +08:00
|
|
|
|
2019-01-19 05:11:28 +08:00
|
|
|
case params[:controller]
|
|
|
|
when "files"
|
|
|
|
rel_path = params[:file_path]
|
|
|
|
object = rel_path && Folder.find_attachment_in_context_with_path(course, CGI.unescape(rel_path))
|
|
|
|
file_id = params[:file_id] || params[:id]
|
2021-04-14 01:16:56 +08:00
|
|
|
file_id ||= uri.query && CGI.parse(uri.query).send(:[], "preview")&.first
|
2021-11-16 07:41:20 +08:00
|
|
|
object ||= context.attachments.find_by(id: file_id) # attachments.find_by(id:) uses the replacement hackery
|
2022-05-11 04:29:54 +08:00
|
|
|
full_path = params[:full_path]
|
|
|
|
folder = full_path && Folder.find_by(name: full_path)
|
|
|
|
object ||= folder if folder && folder.context == context
|
2019-01-19 05:11:28 +08:00
|
|
|
when "wiki_pages"
|
|
|
|
object = context.wiki.find_page(CGI.unescape(params[:id]), include_deleted: true)
|
2019-08-03 04:42:08 +08:00
|
|
|
if !object && params[:id].to_s.include?("+") # maybe it really is a "+"
|
|
|
|
object = context.wiki.find_page(CGI.unescape(params[:id].to_s.gsub("+", "%2B")), include_deleted: true)
|
|
|
|
end
|
2019-04-12 03:42:14 +08:00
|
|
|
when "external_tools"
|
|
|
|
if params[:action] == "retrieve"
|
2022-10-06 22:06:54 +08:00
|
|
|
query_params = CGI.parse(uri.query)
|
|
|
|
tool_url = query_params["url"]&.first
|
|
|
|
resource_link_lookup_uuid = query_params["resource_link_lookup_uuid"]&.first
|
|
|
|
object = if tool_url
|
|
|
|
ContextExternalTool.find_external_tool(tool_url, context)
|
|
|
|
elsif resource_link_lookup_uuid
|
|
|
|
Lti::ResourceLink.where(
|
|
|
|
lookup_uuid: resource_link_lookup_uuid,
|
2023-06-02 06:06:09 +08:00
|
|
|
context:
|
2022-10-06 22:06:54 +08:00
|
|
|
).active.take&.current_external_tool(context)
|
|
|
|
end
|
2019-04-12 03:42:14 +08:00
|
|
|
elsif params[:id]
|
|
|
|
object = ContextExternalTool.find_external_tool_by_id(params[:id], context)
|
|
|
|
end
|
2019-05-08 04:59:12 +08:00
|
|
|
when "context_modules"
|
|
|
|
object = if %w[item_redirect item_redirect_mastery_paths choose_mastery_path].include?(params[:action])
|
|
|
|
context.context_module_tags.find_by(id: params[:id])
|
|
|
|
else
|
|
|
|
context.context_modules.find_by(id: params[:id])
|
|
|
|
end
|
2021-01-08 04:47:39 +08:00
|
|
|
when "media_objects"
|
2023-11-22 09:01:12 +08:00
|
|
|
object = if params[:media_object_id]
|
|
|
|
MediaObject.where(media_id: params[:media_object_id]).first
|
|
|
|
elsif params[:attachment_id]
|
|
|
|
# get possibly replaced attachment, see app/models/attachment.rb find_attachment_possibly_replaced
|
|
|
|
Attachment.find_by(id: params[:attachment_id])&.context&.attachments&.find_by(id: params[:attachment_id])
|
|
|
|
end
|
2021-09-17 03:39:58 +08:00
|
|
|
when "context"
|
|
|
|
object = context.users.find(params[:id]) if params[:action] == "roster_user" && params[:id]
|
2019-01-19 05:11:28 +08:00
|
|
|
else
|
|
|
|
object = context.try(params[:controller].sub(%r{^.+/}, ""))&.find_by(id: params[:id])
|
|
|
|
end
|
|
|
|
object
|
2021-10-20 05:23:50 +08:00
|
|
|
rescue
|
2019-01-19 05:11:28 +08:00
|
|
|
nil
|
|
|
|
end
|
|
|
|
|
2023-01-12 02:11:39 +08:00
|
|
|
def self.api_type_name(klass)
|
|
|
|
case klass.to_s
|
|
|
|
when "Announcement"
|
|
|
|
"announcements"
|
|
|
|
when "Attachment"
|
|
|
|
"files"
|
|
|
|
when "ContextModule"
|
|
|
|
"modules"
|
|
|
|
when "ContentTag"
|
|
|
|
"module_items"
|
|
|
|
when "WikiPage"
|
|
|
|
"pages"
|
|
|
|
else
|
|
|
|
klass.table_name
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-10-13 05:37:31 +08:00
|
|
|
def self.asset_name(asset)
|
2017-11-21 05:40:59 +08:00
|
|
|
name = asset.display_name.presence if asset.respond_to?(:display_name)
|
|
|
|
name ||= asset.title.presence if asset.respond_to?(:title)
|
|
|
|
name ||= asset.short_description.presence if asset.respond_to?(:short_description)
|
2018-01-12 01:36:24 +08:00
|
|
|
name ||= asset.name if asset.respond_to?(:name)
|
2022-06-07 04:14:35 +08:00
|
|
|
name ||= asset.asset_name if asset.respond_to?(:asset_name)
|
2018-01-12 01:36:24 +08:00
|
|
|
name || ""
|
2017-10-13 05:37:31 +08:00
|
|
|
end
|
|
|
|
|
2019-01-19 05:11:28 +08:00
|
|
|
def self.asset_body(asset)
|
|
|
|
asset.try(:body) || asset.try(:message) || asset.try(:description)
|
|
|
|
end
|
|
|
|
|
2015-12-05 00:57:07 +08:00
|
|
|
def self.get_account(context)
|
2016-01-12 06:03:47 +08:00
|
|
|
case context
|
|
|
|
when Account
|
2015-12-05 00:57:07 +08:00
|
|
|
context
|
2016-01-12 06:03:47 +08:00
|
|
|
when Course
|
2015-12-05 00:57:07 +08:00
|
|
|
get_account(context.account)
|
2016-01-12 06:03:47 +08:00
|
|
|
when CourseSection
|
|
|
|
get_account(context.course)
|
|
|
|
when Group
|
2015-12-05 00:57:07 +08:00
|
|
|
get_account(context.context)
|
|
|
|
end
|
2015-08-15 02:42:55 +08:00
|
|
|
end
|
|
|
|
|
2019-09-25 04:58:58 +08:00
|
|
|
def self.get_account_or_parent_account_global_id(context)
|
2019-06-14 07:13:52 +08:00
|
|
|
case context
|
|
|
|
when Account
|
2019-09-25 04:58:58 +08:00
|
|
|
context.root_account? ? context.global_id : context.global_parent_account_id
|
2019-06-14 07:13:52 +08:00
|
|
|
when Course
|
2019-09-25 04:58:58 +08:00
|
|
|
context.global_account_id
|
2019-06-14 07:13:52 +08:00
|
|
|
when CourseSection
|
2019-09-25 04:58:58 +08:00
|
|
|
get_account_or_parent_account_global_id(context.course)
|
2019-06-14 07:13:52 +08:00
|
|
|
when Group
|
2019-09-25 04:58:58 +08:00
|
|
|
get_account_or_parent_account_global_id(context.context)
|
2019-06-14 07:13:52 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2011-02-01 09:57:29 +08:00
|
|
|
def is_a_context?
|
|
|
|
true
|
|
|
|
end
|
2013-07-10 00:14:52 +08:00
|
|
|
|
2014-06-11 08:49:40 +08:00
|
|
|
def concluded?
|
|
|
|
false
|
|
|
|
end
|
|
|
|
|
2013-11-15 02:22:17 +08:00
|
|
|
# Public: Boolean flag re: whether a feature is enabled
|
|
|
|
# provides defaults for objects that do not include FeatureFlags
|
|
|
|
# (note: include Context _before_ FeatureFlags)
|
2013-07-10 00:14:52 +08:00
|
|
|
#
|
|
|
|
# Returns false
|
2015-08-15 02:42:55 +08:00
|
|
|
def feature_enabled?(_feature)
|
2013-07-10 00:14:52 +08:00
|
|
|
false
|
|
|
|
end
|
2015-10-15 21:19:41 +08:00
|
|
|
|
2021-08-05 06:20:03 +08:00
|
|
|
def nickname_for(_user, fallback = :name, prefer_friendly_name: true)
|
2015-10-15 21:19:41 +08:00
|
|
|
send fallback if fallback
|
|
|
|
end
|
2018-06-22 01:42:03 +08:00
|
|
|
|
2021-08-18 05:23:58 +08:00
|
|
|
def self.last_updated_at(klasses_to_ids)
|
|
|
|
scopes = []
|
|
|
|
|
|
|
|
klasses_to_ids.each do |(klass, ids)|
|
|
|
|
next if ids.empty?
|
|
|
|
|
2021-08-27 23:05:02 +08:00
|
|
|
scopes << klass
|
|
|
|
.shard(Shard.current) # prevent it switching shards on us
|
|
|
|
.where(id: ids)
|
2021-08-18 05:23:58 +08:00
|
|
|
.order(updated_at: :desc)
|
|
|
|
.select(:updated_at)
|
|
|
|
.limit(1)
|
|
|
|
end
|
|
|
|
|
|
|
|
return nil if scopes.empty?
|
2021-06-10 01:52:30 +08:00
|
|
|
|
2021-08-18 05:23:58 +08:00
|
|
|
final_scope = scopes.first if scopes.length == 1
|
|
|
|
final_scope ||= scopes.first.union(*scopes[1..], from: true)
|
|
|
|
final_scope.order(updated_at: :desc).limit(1).pluck(:updated_at)&.first
|
2018-06-22 01:42:03 +08:00
|
|
|
end
|
2020-06-11 01:27:02 +08:00
|
|
|
|
|
|
|
def resolved_root_account_id
|
|
|
|
root_account_id if respond_to? :root_account_id
|
|
|
|
end
|
2011-02-01 09:57:29 +08:00
|
|
|
end
|