cleanup SubmissionsController#show

fixes CNVS-25767

- Simplifies SubmissionsController#show & adds test coverage.
- Adds Submissions::DownloadsController#show to handle submission
  download functionality.
- Adds Submissions::PreviewsController#show to handle submission preview
  functionality.

test plan:
- As a teacher, add assignments for each online submission type: Text
  Entry, Website URL & File Uploads.
- As a student, provide a submission for each of these assignments.
  Provide two submissions for at least one of the assignments.
- As the student, view each of these submissions via the submission
  details page. URL path looks like:
  /courses/1/assignments/1/submissions/2,
  and can be accessed from assignment page.
- Observe that the body of the submission shows up in the Submission
  Detail page.
- Observe that for file uploads, the files can be downloaded.

- As the teacher, access the speed grader for each submission.
- Observe that the submission body appears in the speed grader.
- Observe that, for file uploads, the files are displayed inline (this
  doesn't work for every media type and it will tell you as much if it's
  not supported).
- Observe that if there are multiple files provided, the list of files
  appears in the left hand column, and the teacher can tolggle between
  them.
- Observe that if the submission has multiple versions, in the left hand
  column there is a dropdown labeled 'Submission to view:', and toggling
  the selected datetime will change the submission that is displayed.

- As the teacher viewing a submission in the speed grader, leave a
  comment in the right hand column, and attach a file.
- As the student, view the submission details page.
- Observe that there is a link to the fiel in the right hand column, and
  that the file can be downloaded.

Change-Id: Ib3fff3909f47873b37c373e53ab26cb8176b94e1
Reviewed-on: https://gerrit.instructure.com/68559
Tested-by: Jenkins
Reviewed-by: Mike Nomitch <mnomitch@instructure.com>
QA-Review: Michael Hargiss <mhargiss@instructure.com>
Product-Review: Jason Sparks <jsparks@instructure.com>
This commit is contained in:
John Corrigan 2015-12-03 10:15:27 -06:00
parent 05e6d3fff0
commit 65175bcd5f
16 changed files with 1074 additions and 134 deletions

View File

@ -0,0 +1,75 @@
# Copyright (C) 2016 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
module Submissions
class AttachmentForSubmissionDownload
def initialize(submission, options={})
@submission = submission
@options = options
end
attr_reader :submission, :options
def attachment
raise ActiveRecord::RecordNotFound unless download_id.present?
return attachment_from_submission_comment ||
attachment_belonging_to_submission ||
prior_attachment ||
attachment_from_submission_attachments ||
attachment_from_versioned_attachments
end
private
def attachment_belonging_to_submission
submission.attachment_id == download_id && submission.attachment
end
def attachment_from_submission_attachments
submission.attachments.where(id: download_id).first
end
def attachment_from_submission_comment
return nil unless comment_id.present?
submission.all_submission_comments.find(comment_id).attachments.find do |attachment|
attachment.id == download_id
end
end
def attachment_from_versioned_attachments
submission.submission_history.map(&:versioned_attachments).flatten.find do |attachment|
attachment.id == download_id
end
end
def comment_id
options[:comment_id]
end
def download_id
options[:download].nil? ? options[:download] : options[:download].to_i
end
def prior_attachment
prior_attachment_id.present? && Attachment.where(id: prior_attachment_id).first
end
def prior_attachment_id
@_prior_attachment_id ||= submission.submission_history.map(&:attachment_id).find do |attachment_id|
attachment_id == download_id
end
end
end
end

View File

@ -0,0 +1,67 @@
# Copyright (C) 2016 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
module Submissions
class DownloadsController < ApplicationController
include Submissions::ShowHelper
before_filter :require_context
def show
service = Submissions::SubmissionForShow.new(
@context, params.slice(:assignment_id, :id)
)
begin
@submission = service.submission
rescue ActiveRecord::RecordNotFound
@assignment = service.assignment
render_user_not_found and return
end
if authorized_action(@submission, @current_user, :read)
@attachment = Submissions::AttachmentForSubmissionDownload.new(
@submission, params.slice(:comment_id, :download)
).attachment
respond_to do |format|
format.html do
redirect_to redirect_path
end
format.json do
render json: @attachment.as_json({
permissions: {
user: @current_user
}
})
end
end
end
end
private
def download_params
{ verifier: @attachment.uuid, inline: params[:inline] }.tap do |h|
h[:download_frd] = true unless params[:inline]
end
end
def redirect_path
if @attachment.context == @submission || @attachment.context == @submission.assignment
file_download_url(@attachment, download_params)
else
named_context_url(@attachment.context, :context_file_download_url, @attachment, download_params)
end
end
end
end

