add read_only to custom_gradebook_columns

Test Plan:

1 - Run the migrations and validate that the
    `read_only` column exists on the
    `custom_gradebook_columns` table, with a default
    of false
2 - Create a new CustomGradebookColumn through the API
    and validate that it accepts and persists the
    `read_only` parameter. Example curls:

   Create the column:

   curl -H "Authorization: Bearer <token>" \
     http://localhost:3000/api/v1/courses/:id/custom_gradebook_columns \
     -X POST \
     -F "title=ReadOnly" \
     -F "read_only=true"

   Validate the columns:

   curl -H "Authorization: Bearer <token>" \
     http://localhost:3000/api/v1/courses/:id/custom_gradebook_columns/

3 - Open the gradebook for a course and validate that you cannot
    edit the `read_only` columns in both the old and new
    gradebooks. (Tabbing should still work as normal.)

4 - Go to the Individual View. Under Global Settings, check Show Notes
    in Student Info. Under Content Selection, select a student.
    Confirm that you can only edit the custom columns that were
    not created as read_only.

refs PFS-9913, PFS-9914, PFS-10003

Change-Id: I18005cc0eaf19202c00a5d79c2a04421b5d12a5c
Reviewed-on: https://gerrit.instructure.com/139625
Reviewed-by: Keith T. Garner <kgarner@instructure.com>
Reviewed-by: Jeremy Neander <jneander@instructure.com>
Tested-by: Jenkins
Product-Review: Ian Morris <ianm@instructure.com>
QA-Review: Aiona Hernandez <ahernandez@instructure.com>
This commit is contained in:
Nate Collings 2018-01-31 12:29:48 -07:00
parent 7aa3757cfa
commit cbd207736b
12 changed files with 108 additions and 15 deletions

View File

@ -48,6 +48,10 @@ define [
customColURL: ->
ENV.GRADEBOOK_OPTIONS.custom_column_datum_url
disabled:(->
@get('column.isLoading') || @get('column.read_only')
).property('column', 'column.isLoading', 'column.read_only')
saveURL: (->
@customColURL()
.replace(/:id/, @get('column.id'))

View File

@ -14,7 +14,7 @@
value=value
name=id
id=id
disabled=column.isLoading
disabled=disabled
class="span8"
}}
</div>

View File

