diff --git a/app/controllers/courses_controller.rb b/app/controllers/courses_controller.rb index eee1204eec..22d12f794a 100644 --- a/app/controllers/courses_controller.rb +++ b/app/controllers/courses_controller.rb @@ -342,26 +342,14 @@ def subscribe success_method = method(:signup_succeeded_response) end - case @course.registration - when 'open_for_all' + if @course.open_for_user?(current_user) if try_to_subscribe_current_user status: status success_method.call(format) else subscription_failed_response format end - when 'open_for_institution' - if @course.institution == current_user.institution - if try_to_subscribe_current_user status: status - success_method.call(format) - else - subscription_failed_response format - end - else - format.html { redirect_to(course_url(@course, secret: params[:secret]), alert: I18n.t('courses.registration.closed')) } - format.json { render json: { errors: ['course closed'] }, status: :unprocessable_entity } - end - when 'closed' - format.html { redirect_to(@course, alert: I18n.t('courses.registration.closed')) } + else + format.html { redirect_to(course_url(@course, secret: params[:secret]), alert: I18n.t('courses.registration.closed')) } format.json { render json: { errors: ['course closed'] }, status: :unprocessable_entity } end end diff --git a/app/helpers/courses_helper.rb b/app/helpers/courses_helper.rb index bfd92b3ced..150b7ebdac 100644 --- a/app/helpers/courses_helper.rb +++ b/app/helpers/courses_helper.rb @@ -7,7 +7,7 @@ def registration_action_for(**args) membership = args[:membership] if membership.nil? || membership.unsubscribed? - if course.open_for_all? || (course.open_for_institution? && (course.institution == current_user&.institution || current_user.nil?)) + if course.open_for_user?(current_user) || current_user.nil? if course.moderated link_to t('courses.show.request_registration'), subscribe_course_path(@course, secret: secret), diff --git a/app/models/activity.rb b/app/models/activity.rb index ddd41e9baa..b50b33f36a 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -280,7 +280,7 @@ def accessible?(user, course) return true if user&.member_of? course return false if course.moderated && access_private? - course.open_for_all? || (course.open_for_institution? && course.institution == user&.institution) + course.open_for_user?(user) else return true if user&.repository_admin? repository diff --git a/app/models/course.rb b/app/models/course.rb index ccdcb94043..8ab82f33d4 100644 --- a/app/models/course.rb +++ b/app/models/course.rb @@ -55,7 +55,7 @@ class Course < ApplicationRecord has_many :course_labels, dependent: :destroy enum visibility: { visible_for_all: 0, visible_for_institution: 1, hidden: 2 } - enum registration: { open_for_all: 0, open_for_institution: 1, closed: 2 } + enum registration: { open_for_all: 3, open_for_institutional_users: 0, open_for_institution: 1, closed: 2 } # TODO: Remove and use activities? has_many :content_pages, @@ -168,7 +168,7 @@ class Course < ApplicationRecord # Default year & enum values after_initialize do |course| self.visibility ||= 'visible_for_all' - self.registration ||= 'open_for_all' + self.registration ||= 'open_for_institutional_users' unless year now = Time.zone.now y = now.year @@ -214,6 +214,10 @@ def all_activities_accessible? activities.where(access: :private).where.not(repository_id: usable_repositories).count.zero? end + def open_for_user?(user) + open_for_all? || (open_for_institution? && institution == user&.institution) || (open_for_institutional_users? && user&.institutional?) + end + def invalidate_subscribed_members_count_cache Rails.cache.delete(format(SUBSCRIBED_MEMBERS_COUNT_CACHE_STRING, id: id)) end diff --git a/app/models/user.rb b/app/models/user.rb index f25178af9d..9d0c7d5763 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -272,6 +272,14 @@ def repository_admin?(repository) @repository_admin.include?(repository.id) end + def personal? + institution.nil? + end + + def institutional? + institution.present? + end + def attempted_exercises(options) s = submissions.judged s = s.in_course(options[:course]) if options[:course].present? diff --git a/bin/server b/bin/server index 4ae98c895a..ce5526cbbd 100755 --- a/bin/server +++ b/bin/server @@ -1,4 +1,7 @@ #!/bin/bash bundle install yarn install -bundle exec rails s -p 3000 & bundle exec rails jobs:work & yarn build:css --watch & NODE_ENV=development yarn build:js --watch +bundle exec rails s -p 3000 & +bundle exec rails jobs:work & +yarn build:css --watch & +NODE_ENV=development yarn build:js --watch diff --git a/config/locales/models/en.yml b/config/locales/models/en.yml index 3ef3a83b32..623d3b5a85 100644 --- a/config/locales/models/en.yml +++ b/config/locales/models/en.yml @@ -55,6 +55,7 @@ en: open_for_all: Open for everyone open_for_institution: Only open for members of the configured institution closed: Closed + open_for_institutional_users: Open for all users that belong to an institution institution: Institution activity: name: Name diff --git a/config/locales/models/nl.yml b/config/locales/models/nl.yml index 16f8ef964e..6a7335b43c 100644 --- a/config/locales/models/nl.yml +++ b/config/locales/models/nl.yml @@ -56,6 +56,7 @@ nl: open_for_all: Open voor iedereen open_for_institution: Enkel open voor gebruikers van de ingestelde onderwijsinstelling closed: Gesloten + open_for_institutional_users: Open voor alle gebruikers die tot een onderwijsinstelling behoren institution: Onderwijsinstelling activity: name: Naam diff --git a/config/locales/views/courses/en.yml b/config/locales/views/courses/en.yml index 499de39501..e4d7cc81c9 100644 --- a/config/locales/views/courses/en.yml +++ b/config/locales/views/courses/en.yml @@ -17,6 +17,7 @@ en: registration-open_for_all-help_html: Anyone can register for this course. registration-open_for_institution-help_html: Only members of the configured institution can register for this course. registration-closed-help_html: All new registrations are disabled. + registration-open_for_institutional_users-help_html: Anyone that belongs to an institution can register for this course. Users that signed in using a private account, such as a personal Gmail or Outlook account, cannot register. description-help_html: A description is optional. Markdown formatting can be used. visibility-label: For whom is this course visible? registration-label: Who can register for this course? @@ -27,6 +28,7 @@ en: registration-open_for_all_html: Everyone registration-open_for_institution_html: Only users of the configured institution registration-closed_html: Nobody + registration-open_for_institutional_users_html: Everyone that belongs to an institution no_institution: "No institution" copy_admins: "Course admins" copy_options: "Copy options" @@ -98,6 +100,7 @@ en: registration-open_for_all-info: "Registrations are open for everyone: all users can register for this course." registration-open_for_institution-info: "Registrations are open for members of %{institution}: only they can register for this course." registration-closed-info: "Registrations are closed: users can't register until you change the registration settings. Users who are currently registered still have access." + registration-open_for_institutional_users-info: "Registrations are open for users that belong to any institution. Registrations are closed for users who signed in using a private account, such as a personal Gmail or Outlook account." moderated-info: "This course is moderated. Users who register will have to be explicitly approved by a course admin." subscribe: Register request_registration: Request registration diff --git a/config/locales/views/courses/nl.yml b/config/locales/views/courses/nl.yml index 527d887c87..c8f43ed09d 100644 --- a/config/locales/views/courses/nl.yml +++ b/config/locales/views/courses/nl.yml @@ -17,6 +17,7 @@ nl: registration-open_for_all-help_html: Iedereen kan zich voor deze cursus registreren. registration-open_for_institution-help_html: Enkel gebruikers van de ingestelde onderwijsinstelling kunnen zich voor deze cursus registreren. registration-closed-help_html: Er zijn geen nieuwe registraties mogelijk. + registration-open_for_institutional_users-help_html: Iedereen die tot een instelling behoort kan zich voor deze cursus registreren. Gebruikers die zijn ingelogd met een privéaccount, zoals een persoonlijk Gmail- of Outlook-account, kunnen zich niet registreren. description-help_html: Een beschrijving is optioneel en kan in Markdown geschreven worden. visibility-label: Voor wie is deze cursus zichtbaar? registration-label: Wie kan zich registreren voor deze cursus? @@ -27,6 +28,7 @@ nl: registration-open_for_all_html: Iedereen registration-open_for_institution_html: Enkel gebruikers van de ingestelde onderwijsinstelling registration-closed_html: Niemand + registration-open_for_institutional_users_html: Alle gebruikers die tot een onderwijsinstelling behoren no_institution: "Geen onderwijsinstelling" copy_admins: "Cursusbeheerders" copy_options: "Opties voor het kopiëren" @@ -107,6 +109,7 @@ nl: registration-open_for_all-info: 'De registraties staan open voor iedereen: gebruikers die op de registratie-knop klikken worden geregistreerd.' registration-open_for_institution-info: 'De registraties staan open voor gebruikers van %{institution}: enkel zij kunnen zich registreren voor deze cursus.' registration-closed-info: 'De registraties zijn gesloten: niemand kan zich nog registreren tot de registratie-instelling wordt aangepast. Gebruikers die geregistreerd zijn behouden hun toegang.' + registration-open_for_institutional_users-info: 'De registraties staan open voor gebruikers die tot een instelling behoren. De registraties zijn gesloten voor gebruikers die zijn ingelogd met een privéaccount, zoals een persoonlijk Gmail- of Outlook-account.' moderated-info: 'Deze cursus is gemodereerd: Gebruikers die zich registreren moeten expliciet goedgekeurd worden door een cursusbeheerder.' subscribe: Registreren request_registration: Registratieverzoek indienen diff --git a/db/seeds.rb b/db/seeds.rb index 5148a1e5ac..9e34165229 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -198,6 +198,8 @@ def fill_series_with_realistic_submissions(s) courses = [] + + courses << Course.create(description: 'This is a test course.', name: 'Open for Institutional users course', year: academic_year, registration: 'open_for_institutional_users', visibility: 'visible_for_all', moderated: false, teacher: 'Prof. Gobelijn') courses << Course.create(description: 'This is a test course.', name: 'Open for All Test Course', year: academic_year, registration: 'open_for_all', visibility: 'visible_for_all', moderated: false, teacher: 'Prof. Gobelijn') courses << Course.create(description: 'This is a test course.', name: 'Open for Institution Test Course', year: academic_year, registration: 'open_for_institution', visibility: 'visible_for_institution', moderated: false, teacher: 'Prof. Gobelijn', institution: ugent) courses << Course.create(description: 'This is a test course.', name: 'Open Moderated Test Course', year: academic_year, registration: 'open_for_all', visibility: 'visible_for_all', moderated: true, teacher: 'Prof. Barabas') diff --git a/test/controllers/courses_controller_test.rb b/test/controllers/courses_controller_test.rb index 1ab409a008..ad7eb0c931 100644 --- a/test/controllers/courses_controller_test.rb +++ b/test/controllers/courses_controller_test.rb @@ -158,7 +158,7 @@ def with_users_signed_in(users) test 'should get course page with secret' do add_not_subscribed with_users_signed_in @not_subscribed.compact do |who, user| - %w[visible_for_all visible_for_institution hidden].product(%w[open_for_all open_for_institution closed], [true, false]).each do |v, r, m| + %w[visible_for_all visible_for_institution hidden].product(%w[open_for_all open_for_institutional_users open_for_institution closed], [true, false]).each do |v, r, m| @course.update(visibility: v, registration: r, moderated: m) get course_url(@course, secret: @course.secret, format: :json) assert_response :success, "#{who} should get registration page" @@ -712,6 +712,30 @@ def with_users_signed_in(users) assert_not response.body.include?(subscribe_course_path(@course, secret: @course.secret)) end + test 'personal user should not be able to subscribe to open_for_institutional_users course' do + add_externals + @course.update(registration: :open_for_institutional_users) + user = @externals.first + user.update(institution: nil) + sign_in user + post subscribe_course_url(@course) + assert_not @course.subscribed_members.include?(user) + post subscribe_course_url(@course, secret: @course.secret) + assert_not @course.subscribed_members.include?(user) + end + + test 'institutional user should be able to subscribe to open_for_institutional_users course' do + add_externals + @course.update(registration: :open_for_institutional_users) + user = @externals.first + user.update(institution: (create :institution)) + sign_in user + post subscribe_course_url(@course) + assert @course.subscribed_members.include?(user) + post subscribe_course_url(@course, secret: @course.secret) + assert @course.subscribed_members.include?(user) + end + test 'should not destroy course as student' do add_students sign_in @students.first diff --git a/test/fixtures/courses.yml b/test/fixtures/courses.yml index 031c3afd1e..1da0126d5e 100644 --- a/test/fixtures/courses.yml +++ b/test/fixtures/courses.yml @@ -25,5 +25,6 @@ course1: year: 2021-2022 description: This is the description of course 1 visibility: 0 # visible for all - registration: 0 # open for all + registration: 3 # open for all teacher: Prof. Zonnebloem + secret: "PuDCHk3A7qCEAbxKXPpz"