View File

@ -0,0 +1,102 @@
# Copyright (C) 2016 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
module Submissions
class PreviewsController < ApplicationController
include KalturaHelper
include Submissions::ShowHelper
before_filter :require_context
rescue_from ActiveRecord::RecordNotFound, only: :show, with: :render_user_not_found
def show
service = Submissions::SubmissionForShow.new(
@context, params.slice(:assignment_id, :id, :preview, :version)
)
@assignment = service.assignment
@user = service.user
@submission = service.submission
prepare_js_env
@assessment_request = @submission.assessment_requests.where(assessor_id: @current_user).first
@body_classes << 'is-inside-submission-frame'
if @assignment.moderated_grading?
@crocodoc_ids = @submission.crocodoc_whitelist
end
unless @assignment.visible_to_user?(@current_user)
flash[:notice] = t('This assignment will no longer count towards your grade.')
end
@headers = false
if authorized_action(@submission, @current_user, :read)
if redirect?
redirect_to(
named_context_url(
@context, redirect_path_name, @assignment.quiz.id, redirect_params
)
)
else
render 'submissions/show_preview'
end
end
end
private
def current_user_is_student?
@context.user_is_student?(@current_user) && !@context.user_is_instructor?(@current_user)
end
def redirect?
redirect_to_quiz? || redirect_to_quiz_history?
end
def prepare_js_env
hash = {CONTEXT_ACTION_SOURCE: :submissions}
append_sis_data(hash)
js_env(hash)
end
def redirect_params
return { headless: 1 }.tap do |h|
if redirect_to_quiz_history?
h.merge!({
hide_student_name: params[:hide_student_name],
user_id: @submission.user_id,
version: params[:version] || @submission.quiz_submission_version
})
end
end
end
def redirect_path_name
if redirect_to_quiz?
:context_quiz_url
else
:context_quiz_history_url
end
end
def redirect_to_quiz?
@assignment.quiz && @context.is_a?(Course) && current_user_is_student?
end
def redirect_to_quiz_history?
!redirect_to_quiz? && (
@submission.submission_type == "online_quiz" && @submission.quiz_submission_version
)
end
end
end

View File

@ -0,0 +1,19 @@
module Submissions
module ShowHelper
def render_user_not_found
respond_to do |format|
format.html do
flash[:error] = t("The specified user is not a student in this course")
redirect_to named_context_url(@context, :context_assignment_url, @assignment.id)
end
format.json do
render json: {
errors: t("The specified user (%{id}) is not a student in this course", {
id: params[:id]
})
}
end
end
end
end
end

View File

@ -0,0 +1,52 @@
# Copyright (C) 2016 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
module Submissions
class SubmissionForShow
def initialize(context, options={})
@context = context
@options = options
end
attr_reader :options
def assignment
@_assignment ||= @context.assignments.active.find(options[:assignment_id])
end
def submission
@_submission ||= versioned? ? versioned_submission : root_submission
end
def user
@_user ||= @context.all_students.find(options[:id])
end
private
def versioned?
options[:preview] && options[:version] && !assignment.quiz
end
def root_submission
@_root_submission ||= assignment.submissions.
preload(:versions).where(user_id: user).first_or_initialize
end
def versioned_submission
root_submission.submission_history[options[:version].to_i] || root_submission
end
end
end

View File

