mirror of
https://gh.wpcy.net/https://github.com/discourse/discourse.git
synced 2026-05-01 04:07:14 +08:00
Currently all our preinstalled plugins use the default "gear" icon from fontawesome — this PR adds unique icons for each of them using a new plugin API method, `setAdminPluginIcon()`. Plugins without an icon defined will still fall back to the gear icon. Before: <img width="250" alt="image" src="https://github.com/user-attachments/assets/06b5c6e7-0aae-44f8-b8ee-1486b98bfc6b" /> After: <img width="250" alt="image" src="https://github.com/user-attachments/assets/46d45d4b-6c75-4473-ae53-1a0e71c4d6fb" />
78 lines
2.4 KiB
Ruby
78 lines
2.4 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
# name: discourse-lti
|
|
# about: Allows login integration with a number of learning management systems via the LTI 1.3 standard.
|
|
# meta_topic_id: 196174
|
|
# version: 1.0
|
|
# authors: David Taylor
|
|
# url: https://github.com/discourse/discourse/tree/main/plugins/discourse-lti
|
|
|
|
enabled_site_setting :lti_enabled
|
|
|
|
register_svg_icon "graduation-cap"
|
|
|
|
module ::DiscourseLti
|
|
PLUGIN_NAME = "discourse-lti"
|
|
CUSTOM_DATA_CLAIM = "https://purl.imsglobal.org/spec/lti/claim/custom"
|
|
DISCOURSE_INVITE_KEYS = %w[discourse_invite_link custom_discourse_invite_link] # Coursera prefixes keys with `custom_`
|
|
|
|
class ShouldReconnect < StandardError
|
|
end
|
|
end
|
|
|
|
after_initialize do
|
|
# Check for invite URL in LTI custom fields
|
|
# If present, and this is a new user, redirect the user to the invite url.
|
|
# Otherwise, continue as normal
|
|
on(:after_auth) do |authenticator, auth_result, session, cookies, request|
|
|
next if !(authenticator.name.to_sym == :lti)
|
|
next if auth_result.user # Only redirect new users to invite
|
|
|
|
uaa =
|
|
UserAssociatedAccount.find_by(
|
|
provider_name: "lti",
|
|
provider_uid: auth_result.extra_data[:uid],
|
|
)
|
|
next if uaa.nil?
|
|
|
|
custom_data = uaa.extra.dig("raw_info", DiscourseLti::CUSTOM_DATA_CLAIM)
|
|
next if custom_data.nil?
|
|
|
|
invite = nil
|
|
DiscourseLti::DISCOURSE_INVITE_KEYS.each { |k| break if invite = custom_data[k] }
|
|
next if invite.nil?
|
|
|
|
parsed =
|
|
begin
|
|
URI.parse(invite)
|
|
rescue URI::Error
|
|
next
|
|
end
|
|
next if parsed.nil?
|
|
next if parsed.host && parsed.host != Discourse.current_hostname
|
|
|
|
route = Discourse.route_for(parsed.path)
|
|
next if !(route[:controller] == "invites" && route[:action] == "show")
|
|
|
|
request.server_session[:destination_url] = parsed.to_s
|
|
end
|
|
|
|
on(:after_auth) do |authenticator, auth_result, session, cookies|
|
|
next if !(authenticator.name.to_sym == :lti)
|
|
if !auth_result.user && cookies["_t"] # User (probably) already logged in
|
|
raise DiscourseLti::ShouldReconnect.new
|
|
end
|
|
end
|
|
|
|
reloadable_patch do
|
|
::Users::OmniauthCallbacksController.rescue_from(DiscourseLti::ShouldReconnect) do
|
|
session[:auth_reconnect] = true
|
|
complete
|
|
end
|
|
end
|
|
end
|
|
|
|
require_relative "lib/discourse_lti/lti_omniauth_strategy"
|
|
require_relative "lib/discourse_lti/lti_authenticator"
|
|
|
|
auth_provider authenticator: DiscourseLti::LtiAuthenticator.new
|