diff --git a/Changelog.md b/Changelog.md index 977bacd9b0..cd1d480bb0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,17 @@ # Changelog +## [unreleased] +- Remove unmaintained locales (#5727) +- Added the ability to submit URLs (#5822) + +## [v2.0.6] +- Fix bug for menu icon not working on mobile devices / smaller screens (#5818) +- Fix bug for "Delete Group" button generating an invalid path (#5768) +- When role switched, 403 errors are displayed as flash messages after redirecting back to the previous page (#5785) +- Update wiki urls to point to https://github.com/MarkUsProject/Wiki (#5781) +- Fix bugs when submitting and cancelling remark requests (#5838) +- Do not trigger starter file changed timestamp when only starter_files_after_due assignment setting is changed (#5845) + ## [v2.0.5] - Add ability to annotate notebook (jupyter and Rmd) submissions (#5749) diff --git a/INSTALL b/INSTALL index 740c81b8a3..3c85f61dd9 100644 --- a/INSTALL +++ b/INSTALL @@ -7,9 +7,4 @@ Thanks for using MarkUs! -Installation instructions have moved to the MarkUs wiki. - -For production installation instructions: https://github.com/MarkUsProject/Markus/wiki/Installation -For development installation instruction: - - with Docker: https://github.com/MarkUsProject/Markus/wiki/Developer-Guide--Set-Up-With-Docker - - with Vagrant: https://github.com/MarkUsProject/Markus/wiki/Developer-Guide--Set-Up-With-Vagrant +Installation instructions have moved to the MarkUs wiki: https://github.com/MarkUsProject/Wiki diff --git a/README.md b/README.md index dc56413dc9..4f11447bbd 100644 --- a/README.md +++ b/README.md @@ -12,14 +12,13 @@ MarkUs is a Ruby on Rails and React web application for the submission and gradi - Graders can easily annotate students' code - Instructors can form groups or students can form groups on their own - Web-based course administration -- [RESTful API](https://github.com/MarkUsProject/Markus/wiki/RESTful-API) -- See the [Wiki pages](https://github.com/MarkUsProject/Markus/wiki) for more features +- See the [Wiki pages](https://github.com/MarkUsProject/Wiki) for more features ## 2. Installation -To install MarkUs for production, see the [Installation Guide](https://github.com/MarkUsProject/Markus/wiki/Installation) for details and step by step instructions. +To install MarkUs for production, see the [Installation Guide](https://github.com/MarkUsProject/Wiki/blob/release/Installation.md) for details and step by step instructions. -To install MarkUs for development, see either of the [docker installation](https://github.com/MarkUsProject/Markus/wiki/Developer-Guide--Set-Up-With-Docker) or [vagrant installation](https://github.com/MarkUsProject/Markus/wiki/Developer-Guide--Set-Up-With-Vagrant) guides. +To install MarkUs for development, see either of the [docker installation](https://github.com/MarkUsProject/Wiki/blob/master/Developer-Guide--Set-Up-With-Docker.md) or [vagrant installation](https://github.com/MarkUsProject/Wiki/blob/master/Developer-Guide--Set-Up-With-Vagrant) guides. ## 3. Who is Using MarkUs? diff --git a/app/MARKUS_VERSION b/app/MARKUS_VERSION index 155e58e4e4..8975a2132e 100644 --- a/app/MARKUS_VERSION +++ b/app/MARKUS_VERSION @@ -1 +1 @@ -VERSION=2.0.5,PATCH_LEVEL=DEV +VERSION=2.0.6,PATCH_LEVEL=DEV diff --git a/app/assets/javascripts/menu.js b/app/assets/javascripts/menu.js index 5ea8a0f5f5..1e195cfd40 100755 --- a/app/assets/javascripts/menu.js +++ b/app/assets/javascripts/menu.js @@ -32,7 +32,8 @@ function initMenu() { } }; } - -window.onload = () => { +// using addEventListener as opposed to direct assignment so that event listeners added elsewhere +// don't get overridden +window.addEventListener("DOMContentLoaded", () => { initMenu(); -}; +}); diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 01248c8cfa..d360b201f1 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -68,7 +68,7 @@ def set_markus_version k,v = token.split('=') version_info[k.downcase] = v end - @markus_version = "#{version_info['version']}.#{version_info['patch_level']}" + @markus_version = version_info['version'] end # Set locale according to URL parameter. If unknown parameter is @@ -127,7 +127,10 @@ def user_not_authorized # Render 403 if the current user is switching roles and they try to view a route for a different course def check_course_switch - user_not_authorized if session[:role_switch_course_id] && current_course&.id != session[:role_switch_course_id] + if session[:role_switch_course_id] && current_course&.id != session[:role_switch_course_id] + flash_message(:error, I18n.t('main.role_switch.forbidden_warning')) + redirect_back(fallback_location: course_assignments_path(session[:role_switch_course_id])) + end end def implicit_authorization_target diff --git a/app/controllers/assignments_controller.rb b/app/controllers/assignments_controller.rb index b0278a253d..76f63344ea 100644 --- a/app/controllers/assignments_controller.rb +++ b/app/controllers/assignments_controller.rb @@ -434,17 +434,19 @@ def update_starter_file success = true ApplicationRecord.transaction do assignment.assignment_properties.update!(starter_file_assignment_params) - all_changed = assignment.assignment_properties.saved_changes? - params[:sections].each do |section_params| + all_changed = + assignment.assignment_properties.saved_change_to_starter_file_type? || + assignment.assignment_properties.saved_change_to_default_starter_file_group_id? + params[:sections]&.each do |section_params| Section.find_by(id: section_params[:section_id]) &.update_starter_file_group(assignment.id, section_params[:group_id]) end starter_file_group_params.each do |group_params| starter_file_group = assignment.starter_file_groups.find_by(id: group_params[:id]) starter_file_group.update!(group_params) - all_changed ||= starter_file_group.saved_changes? || assignment.assignment_properties.saved_changes? + all_changed ||= starter_file_group.saved_changes? end - assignment.assignment_properties.update!(starter_file_updated_at: Time.current) + assignment.assignment_properties.update!(starter_file_updated_at: Time.current) if all_changed rescue ActiveRecord::RecordInvalid => e flash_message(:error, e.message) success = false diff --git a/app/controllers/grade_entry_forms_controller.rb b/app/controllers/grade_entry_forms_controller.rb index 93e0d086bc..37ba3fb068 100644 --- a/app/controllers/grade_entry_forms_controller.rb +++ b/app/controllers/grade_entry_forms_controller.rb @@ -148,7 +148,7 @@ def populate_grades_table .pluck_to_hash(*student_pluck_attrs) grades = current_role.grade_entry_students .where(grade_entry_form: grade_entry_form) - .joins(role: :end_user) + .joins(:grades) .pluck(:id, 'grades.grade_entry_item_id', 'grades.grade') .group_by { |x| x[0] } end diff --git a/app/controllers/main_controller.rb b/app/controllers/main_controller.rb index 5f23736919..e7226b3a04 100644 --- a/app/controllers/main_controller.rb +++ b/app/controllers/main_controller.rb @@ -23,7 +23,11 @@ class MainController < ApplicationController def login # redirect to main page if user is already logged in. if logged_in? && !request.post? - redirect_to controller: 'courses', action: 'index' + if allowed_to?(:role_is_switched?) + redirect_to course_assignments_path(session[:role_switch_course_id]) + else + redirect_to controller: 'courses', action: 'index' + end return end unless Settings.remote_auth_login_url || Settings.validate_file diff --git a/app/controllers/results_controller.rb b/app/controllers/results_controller.rb index d6f3f03e2b..4a0a9e2cf9 100644 --- a/app/controllers/results_controller.rb +++ b/app/controllers/results_controller.rb @@ -651,7 +651,7 @@ def update_overall_comment def update_remark_request @submission = Submission.find(params[:submission_id]) - @assignment = submission.grouping.assignment + @assignment = @submission.grouping.assignment if @assignment.past_remark_due_date? head :bad_request else @@ -684,7 +684,8 @@ def cancel_remark_request redirect_to controller: 'results', action: 'view_marks', - id: params[:id] + course_id: current_course.id, + id: submission.get_original_result.id end def delete_grace_period_deduction diff --git a/app/controllers/submissions_controller.rb b/app/controllers/submissions_controller.rb index d5a27134d6..ff58ed17c9 100644 --- a/app/controllers/submissions_controller.rb +++ b/app/controllers/submissions_controller.rb @@ -339,7 +339,8 @@ def update_files upload_files_helper(new_folders, new_files, unzip: unzip) do |f| if f.is_a?(String) # is a directory - success, msgs = add_folder(f, current_role, repo, path: path, txn: txn) + authorize! to: :manage_subdirectories? # ensure user is authorized for directories in zip files + success, msgs = add_folder(f, current_role, repo, path: path, txn: txn, required_files: required_files) should_commit &&= success messages = messages.concat msgs else diff --git a/app/helpers/repository_helper.rb b/app/helpers/repository_helper.rb index e513685f9b..010fa8869b 100644 --- a/app/helpers/repository_helper.rb +++ b/app/helpers/repository_helper.rb @@ -118,7 +118,7 @@ def remove_files(files, user, repo, path: '/', txn: nil, keep_folder: true) end end - def add_folder(folder_path, user, repo, path: '/', txn: nil) + def add_folder(folder_path, user, repo, path: '/', txn: nil, required_files: nil) messages = [] if txn.nil? @@ -132,6 +132,14 @@ def add_folder(folder_path, user, repo, path: '/', txn: nil) folder_path = current_path.join(folder_path) folder_path = folder_path.to_s + + # check if only required files are allowed for a submission + # allowed folders = paths in required files + if required_files.present? && required_files.none? { |file| file.starts_with?(folder_path) } + messages << [:invalid_folder_name, folder_path] + return false, messages + end + txn.add_path(folder_path) if commit_txn @@ -225,6 +233,8 @@ def flash_repository_messages(messages, course, suppress: nil) flash_message(:warning, I18n.t('student.submission.no_action_detected')) when :txn_conflicts flash_message(:error, partial: 'submissions/file_conflicts_list', locals: { conflicts: other_info }) + when :invalid_folder_name + flash_message(:error, I18n.t('student.submission.invalid_folder_name')) end end end diff --git a/app/models/starter_file_group.rb b/app/models/starter_file_group.rb index bc3233ffb8..c70be9418c 100644 --- a/app/models/starter_file_group.rb +++ b/app/models/starter_file_group.rb @@ -106,7 +106,7 @@ def update_default end def update_timestamp - assignment.assignment_properties.update(starter_file_updated_at: Time.current) + assignment.assignment_properties.update(starter_file_updated_at: Time.current) if saved_changes? end def set_name diff --git a/app/models/user.rb b/app/models/user.rb index 35c05572c8..458b84a8b2 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -87,6 +87,10 @@ def admin_user? self.class == AdminUser end + def end_user? + self.instance_of?(EndUser) + end + def set_display_name strip_name self.display_name ||= "#{self.first_name} #{self.last_name}" diff --git a/app/policies/submission_policy.rb b/app/policies/submission_policy.rb index 76ab6487ef..b5a65ba45d 100644 --- a/app/policies/submission_policy.rb +++ b/app/policies/submission_policy.rb @@ -19,7 +19,7 @@ def manage_files? end def manage_subdirectories? - role.instructor? || role.ta? + user.end_user? end def view_files? diff --git a/app/views/annotation_categories/_annotation_upload_modal.html.erb b/app/views/annotation_categories/_annotation_upload_modal.html.erb index 9b31af1fd3..d08df5b750 100644 --- a/app/views/annotation_categories/_annotation_upload_modal.html.erb +++ b/app/views/annotation_categories/_annotation_upload_modal.html.erb @@ -4,7 +4,7 @@ <%= content_for :modal_content do %> - <%= t('upload_help_html', section_id: AnnotationCategory.name.pluralize.underscore.dasherize) %> + <%= t('upload_help_html', markus_version: @markus_version, section_id: AnnotationCategory.name.pluralize.underscore.dasherize) %> <%= form_tag({ action: 'upload' }, { multipart: true }) do %> diff --git a/app/views/assignments/_assignment_upload_modal.html.erb b/app/views/assignments/_assignment_upload_modal.html.erb index 1ef59c4b14..ad7925db42 100644 --- a/app/views/assignments/_assignment_upload_modal.html.erb +++ b/app/views/assignments/_assignment_upload_modal.html.erb @@ -10,7 +10,7 @@ upload_id: 'upload_file', button_id: 'submit_upload', nonce: true %> - <%= t('upload_help_html', section_id: Assignment.name.pluralize.underscore.dasherize) %> + <%= t('upload_help_html', markus_version: @markus_version, section_id: Assignment.name.pluralize.underscore.dasherize) %> <%= form_tag upload_assignments_course_path(@current_course), { multipart: true } do %> diff --git a/app/views/assignments/_cp_assignment_upload_modal.html.erb b/app/views/assignments/_cp_assignment_upload_modal.html.erb index 9fe94bcee6..a915ac7ca9 100644 --- a/app/views/assignments/_cp_assignment_upload_modal.html.erb +++ b/app/views/assignments/_cp_assignment_upload_modal.html.erb @@ -4,7 +4,7 @@ <%= content_for :modal_content do %> - <%= t('assignments.upload_config_help_html', section_id: 'assignment-configuration') %> + <%= t('assignments.upload_config_help_html', markus_version: @markus_version, section_id: 'assignment-configuration') %> <%= javascript_include_tag 'upload_button_control.js', diff --git a/app/views/assignments/show.html.erb b/app/views/assignments/show.html.erb index 909ca4d6eb..4ea0d42104 100644 --- a/app/views/assignments/show.html.erb +++ b/app/views/assignments/show.html.erb @@ -205,7 +205,7 @@ @grouping.accepted_students.size == 1 && @grouping.extension.nil? %> <%= button_to t('helpers.submit.delete', model: Group.model_name.human), - course_assignment_group_path(@current_course.id, @assignment.id), + course_assignment_group_path(@current_course.id, @assignment.id, @grouping.id), method: :delete, data: { confirm: t('groups.student_interface.confirm_delete_group') } %> diff --git a/app/views/criteria/_upload_modal.html.erb b/app/views/criteria/_upload_modal.html.erb index 9bcfb8632a..9378267be0 100644 --- a/app/views/criteria/_upload_modal.html.erb +++ b/app/views/criteria/_upload_modal.html.erb @@ -15,7 +15,7 @@
<%= t('assignments.due_date.marking_overwrite_warning_html') %>
<% end %> - <%= t('upload_help_html', section_id: Criterion.name.pluralize.underscore.dasherize) %> + <%= t('upload_help_html', markus_version: @markus_version, section_id: Criterion.name.pluralize.underscore.dasherize) %> <%= form_tag upload_course_assignment_criteria_path(@current_course, @assignment), multipart: true, size: 1 do %><%= file_field_tag :upload_file, required: true %>
diff --git a/app/views/grade_entry_forms/_upload_modal.html.erb b/app/views/grade_entry_forms/_upload_modal.html.erb index a09aaa4d5a..5eabf25cb9 100644 --- a/app/views/grade_entry_forms/_upload_modal.html.erb +++ b/app/views/grade_entry_forms/_upload_modal.html.erb @@ -3,7 +3,7 @@ <%= content_for :modal_open_link, '#uploadModal' %> <%= content_for :modal_content do %> - <%= t('upload_help_html', section_id: 'marks-spreadsheet-grades') %> + <%= t('upload_help_html', markus_version: @markus_version, section_id: 'marks-spreadsheet-grades') %> <%= form_tag upload_course_grade_entry_form_path(@current_course, @grade_entry_form), { multipart: true, size: 1 } do %> diff --git a/app/views/graders/_upload_modal.html.erb b/app/views/graders/_upload_modal.html.erb index 24d4b3d033..153a9c175c 100644 --- a/app/views/graders/_upload_modal.html.erb +++ b/app/views/graders/_upload_modal.html.erb @@ -10,7 +10,7 @@ upload_id: 'upload_file', button_id: 'upload-groupings', nonce: true %> - <%= t('upload_help_html', section_id: 'graders') %> + <%= t('upload_help_html', markus_version: @markus_version, section_id: 'graders') %> <%= form_tag upload_course_assignment_graders_path(@current_course, @assignment), { multipart: true } do %> diff --git a/app/views/groups/_upload_modal.html.erb b/app/views/groups/_upload_modal.html.erb index 448e539e4d..be06b4acf3 100644 --- a/app/views/groups/_upload_modal.html.erb +++ b/app/views/groups/_upload_modal.html.erb @@ -11,7 +11,7 @@ button_id: 'upload', nonce: true %>- <%= t('upload_help_html', section_id: 'groups') %> + <%= t('upload_help_html', markus_version: @markus_version, section_id: 'groups') %>
<%= form_tag upload_course_assignment_groups_path(@current_course, @assignment), { multipart: true } do %> diff --git a/app/views/marks_graders/_upload_modal.html.erb b/app/views/marks_graders/_upload_modal.html.erb index 9682d36ab2..760d1dd5f5 100644 --- a/app/views/marks_graders/_upload_modal.html.erb +++ b/app/views/marks_graders/_upload_modal.html.erb @@ -10,7 +10,7 @@ button_id: 'upload', nonce: true %> - <%= t('upload_help_html', section_id: 'graders') %> + <%= t('upload_help_html', markus_version: @markus_version, section_id: 'graders') %> <%= form_tag({ controller: 'marks_graders', action: 'upload' }, diff --git a/app/views/peer_reviews/_upload_modal.html.erb b/app/views/peer_reviews/_upload_modal.html.erb index 2c5e28e2b0..df8b10727b 100644 --- a/app/views/peer_reviews/_upload_modal.html.erb +++ b/app/views/peer_reviews/_upload_modal.html.erb @@ -10,7 +10,7 @@ upload_id: 'upload_file', button_id: 'upload', nonce: true %> - <%= t('upload_help_html', section_id: 'peer-reviews') %> + <%= t('upload_help_html', markus_version: @markus_version, section_id: 'peer-reviews') %> <%= form_tag({ controller: 'peer_reviews', action: 'upload' }, diff --git a/app/views/shared/_flash_message.html.erb b/app/views/shared/_flash_message.html.erb index db84c70fc4..bf4902d255 100644 --- a/app/views/shared/_flash_message.html.erb +++ b/app/views/shared/_flash_message.html.erb @@ -18,11 +18,13 @@ <% end %> <% end %> <%= javascript_tag nonce: true do %> - window.onload = function () { + // using addEventListener as opposed to direct assignment so that event listeners added elsewhere + // don't get overridden + window.addEventListener("DOMContentLoaded", function () { Array.from(document.getElementsByClassName('hide-flash')).forEach(function (elem) { elem.addEventListener("click", function(e) { e.target.parentElement.style.display = 'none'; }) }) - } + }) <% end %> diff --git a/app/views/students/_upload_modal.html.erb b/app/views/students/_upload_modal.html.erb index 76f34e0bbf..270a48fa43 100644 --- a/app/views/students/_upload_modal.html.erb +++ b/app/views/students/_upload_modal.html.erb @@ -12,7 +12,7 @@ nonce: true %>- <%= t('upload_help_html', section_id: 'students') %> + <%= t('upload_help_html', markus_version: @markus_version, section_id: 'students') %>
<%= form_tag upload_course_students_path(@current_course), { multipart: true } do %>- <%= t('upload_help_html', section_id: 'students') %> + <%= t('upload_help_html', markus_version: @markus_version, section_id: 'students') %>
<%= form_tag upload_course_tas_path(@current_course), { multipart: true } do %>- <%= t('users.api_key.help_html') %> + <%= t('users.api_key.help_html', markus_version: @markus_version,) %>
- + <%= t('key_pairs.help.windows') %>
- + <%= t('key_pairs.help.macOS_linux') %>
diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml index 4d9b0319b4..9cf0d689fa 100644 --- a/config/i18n-tasks.yml +++ b/config/i18n-tasks.yml @@ -3,7 +3,7 @@ # The "main" locale. base_locale: en ## All available locales are inferred from the data by default. Alternatively, specify them explicitly: -# locales: [en] +locales: [en] ## Reporting locale, default: en. Available: en, ru. # internal_locale: en diff --git a/config/locales/defaults/download_upload/en.yml b/config/locales/defaults/download_upload/en.yml index a9d682049e..07ab3e8eb6 100644 --- a/config/locales/defaults/download_upload/en.yml +++ b/config/locales/defaults/download_upload/en.yml @@ -18,7 +18,7 @@ en: malformed_csv: The selected file was improperly formed. Please ensure the file meets MarkUs specifications. syntax_error: 'There is an error in the file you uploaded: %{error}' unparseable_csv: The selected file is not a CSV file (even though it may have a .csv extension). - upload_help_html: ' For information about the file format(s) supported, please visit this page.' + upload_help_html: ' For information about the file format(s) supported, please visit this page.' upload_success: one: "%{count} object successfully uploaded." other: "%{count} objects successfully uploaded." diff --git a/config/locales/en.yml b/config/locales/en.yml index 73fbd734b7..a1f4e08b7f 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -38,6 +38,7 @@ en: file_conflicts: 'Your changes have not been made. The following file conflicts were detected:' file_too_large: The size of the uploaded file %{file_name} exceeds the maximum of %{max_size} MB. invalid_file_name: Invalid file name on submitted file + invalid_folder_name: Invalid folder name on submitted folder missing_file: Could not download %{file_name}. File may be missing. missing_files: 'You still need to submit %{file} required file(s) for this assignment:' no_action_detected: No actions were detected in the last submit. Nothing was changed. diff --git a/config/locales/views/assignments/en.yml b/config/locales/views/assignments/en.yml index 7a688322af..c2aee46498 100644 --- a/config/locales/views/assignments/en.yml +++ b/config/locales/views/assignments/en.yml @@ -82,5 +82,5 @@ en: started_on: 'You started on:' starter_file_prompt: Click below to download the starter files for this assessment. time_until_due_warning: This assessment is due before %{due_date} without penalty. - upload_config_help_html: "This zip file will configure and create this assignment.
This does not configure student or grader settings.
For more information please visit this page.
This zip file will configure and create this assignment.
This does not configure student or grader settings.
For more information please visit this page.