@ -85,7 +85,7 @@ require 'action_controller_test_process'
# }
#
class SubmissionsController < ApplicationController
include KalturaHelper
include Submissions::ShowHelper
before_filter :get_course_from_section, :only => :create
before_filter :require_context
@ -103,111 +103,34 @@ class SubmissionsController < ApplicationController
end
end
rescue_from ActiveRecord::RecordNotFound, only: :show, with: :render_user_not_found
def show
@assignment = @context.assignments.active.find(params[:assignment_id])
if @context_enrollment && @context_enrollment.is_a?(ObserverEnrollment) && @context_enrollment.associated_user_id
id = @context_enrollment.associated_user_id
else
id = @current_user.try(:id)
end
@user = @context.all_students.find(params[:id]) rescue nil
unless @user
flash[:error] = t('errors.student_not_enrolled', "The specified user is not a student in this course")
respond_to do |format|
format.html { redirect_to named_context_url(@context, :context_assignment_url, @assignment.id) }
format.json { render :json => {:errors => t('errors.student_not_enrolled_id', "The specified user (%{id}) is not a student in this course", :id => params[:id])}}
end
return
end
hash = {:CONTEXT_ACTION_SOURCE => :submissions}
append_sis_data(hash)
js_env(hash)
@submission = @assignment.submissions.where(user_id: @user).first
@submission ||= @assignment.submissions.build(:user => @user)
if @assignment.moderated_grading?
@crocodoc_ids = @submission.crocodoc_whitelist
end
@rubric_association = @assignment.rubric_association
@rubric_association.assessing_user_id = @submission.user_id if @rubric_association
# can't just check the permission, because peer reviewiers can never read the grade
if @assignment.muted? && !@submission.grants_right?(@current_user, :read_grade)
@visible_rubric_assessments = []
else
@visible_rubric_assessments = @submission.rubric_assessments.select{|a| a.grants_right?(@current_user, session, :read)}.sort_by{|a| [a.assessment_type == 'grading' ? CanvasSort::First : CanvasSort::Last, Canvas::ICU.collation_key(a.assessor_name)] }
end
service = Submissions::SubmissionForShow.new(
@context, params.slice(:assignment_id, :id)
)
@assignment = service.assignment
@submission = service.submission
@rubric_association = @submission.rubric_association_with_assessing_user_id
@visible_rubric_assessments = @submission.visible_rubric_assessments_for(@current_user)
@assessment_request = @submission.assessment_requests.where(assessor_id: @current_user).first
if authorized_action(@submission, @current_user, :read)
respond_to do |format|
json_handled = false
if params[:preview]
if params[:version] && !@assignment.quiz
@submission = @submission.submission_history[params[:version].to_i]
end
if @submission && !@assignment.visible_to_user?(@current_user)
flash[:notice] = t 'notices.submission_doesnt_count', "This assignment will no longer count towards your grade."
end
@headers = false
@body_classes << 'is-inside-submission-frame'
if @assignment.quiz && @context.is_a?(Course) && @context.user_is_student?(@current_user) && !@context.user_is_instructor?(@current_user)
format.html { redirect_to(named_context_url(@context, :context_quiz_url, @assignment.quiz.id, :headless => 1)) }
elsif @submission.submission_type == "online_quiz" && @submission.quiz_submission_version
format.html {
quiz_params = {
headless: 1,
hide_student_name: params[:hide_student_name],
user_id: @submission.user_id,
version: params[:version] || @submission.quiz_submission_version
}
redirect_to named_context_url(@context,
:context_quiz_history_url,
@assignment.quiz.id, quiz_params)
}
else
format.html { render :show_preview }
end
elsif params[:download]
if params[:comment_id]
@attachment = @submission.all_submission_comments.find(params[:comment_id]).attachments.find{|a| a.id == params[:download].to_i }
else
@attachment = @submission.attachment if @submission.attachment_id == params[:download].to_i
prior_attachment_id = @submission.submission_history.map(&:attachment_id).find{|a| a == params[:download].to_i }
@attachment ||= Attachment.where(id: prior_attachment_id).first if prior_attachment_id
@attachment ||= @submission.attachments.where(id: params[:download]).first if params[:download].present?
@attachment ||= @submission.submission_history.map(&:versioned_attachments).flatten.find{|a| a.id == params[:download].to_i }
end
raise ActiveRecord::RecordNotFound unless @attachment
format.html {
download_params = { verifier: @attachment.uuid, inline: params[:inline] }
download_params[:download_frd] = true if !download_params[:inline]
if @attachment.context == @submission || @attachment.context == @assignment
redirect_to(file_download_url(@attachment, download_params))
else
redirect_to(named_context_url(@attachment.context, :context_file_download_url, @attachment, download_params))
end
}
json_handled = true
format.json { render :json => @attachment.as_json(:permissions => {:user => @current_user}) }
else
@submission.limit_comments(@current_user, session)
format.html
format.json do
@submission.limit_comments(@current_user, session)
format.html
end
unless json_handled
format.json {
@submission.limit_comments(@current_user, session)
excludes = @assignment.grants_right?(@current_user, session, :grade) ? [:grade, :score] : []
render :json => @submission.as_json(
Submission.json_serialization_full_parameters(
:exclude => excludes,
:except => %w(quiz_submission submission_history)
).merge(:permissions => {:user => @current_user, :session => session, :include_permissions => false})
)
}
excludes = @assignment.grants_right?(@current_user, session, :grade) ? [:grade, :score] : []
render :json => @submission.as_json(
Submission.json_serialization_full_parameters(
exclude: excludes,
except: %w(quiz_submission submission_history)
).merge(permissions: {
user: @current_user,
session: session,
include_permissions: false
})
)
end
end
end

