canvas-lms/app/helpers/broken_link_helper.rb

66 lines
2.5 KiB
Ruby

# frozen_string_literal: true
#
# Copyright (C) 2019 - 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 BrokenLinkHelper
def send_broken_content!
# call when processing a 4xx error.
# this will examine the referrer to see if we got here because of a bad link
from_url = request.referer
return false unless from_url
record = Context.find_asset_by_url(from_url)
record ||= Context.get_front_wiki_page_for_course_from_url(from_url)
return false unless record
body = Nokogiri::HTML5(Context.asset_body(record))
anchor = body.css("a[href$='#{request.fullpath}']").text
return false if anchor.blank?
users = record.context.is_a?(Course) ? record.context.participating_admins_by_date : record.context.participating_users
recipient_keys = users.select { |admin| record.grants_any_right?(admin, :update, :update_content) }.map(&:asset_string)
return false unless recipient_keys.present? && recipient_keys.exclude?(@current_user.asset_string)
notification = BroadcastPolicy.notification_finder.by_name("Content Link Error")
error_type = error_type(record.context, request.url)
data = { location: request.referer, url: request.url, anchor:, error_type: }
DelayedNotification.delay_if_production(priority: Delayed::LOW_PRIORITY)
.process(record, notification, recipient_keys, data)
true
rescue
false
end
def error_type(course, url)
course_id = url.match(%r{/courses/(\d+)})&.[](1)&.to_i
return :course_mismatch if course_id && course_id != course.id
link_obj = Context.find_asset_by_url(url)
return response_code_type unless link_obj
course_validator = CourseLinkValidator.new(course)
course_validator.check_object_status(url, object: link_obj) || response_code_type
rescue
:missing_item
end
def response_code_type
(response.status == 401 || response.status == 403) ? :inaccessible : :missing_item
end
end