diff --git a/app/assets/javascripts/exercise.ts b/app/assets/javascripts/exercise.ts
index daf10d4be8..7f3e37a7da 100644
--- a/app/assets/javascripts/exercise.ts
+++ b/app/assets/javascripts/exercise.ts
@@ -129,7 +129,7 @@ function initExerciseDescription(): void {
initCodeFragments();
}
-function initExerciseShow(exerciseId: number, programmingLanguage: string, loggedIn: boolean, editorShown: boolean, courseId: number, _deadline: string, baseSubmissionsUrl: string): void {
+function initExerciseShow(exerciseId: number, programmingLanguage: string, loggedIn: boolean, editorShown: boolean, courseId: number, _deadline: string, baseSubmissionsUrl: string, boilerplate: string): void {
let editor: AceAjax.Editor;
let lastSubmission: string;
let lastTimeout: number;
@@ -140,6 +140,7 @@ function initExerciseShow(exerciseId: number, programmingLanguage: string, logge
initDeadlineTimeout();
enableSubmissionTableLinks();
swapActionButtons();
+ initRestoreBoilerplateButton(boilerplate);
}
// submit source code if button is clicked on editor panel
@@ -444,6 +445,20 @@ function initExerciseShow(exerciseId: number, programmingLanguage: string, logge
showDeadlineAlerts();
}
+ function initRestoreBoilerplateButton(boilerplate: string): void {
+ const restoreWarning = document.getElementById("restore-boilerplate");
+ if (!restoreWarning) {
+ return;
+ }
+
+ const resetButton = restoreWarning.querySelector("a");
+ resetButton.addEventListener("click", () => {
+ editor.setValue(boilerplate);
+ editor.focus();
+ restoreWarning.hidden = true;
+ });
+ }
+
init();
}
diff --git a/app/assets/stylesheets/models/activities.css.scss b/app/assets/stylesheets/models/activities.css.scss
index f0c7fd07f4..da0e9512ae 100644
--- a/app/assets/stylesheets/models/activities.css.scss
+++ b/app/assets/stylesheets/models/activities.css.scss
@@ -186,6 +186,12 @@ center img {
}
}
+#restore-boilerplate {
+ a {
+ cursor: pointer;
+ }
+}
+
pre {
position: relative;
diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb
index 258255155a..b3a9c93993 100644
--- a/app/controllers/activities_controller.rb
+++ b/app/controllers/activities_controller.rb
@@ -124,17 +124,20 @@ def show
@submissions = @activity.submissions.includes(:annotations)
@submissions = @submissions.in_course(@course) if @course.present? && current_user&.member_of?(@course)
@submissions = @submissions.of_user(current_user) if current_user
- @submissions = policy_scope(@submissions).paginate(page: parse_pagination_param(params[:page]))
+ @submissions = policy_scope(@submissions)
if params[:edit_submission]
@edit_submission = Submission.find(params[:edit_submission])
authorize @edit_submission, :edit?
+ elsif @submissions.any?
+ @last_submission = @submissions.first
end
+ @submissions = @submissions.paginate(page: parse_pagination_param(params[:page]))
if params[:from_solution]
@solution = @activity.solutions[params[:from_solution]]
authorize @activity, :info?
end
- @code = @edit_submission.try(:code) || @solution || @activity.boilerplate
+ @code = @edit_submission.try(:code) || @solution || @last_submission.try(:code) || @activity.boilerplate
elsif @activity.content_page?
@read_state = if current_user&.member_of?(@course)
@activity.activity_read_states.find_by(user: current_user, course: @course)
diff --git a/app/views/activities/show.html.erb b/app/views/activities/show.html.erb
index a3f629ea56..c5bf88e02d 100644
--- a/app/views/activities/show.html.erb
+++ b/app/views/activities/show.html.erb
@@ -114,6 +114,14 @@ end %>
<%= t('.deadline_passed', deadline: @series.deadline.today? ? @series.deadline.strftime('%R') : @series.deadline.strftime('%F %R')) %>
<% end %>
+ <% if !@edit_submission && !@solution && @last_submission %>
+
+ <% end %>
@@ -173,7 +181,8 @@ end %>
<%= policy(@activity).submit? || !user_signed_in? %>,
<%= @course&.id || "null" %>,
<%= raw "\"#{@series&.deadline&.httpdate}\"" || "null" %>,
- "<%= submissions_url %>"
+ "<%= submissions_url %>",
+ `<%= (@activity.exercise? && raw(@activity.boilerplate&.gsub!('`', '\\\\`')))|| "" %>`,
);
});
diff --git a/config/locales/views/activities/en.yml b/config/locales/views/activities/en.yml
index 381ba551d4..2d7ec551f3 100644
--- a/config/locales/views/activities/en.yml
+++ b/config/locales/views/activities/en.yml
@@ -94,6 +94,9 @@ en:
back_to_course_actionable: Go back to course
mark_as_read: Mark as read
read_at: "Marked as read on %{timestamp}"
+ preloaded_info: "We have preloaded your latest submission into the editor."
+ preloaded_clear: Clear editor.
+ preloaded_restore: Restore the boilerplate code.
series_activities_add_table:
course_added_to_usable: "Adding this exercise will allow this course to use all of the private exercises in this exercise's repository. Are you sure?"
edit:
diff --git a/config/locales/views/activities/nl.yml b/config/locales/views/activities/nl.yml
index 6ac81aa535..4ead22cf5d 100644
--- a/config/locales/views/activities/nl.yml
+++ b/config/locales/views/activities/nl.yml
@@ -94,6 +94,9 @@ nl:
back_to_course_actionable: Ga terug naar de cursus
mark_as_read: Markeren als gelezen
read_at: "Gelezen op %{timestamp}"
+ preloaded_info: We hebben jouw laatste oplossing ingeladen in de editor.
+ preloaded_clear: Maak de editor leeg.
+ preloaded_restore: Herstel de boilerplate-code.
series_activities_add_table:
course_added_to_usable: "Deze oefening toevoegen zal deze cursus toegang geven tot alle privΓ© oefeningen in de repository van deze oefening. Ben je zeker?"
edit:
diff --git a/test/system/activities_test.rb b/test/system/activities_test.rb
new file mode 100644
index 0000000000..181eb1675a
--- /dev/null
+++ b/test/system/activities_test.rb
@@ -0,0 +1,60 @@
+require 'capybara/minitest'
+require 'application_system_test_case'
+
+class ActivitiesTest < ApplicationSystemTestCase
+ include Devise::Test::IntegrationHelpers
+ # Make the Capybara DSL available in all integration tests
+ include Capybara::DSL
+ # Make `assert_*` methods behave like Minitest assertions
+ include Capybara::Minitest::Assertions
+
+ setup do
+ @instance = exercises(:python_exercise)
+ @user = users(:zeus)
+ sign_in @user
+ end
+
+ test 'should show exercise' do
+ visit exercise_path(id: @instance.id)
+ assert_text @instance.name_en
+ end
+
+ test 'should show boilerplate in code editor' do
+ @instance.stubs(:boilerplate).returns('boilerplate') do
+ visit exercise_path(id: @instance.id)
+ assert_text 'boilerplate'
+ end
+ end
+
+ test 'show latest submission in code editor' do
+ create(:submission, exercise: @instance, user: @user, status: :correct, code: 'print("hello")')
+ visit exercise_path(id: @instance.id)
+ assert_text 'print("hello")'
+ end
+
+ test 'should show message to clear editor if latest submission is shown' do
+ create(:submission, exercise: @instance, user: @user, status: :correct, code: 'print("hello")')
+ visit exercise_path(id: @instance.id)
+ assert_text 'Clear editor'
+ end
+
+ test 'should show message to restore boilerplate if latest submission is shown' do
+ @instance.stubs(:boilerplate).returns('boilerplate') do
+ create(:submission, exercise: @instance, user: @user, status: :correct, code: 'print("hello")')
+ visit exercise_path(id: @instance.id)
+ assert_text 'Restore boilerplate'
+ end
+ end
+
+ test 'should not break on complex unicode characters' do
+ @instance.stubs(:boilerplate).returns('``') do
+ visit exercise_path(id: @instance.id)
+ assert_text '``'
+
+ create(:submission, exercise: @instance, user: @user, status: :correct, code: 'print("π")')
+ visit exercise_path(id: @instance.id)
+ assert_text 'print("π")'
+ assert_text 'Restore boilerplate'
+ end
+ end
+end