View File

@ -1374,6 +1374,26 @@ class Submission < ActiveRecord::Base
!self.has_submission? && !self.graded?
end
def visible_rubric_assessments_for(viewing_user)
return [] if self.assignment.muted? && !grants_right?(viewing_user, :read_grade)
filtered_assessments = self.rubric_assessments.select do |a|
a.grants_right?(viewing_user, :read)
end
filtered_assessments.sort_by do |a|
if a.assessment_type == 'grading'
[CanvasSort::First]
else
[CanvasSort::Last, Canvas::ICU.collation_key(a.assessor_name)]
end
end
end
def rubric_association_with_assessing_user_id
self.assignment.rubric_association.tap do |association|
association.assessing_user_id = self.user_id if association
end
end
def self.queue_bulk_update(context, section, grader, grade_data)
progress = Progress.create!(:context => context, :tag => "submissions_update")
progress.process_job(self, :process_bulk_update, {}, context, section, grader, grade_data)

View File

@ -235,6 +235,14 @@ CanvasRails::Application.routes.draw do
concerns :discussions
resources :assignments do
get 'moderate' => 'assignments#show_moderate'
get 'submissions/:id', to: 'submissions/previews#show',
constraints: ->(request) do
request.query_parameters.key?(:preview) && request.format == :html
end
get 'submissions/:id', to: 'submissions/downloads#show',
constraints: ->(request) do
request.query_parameters.key?(:download)
end
resources :submissions do
post 'turnitin/resubmit' => 'submissions#resubmit_to_turnitin', as: :resubmit_to_turnitin
get 'turnitin/:asset_string' => 'submissions#turnitin_report', as: :turnitin_report

View File

@ -0,0 +1,124 @@
# Copyright (C) 2016 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
describe Submissions::AttachmentForSubmissionDownload do
before :once do
course_with_student(active_all: true)
assignment_model(course: @course)
submission_model({
assignment: @assignment,
body: 'here my assignment',
submission_type: 'online_text_entry',
user: @student
})
@submission.submitted_at = 3.hours.ago
@submission.save
@options = {}
end
subject do
Submissions::AttachmentForSubmissionDownload.new(@submission, @options)
end
describe '#attachment' do
it 'raises ActiveRecord::RecordNotFound when download_id is not present' do
expect(@options.key?(:download_id)).to be_falsey, 'precondition'
expect {
subject.attachment
}.to raise_error(ActiveRecord::RecordNotFound)
end
context 'when attachment belongs to a submission' do
before do
@attachment = @submission.attachment = attachment_model(context: @course)
@submission.save
@options = { download: @attachment.id }
end
it 'returns the attachment that belongs to the submission' do
expect(subject.attachment).to eq @attachment
end
end
context 'when submission has prior attachment' do
before :once do
@attachment = @submission.attachment = attachment_model(context: @course)
@submission.submitted_at = 3.hours.ago
@submission.save
end
it 'returns prior attachment' do
expect(@submission.attachment).not_to be_nil, 'precondition'
expect {
@submission.with_versioning(explicit: true) do
@submission.attachment = nil
@submission.submitted_at = 1.hour.ago
@submission.save
end
}.to change(@submission.versions, :count), 'precondition'
expect(@submission.attachment).to be_nil, 'precondition'
@options = { download: @attachment.id }
expect(subject.attachment).to eq @attachment
end
end
context 'when download id is found in attachments collection ids' do
before :once do
@attachment = attachment_model(context: @course)
AttachmentAssociation.create!(context: @submission, attachment: @attachment)
@options = { download: @attachment.id }
end
it 'returns attachment from attachments collection' do
expect(subject.attachment).to eq @attachment
end
end
context 'when comment id & download id are present' do
before :once do
@original_course = @course
@original_student = @student
course_with_student(active_all:true)
submission_comment_model
@attachment = attachment_model(context: @assignment)
@submission_comment.attachments = [@attachment]
@submission_comment.save
@options = { comment_id: @submission_comment.id, download: @attachment.id }
end
it 'returns submission comment attachment' do
expect(subject.attachment).to eq @attachment
end
end
context 'when download id is in versioned_attachments' do
before :once do
@attachment = attachment_model(context: @assignment)
@submission.attachment_ids = "#{@attachment.id}"
@submission.save
@options = { download: @attachment.id }
end
it 'returns attachment from versioned_attachments' do
expect(@submission.attachment_ids).not_to be_nil
expect(subject.attachment).to eq @attachment
end
end
end
end