@ -41,6 +41,8 @@ define [
@column = Ember.Object.create
id: '22'
title: 'Notes'
read_only: false
is_loading: false
@student = Ember.Object.create
id: '45'
@dataForStudent = [
@ -69,6 +71,21 @@ define [
test "saveUrl", ->
equal @component.get('saveURL'), '/api/v1/custom_gradebook_columns/22/45'
test "disabled is true when column isLoading", ->
@component.column.set('isLoading', true)
@component.column.set('read_only', false)
equal @component.get('disabled'), true
test "disabled is true when column is read_only", ->
@component.column.set('isLoading', false)
@component.column.set('read_only', true)
equal @component.get('disabled'), true
test "disabled is false when column is not loading and not read_only", ->
@component.column.set('isLoading', false)
@component.column.set('read_only', false)
equal @component.get('disabled'), false
test "focusOut", (assert) ->
assert.expect(1)
stub = @stub @component, 'boundSaveSuccess'

View File

@ -1323,15 +1323,20 @@ define [
customColumnDefinitions: ->
@customColumns.map (c) ->
id: "custom_col_#{c.id}"
name: if c.teacher_notes then I18n.t('Notes') else htmlEscape c.title
field: "custom_col_#{c.id}"
width: 100
cssClass: "meta-cell custom_column"
resizable: true
editor: LongTextEditor
autoEdit: false
maxLength: 255
cssClasses = ["meta-cell", "custom_column"]
cssClasses.push("cannot_edit") if c.read_only
{
id: "custom_col_#{c.id}"
name: if c.teacher_notes then I18n.t('Notes') else htmlEscape c.title
field: "custom_col_#{c.id}"
width: 100
cssClass: cssClasses.join(" ")
resizable: true
editor: LongTextEditor
autoEdit: false
maxLength: 255
}
initGrid: =>
#this is used to figure out how wide to make each column

View File

@ -1679,6 +1679,8 @@ define [
# The target cell will enter editing mode
onBeforeEditCell: (event, obj) =>
if obj.column.type == 'custom_column' && @getCustomColumn(obj.column.customColumnId)?.read_only
return false
return true if obj.column.type != 'assignment'
return false unless student = @student(obj.item.id)
return false if student.isConcluded

View File

@ -50,6 +50,11 @@
# "description": "won't be displayed if hidden is true",
# "example": false,
# "type": "boolean"
# },
# "read_only": {
# "description": "won't be editable in the gradebook UI",
# "example": true,
# "type": "boolean"
# }
# }
# }
@ -94,6 +99,8 @@ class CustomGradebookColumnsApiController < ApplicationController
# @argument column[teacher_notes] [Boolean]
# Set this if the column is created by a teacher. The gradebook only
# supports one teacher_notes column.
# @argument column[read_only] [Boolean]
# Set this to prevent the column from being editable in the gradebook ui
#
# @returns CustomColumn
def create
@ -151,6 +158,6 @@ class CustomGradebookColumnsApiController < ApplicationController
end
def column_params
params.require(:column).permit(:title, :position, :teacher_notes, :hidden)
params.require(:column).permit(:title, :position, :teacher_notes, :hidden, :read_only)
end
end

View File

@ -0,0 +1,32 @@
#
# Copyright (C) 2018 - present 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/>.
class AddReadonlyToCustomGradebookColumn < ActiveRecord::Migration[5.0]
tag :predeploy
disable_ddl_transaction!
def up
add_column :custom_gradebook_columns, :read_only, :boolean
change_column_default :custom_gradebook_columns, :read_only, false
DataFixup::BackfillNulls.run(CustomGradebookColumn, :read_only, default_value: false)
change_column_null :custom_gradebook_columns, :read_only, false
end
def down
remove_column :custom_gradebook_columns, :read_only
end
end

View File

@ -21,7 +21,7 @@ module Api::V1::CustomGradebookColumn
def custom_gradebook_column_json(column, user, session)
json = api_json column, user, session, :only => %w(id title position
teacher_notes)
teacher_notes read_only)
json[:hidden] = column.hidden?
json
end

View File

@ -97,7 +97,7 @@ describe CustomGradebookColumnsApiController, type: :request do
"/api/v1/courses/#{@course.id}/custom_gradebook_columns",
{course_id: @course.to_param, action: "create",
controller: "custom_gradebook_columns_api", format: "json"},
"column[title]" => "Blah blah blah", "column[position]" => 1
"column[title]" => "Blah blah blah", "column[position]" => 1, "column[read_only]" => true
expect(response).to be_success
expect(CustomGradebookColumn.find(json["id"])).not_to be_nil
end
@ -122,9 +122,10 @@ describe CustomGradebookColumnsApiController, type: :request do
"/api/v1/courses/#{@course.id}/custom_gradebook_columns/#{@col.id}",
{course_id: @course.to_param, id: @col.to_param, action: "update",
controller: "custom_gradebook_columns_api", format: "json"},
"column[title]" => "Bar"
"column[title]" => "Bar", "column[read_only]" => true
expect(response).to be_success
expect(json["title"]).to eq "Bar"
expect(json["read_only"]).to eq(true)
expect(@col.reload.title).to eq "Bar"
end
end

View File

@ -399,6 +399,22 @@ test('It does not sort columns when gradebookColumnOrderSettings is undefined',
notOk(this.makeColumnSortFn.called)
})
QUnit.module('Gradebook#customColumnDefinitions', {
setup() {
this.gradebook = createGradebook()
this.gradebook.customColumns = [
{ id: '1', teacher_notes: false, hidden: false, title: 'Read Only', read_only: true },
{ id: '2', teacher_notes: false, hidden: false, title: 'Not Read Only', read_only: false }
]
}
})
test('includes the cannot_edit class for read_only columns', function () {
columns = this.gradebook.customColumnDefinitions()
equal(columns[0].cssClass, "meta-cell custom_column cannot_edit")
equal(columns[1].cssClass, "meta-cell custom_column")
})
QUnit.module('Gradebook#fieldsToExcludeFromAssignments', {
setup() {
return (this.excludedFields = Gradebook.prototype.fieldsToExcludeFromAssignments)

View File

@ -4414,6 +4414,10 @@ QUnit.module('Gradebook Grid Events', () => {
hooks.beforeEach(() => {
gradebook = createGradebook();
gradebook.initSubmissionStateMap();
gradebook.gradebookContent.customColumns = [
{ id: '1', teacher_notes: false, hidden: false, title: 'Read Only', read_only: true },
{ id: '2', teacher_notes: false, hidden: false, title: 'Not Read Only', read_only: false }
];
gradebook.students = { 1101: { id: '1101', isConcluded: false } };
eventObject = {
column: { assignmentId: '2301', type: 'assignment' },
@ -4452,6 +4456,11 @@ QUnit.module('Gradebook Grid Events', () => {
eventObject.column = { type: 'custom_column' };
strictEqual(gradebook.onBeforeEditCell(null, eventObject), true);
});
test('returns false when the cell is read_only', () => {
eventObject.column = { type: 'custom_column', customColumnId: '1' };
strictEqual(gradebook.onBeforeEditCell(null, eventObject), false);
});
});
QUnit.module('onColumnsResized', (hooks) => {

View File

@ -31,7 +31,7 @@ describe "Api::V1::CustomGradebookColumn" do
describe "custom_gradebook_column_json" do
it "works" do
json = @col.attributes.slice(*%w(id title position teacher_notes))
json = @col.attributes.slice(*%w(id title position teacher_notes read_only))
json["hidden"] = false
expect(controller.custom_gradebook_column_json(@col, @teacher, nil)).to eq json
end