diff --git a/app/controllers/concerns/lti.rb b/app/controllers/concerns/lti.rb index e8241af3..155e8895 100644 --- a/app/controllers/concerns/lti.rb +++ b/app/controllers/concerns/lti.rb @@ -52,6 +52,11 @@ module Lti end private :external_user_name + def mooc_course + # All Xikolo platforms set the custom_course to the course code + params[:custom_course] + end + def refuse_lti_launch(options = {}) return_to_consumer(lti_errorlog: options[:message], lti_errormsg: t('sessions.oauth.failure')) end @@ -133,6 +138,13 @@ module Lti end private :set_current_user + def set_study_group_membership + return if mooc_course + group = StudyGroup.find_or_create_by(external_id: @provider.resource_link_id, consumer: @consumer) + group.users |= [@current_user] # add current user if not already member of the group + group.save + end + def store_lti_session_data(options = {}) lti_parameters = LtiParameter.find_or_create_by(consumers_id: options[:consumer].id, external_users_id: @current_user.id, diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 8f698d1a..24b2e6b1 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -1,7 +1,7 @@ class SessionsController < ApplicationController include Lti - [:require_oauth_parameters, :require_valid_consumer_key, :require_valid_oauth_signature, :require_unique_oauth_nonce, :set_current_user, :require_valid_exercise_token].each do |method_name| + [:require_oauth_parameters, :require_valid_consumer_key, :require_valid_oauth_signature, :require_unique_oauth_nonce, :set_current_user, :set_study_group_membership, :require_valid_exercise_token].each do |method_name| before_action(method_name, only: :create_through_lti) end diff --git a/app/models/external_user.rb b/app/models/external_user.rb index 29c52692..053d3795 100644 --- a/app/models/external_user.rb +++ b/app/models/external_user.rb @@ -4,11 +4,11 @@ class ExternalUser < User validates :external_id, presence: true def displayname - result = name - if(result == nil || result == "") - result = "User " + id.to_s + if name.blank? + "User " + id.to_s + else + name end - result end end diff --git a/app/models/study_group.rb b/app/models/study_group.rb new file mode 100644 index 00000000..b86f4969 --- /dev/null +++ b/app/models/study_group.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class StudyGroup < ApplicationRecord + has_many :study_group_memberships + # Use `ExternalUser` as `source_type` for now. + # Using `User` will lead ActiveRecord to access the inexistent table `users`. + # Issue created: https://github.com/rails/rails/issues/34531 + has_many :users, through: :study_group_memberships, source_type: 'ExternalUser' + has_many :submissions + belongs_to :consumer +end diff --git a/app/models/study_group_membership.rb b/app/models/study_group_membership.rb new file mode 100644 index 00000000..1f7aad91 --- /dev/null +++ b/app/models/study_group_membership.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +class StudyGroupMembership < ApplicationRecord + belongs_to :user, polymorphic: true + belongs_to :study_group + + validates_uniqueness_of :user_id, :scope => [:user_type, :study_group_id] +end diff --git a/app/models/submission.rb b/app/models/submission.rb index e9d3b235..fe765543 100644 --- a/app/models/submission.rb +++ b/app/models/submission.rb @@ -6,6 +6,7 @@ class Submission < ApplicationRecord FILENAME_URL_PLACEHOLDER = '{filename}' belongs_to :exercise + belongs_to :study_group, optional: true has_many :testruns has_many :structured_errors diff --git a/app/models/user.rb b/app/models/user.rb index 5c045101..a9c9dab2 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -4,6 +4,8 @@ class User < ApplicationRecord ROLES = %w(admin teacher) belongs_to :consumer + has_many :study_group_memberships, as: :user + has_many :study_groups, through: :study_group_memberships, as: :user has_many :exercises, as: :user has_many :file_types, as: :user has_many :submissions, as: :user diff --git a/db/migrate/20181122084546_create_study_groups.rb b/db/migrate/20181122084546_create_study_groups.rb new file mode 100644 index 00000000..63919d75 --- /dev/null +++ b/db/migrate/20181122084546_create_study_groups.rb @@ -0,0 +1,13 @@ +class CreateStudyGroups < ActiveRecord::Migration[5.2] + def change + create_table :study_groups do |t| + t.string :name + t.string :external_id + t.belongs_to :consumer + t.timestamps + end + + add_index :study_groups, [:external_id, :consumer_id], unique: true + end +end + diff --git a/db/migrate/20181122090243_create_study_group_memberships.rb b/db/migrate/20181122090243_create_study_group_memberships.rb new file mode 100644 index 00000000..8cb34dd3 --- /dev/null +++ b/db/migrate/20181122090243_create_study_group_memberships.rb @@ -0,0 +1,8 @@ +class CreateStudyGroupMemberships < ActiveRecord::Migration[5.2] + def change + create_table :study_group_memberships do |t| + t.belongs_to :study_group + t.belongs_to :user, polymorphic: true + end + end +end diff --git a/db/migrate/20181122090244_add_study_group_to_submission.rb b/db/migrate/20181122090244_add_study_group_to_submission.rb new file mode 100644 index 00000000..94cd30d6 --- /dev/null +++ b/db/migrate/20181122090244_add_study_group_to_submission.rb @@ -0,0 +1,5 @@ +class AddStudyGroupToSubmission < ActiveRecord::Migration[5.2] + def change + add_reference :submissions, :study_group, index: true, null: true, foreign_key: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 82ec19bf..3435e610 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -315,6 +315,24 @@ ActiveRecord::Schema.define(version: 2018_11_29_093207) do t.index ["submission_id"], name: "index_structured_errors_on_submission_id" end + create_table "study_group_memberships", force: :cascade do |t| + t.bigint "study_group_id" + t.string "user_type" + t.bigint "user_id" + t.index ["study_group_id"], name: "index_study_group_memberships_on_study_group_id" + t.index ["user_type", "user_id"], name: "index_study_group_memberships_on_user_type_and_user_id" + end + + create_table "study_groups", force: :cascade do |t| + t.string "name" + t.string "external_id" + t.bigint "consumer_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["consumer_id"], name: "index_study_groups_on_consumer_id" + t.index ["external_id", "consumer_id"], name: "index_study_groups_on_external_id_and_consumer_id", unique: true + end + create_table "submissions", force: :cascade do |t| t.integer "exercise_id" t.float "score" @@ -323,7 +341,9 @@ ActiveRecord::Schema.define(version: 2018_11_29_093207) do t.datetime "updated_at" t.string "cause", limit: 255 t.string "user_type", limit: 255 + t.bigint "study_group_id" t.index ["exercise_id"], name: "index_submissions_on_exercise_id" + t.index ["study_group_id"], name: "index_submissions_on_study_group_id" t.index ["user_id"], name: "index_submissions_on_user_id" end diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb index c1ea7ba1..6692e56b 100644 --- a/spec/controllers/sessions_controller_spec.rb +++ b/spec/controllers/sessions_controller_spec.rb @@ -66,7 +66,7 @@ describe SessionsController do it 'refuses the LTI launch' do expect_any_instance_of(IMS::LTI::ToolProvider).to receive(:valid_request?).and_return(true) expect(controller).to receive(:refuse_lti_launch).with(message: I18n.t('sessions.oauth.invalid_exercise_token')).and_call_original - post :create_through_lti, params: { custom_token: '', oauth_consumer_key: consumer.oauth_key, oauth_nonce: nonce, oauth_signature: SecureRandom.hex } + post :create_through_lti, params: { custom_token: '', oauth_consumer_key: consumer.oauth_key, oauth_nonce: nonce, oauth_signature: SecureRandom.hex, user_id: '123' } end end