View File

@ -0,0 +1,180 @@
#
# Copyright (C) 2016 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
require 'spec_helper'
describe Submissions::DownloadsController do
describe 'GET :show' do
before do
course_with_student_and_submitted_homework
@context = @course
user_session(@student)
end
context 'with user id not present in course' do
before do
@attachment = @submission.attachment = attachment_model(context: @context)
@submission.save
course_with_student(active_all: true)
user_session(@student)
end
it 'should set flash error' do
get :show, {
course_id: @context.id,
assignment_id: @assignment.id,
id: @student.id,
download: @submission.attachment_id
}
expect(flash[:error]).not_to be_nil
end
it "should redirect to context assignment url" do
get :show, {
course_id: @context.id,
assignment_id: @assignment.id,
id: @student.id,
download: @submission.attachment_id
}
expect(response).to redirect_to(course_assignment_url(@context, @assignment))
end
end
context "when attachment belongs to submission" do
before do
@attachment = @submission.attachment = attachment_model(context: @context)
@submission.save
end
it "sets attachment the submission belongs to by default" do
get :show, {
course_id: @context.id,
assignment_id: @assignment.id,
id: @student.id,
download: @submission.attachment_id
}
expect(assigns(:attachment)).to eq @attachment
expect(response).to redirect_to(course_file_download_url(@context, @attachment, {
download_frd: true,
inline: nil,
verifier: @attachment.uuid
}))
end
it "renders as json" do
request.accept = Mime::JSON.to_s
get :show, {
course_id: @context.id,
assignment_id: @assignment.id,
id: @student.id,
download: @submission.attachment_id,
format: :json
}
expect(JSON.parse(response.body)['attachment']['id']).to eq @submission.attachment_id
end
end
it "sets attachment from submission history if present" do
attachment = @submission.attachment = attachment_model(context: @context)
@submission.submitted_at = 3.hours.ago
@submission.save
expect(@submission.attachment).not_to be_nil, 'precondition'
expect {
@submission.with_versioning(explicit: true) do
@submission.attachment = nil
@submission.submitted_at = 1.hour.ago
@submission.save
end
}.to change(@submission.versions, :count), 'precondition'
expect(@submission.attachment).to be_nil, 'precondition'
get :show, {
course_id: @context.id,
assignment_id: @assignment.id,
id: @student.id,
download: attachment.id
}
expect(assigns(:attachment)).not_to be_nil
expect(assigns(:attachment)).to eq attachment
end
it "sets attachment from attachments collection when attachment_id is not present" do
attachment = attachment_model(context: @context)
AttachmentAssociation.create!(context: @submission, attachment: attachment)
get :show, {
course_id: @context.id,
assignment_id: @assignment.id,
id: @student.id,
download: @submission.attachments.first.id
}
expect(assigns(:attachment)).not_to be_nil
expect(@submission.attachments).to include assigns(:attachment)
end
context "and params[:comment_id]" do
before do
# our factory system is broken
@original_context = @context
@original_student = @student
course_with_student(active_all:true)
submission_comment_model
@attachment = attachment_model(context: @assignment)
@submission_comment.attachments = [@attachment]
@submission_comment.save
end
it "sets attachment from comment_id & download_id" do
expect(@assignment.attachments).to include(@attachment), 'precondition'
expect(@submission_comment.attachments).to include(@attachment), 'precondition'
get :show, {
course_id: @original_context.id,
assignment_id: @assignment.id,
id: @original_student.id,
download: @attachment.id,
comment_id: @submission_comment.id
}
expect(assigns(:attachment)).to eq @attachment
expect(response).to redirect_to(file_download_url(@attachment, {
download_frd: true,
inline: nil,
verifier: @attachment.uuid
}))
end
end
it "should redirect download requests with the download_frd parameter" do
# This is because the files controller looks for download_frd to indicate a forced download
course_with_teacher_logged_in
assignment = assignment_model(course: @course)
student_in_course
att = attachment_model(:uploaded_data => stub_file_data('test.txt', 'asdf', 'text/plain'), :context => @student)
submission_model(
course: @course,
assignment: assignment,
submission_type: "online_upload",
attachment_ids: att.id,
attachments: [att],
user: @student)
get :show, assignment_id: assignment.id, course_id: @course.id, id: @user.id, download: att.id
expect(response).to be_redirect
expect(response.headers["Location"]).to match %r{users/#{@student.id}/files/#{att.id}/download\?download_frd=true}
end
end
end

View File

@ -0,0 +1,80 @@
#
# Copyright (C) 2016 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
require 'spec_helper'
describe Submissions::PreviewsController do
describe 'GET :show' do
before do
course_with_student_and_submitted_homework
@context = @course
user_session(@student)
end
it "should render show_preview" do
get :show, course_id: @context.id, assignment_id: @assignment.id, id: @student.id, preview: true
expect(response).to render_template(:show_preview)
end
context "when assignment is a quiz" do
before do
quiz_with_submission
end
it "should redirect to course_quiz_url" do
get :show, course_id: @context.id, assignment_id: @quiz.assignment.id, id: @student.id, preview: true
expect(response).to redirect_to(course_quiz_url(@context, @quiz, headless: 1))
end
context "and user is a teacher" do
before do
user_session(@teacher)
submission = @quiz.assignment.submissions.where(user_id: @student).first
submission.quiz_submission.with_versioning(true) do
submission.quiz_submission.update_attribute(:finished_at, 1.hour.ago)
end
end
it "should redirect to course_quiz_history_url" do
get :show, course_id: @context.id, assignment_id: @quiz.assignment.id, id: @student.id, preview: true
expect(response).to redirect_to(course_quiz_history_url(@context, @quiz, {
headless: 1,
user_id: @student.id,
version: assigns(:submission).quiz_submission_version
}))
end
it "should favor params[:version] when set" do
version = 1
get :show, {
course_id: @context.id,
assignment_id: @quiz.assignment.id,
id: @student.id,
preview: true,
version: version
}
expect(response).to redirect_to(course_quiz_history_url(@context, @quiz, {
headless: 1,
user_id: @student.id,
version: version
}))
end
end
end
end
end

View File

@ -0,0 +1,65 @@
# Copyright (C) 2016 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
describe 'Submissions::ShowHelper' do
describe 'included in a controller', type: :controller do
controller do
include Submissions::ShowHelper
def show
@context = Course.find(params[:context_id])
@assignment = Assignment.find(params[:assignment_id])
render_user_not_found
end
end
describe '#render_user_not_found' do
before do
course
assignment_model
routes.draw { get 'anonymous' => 'anonymous#show' }
end
context 'with format html' do
before do
get :show, context_id: @course.id, assignment_id: @assignment.id
end
it 'redirects to assignment url' do
expect(response).to redirect_to(course_assignment_url(@course, @assignment.id))
end
it 'set flash error' do
expect(flash[:error]).to be_present
end
end
context 'with format json' do
before do
get :show, context_id: @course.id, assignment_id: @assignment.id, format: :json
end
it 'render json with errors key' do
json = JSON.parse(response.body)
expect(json.key?('errors')).to be_truthy
end
end
end
end
end

View File

@ -0,0 +1,102 @@
# Copyright (C) 2016 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
describe Submissions::SubmissionForShow do
before :once do
course_with_student(active_all: true)
assignment_model(course: @course)
@options = {
assignment_id: @assignment.id,
id: @student.id
}
end
subject { Submissions::SubmissionForShow.new(@course, @options) }
describe '#assignment' do
it 'returns assignment found with provided assignment_id' do
expect(subject.assignment).to eq @assignment
end
end
describe '#user' do
it 'returns user found with provided id' do
expect(subject.user).to eq @user
end
end
describe '#submission' do
it 'instantiates a new submission when one is not present' do
expect(subject.submission).to be_new_record
end
context 'when submission exists' do
before :once do
submission_model({
assignment: @assignment,
body: 'here my assignment',
submission_type: 'online_text_entry',
user: @student
})
@submission.submitted_at = 3.hours.ago
@submission.save
end
it 'returns existing submission when present' do
expect(subject.submission).to eq @submission
end
# Note that submission_history returns a zero-indexed array,
# and even though I couldn't believe it, that is what is passed
# to the controller as the version query param.
context 'when version & preview params are provided' do
before :once do
@options = @options.merge({ preview: true, version: 0 })
end
it 'returns version from submission history' do
expect {
@submission.with_versioning(explicit: true) do
@submission.submitted_at = 1.hour.ago
@submission.save
end
}.to change(@submission.versions, :count), 'precondition'
expect(@submission.version_number).to eq(2), 'precondition'
expect(subject.submission.version_number).to eq 1
end
context 'when assignment is a quiz' do
before :once do
quiz_with_submission
@assignment = @quiz.assignment
submission = @quiz.assignment.submissions.where(user_id: @student).first
submission.quiz_submission.with_versioning(true) do
submission.quiz_submission.update_attribute(:finished_at, 1.hour.ago)
end
@options = @options.merge({ preview: true, version: submission.quiz_submission_version })
end
it 'ignores version params' do
expect(subject.submission.version_number).not_to eq @options[:version]
end
end
end
end
end
end

View File

@ -432,14 +432,6 @@ describe SubmissionsController do
end
end
def course_with_student_and_submitted_homework
course_with_teacher_logged_in(:active_all => true)
@teacher = @user
student_in_course
@assignment = @course.assignments.create!(:title => "some assignment", :submission_types => "online_url,online_upload")
@submission = @assignment.submit_homework(@user)
end
describe "GET zip" do
it "should zip and download" do
course_with_student_and_submitted_homework
@ -461,10 +453,82 @@ describe SubmissionsController do
end
end
describe "GET show" do
it "should not expose muted assignment's scores" do
describe 'GET /submissions/:id param routing', type: :request do
before do
course_with_student_and_submitted_homework
@context = @course
end
describe "preview query param", type: :request do
it 'renders with submissions/previews#show if params is present and format is html' do
get "/courses/#{@context.id}/assignments/#{@assignment.id}/submissions/#{@student.id}?preview=1"
expect(request.params[:controller]).to eq 'submissions/previews'
expect(request.params[:action]).to eq 'show'
end
it 'renders with submissions#show if params is present and format is json' do
get "/courses/#{@context.id}/assignments/#{@assignment.id}/submissions/#{@student.id}?preview=1", format: :json
expect(request.params[:controller]).to eq 'submissions'
expect(request.params[:action]).to eq 'show'
end
it 'renders with action #show if params is not present' do
get "/courses/#{@context.id}/assignments/#{@assignment.id}/submissions/#{@student.id}"
expect(request.params[:controller]).to eq 'submissions'
expect(request.params[:action]).to eq 'show'
end
end
describe "download query param", type: :request do
it 'renders with action #download if params is present' do
get "/courses/#{@context.id}/assignments/#{@assignment.id}/submissions/#{@student.id}?download=12345"
expect(request.params[:controller]).to eq 'submissions/downloads'
expect(request.params[:action]).to eq 'show'
end
it 'renders with action #show if params is not present' do
get "/courses/#{@context.id}/assignments/#{@assignment.id}/submissions/#{@student.id}"
expect(request.params[:controller]).to eq 'submissions'
expect(request.params[:action]).to eq 'show'
end
end
end
describe "GET show" do
before do
course_with_student_and_submitted_homework
@context = @course
user_session(@teacher)
end
it "renders show template" do
get :show, course_id: @context.id, assignment_id: @assignment.id, id: @student.id
expect(response).to render_template(:show)
end
it "renders json" do
request.accept = Mime::JSON.to_s
get :show, course_id: @context.id, assignment_id: @assignment.id, id: @student.id, format: :json
expect(JSON.parse(response.body)['submission']['id']).to eq @submission.id
end
context "with user id not present in course" do
before(:once) do
course_with_student(active_all: true)
end
it "sets flash error" do
get :show, course_id: @context.id, assignment_id: @assignment.id, id: @student.id
expect(flash[:error]).not_to be_nil
end
it "should redirect to context assignment url" do
get :show, course_id: @context.id, assignment_id: @assignment.id, id: @student.id
expect(response).to redirect_to(course_assignment_url(@context, @assignment))
end
end
it "should not expose muted assignment's scores" do
get "show", :id => @submission.to_param, :assignment_id => @assignment.to_param, :course_id => @course.to_param
expect(response).to be_success
@ -474,39 +538,20 @@ describe SubmissionsController do
end
it "should show rubric assessments to peer reviewers" do
course_with_student_and_submitted_homework
@assessor = student_in_course.user
course_with_student(active_all: true)
@assessor = @student
outcome_with_rubric
@association = @rubric.associate_with @assignment, @course, :purpose => 'grading'
@association = @rubric.associate_with @assignment, @context, :purpose => 'grading'
@assignment.assign_peer_review(@assessor, @submission.user)
@assessment = @association.assess(:assessor => @assessor, :user => @submission.user, :artifact => @submission, :assessment => { :assessment_type => 'grading'})
user_session(@assessor)
get "show", :id => @submission.to_param, :assignment_id => @assignment.to_param, :course_id => @course.to_param
get "show", :id => @submission.user.id, :assignment_id => @assignment.id, :course_id => @context.id
expect(response).to be_success
expect(assigns[:visible_rubric_assessments]).to eq [@assessment]
end
it "should redirect download requests with the download_frd parameter" do
# This is because the files controller looks for download_frd to indicate a forced download
course_with_teacher_logged_in
assignment = assignment_model(course: @course)
student_in_course
att = attachment_model(:uploaded_data => stub_file_data('test.txt', 'asdf', 'text/plain'), :context => @student)
submission = submission_model(
course: @course,
assignment: assignment,
submission_type: "online_upload",
attachment_ids: att.id,
attachments: [att],
user: @student)
get 'show', assignment_id: assignment.id, course_id: @course.id, id: @user.id, download: att.id
expect(response).to be_redirect
expect(response.headers["Location"]).to match %r{users/#{@student.id}/files/#{att.id}/download\?download_frd=true}
end
end
describe 'GET turnitin_report' do

View File

@ -153,3 +153,18 @@ def create_courses(records, options = {})
end
course_data
end
def course_with_student_and_submitted_homework
course_with_teacher_logged_in(active_all: true)
@teacher = @user
student_in_course(active_all: true)
@assignment = @course.assignments.create!({
title: "some assignment",
submission_types: "online_url,online_upload"
})
@submission = @assignment.submit_homework(@user, {
submission_type: "online_url",
url: "http://www.google.com"
})
end

View File

@ -1407,6 +1407,69 @@ describe Submission do
end
end
end
describe '#rubric_association_with_assessing_user_id' do
before :once do
submission_model assignment: @assignment, user: @student
rubric_association_model association_object: @assignment, purpose: 'grading'
end
subject { @submission.rubric_association_with_assessing_user_id }
it 'sets assessing_user_id to submission.user_id' do
expect(subject.assessing_user_id).to eq @submission.user_id
end
end
describe '#visible_rubric_assessments_for' do
before :once do
submission_model assignment: @assignment, user: @student
@viewing_user = @teacher
end
subject { @submission.visible_rubric_assessments_for(@viewing_user) }
it 'returns empty if assignment is muted?' do
@assignment.update_attribute(:muted, true)
expect(@assignment.muted?).to be_truthy, 'precondition'
expect(subject).to be_empty
end
it 'returns empty if viewing user cannot :read_grade' do
student_in_course(active_all: true)
@viewing_user = @student
expect(@submission.grants_right?(@viewing_user, :read_grade)).to be_falsey, 'precondition'
expect(subject).to be_empty
end
context 'with rubric_assessments' do
before :once do
@assessed_user = @student
rubric_association_model association_object: @assignment, purpose: 'grading'
student_in_course(active_all: true)
[ @teacher, @student ].each do |user|
@rubric_association.rubric_assessments.create!({
artifact: @submission,
assessment_type: 'grading',
assessor: user,
rubric: @rubric,
user: @assessed_user
})
end
@teacher_assessment = @submission.rubric_assessments.where(assessor_id: @teacher).first
@student_assessment = @submission.rubric_assessments.where(assessor_id: @student).first
end
subject { @submission.visible_rubric_assessments_for(@viewing_user) }
it 'returns rubric_assessments for teacher' do
expect(subject).to include(@teacher_assessment)
end
it 'returns only student rubric assessment' do
@viewing_user = @student
expect(subject).not_to include(@teacher_assessment)
expect(subject).to include(@student_assessment)
end
end
end
end
def submission_spec_model(opts={})