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:
wdransfield 2017-10-16 12:21:08 -06:00 committed by Weston Dransfield
parent 0041d8beba
commit 053ffc39ca
12 changed files with 144 additions and 19 deletions

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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">

View File

@ -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 providers 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 tools 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"
}
]
}
```

BIN
doc/images/eula.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 KiB

View File

@ -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');

View File

@ -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) {

View File

@ -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

View File

@ -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();

View File

@ -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")

View File

@ -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)