Store EULA agreement timestamp
Closes PLAT-2879 Test Plan: - Install a plagiarim tool that offers a service named 'vnd.Canvas.Eula'. Example: {"endpoint"=>"http://originality.docker/eula", "action"=>["GET"], "@id"=>"http://originality.docker/lti/v2/services#vnd.Canvas.Eula", "@type"=>"RestService"} - Associate the tool with an assignment. - As a student go to the assignment submission page. Verify and verify the page requires agreeing to the Eula before submitting. - Retrieve the submission via api using the plagiarism tool and verify the submission object returned contains a timestamp showing when the user clicked the checkbox. Change-Id: I2a8cac02666b66fbb76d4b55f980a45dfdac2fa6 Reviewed-on: https://gerrit.instructure.com/129858 Reviewed-by: Nathan Mills <nathanm@instructure.com> QA-Review: August Thornton <august@instructure.com> Tested-by: Jenkins Product-Review: Karl Lloyd <karl@instructure.com>
This commit is contained in:
parent
0041d8beba
commit
053ffc39ca
|
@ -67,6 +67,11 @@
|
|||
# "example": 134,
|
||||
# "type": "integer"
|
||||
# },
|
||||
# "eula_agreement_timestamp": {
|
||||
# "description": "UTC timestamp showing when the user agreed to the EULA (if given by the tool provider)",
|
||||
# "example": "1508250487578",
|
||||
# "type": "string"
|
||||
# },
|
||||
# "workflow_state": {
|
||||
# "description": "The current state of the submission",
|
||||
# "example": "submitted",
|
||||
|
@ -218,6 +223,9 @@ module Lti
|
|||
submission_attributes = %w(id body url submitted_at assignment_id user_id submission_type workflow_state attempt attachments)
|
||||
sub_hash = filtered_json(model: submission, whitelist: submission_attributes)
|
||||
sub_hash[:user_id] = Lti::Asset.opaque_identifier_for(User.find(sub_hash[:user_id]))
|
||||
if submission.turnitin_data[:eula_agreement_timestamp].present?
|
||||
sub_hash[:eula_agreement_timestamp] = submission.turnitin_data[:eula_agreement_timestamp]
|
||||
end
|
||||
attachments = submission.versioned_attachments
|
||||
sub_hash[:attachments] = attachments.map { |a| attachment_json(a) }
|
||||
sub_hash
|
||||
|
|
|
@ -249,7 +249,8 @@ class SubmissionsController < ApplicationController
|
|||
|
||||
submission_params = params[:submission].permit(
|
||||
:body, :url, :submission_type, :comment, :group_comment,
|
||||
:media_comment_type, :media_comment_id, :attachment_ids => []
|
||||
:media_comment_type, :media_comment_id, :eula_agreement_timestamp,
|
||||
:attachment_ids => []
|
||||
)
|
||||
submission_params[:attachments] = self.class.copy_attachments_to_submissions_folder(@context, params[:submission][:attachments].compact.uniq)
|
||||
|
||||
|
|
|
@ -1628,6 +1628,7 @@ class Assignment < ActiveRecord::Base
|
|||
%w[comment group_comment attachments]).to_set
|
||||
|
||||
def submit_homework(original_student, opts={})
|
||||
eula_timestamp = opts[:eula_agreement_timestamp]
|
||||
# Only allow a few fields to be submitted. Cannot submit the grade of a
|
||||
# homework assignment, for instance.
|
||||
opts.keys.each { |k|
|
||||
|
@ -1669,6 +1670,7 @@ class Assignment < ActiveRecord::Base
|
|||
})
|
||||
homework.submitted_at = Time.zone.now
|
||||
homework.lti_user_id = Lti::Asset.opaque_identifier_for(student)
|
||||
homework.turnitin_data[:eula_agreement_timestamp] = eula_timestamp if eula_timestamp.present?
|
||||
homework.with_versioning(:explicit => (homework.submission_type != "discussion_topic")) do
|
||||
if group
|
||||
if student == original_student
|
||||
|
|
|
@ -65,6 +65,7 @@
|
|||
<%= form_tag(context_url(@context, :controller => :submissions, :assignment_id => @assignment.id, :action => :create), {:id => "submit_online_upload_form", :class => "submit_assignment_form", :multipart => true }) do %>
|
||||
<%= hidden_field :submission, :submission_type, :value => "online_upload" %>
|
||||
<%= hidden_field :submission, :attachment_ids, :value => "", :id => "submission_attachment_ids" %>
|
||||
<%= hidden_field :submission, :eula_agreement_timestamp, :id => "eula_agreement_timestamp" %>
|
||||
<table class="formtable" style="width: 100%;">
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
|
|
|
@ -13,6 +13,9 @@ Once registered, tools will be provided with an LTI tool placement in the Canvas
|
|||
#### Section 3 - Originality Reports
|
||||
The webhook sent to the TP contains the data needed for the tool to retrieve the submission from Canvas. The TP can then process the submission, determine its "originality score", and create an Originality Report. This data is then sent back to Canvas and associated with the submission via the Canvas Originality Report API.
|
||||
|
||||
#### Section 4 - Other Features
|
||||
Descriptions of optional features tool providers may use.
|
||||
|
||||
For additional help please see the following reference tools:
|
||||
* [Plagiarism Detection Reference Tool](https://github.com/instructure/lti_originality_report_example)
|
||||
* [LTI 2.1 Reference Tool](https://github.com/instructure/lti2_reference_tool_provider)
|
||||
|
@ -226,3 +229,64 @@ Once the TP has been notified of a new submission (see section 2.2), it may acce
|
|||
After processing the submission, an Originality Report may be created for the submission.
|
||||
|
||||
Using the Originality Report and Submissions APIs requires a JWT access token be sent in the authorization header. For more information on using JWT tokens in Canvas see <a href="jwt_access_tokens.html">JWT access tokens</a>.
|
||||
|
||||
### 4. Optional Features
|
||||
The following are optional features the tool provider may wish to implement.
|
||||
|
||||
#### End-User License Agreement Verification
|
||||
##### Description
|
||||
This feature provides a mechanism for a tool provider to specify a link to a EULA or terms of service they wish to show to students submitting homework.
|
||||
|
||||
This feature also guarantees that the user has agreed to the EULA/terms of service before they submit homework. The timestamp of when the user agreed is also provided to the tool provider during the normal submission/attachment retrieval flow.
|
||||
|
||||
##### Implementation
|
||||
To implement this feature a tool provider must add the `vnd.Canvas.Eula` service to the `service_offered` array of their tool proxy:
|
||||
```json
|
||||
{
|
||||
…
|
||||
"service_offered": [
|
||||
…
|
||||
{
|
||||
// Must end in "#vnd.Canvas.Eula"
|
||||
"@id": "my.tool.com/service#vnd.Canvas.Eula",
|
||||
"endpoint": "http://my.tool.com/eula", // URL of EULA for Canvas to link to
|
||||
"@type": "RestService",
|
||||
"format": ["application/json"],
|
||||
"action": ["GET"]
|
||||
},
|
||||
…
|
||||
],
|
||||
…
|
||||
}
|
||||
```
|
||||
|
||||
As shown in the example above the value of `endpoint` should be a fully qualified URL pointing to the tool provider’s terms of service or EULA. The value of the `@id` must end in `#vnd.Canvas.Eula`. The `action` array must contain `GET`.
|
||||
|
||||
Once the tool is registered in Canvas and associated with an assignment, students will be presented with a link to the tool’s EULA that they must agree to before Canvas will allow a homework submission:
|
||||
|
||||
<img style="width:35vw;" src="./images/eula.png" alt="Submission details" />
|
||||
|
||||
When the tool provider retrieves the assignment details from Canvas the body of the response payload will contain a property named `eula_agreement_timestamp` which contains the timestamp indicating when the user clicked the checkbox:
|
||||
```json
|
||||
{
|
||||
"id":215,
|
||||
"submitted_at":"2017-10-17T14:28:09Z",
|
||||
"assignment_id":216,
|
||||
"user_id":"86157096483e6b3a50bfedc6bac902c0b20a824f",
|
||||
"submission_type":"online_upload",
|
||||
"workflow_state":"submitted",
|
||||
"attempt":1,
|
||||
"eula_agreement_timestamp":"1508250487578",
|
||||
"attachments":[
|
||||
{
|
||||
"id":230,
|
||||
"size":14942,
|
||||
"filename":"1508250488_822__Test_Student_Submission.doc",
|
||||
"display_name":"Test_Student_Submission.doc",
|
||||
"created_at":"2017-10-17T14:28:08Z",
|
||||
"updated_at":"2017-10-17T14:28:08Z",
|
||||
"url":"http://canvas.docker/api/lti/assignments/10000000000216/submissions/10000000000215/attachment/10000000000230"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 273 KiB |
|
@ -25,7 +25,7 @@ import homework_submission_tool from 'jst/assignments/homework_submission_tool'
|
|||
import HomeworkSubmissionLtiContainer from 'compiled/external_tools/HomeworkSubmissionLtiContainer'
|
||||
import RCEKeyboardShortcuts from 'compiled/views/editor/KeyboardShortcuts' /* TinyMCE Keyboard Shortcuts for a11y */
|
||||
import RichContentEditor from 'jsx/shared/rce/RichContentEditor'
|
||||
import {submitContentItem} from './submit_assignment_helper'
|
||||
import {submitContentItem, recordEulaAgreement} from './submit_assignment_helper'
|
||||
import 'compiled/jquery.rails_flash_notifications'
|
||||
import './jquery.ajaxJSON'
|
||||
import './jquery.inst_tree'
|
||||
|
@ -251,6 +251,11 @@ import 'jqueryui/tabs'
|
|||
}
|
||||
});
|
||||
|
||||
$('input.turnitin_pledge').click((e) => {
|
||||
recordEulaAgreement(document.querySelector('#eula_agreement_timestamp'),
|
||||
e.target.checked);
|
||||
})
|
||||
|
||||
$(".submit_assignment_link").click(function(event, skipConfirmation) {
|
||||
event.preventDefault();
|
||||
var late = $(this).hasClass('late');
|
||||
|
|
|
@ -20,18 +20,22 @@ import I18n from 'i18n!assignments'
|
|||
import './jquery.instructure_misc_plugins'
|
||||
import 'compiled/jquery.rails_flash_notifications'
|
||||
|
||||
var validFileSubmission = function(ext, contentItem) {
|
||||
return !ENV.SUBMIT_ASSIGNMENT ||
|
||||
!ENV.SUBMIT_ASSIGNMENT.ALLOWED_EXTENSIONS ||
|
||||
ENV.SUBMIT_ASSIGNMENT.ALLOWED_EXTENSIONS.length <= 0 ||
|
||||
(contentItem.url.match(/\./) && $.inArray(ext, ENV.SUBMIT_ASSIGNMENT.ALLOWED_EXTENSIONS) >= 0);
|
||||
};
|
||||
var validFileSubmission = function(ext, contentItem) {
|
||||
return !ENV.SUBMIT_ASSIGNMENT ||
|
||||
!ENV.SUBMIT_ASSIGNMENT.ALLOWED_EXTENSIONS ||
|
||||
ENV.SUBMIT_ASSIGNMENT.ALLOWED_EXTENSIONS.length <= 0 ||
|
||||
(contentItem.url.match(/\./) && $.inArray(ext, ENV.SUBMIT_ASSIGNMENT.ALLOWED_EXTENSIONS) >= 0);
|
||||
};
|
||||
|
||||
var invalidToolReturn = function(message) {
|
||||
$.flashError(I18n.t("The launched tool returned an invalid resource for this assignment"));
|
||||
console.log(message);
|
||||
return false;
|
||||
};
|
||||
var invalidToolReturn = function(message) {
|
||||
$.flashError(I18n.t("The launched tool returned an invalid resource for this assignment"));
|
||||
console.log(message);
|
||||
return false;
|
||||
};
|
||||
|
||||
export function recordEulaAgreement (input, checked) {
|
||||
input.value = checked ? new Date().getTime() : '';
|
||||
}
|
||||
|
||||
export function submitContentItem (contentItem) {
|
||||
if (!contentItem) {
|
||||
|
|
|
@ -140,6 +140,13 @@ module Lti
|
|||
submission.global_id.to_s
|
||||
)
|
||||
end
|
||||
|
||||
it 'includes the eula agreement timestamp if present' do
|
||||
submission.turnitin_data[:eula_agreement_timestamp] = Time.now.to_i
|
||||
submission.save!
|
||||
get endpoint, headers: request_headers
|
||||
expect(JSON.parse(response.body)['eula_agreement_timestamp']).to eq submission.turnitin_data[:eula_agreement_timestamp]
|
||||
end
|
||||
end
|
||||
|
||||
describe "#history" do
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
# 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/>.
|
||||
|
||||
define ['submit_assignment_helper','jquery'], ({submitContentItem}, $) ->
|
||||
define ['submit_assignment_helper','jquery'], ({submitContentItem, recordEulaAgreement}, $) ->
|
||||
formHtml = """
|
||||
<div id="form-container">
|
||||
<form accept-charset="UTF-8" action="/courses/2/assignments/1/submissions" id="submit_online_url_form" method="post">
|
||||
|
@ -125,8 +125,12 @@ define ['submit_assignment_helper','jquery'], ({submitContentItem}, $) ->
|
|||
result = submitContentItem(undefined)
|
||||
equal result, false
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
test "Sets the input value to the current time if checked is true", ->
|
||||
now = new Date();
|
||||
clock = sinon.useFakeTimers(now.getTime());
|
||||
inputHtml = "<input type='checkbox' name='test' id='checkbox-test'></input>"
|
||||
$('#fixtures').append(inputHtml)
|
||||
input = document.querySelector('#checkbox-test')
|
||||
recordEulaAgreement(input, true)
|
||||
equal document.querySelector('#checkbox-test').value, now.getTime()
|
||||
clock.restore();
|
||||
|
|
|
@ -72,6 +72,28 @@ describe SubmissionsController do
|
|||
expect(assigns[:submission][:submission_type]).to eql("online_upload")
|
||||
end
|
||||
|
||||
it "accepts 'eula_agreement_timestamp' params and persists it in the 'turnitin_data'" do
|
||||
course_with_student_logged_in(:active_all => true)
|
||||
timestamp = Time.now.to_i
|
||||
@assignment = @course.assignments.create!(:title => "some assignment", :submission_types => "online_upload")
|
||||
a1 = attachment_model(:context => @user)
|
||||
post 'create',
|
||||
params: {
|
||||
:course_id => @course.id,
|
||||
:assignment_id => @assignment.id,
|
||||
:submission => {
|
||||
:submission_type => "online_upload",
|
||||
:attachment_ids => a1.id,
|
||||
:eula_agreement_timestamp => timestamp
|
||||
},
|
||||
:attachments => {
|
||||
"0" => { :uploaded_data => "" },
|
||||
"-1" => { :uploaded_data => "" }
|
||||
}
|
||||
}
|
||||
expect(assigns[:submission].turnitin_data[:eula_agreement_timestamp]).to eq timestamp.to_s
|
||||
end
|
||||
|
||||
it "should copy attachments to the submissions folder if that feature is enabled" do
|
||||
course_with_student_logged_in(:active_all => true)
|
||||
@assignment = @course.assignments.create!(:title => "some assignment", :submission_types => "online_upload")
|
||||
|
|
|
@ -1407,6 +1407,13 @@ describe Assignment do
|
|||
points_possible: 10
|
||||
end
|
||||
|
||||
it "sets the 'eula_agreement_timestamp'" do
|
||||
setup_assignment_without_submission
|
||||
timestamp = Time.now.to_i.to_s
|
||||
@a.submit_homework(@user, {eula_agreement_timestamp: timestamp})
|
||||
expect(@a.submissions.first.turnitin_data[:eula_agreement_timestamp]).to eq timestamp
|
||||
end
|
||||
|
||||
it "creates a new version for each submission" do
|
||||
setup_assignment_without_submission
|
||||
@a.submit_homework(@user)
|
||||
|
|
Loading…
Reference in New Issue