enable assignment properties to be locked
this creates a "plugin" that allows an account to choose assignment properties that can be locked when a course is copied Test Plan: * enable the plugin for an account * create an assignment and set it to be locked on copy * copy the course to another course * as an admin of the account you should still be able to edit the assignment * as a non-admin teacher of the course you should not be able to edit the frozen properties refs #7931 Change-Id: I61d5dbfdf10f8f7519f8db06449b14ef5e7b8454 Reviewed-on: https://gerrit.instructure.com/10190 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Cody Cutrer <cody@instructure.com>
This commit is contained in:
parent
a56a41367c
commit
98fe5ee5c0
|
@ -185,16 +185,20 @@ class AssignmentsApiController < ApplicationController
|
|||
@assignment = @context.assignments.find(params[:id])
|
||||
|
||||
if authorized_action(@assignment, @current_user, :update_content)
|
||||
if custom_vals = params[:assignment][:set_custom_field_values]
|
||||
@assignment.set_custom_field_values = custom_vals
|
||||
end
|
||||
|
||||
if @assignment.update_attributes(assignment_params)
|
||||
render :json => assignment_json(@assignment, @current_user, session, [], @context.user_is_teacher?(@current_user)).to_json, :status => 201
|
||||
if @assignment.frozen?
|
||||
render :json => {:message => t('errors.no_edit_frozen', "You cannot edit a frozen assignment.")}.to_json, :status => 400
|
||||
else
|
||||
# TODO: we don't really have a strategy in the API yet for returning
|
||||
# errors.
|
||||
render :json => 'error'.to_json, :status => 400
|
||||
if custom_vals = params[:assignment][:set_custom_field_values]
|
||||
@assignment.set_custom_field_values = custom_vals
|
||||
end
|
||||
|
||||
if @assignment.update_attributes(assignment_params)
|
||||
render :json => assignment_json(@assignment, @current_user, session, [], @context.user_is_teacher?(@current_user)).to_json, :status => 201
|
||||
else
|
||||
# TODO: we don't really have a strategy in the API yet for returning
|
||||
# errors.
|
||||
render :json => 'error'.to_json, :status => 400
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -281,6 +281,7 @@ class AssignmentsController < ApplicationController
|
|||
params[:assignment] = p
|
||||
else
|
||||
params[:assignment] ||= {}
|
||||
@assignment.updating_user = @current_user
|
||||
if params[:assignment][:default_grade]
|
||||
params[:assignment][:overwrite_existing_grades] = (params[:assignment][:overwrite_existing_grades] == "1")
|
||||
@assignment.set_default_grade(params[:assignment])
|
||||
|
|
|
@ -32,8 +32,8 @@ class Assignment < ActiveRecord::Base
|
|||
:peer_reviews, :automatic_peer_reviews, :grade_group_students_individually,
|
||||
:notify_of_update, :time_zone_edited, :turnitin_enabled, :turnitin_settings,
|
||||
:set_custom_field_values, :context, :position, :allowed_extensions,
|
||||
:external_tool_tag_attributes
|
||||
attr_accessor :original_id
|
||||
:external_tool_tag_attributes, :freeze_on_copy
|
||||
attr_accessor :original_id, :updating_user
|
||||
|
||||
has_many :submissions, :class_name => 'Submission', :dependent => :destroy
|
||||
has_many :attachments, :as => :context, :dependent => :destroy
|
||||
|
@ -77,6 +77,7 @@ class Assignment < ActiveRecord::Base
|
|||
validates_presence_of :context_type
|
||||
validates_length_of :title, :maximum => maximum_string_length, :allow_nil => true
|
||||
validates_length_of :description, :maximum => maximum_long_text_length, :allow_nil => true, :allow_blank => true
|
||||
validate :frozen_atts_not_altered, :if => :frozen?, :on => :update
|
||||
|
||||
acts_as_list :scope => :assignment_group_id
|
||||
has_a_broadcast_policy
|
||||
|
@ -1261,7 +1262,7 @@ class Assignment < ActiveRecord::Base
|
|||
named_scope :only_graded, :conditions => "submission_types != 'not_graded'"
|
||||
|
||||
named_scope :with_just_calendar_attributes, lambda {
|
||||
{ :select => ((Assignment.column_names & CalendarEvent.column_names) + ['due_at', 'assignment_group_id', 'could_be_locked', 'unlock_at', 'lock_at', 'submission_types'] - ['cloned_item_id', 'migration_id']).join(", ") }
|
||||
{ :select => ((Assignment.column_names & CalendarEvent.column_names) + ['due_at', 'assignment_group_id', 'could_be_locked', 'unlock_at', 'lock_at', 'submission_types', '(freeze_on_copy AND copied) AS frozen'] - ['cloned_item_id', 'migration_id']).join(", ") }
|
||||
}
|
||||
|
||||
named_scope :due_between, lambda { |start, ending|
|
||||
|
@ -1520,6 +1521,7 @@ class Assignment < ActiveRecord::Base
|
|||
description += hash[:instructions_in_html] == false ? ImportedHtmlConverter.convert_text(hash[:instructions] || "", context) : ImportedHtmlConverter.convert(hash[:instructions] || "", context)
|
||||
description += Attachment.attachment_list_from_migration(context, hash[:attachment_ids])
|
||||
item.description = description
|
||||
item.copied = true
|
||||
if !hash[:submission_types].blank?
|
||||
item.submission_types = hash[:submission_types]
|
||||
elsif ['discussion_topic'].include?(hash[:submission_format])
|
||||
|
@ -1595,7 +1597,7 @@ class Assignment < ActiveRecord::Base
|
|||
[:all_day, :turnitin_enabled, :peer_reviews_assigned, :peer_reviews,
|
||||
:automatic_peer_reviews, :anonymous_peer_reviews,
|
||||
:grade_group_students_individually, :allowed_extensions, :min_score,
|
||||
:max_score, :mastery_score, :position, :peer_review_count
|
||||
:max_score, :mastery_score, :position, :peer_review_count, :freeze_on_copy
|
||||
].each do |prop|
|
||||
item.send("#{prop}=", hash[prop]) unless hash[prop].nil?
|
||||
end
|
||||
|
@ -1687,4 +1689,38 @@ class Assignment < ActiveRecord::Base
|
|||
end
|
||||
protected :infer_comment_context_from_filename
|
||||
|
||||
FREEZABLE_ATTRIBUTES = %w{title description lock_at points_possible grading_type
|
||||
submission_types assignment_group_id allowed_extensions
|
||||
group_category_id notify_of_update peer_reviews workflow_state}
|
||||
def frozen?
|
||||
!!(self.freeze_on_copy && self.copied && PluginSetting.settings_for_plugin(:assignment_freezer))
|
||||
end
|
||||
|
||||
def frozen_for_user?(user)
|
||||
frozen? && !self.context.grants_right?(user, :manage_frozen_assignments)
|
||||
end
|
||||
|
||||
def att_frozen?(att, user=nil)
|
||||
return false unless frozen?
|
||||
if settings = PluginSetting.settings_for_plugin(:assignment_freezer)
|
||||
if Canvas::Plugin.value_to_boolean(settings[att.to_s])
|
||||
if user
|
||||
return !self.context.grants_right?(user, :manage_frozen_assignments)
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
def frozen_atts_not_altered
|
||||
FREEZABLE_ATTRIBUTES.each do |att|
|
||||
if self.changes[att] && att_frozen?(att, @updating_user)
|
||||
self.errors.add(att, t('errors.cannot_save_att', "You don't have permission to edit the locked attribute %{att_name}", :att_name => att))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -602,6 +602,11 @@ class RoleOverride < ActiveRecord::Base
|
|||
:label => lambda { t('permissions.manage_sections', "Manage (create / edit / delete) course sections") },
|
||||
:true_for => %w(AccountAdmin TeacherEnrollment DesignerEnrollment),
|
||||
:available_to => %w(AccountAdmin AccountMembership TeacherEnrollment TaEnrollment DesignerEnrollment),
|
||||
},
|
||||
:manage_frozen_assignments => {
|
||||
:label => lambda { t('permissions.manage_frozen_assignment', "Manage (edit / delete) frozen assignments") },
|
||||
:true_for => %w(AccountAdmin),
|
||||
:available_to => %w(AccountAdmin AccountMembership),
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
<% fields_for :settings, OpenObject.new(settings) do |f| %>
|
||||
<table style="width: 500px;" class="formtable">
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<p><%= mt(:description, <<-TEXT)
|
||||
This plugin allows admins to check a property on assignments that makes the configured
|
||||
properties un-editable once the assignment is copied into another course.
|
||||
TEXT
|
||||
%></p>
|
||||
</td>
|
||||
</tr>
|
||||
<% Assignment::FREEZABLE_ATTRIBUTES.sort.each do |att| %>
|
||||
<tr>
|
||||
<td colspan=2><%= f.check_box att, {}, 'yes', 'no' %>
|
||||
<%= f.label att, att %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</table>
|
||||
<% end %>
|
|
@ -8,10 +8,12 @@
|
|||
<% cache(['assignment_render', assignment || context.asset_string, Time.zone.utc_offset].cache_key, :expires_in => (tomorrow_at_midnight - Time.zone.now).to_i) do %>
|
||||
<% assignment_url = context_url(context, :context_assignment_url, assignment_id) %>
|
||||
<% submission_url = context_url(context, :context_assignment_submission_url, "{{ assignment_id }}", "{{ user_id }}") %>
|
||||
<div class=" group_assignment <%= 'points_uneditable' if assignment && assignment.points_uneditable? %> assignment_<%= assignment ? assignment.id : "blank" %> <%= "muted" if assignment && assignment.muted? %>" id="assignment_<%= assignment ? assignment.id : "blank" %>" style="<%= hidden unless assignment %>">
|
||||
<div class=" group_assignment <%= 'frozen' if assignment && assignment.att_frozen?(:assignment_group_id, @current_user) %> <%= 'points_uneditable' if assignment && assignment.points_uneditable? %> assignment_<%= assignment ? assignment.id : "blank" %> <%= "muted" if assignment && assignment.muted? %>" id="assignment_<%= assignment ? assignment.id : "blank" %>" style="<%= hidden unless assignment %>">
|
||||
<div class="content">
|
||||
<div class="move data">
|
||||
<%= image_tag 'move.png', :alt => t('alts.move', "Move"), :class => "move_icon", :title => t('titles.sort_or_move', "Sort Assignments or Move to Another Group"), :style => "margin-top: 3px;" %>
|
||||
<% if assignment && !assignment.att_frozen?(:assignment_group_id, @current_user) %>
|
||||
<%= image_tag 'move.png', :alt => t('alts.move', "Move"), :class => "move_icon", :title => t('titles.sort_or_move', "Sort Assignments or Move to Another Group"), :style => "margin-top: 3px;" %>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="data assignment_title ellipsis">
|
||||
<a href="<%= assignment_url %>" class="title" title="<%= assignment.try_rescue(:title) %>"><%= assignment.try_rescue(:title) %> <%= image_tag("sound_mute.png", :title => t(:student_mute_notification, "Instructor is working on grades")) if assignment && assignment.muted? %></a>
|
||||
|
@ -47,12 +49,14 @@
|
|||
<a href="<%= assignment_url %>" class="preview_assignment_link no-hover" style="<%= hidden unless false %>">
|
||||
<%= image_tag 'preview.png', :alt => "Preview", :title => t('links.preview_assignment', "Preview Assignment"), :style => "display: none;" %>
|
||||
</a>
|
||||
<a href="<%= assignment_url %>/edit" rel="nofollow" class="edit_assignment_link no-hover">
|
||||
<%= image_tag 'edit.png', :alt => "Edit", :title => t('links.edit_assignment', "Edit Assignment") %>
|
||||
</a>
|
||||
<a href="<%= assignment_url %>" rel="nofollow" class="delete_assignment_link no-hover">
|
||||
<%= image_tag 'delete.png', :alt => "Delete", :title => t('links.delete_assignment', "Delete Assignment") %>
|
||||
</a>
|
||||
<% if assignment && !assignment.frozen_for_user?(@current_user) %>
|
||||
<a href="<%= assignment_url %>/edit" rel="nofollow" class="edit_assignment_link no-hover">
|
||||
<%= image_tag 'edit.png', :alt => "Edit", :title => t('links.edit_assignment', "Edit Assignment") %>
|
||||
</a>
|
||||
<a href="<%= assignment_url %>" rel="nofollow" class="delete_assignment_link no-hover">
|
||||
<%= image_tag 'delete.png', :alt => "Delete", :title => t('links.delete_assignment', "Delete Assignment") %>
|
||||
</a>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
end
|
||||
is_discussion_topic = !!(assignment && assignment.submission_types == "discussion_topic" && assignment.discussion_topic)
|
||||
is_external_tool = !!(assignment.try(:submission_types) == "external_tool")
|
||||
js_env :HIDE_DESCRIPTION => assignment.att_frozen?(:description, @current_user)
|
||||
%>
|
||||
<% js_block do %>
|
||||
<script type="text/javascript">
|
||||
|
@ -65,8 +66,9 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
|
||||
<div class="description user_content"><%= assignment && assignment.description && !assignment.description.empty? ? user_content(assignment.description) : t('defaults.no_content', "No Content") %></div>
|
||||
|
||||
<% description = assignment && assignment.description && !assignment.description.empty? ? user_content(assignment.description) : t('defaults.no_content', "No Content") %>
|
||||
<div class="description user_content"><%= description %></div>
|
||||
<div class="course_id" style="display: none;"><%= @context.id if @context && @context.is_a?(Course) %></div>
|
||||
|
||||
<div style="display: none;">
|
||||
|
@ -83,7 +85,9 @@
|
|||
<table class="formtable full_assignment_table" style="width: 100%;">
|
||||
<tr>
|
||||
<td colspan="4">
|
||||
<a href="#" style="font-size: 0.8em; float: right;" class="switch_full_assignment_view"><%= t 'links.switch_views', "Switch Views" %></a>
|
||||
<% unless assignment.att_frozen?(:description, @current_user) %>
|
||||
<a href="#" style="font-size: 0.8em; float: right;" class="switch_full_assignment_view"><%= t 'links.switch_views', "Switch Views" %></a>
|
||||
<% end %>
|
||||
<div class="assignment_content not_graded_content">
|
||||
<%= before_label :assignment_description, "Assignment Description" %>
|
||||
</div>
|
||||
|
@ -94,14 +98,40 @@
|
|||
<%= before_label :external_tool_introduction, "External Tool Instructions/Introduction" %>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
<div><%= f.text_area :description, :style => 'width: 100%;' %></div>
|
||||
<% if assignment.att_frozen?(:description, @current_user) %>
|
||||
<div class="description user_content"><%= description %></div>
|
||||
<% else %>
|
||||
<div><%= f.text_area :description, :style => 'width: 100%;' %></div>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
<% if can_do(assignment, @current_user, :update) %>
|
||||
<% if assignment.frozen_for_user?(@current_user)%>
|
||||
<tr>
|
||||
<td colspan="4">
|
||||
<%= t('description.assignment_is_frozen', 'Some settings have been administratively locked.') %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
<tr>
|
||||
<td style="width: 10%;">
|
||||
<div style="margin-bottom: 10px; <%= hidden if is_discussion_topic %>"><%= before_label :type, "Type" %></div>
|
||||
</td><td style="width: 40%;">
|
||||
<% if assignment.att_frozen?(:submission_types, @current_user) %>
|
||||
<% if assignment.submission_types == 'discussion_topic' %>
|
||||
<%= t 'options.discussion', "Discussion" %>
|
||||
<% elsif assignment.submission_types == 'online_quiz' %>
|
||||
<%= t 'options.quiz', "Quiz" %>
|
||||
<% elsif assignment.submission_types == 'external_tool' %>
|
||||
<%= t 'options.external_tool', "External Tool" %>
|
||||
<% elsif assignment.submission_types == 'attendance' %>
|
||||
<%= t 'options.attendance', "Attendance" %>
|
||||
<% elsif assignment.submission_types == 'not_graded' %>
|
||||
<%= t 'options.not_graded', "Not Graded" %>
|
||||
<% else %>
|
||||
<%= t 'options.assignment', "Assignment" %>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<select class="assignment_type" name="assignment_type" style="<%= hidden if is_discussion_topic %>">
|
||||
<option value="assignment" <%= "selected" if (assignment.submission_types != 'online_quiz' && assignment.submission_types != 'attendance' && assignment.submission_types != 'not_graded' rescue true) %>><%= t 'options.assignment', "Assignment" %></option>
|
||||
<option value="discussion_topic" <%= "selected" if (assignment.submission_types == 'discussion_topic' rescue false) %>><%= t 'options.discussion', "Discussion" %></option>
|
||||
|
@ -112,53 +142,75 @@
|
|||
<% end %>
|
||||
<option value="not_graded" <%= "selected" if (assignment.submission_types == 'not_graded' rescue false) %>><%= t 'options.not_graded', "Not Graded" %></option>
|
||||
</select>
|
||||
<% end %>
|
||||
</td>
|
||||
<td colspan="2" style="width: 50%;">
|
||||
<a href="#" class="more_options_link"><%= t 'links.more_options', "more options" %></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="not_graded_content">
|
||||
<td colspan="4" style="font-size: 0.8em; padding-left: 10px; padding-top: 0; font-style: italic;"><%= t 'descriptions.not_graded', "This assignment will not appear in the gradebook" %></td>
|
||||
</tr>
|
||||
<tr class="discussion_topic_content">
|
||||
<td colspan="4" style="font-size: 0.8em; padding-left: 10px; padding-top: 0; font-style: italic;"><%= t 'descriptions.grades_from_discussion', "Grading for this assignment will be based on posts in the discussion" %></td>
|
||||
</tr>
|
||||
<tr class="quiz_content">
|
||||
<td colspan="4" style="font-size: 0.8em; padding-left: 10px; padding-top: 0; font-style: italic;"><%= t 'descriptions.grades_from_quiz', "Grades for this assignment will be pulled from the quiz results" %></td>
|
||||
</tr>
|
||||
<tr class="external_tool_content">
|
||||
<td colspan="4" style="font-size: 0.8em; padding-left: 10px; padding-top: 0; font-style: italic;"><%= t 'descriptions.grades_for_external_tool', "Grades for this assignment can be sent from the external tool, or entered manually." %></td>
|
||||
</tr>
|
||||
<tr class="external_tool_content">
|
||||
<td style="width: 10%;"><%= f.blabel :external_tool_url, :en => "URL" %></td>
|
||||
<% f.object.external_tool_tag || f.object.build_external_tool_tag
|
||||
f.fields_for :external_tool_tag do |tag_form| %>
|
||||
<td colspan="3" id="external_tool_url">
|
||||
<%= link_to(image_tag('edit.png', :alt => 'Edit URL'), '#', :id => 'edit_external_tool_url') %>
|
||||
<%= tag_form.text_field(:url, :style => "width:95%", :readonly => true) %>
|
||||
<%= tag_form.hidden_field(:new_tab) %>
|
||||
</td>
|
||||
<% end %>
|
||||
</tr>
|
||||
<% unless assignment.att_frozen?(:submission_types, @current_user) %>
|
||||
<tr class="not_graded_content">
|
||||
<td colspan="4" style="font-size: 0.8em; padding-left: 10px; padding-top: 0; font-style: italic;"><%= t 'descriptions.not_graded', "This assignment will not appear in the gradebook" %></td>
|
||||
</tr>
|
||||
<tr class="discussion_topic_content">
|
||||
<td colspan="4" style="font-size: 0.8em; padding-left: 10px; padding-top: 0; font-style: italic;"><%= t 'descriptions.grades_from_discussion', "Grading for this assignment will be based on posts in the discussion" %></td>
|
||||
</tr>
|
||||
<tr class="quiz_content">
|
||||
<td colspan="4" style="font-size: 0.8em; padding-left: 10px; padding-top: 0; font-style: italic;"><%= t 'descriptions.grades_from_quiz', "Grades for this assignment will be pulled from the quiz results" %></td>
|
||||
</tr>
|
||||
<tr class="external_tool_content">
|
||||
<td colspan="4" style="font-size: 0.8em; padding-left: 10px; padding-top: 0; font-style: italic;"><%= t 'descriptions.grades_for_external_tool', "Grades for this assignment can be sent from the external tool, or entered manually." %></td>
|
||||
</tr>
|
||||
<tr class="external_tool_content">
|
||||
<td style="width: 10%;"><%= f.blabel :external_tool_url, :en => "URL" %></td>
|
||||
<% f.object.external_tool_tag || f.object.build_external_tool_tag
|
||||
f.fields_for :external_tool_tag do |tag_form| %>
|
||||
<td colspan="3" id="external_tool_url">
|
||||
<%= link_to(image_tag('edit.png', :alt => 'Edit URL'), '#', :id => 'edit_external_tool_url') %>
|
||||
<%= tag_form.text_field(:url, :style => "width:95%", :readonly => true) %>
|
||||
<%= tag_form.hidden_field(:new_tab) %>
|
||||
</td>
|
||||
<% end %>
|
||||
</tr>
|
||||
<% end %>
|
||||
<tr>
|
||||
<td style="width: 10%;"><%= f.blabel :title, :en => "Title" %></td>
|
||||
<td style="width: 40%;"><%= f.text_field :title, :style => "width: 140px;" %></td>
|
||||
<td style="width: 40%;">
|
||||
<% if assignment.att_frozen?(:title, @current_user) %>
|
||||
<%= assignment.title %>
|
||||
<%= f.hidden_field :title, :class => "title" %>
|
||||
<% else %>
|
||||
<%= f.text_field :title, :style => "width: 140px;" %>
|
||||
<% end %>
|
||||
</td>
|
||||
<td style="width: 25%;" class="more_assignment_values"><%= f.blabel :assignment_group_id, :en => "Assignment Group" %></td>
|
||||
<td style="width: 25%;" class="more_assignment_values">
|
||||
<%= f.select :assignment_group_id, (@assignment_groups.map {|g| [g.name, g.id]} << ["[ New Group ]", "new"]), :class => "#{(@context.weight_assignment_groups rescue false) ? 'weight' : ''}" %>
|
||||
<% if assignment.att_frozen?(:assignment_group_id, @current_user) %>
|
||||
<%= assignment.assignment_group.name %>
|
||||
<% else %>
|
||||
<%= f.select :assignment_group_id, (@assignment_groups.map {|g| [g.name, g.id]} << ["[ New Group ]", "new"]), :class => "#{(@context.weight_assignment_groups rescue false) ? 'weight' : ''}" %>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr><tr class="assignment_content">
|
||||
<td style="width: 10%;"><%= f.blabel :points_possible, :en => "Points", :style => "width: 50px;" %></td>
|
||||
<td style="width: 40%;">
|
||||
<!-- javascript check on change, if score is 0 then show text, "No score. Any points set in the gradebook will be extra credit for this assignment's type" -->
|
||||
<%= f.text_field :points_possible, :style => "width: 140px;", :class => "points_possible" %>
|
||||
<% if assignment.att_frozen?(:points_possible, @current_user) %>
|
||||
<%= assignment.points_possible %>
|
||||
<% else %>
|
||||
<!-- javascript check on change, if score is 0 then show text, "No score. Any points set in the gradebook will be extra credit for this assignment's type" -->
|
||||
<%= f.text_field :points_possible, :style => "width: 140px;", :class => "points_possible" %>
|
||||
<% end %>
|
||||
</td>
|
||||
<td style="width: 25%;" class="nobr more_assignment_values">
|
||||
<%= f.blabel :grading_type, :en => "Grading By" %>
|
||||
<!--span style="padding-left: 15px; font-size: 0.8em;">Graded As</span-->
|
||||
</td><td style="width: 25%;" class="more_assignment_values">
|
||||
<%= f.hidden_field :grading_standard_id, :class => "grading_standard_id" %>
|
||||
<%= f.select :grading_type, {t('options.points', "Points") => "points", t('options.letter_grade', "Letter Grade") => "letter_grade", t('options.percentage', "Percentage") => "percent", t('options.pass_fail', "Complete/Incomplete") => "pass_fail" }, {:selected => (assignment.grading_type || "points")}, {:class => "grading_type"} %>
|
||||
<% if assignment.att_frozen?(:grading_type, @current_user) %>
|
||||
<%= assignment.grading_type %>
|
||||
<% else %>
|
||||
<%= f.select :grading_type, {t('options.points', "Points") => "points", t('options.letter_grade', "Letter Grade") => "letter_grade", t('options.percentage', "Percentage") => "percent", t('options.pass_fail', "Complete/Incomplete") => "pass_fail" }, {:selected => (assignment.grading_type || "points")}, {:class => "grading_type"} %>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr><tr class="edit_letter_grades_link assignment_content" style="<%= "display: none;" if assignment.grading_type != "letter_grade" %>">
|
||||
<td style="width: 10%;" class="more_assignment_values"></td>
|
||||
|
@ -198,114 +250,183 @@
|
|||
<!-- Accept multiple submissions? Take the best, first, last, etc? Does that even make sense? -->
|
||||
<td style="width: 10%; vertical-align: top;"><%= before_label :due, "Due" %></td>
|
||||
<td style="width: 40%; vertical-align: top;">
|
||||
<%= f.text_field :due_at, :value => datetime_string(assignment.due_at, :due_date), :style => "width: 120px;", :class => "date_field" %>
|
||||
<% if assignment.att_frozen?(:due_at, @current_user) %>
|
||||
<span class="due_date"><%= date_string(assignment.due_at) rescue "" %></span>
|
||||
<span class="due_time"><%= time_string(assignment.due_at) rescue "" %></span>
|
||||
<% else %>
|
||||
<%= f.text_field :due_at, :value => datetime_string(assignment.due_at, :due_date), :style => "width: 120px;", :class => "date_field" %>
|
||||
<% end %>
|
||||
</td>
|
||||
<td style="width: 50%;" class="more_assignment_values assignment_content" colspan="2">
|
||||
<input type="checkbox" name="group_assignment" id="assignment_group_assignment" <%= 'checked' if @assignment && @assignment.has_group_category? %>/>
|
||||
<label for="assignment_group_assignment"><%= t 'labels.group_assignment', "This is a Group Assignment" %></label>
|
||||
<div id="assignment_group_category" style="<%= hidden unless @assignment && @assignment.has_group_category? %>; margin-left: 20px;">
|
||||
<label for="assignment_group_category_select"><%= before_label :group_set, "Group Set" %></label>
|
||||
<select id="assignment_group_category_select" name="assignment[group_category_id]">
|
||||
<option value=""><%= t 'options.select_group_set', "[ Select Group Set ]" %></option>
|
||||
<% @context.group_categories.select{ |c| !c.student_organized? }.each do |category| %>
|
||||
<option value="<%= category.id %>" <%= 'selected' if @assignment && category == @assignment.group_category %>><%= category.name %></option>
|
||||
<% end %>
|
||||
<option value="new"><%= t 'options.new_group_set', "[ New Group Set ]" %></option>
|
||||
</select><br/>
|
||||
<%= f.check_box :grade_group_students_individually %>
|
||||
<label for="grade_group_students_individually">
|
||||
<%= t 'labels.assign_grades_individually', "Assign grades to each student individually" %><br />
|
||||
<span style='font-size: 0.8em;'><%= t 'labels.assign_grades_individually_subtext', "(normally all students receive the same grade on group assignments)" %></span>
|
||||
</label>
|
||||
</div>
|
||||
<% if assignment.att_frozen?(:group_category_id, @current_user) %>
|
||||
<% if assignment.has_group_category? %>
|
||||
<%= t('descriptions.assignment_group', 'The group for this assignment is: %{group_name}', :group_name => assignment.group_category.name) %>
|
||||
<% else %>
|
||||
<%= t('descriptions.no_group', 'There is no group for this assignment.') %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<div style="<%= hidden if assignment.att_frozen?(:group_category_id, @current_user) %>">
|
||||
<input type="checkbox" name="group_assignment" id="assignment_group_assignment" <%= 'checked' if @assignment && @assignment.has_group_category? %>/>
|
||||
<label for="assignment_group_assignment"><%= t 'labels.group_assignment', "This is a Group Assignment" %></label>
|
||||
<div id="assignment_group_category" style="<%= hidden unless @assignment && @assignment.has_group_category? %>; margin-left: 20px;">
|
||||
<label for="assignment_group_category_select"><%= before_label :group_set, "Group Set" %></label>
|
||||
<select id="assignment_group_category_select" name="assignment[group_category_id]">
|
||||
<option value=""><%= t 'options.select_group_set', "[ Select Group Set ]" %></option>
|
||||
<% @context.group_categories.select{ |c| !c.student_organized? }.each do |category| %>
|
||||
<option value="<%= category.id %>" <%= 'selected' if @assignment && category == @assignment.group_category %>><%= category.name %></option>
|
||||
<% end %>
|
||||
<option value="new"><%= t 'options.new_group_set', "[ New Group Set ]" %></option>
|
||||
</select><br/>
|
||||
<%= f.check_box :grade_group_students_individually %>
|
||||
<label for="grade_group_students_individually">
|
||||
<%= t 'labels.assign_grades_individually', "Assign grades to each student individually" %><br />
|
||||
<span style='font-size: 0.8em;'><%= t 'labels.assign_grades_individually_subtext', "(normally all students receive the same grade on group assignments)" %></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td style="width: 10%; vertical-align: top;" class="more_assignment_values"><%= before_label :locked_until, "Locked Until" %></td>
|
||||
<td style="width: 40%; vertical-align: top;" class="more_assignment_values">
|
||||
<%= f.text_field :unlock_at, :value => datetime_string(assignment.unlock_at), :style => "width: 120px;", :class => "date_field" %>
|
||||
<% if assignment.att_frozen?(:unlock_at, @current_user) %>
|
||||
<span class="unlock_date"><%= date_string(assignment.unlock_at) rescue "" %></span>
|
||||
<span class="unlock_time"><%= time_string(assignment.unlock_at) rescue "" %></span>
|
||||
<% else %>
|
||||
<%= f.text_field :unlock_at, :value => datetime_string(assignment.unlock_at), :style => "width: 120px;", :class => "date_field" %>
|
||||
<% end %>
|
||||
</td>
|
||||
<td style="width: 50%;" class="more_assignment_values assignment_content" colspan="2">
|
||||
<%= f.check_box :peer_reviews, :id => 'assignment_peer_reviews' %>
|
||||
<label for="assignment_peer_reviews"><%= t 'labels.require_peer_reviews', "Require Peer Reviews" %></label>
|
||||
<table id="assignment_peer_reviews_options" style="margin-left: 10px; <%= hidden unless @assignment && @assignment.has_peer_reviews? %>">
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div>
|
||||
<input type="radio" name="assignment[automatic_peer_reviews]" value="0" id="manual_peer_reviews" <%= 'checked' if !@assignment || !@assignment.automatic_peer_reviews %> />
|
||||
<label for="manual_peer_reviews"><%= t 'labels.manually_assign_peer_reviews', "Manually Assign Peer Reviews" %></label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="radio" name="assignment[automatic_peer_reviews]" value="1" id="auto_peer_reviews" <%= 'checked' if @assignment && @assignment.automatic_peer_reviews %> />
|
||||
<label for="auto_peer_reviews"><%= t 'labels.automatically_assign_peer_reviews', "Automatically Assign Peer Reviews" %></label>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="auto_peer_reviews">
|
||||
<td><%= f.blabel :peer_review_count, :en => "Reviews Per User" %></td>
|
||||
<td><%= f.text_field :peer_review_count, :style => "width: 20px;" %></td>
|
||||
</tr><tr class="auto_peer_reviews">
|
||||
<td style="vertical-align: top;"><%= f.blabel :peer_reviews_assign_at, :en => "Assign Reviews", :style => "float: left;" %></td>
|
||||
<td>
|
||||
<div>
|
||||
<%= f.text_field :peer_reviews_assign_at, :value => datetime_string(assignment.peer_reviews_assign_at, :due_date), :style => "width: 120px;", :class => "date_field" %>
|
||||
</div>
|
||||
<div style="font-size: 0.8em;"><%= t 'descriptions.assign_reviews_after_due_date', "Must come after due date. If blank, uses due date." %></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<% if assignment.att_frozen?(:peer_reviews, @current_user) %>
|
||||
<% if assignment.peer_reviews %>
|
||||
<%= t('descriptions.peer_reviews_required', 'This assignment requires peer reviews.') %>
|
||||
<% else %>
|
||||
<%= t('descriptions.peer_reviews_not_required', 'This assignment does not require peer reviews.') %>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<%= f.check_box :peer_reviews, :id => 'assignment_peer_reviews' %>
|
||||
<label for="assignment_peer_reviews"><%= t 'labels.require_peer_reviews', "Require Peer Reviews" %></label>
|
||||
<table id="assignment_peer_reviews_options" style="margin-left: 10px; <%= hidden unless @assignment && @assignment.has_peer_reviews? %>">
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div>
|
||||
<input type="radio" name="assignment[automatic_peer_reviews]" value="0" id="manual_peer_reviews" <%= 'checked' if !@assignment || !@assignment.automatic_peer_reviews %> />
|
||||
<label for="manual_peer_reviews"><%= t 'labels.manually_assign_peer_reviews', "Manually Assign Peer Reviews" %></label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="radio" name="assignment[automatic_peer_reviews]" value="1" id="auto_peer_reviews" <%= 'checked' if @assignment && @assignment.automatic_peer_reviews %> />
|
||||
<label for="auto_peer_reviews"><%= t 'labels.automatically_assign_peer_reviews', "Automatically Assign Peer Reviews" %></label>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="auto_peer_reviews">
|
||||
<td><%= f.blabel :peer_review_count, :en => "Reviews Per User" %></td>
|
||||
<td><%= f.text_field :peer_review_count, :style => "width: 20px;" %></td>
|
||||
</tr><tr class="auto_peer_reviews">
|
||||
<td style="vertical-align: top;"><%= f.blabel :peer_reviews_assign_at, :en => "Assign Reviews", :style => "float: left;" %></td>
|
||||
<td>
|
||||
<div>
|
||||
<%= f.text_field :peer_reviews_assign_at, :value => datetime_string(assignment.peer_reviews_assign_at, :due_date), :style => "width: 120px;", :class => "date_field" %>
|
||||
</div>
|
||||
<div style="font-size: 0.8em;"><%= t 'descriptions.assign_reviews_after_due_date', "Must come after due date. If blank, uses due date." %></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<% end %>
|
||||
|
||||
|
||||
</td>
|
||||
</tr><tr class="submission_content">
|
||||
<td style="width: 10%; vertical-align: top;"><%= before_label :submission, "Submission" %></td>
|
||||
<td style="vertical-align: top;" colspan="1">
|
||||
<select name="submission_type" class="submission_type_option">
|
||||
<option value="none" <%= "selected" if (!assignment.submission_types || assignment.submission_types == "none") %>><%= t 'options.no_submission', "No Submission" %></option>
|
||||
<option value="online" <%= "selected" if (assignment.submission_types && assignment.submission_types.match(/media|online_text_entry|online_upload|online_url/)) %>><%= t 'options.online_submission', "Online Submission" %></option>
|
||||
<option value="on_paper" <%= "selected" if (assignment.submission_types == "on_paper") %>><%= t 'options.on_paper', "On Paper" %></option>
|
||||
</select>
|
||||
<% if assignment.att_frozen?(:submission_types, @current_user) %>
|
||||
<% if (!assignment.submission_types || assignment.submission_types == "none") %>
|
||||
<%= t 'options.no_submission', "No Submission" %>
|
||||
<% elsif assignment.submission_types == "on_paper"%>
|
||||
<%= t 'options.on_paper', "On Paper" %>
|
||||
<% else %>
|
||||
<%= t 'options.online_submission', "Online Submission" %>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<select name="submission_type" class="submission_type_option">
|
||||
<option value="none" <%= "selected" if (!assignment.submission_types || assignment.submission_types == "none") %>><%= t 'options.no_submission', "No Submission" %></option>
|
||||
<option value="online" <%= "selected" if (assignment.submission_types && assignment.submission_types.match(/media|online_text_entry|online_upload|online_url/)) %>><%= t 'options.online_submission', "Online Submission" %></option>
|
||||
<option value="on_paper" <%= "selected" if (assignment.submission_types == "on_paper") %>><%= t 'options.on_paper', "On Paper" %></option>
|
||||
</select>
|
||||
<% end %>
|
||||
</td>
|
||||
<td style="width: 10%; vertical-align: top;" class="more_assignment_values"><%= before_label :lock_submits_after, "Lock Submits After" %></td>
|
||||
<td style="width: 40%; vertical-align: top;" class="more_assignment_values">
|
||||
<%= f.text_field :lock_at, :value => datetime_string(assignment.lock_at), :style => "width: 120px;", :class => "date_field" %>
|
||||
<% if assignment.att_frozen?(:lock_at, @current_user) %>
|
||||
<span class="set_lock_date"><%= date_string(assignment.lock_at) rescue "" %></span>
|
||||
<span class="set_lock_time"><%= time_string(assignment.lock_at) rescue "" %></span>
|
||||
<% else %>
|
||||
<%= f.text_field :lock_at, :value => datetime_string(assignment.lock_at), :style => "width: 120px;", :class => "date_field" %>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr><tr class="submission_content">
|
||||
<td colspan="2">
|
||||
<div style="padding: 0px 30px;<%= "display: none;" if (!assignment.submission_types || !assignment.submission_types.match("online")) %>" class="online_submission_types">
|
||||
<div><input type="checkbox" name="online_upload" <%= "checked" if (assignment.submission_types && assignment.submission_types.match("online_upload")) %> id="assignment_online_upload"/><label for="assignment_online_upload"> <%= t 'labels.allow_file_uploads', "Allow File Uploads" %></label></div>
|
||||
<div><input type="checkbox" name="online_text_entry" <%= "checked" if (assignment.submission_types && assignment.submission_types.match("online_text_entry")) %> id="assignment_online_text_entry"/><label for="assignment_online_text_entry"> <%= t 'labels.allow_text_entry', "Allow Text Entry" %></label></div>
|
||||
<div><input type="checkbox" name="online_url" <%= "checked" if (assignment.submission_types && assignment.submission_types.match("online_url")) %> id="assignment_online_url"/><label for="assignment_online_url"> <%= t 'labels.allow_url', "Allow Website URL" %></label></div>
|
||||
<% if feature_enabled?(:kaltura) %>
|
||||
<div><input type="checkbox" name="media_recording" <%= "checked" if (assignment.submission_types && assignment.submission_types.match("media_recording")) %> id="assignment_media_recording"/><label for="assignment_media_recording"> <%= t 'labels.allow_media_recordings', "Allow Media Recordings" %></label></div>
|
||||
<% end %>
|
||||
<% if @context.turnitin_enabled? %>
|
||||
<div style="margin-top: 10px;">
|
||||
<%= f.check_box :turnitin_enabled %>
|
||||
<%= f.label :turnitin_enabled, :en => "Enable Turnitin Submission Evaluations" %>
|
||||
<div id="assignment_turnitin_settings" style="<%= hidden unless assignment && assignment.turnitin_enabled %>; margin-left: 20px;">
|
||||
<a href="#" class="show_turnitin_settings" style="font-size: 0.9em"><%= t 'links.advanced_turnitin_settings', 'Advanced Settings...' %></a>
|
||||
<% unless assignment.att_frozen?(:submission_types, @current_user) %>
|
||||
<div style="padding: 0px 30px;<%= "display: none;" if (!assignment.submission_types || !assignment.submission_types.match("online")) %>" class="online_submission_types">
|
||||
<div><input type="checkbox" name="online_upload" <%= "checked" if (assignment.submission_types && assignment.submission_types.match("online_upload")) %> id="assignment_online_upload"/><label for="assignment_online_upload"> <%= t 'labels.allow_file_uploads', "Allow File Uploads" %></label></div>
|
||||
<div><input type="checkbox" name="online_text_entry" <%= "checked" if (assignment.submission_types && assignment.submission_types.match("online_text_entry")) %> id="assignment_online_text_entry"/><label for="assignment_online_text_entry"> <%= t 'labels.allow_text_entry', "Allow Text Entry" %></label></div>
|
||||
<div><input type="checkbox" name="online_url" <%= "checked" if (assignment.submission_types && assignment.submission_types.match("online_url")) %> id="assignment_online_url"/><label for="assignment_online_url"> <%= t 'labels.allow_url', "Allow Website URL" %></label></div>
|
||||
<% if feature_enabled?(:kaltura) %>
|
||||
<div><input type="checkbox" name="media_recording" <%= "checked" if (assignment.submission_types && assignment.submission_types.match("media_recording")) %> id="assignment_media_recording"/><label for="assignment_media_recording"> <%= t 'labels.allow_media_recordings', "Allow Media Recordings" %></label></div>
|
||||
<% end %>
|
||||
<% if @context.turnitin_enabled? %>
|
||||
<div style="margin-top: 10px;">
|
||||
<%= f.check_box :turnitin_enabled %>
|
||||
<%= f.label :turnitin_enabled, :en => "Enable Turnitin Submission Evaluations" %>
|
||||
<div id="assignment_turnitin_settings" style="<%= hidden unless assignment && assignment.turnitin_enabled %>; margin-left: 20px;">
|
||||
<a href="#" class="show_turnitin_settings" style="font-size: 0.9em"><%= t 'links.advanced_turnitin_settings', 'Advanced Settings...' %></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</td>
|
||||
<td style="width: 50%;" class="more_assignment_values assignment_content" colspan="2">
|
||||
<div id='restrict_file_extensions_options'>
|
||||
<input type="checkbox" name="restrict_file_extensions" id="assignment_restrict_file_extensions" <%= 'checked' if @assignment && @assignment.allowed_extensions.present? %>/>
|
||||
<label for="assignment_restrict_file_extensions"><%= t 'labels.restrict_file_extensions', "Restrict the Permitted File Upload Extensions" %></label>
|
||||
<div id='allowed_extensions_options' style="margin-left: 20px;">
|
||||
<label for="assignment_allowed_extensions"><%= before_label :allowed_extensions, "Allowed File Extensions" %></label>
|
||||
<br>
|
||||
<%= f.text_field :allowed_extensions, :value => (assignment.allowed_extensions || []).join(",") %>
|
||||
<br>
|
||||
<span style='font-size: 0.8em;'><%= t 'descriptions.allowed_extensions', "enter a list of accepted extensions, for example: doc,xls,txt" %></span>
|
||||
</div>
|
||||
<% if assignment.att_frozen?(:allowed_extensions, @current_user) %>
|
||||
<% if assignment.allowed_extensions.present? %>
|
||||
<%= t('descriptions.allowed_extensions_list', 'Allowed file extensions: %{extensions}', :extensions => (assignment.allowed_extensions || []).join(",")) %>
|
||||
<% else %>
|
||||
<%= t('descriptions.no_extension_restriction', 'All file extensions are permitted.') %>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<input type="checkbox" name="restrict_file_extensions" id="assignment_restrict_file_extensions" <%= 'checked' if @assignment && @assignment.allowed_extensions.present? %>/>
|
||||
<label for="assignment_restrict_file_extensions"><%= t 'labels.restrict_file_extensions', "Restrict the Permitted File Upload Extensions" %></label>
|
||||
<div id='allowed_extensions_options' style="margin-left: 20px;">
|
||||
<label for="assignment_allowed_extensions"><%= before_label :allowed_extensions, "Allowed File Extensions" %></label>
|
||||
<br>
|
||||
<%= f.text_field :allowed_extensions, :value => (assignment.allowed_extensions || []).join(",") %>
|
||||
<br>
|
||||
<span style='font-size: 0.8em;'><%= t 'descriptions.allowed_extensions', "enter a list of accepted extensions, for example: doc,xls,txt" %></span>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
<tr>
|
||||
<td colspan="4">
|
||||
<%= f.check_box :notify_of_update %>
|
||||
<%= f.label :notify_of_update, :en => "Notify users that this content has changed" %>
|
||||
<td colspan="2">
|
||||
<% if assignment.att_frozen?(:notify_of_update, @current_user) %>
|
||||
<% if assignment.notify_of_update %>
|
||||
<%= t('descriptions.users_are_notified', 'Users will be notified that content has changed') %>
|
||||
<% else %>
|
||||
<%= t('descriptions.users_are_not_notified', 'Users will not be notified that content has changed') %>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<%= f.check_box :notify_of_update %>
|
||||
<%= f.label :notify_of_update, :en => "Notify users that this content has changed" %>
|
||||
<% end %>
|
||||
</td>
|
||||
<td colspan="2" class="more_assignment_values">
|
||||
<% if PluginSetting.settings_for_plugin(:assignment_freezer) && !assignment.frozen_for_user?(@current_user)%>
|
||||
<%= f.check_box :freeze_on_copy %>
|
||||
<%= f.label :freeze_on_copy, :en => "Lock assignment properties when copied" %>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
|
|
@ -89,6 +89,9 @@
|
|||
<span class="assignment_assignment_group_id assignment[assignment_group_id]"><%= topic.try_rescue(:assignment).try_rescue(:assignment_group_id) || nbsp %></span>
|
||||
<span class="assignment_points_possible assignment[points_possible]"><%= topic.try_rescue(:assignment).try_rescue(:points_possible) || nbsp %></span>
|
||||
<span class="assignment_due_at assignment[due_at]"><%= datetime_string(topic.try_rescue(:assignment).try_rescue(:due_at), :long) || nbsp %></span>
|
||||
<% if topic.try_rescue(:assignment) && topic.assignment.frozen_for_user?(@current_user)%>
|
||||
<span class="assignment_is_frozen assignment[is_frozen]">true</span>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
class AddAssignmentLock < ActiveRecord::Migration
|
||||
tag :predeploy
|
||||
|
||||
def self.up
|
||||
add_column :assignments, :freeze_on_copy, :boolean
|
||||
add_column :assignments, :copied, :boolean
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column :assignments, :freeze_on_copy
|
||||
remove_column :assignments, :copied
|
||||
end
|
||||
end
|
|
@ -190,3 +190,13 @@ if Attachment.s3_storage?
|
|||
:settings => Attachment.s3_config
|
||||
})
|
||||
end
|
||||
Canvas::Plugin.register('assignment_freezer', nil, {
|
||||
:name => lambda{ t :name, 'Assignment Property Freezer' },
|
||||
:description => lambda{ t :description, 'Freeze Assignment Properties on Copy' },
|
||||
:website => 'http://www.instructure.com',
|
||||
:author => 'Instructure',
|
||||
:author_website => 'http://www.instructure.com',
|
||||
:version => '1.0.0',
|
||||
:settings_partial => 'plugins/assignment_freezer_settings',
|
||||
:settings => nil
|
||||
})
|
||||
|
|
|
@ -86,7 +86,7 @@ module CC
|
|||
atts = [:points_possible, :min_score, :max_score, :mastery_score, :grading_type,
|
||||
:all_day, :submission_types, :position, :turnitin_enabled, :peer_review_count,
|
||||
:peer_reviews_assigned, :peer_reviews, :automatic_peer_reviews,
|
||||
:anonymous_peer_reviews, :grade_group_students_individually]
|
||||
:anonymous_peer_reviews, :grade_group_students_individually, :freeze_on_copy]
|
||||
atts.each do |att|
|
||||
node.tag!(att, assignment.send(att)) if assignment.send(att) == false || !assignment.send(att).blank?
|
||||
end
|
||||
|
|
|
@ -56,7 +56,7 @@ module CC::Importer::Canvas
|
|||
assignment[string_type] = val unless val.nil?
|
||||
end
|
||||
["all_day", "turnitin_enabled", "peer_reviews_assigned", "peer_reviews",
|
||||
"automatic_peer_reviews", "anonymous_peer_reviews",
|
||||
"automatic_peer_reviews", "anonymous_peer_reviews", "freeze_on_copy",
|
||||
"grade_group_students_individually", "external_tool_new_tab"].each do |bool_val|
|
||||
val = get_bool_val(meta_doc, bool_val)
|
||||
assignment[bool_val] = val unless val.nil?
|
||||
|
|
|
@ -96,6 +96,7 @@
|
|||
<xs:element name="grade_group_students_individually" type="xs:boolean" minOccurs="0"/>
|
||||
<xs:element name="external_tool_url" type="xs:string" minOccurs="0"/>
|
||||
<xs:element name="external_tool_new_tab" type="xs:boolean" minOccurs="0"/>
|
||||
<xs:element name="freeze_on_copy" type="xs:boolean" minOccurs="0"/>
|
||||
</xs:all>
|
||||
<xs:attribute name="identifier" type="xs:ID" use="required"/>
|
||||
</xs:complexType>
|
||||
|
|
|
@ -313,7 +313,7 @@ define([
|
|||
}
|
||||
};
|
||||
var assignment_sortable_options = {
|
||||
items: '.group_assignment',
|
||||
items: '.group_assignment:not(.frozen)',
|
||||
connectWith: '.assignment_group .assignment_list',
|
||||
handle: '.move_icon, .move',
|
||||
axis: 'y',
|
||||
|
|
|
@ -666,8 +666,8 @@ define([
|
|||
$box.find(".title").attr('href', $event.find('.title').attr('href'));
|
||||
$box.find(".view_event_link").attr('href', $event.find('.title').attr('href'));
|
||||
$box.find(".delete_event_link").attr('href', $event.find('.delete_' + data.event_type + '_link').attr('href'));
|
||||
$box.find(".edit_event").showIf(data.can_edit);
|
||||
$box.find(".delete_event").showIf(data.can_delete);
|
||||
$box.find(".edit_event").showIf(data.can_edit && !data.frozen);
|
||||
$box.find(".delete_event").showIf(data.can_delete && !data.frozen);
|
||||
var isNew = $event.attr('id') == "event_blank";
|
||||
var type_name = "Event";
|
||||
var $form = null;
|
||||
|
|
|
@ -78,17 +78,22 @@ define([
|
|||
$form.find("select[name='points_type']").change();
|
||||
$form.fillFormData(data, {object_name: 'assignment'});
|
||||
$assignment.find(".description, .edit_full_assignment_link").hide();
|
||||
$form.show().find("textarea:first").editorBox();
|
||||
if (wikiSidebar) {
|
||||
wikiSidebar.attachToEditor($form.find("textarea:first"));
|
||||
wikiSidebar.show();
|
||||
$("#sidebar_content").hide();
|
||||
$form.show();
|
||||
if (!ENV.HIDE_DESCRIPTION){
|
||||
$form.find("textarea:first").editorBox();
|
||||
if (wikiSidebar) {
|
||||
wikiSidebar.attachToEditor($form.find("textarea:first"));
|
||||
wikiSidebar.show();
|
||||
$("#sidebar_content").hide();
|
||||
}
|
||||
}
|
||||
$form.find(".more_options_link").show();
|
||||
$form.find(".more_assignment_values").hide();
|
||||
setTimeout(function(){
|
||||
doFocus($form.find("textarea:first").attr('id'));
|
||||
}, 500);
|
||||
if (!ENV.HIDE_DESCRIPTION){
|
||||
setTimeout(function(){
|
||||
doFocus($form.find("textarea:first").attr('id'));
|
||||
}, 500);
|
||||
}
|
||||
if (!$form.parents(".ui-dialog").length ) {
|
||||
$("html,body").scrollTo($form);
|
||||
}
|
||||
|
|
|
@ -135,7 +135,7 @@ define([
|
|||
$form.addClass('add_topic_form_new').attr('id', 'add_topic_form_' + id)
|
||||
.find(".topic_content").addClass('topic_content_new').attr('id', 'topic_content_' + id);
|
||||
var data = $topic.getTemplateData({
|
||||
textValues: ['title', 'is_announcement', 'delayed_post_at', 'assignment[id]', 'attachment_name', 'assignment[points_possible]', 'assignment[assignment_group_id]', 'assignment[due_at]', 'podcast_enabled', 'podcast_has_student_posts', 'require_initial_post', 'threaded'],
|
||||
textValues: ['title', 'is_announcement', 'delayed_post_at', 'assignment[id]', 'attachment_name', 'assignment[points_possible]', 'assignment[assignment_group_id]', 'assignment[due_at]', 'assignment[is_frozen]', 'podcast_enabled', 'podcast_has_student_posts', 'require_initial_post', 'threaded'],
|
||||
htmlValues: ['message']
|
||||
});
|
||||
data.message = $topic.find(".content .message_html").val();
|
||||
|
@ -146,8 +146,14 @@ define([
|
|||
$.each(['podcast_enabled', 'podcast_has_student_posts', 'require_initial_post', 'threaded'], function(i, bool){
|
||||
if (data[bool] === 'true') data[bool] = '1';
|
||||
});
|
||||
if (data['assignment[id]'])
|
||||
data['assignment[set_assignment]'] = '1';
|
||||
if (data['assignment[id]']){
|
||||
if (data['assignment[is_frozen]']){
|
||||
var $asmnt_options = $form.find('.assignment_options');
|
||||
$asmnt_options.text(I18n.t('assignment_is_locked', "Some properties for this assignment are locked, go to the assignment page to edit."));
|
||||
} else {
|
||||
data['assignment[set_assignment]'] = '1';
|
||||
}
|
||||
}
|
||||
var addOrUpdate = $topic.hasClass('announcement') ?
|
||||
I18n.t('update_announcment', "Update Announcement") :
|
||||
I18n.t('update_topic', "Update Topic");
|
||||
|
|
|
@ -218,6 +218,31 @@ describe AssignmentsApiController, :type => :integration do
|
|||
a.get_custom_field_value('test_custom').true?.should == true
|
||||
end
|
||||
|
||||
it "should not allow updating an assignment via the API if it is locked" do
|
||||
course_with_teacher(:active_all => true)
|
||||
@group = @course.assignment_groups.create!({:name => "some group"})
|
||||
PluginSetting.stubs(:settings_for_plugin).returns({"title" => "yes"}) #enable plugin
|
||||
@assignment = @course.assignments.create!(:title => "some assignment", :freeze_on_copy => true)
|
||||
@assignment.copied = true
|
||||
@assignment.save!
|
||||
|
||||
raw_api_call(:put,
|
||||
"/api/v1/courses/#{@course.id}/assignments/#{@assignment.id}.json",
|
||||
{ :controller => 'assignments_api', :action => 'update',
|
||||
:format => 'json', :course_id => @course.id.to_s, :id => @assignment.id.to_s },
|
||||
{ :assignment => { 'name' => 'new name'}})
|
||||
|
||||
response.code.should eql '400'
|
||||
json = JSON.parse(response.body)
|
||||
|
||||
json.should == {
|
||||
'message' => 'You cannot edit a frozen assignment.'
|
||||
}
|
||||
|
||||
a = @course.assignments.first
|
||||
a.title.should == "some assignment"
|
||||
end
|
||||
|
||||
it "should return the discussion topic url" do
|
||||
course_with_teacher(:active_all => true)
|
||||
@context = @course
|
||||
|
|
|
@ -699,6 +699,7 @@ describe "Canvas Cartridge importing" do
|
|||
asmnt.peer_reviews = true
|
||||
asmnt.anonymous_peer_reviews = true
|
||||
asmnt.peer_review_count = 37
|
||||
asmnt.freeze_on_copy = true
|
||||
asmnt.save!
|
||||
|
||||
#export to xml/html
|
||||
|
@ -732,6 +733,8 @@ describe "Canvas Cartridge importing" do
|
|||
asmnt_2.mastery_score.should be_nil
|
||||
asmnt_2.max_score.should be_nil
|
||||
asmnt_2.min_score.should be_nil
|
||||
asmnt_2.freeze_on_copy.should == true
|
||||
asmnt_2.copied.should == true
|
||||
end
|
||||
|
||||
it "should import external tool assignments" do
|
||||
|
|
|
@ -1570,6 +1570,129 @@ describe Assignment do
|
|||
@assignment.instance_variable_get(:@ignored_files).should == [ignore_file]
|
||||
end
|
||||
end
|
||||
|
||||
context "attribute freezing" do
|
||||
before do
|
||||
course
|
||||
@asmnt = @course.assignments.create!(:title => 'lock locky')
|
||||
@att_map = {"lock_at" => "yes",
|
||||
"assignment_group" => "no",
|
||||
"title" => "no",
|
||||
"assignment_group_id" => "no",
|
||||
"submission_types" => "yes",
|
||||
"points_possible" => "yes",
|
||||
"description" => "yes",
|
||||
"grading_type" => "yes"}
|
||||
end
|
||||
|
||||
def stub_plugin
|
||||
PluginSetting.stubs(:settings_for_plugin).returns(@att_map)
|
||||
end
|
||||
|
||||
it "should not be frozen if not copied" do
|
||||
stub_plugin
|
||||
@asmnt.freeze_on_copy = true
|
||||
@asmnt.frozen?.should == false
|
||||
@att_map.each_key{|att| @asmnt.att_frozen?(att).should == false}
|
||||
end
|
||||
|
||||
it "should not be frozen if copied but not frozen set" do
|
||||
stub_plugin
|
||||
@asmnt.copied = true
|
||||
@asmnt.frozen?.should == false
|
||||
@att_map.each_key{|att| @asmnt.att_frozen?(att).should == false}
|
||||
end
|
||||
|
||||
it "should not be frozen if plugin not enabled" do
|
||||
@asmnt.copied = true
|
||||
@asmnt.freeze_on_copy = true
|
||||
@asmnt.frozen?.should == false
|
||||
@att_map.each_key{|att| @asmnt.att_frozen?(att).should == false}
|
||||
end
|
||||
|
||||
context "assignments are frozen" do
|
||||
append_before (:each) do
|
||||
stub_plugin
|
||||
@asmnt.copied = true
|
||||
@asmnt.freeze_on_copy = true
|
||||
@admin = account_admin_user(opts={})
|
||||
teacher_in_course(:course => @course)
|
||||
end
|
||||
|
||||
it "should be frozen" do
|
||||
@asmnt.frozen?.should == true
|
||||
end
|
||||
|
||||
it "should flag specific attributes as frozen for no user" do
|
||||
@att_map.each_pair do |att, setting|
|
||||
@asmnt.att_frozen?(att).should == (setting == "yes")
|
||||
end
|
||||
end
|
||||
|
||||
it "should flag specific attributes as frozen for teacher" do
|
||||
@att_map.each_pair do |att, setting|
|
||||
@asmnt.att_frozen?(att, @teacher).should == (setting == "yes")
|
||||
end
|
||||
end
|
||||
|
||||
it "should not flag attributes as frozen for admin" do
|
||||
@att_map.each_pair do |att, setting|
|
||||
@asmnt.att_frozen?(att, @admin).should == false
|
||||
end
|
||||
end
|
||||
|
||||
it "should be frozen for nil user" do
|
||||
@asmnt.frozen_for_user?(nil).should == true
|
||||
end
|
||||
|
||||
it "should be frozen for teacher" do
|
||||
@asmnt.frozen_for_user?(@teacher).should == true
|
||||
end
|
||||
|
||||
it "should not be frozen for admin" do
|
||||
@asmnt.frozen_for_user?(@admin).should == false
|
||||
end
|
||||
|
||||
it "should not validate if saving without user" do
|
||||
@asmnt.description = "new description"
|
||||
@asmnt.save
|
||||
@asmnt.valid?.should == false
|
||||
@asmnt.errors["description"].should == "You don't have permission to edit the locked attribute description"
|
||||
end
|
||||
|
||||
it "should allow teacher to edit unlocked attributes" do
|
||||
@asmnt.title = "new title"
|
||||
@asmnt.updating_user = @teacher
|
||||
@asmnt.save!
|
||||
|
||||
@asmnt.reload
|
||||
@asmnt.title.should == "new title"
|
||||
end
|
||||
|
||||
it "should not allow teacher to edit locked attributes" do
|
||||
@asmnt.description = "new description"
|
||||
@asmnt.updating_user = @teacher
|
||||
@asmnt.save
|
||||
|
||||
@asmnt.valid?.should == false
|
||||
@asmnt.errors["description"].should == "You don't have permission to edit the locked attribute description"
|
||||
|
||||
@asmnt.reload
|
||||
@asmnt.description.should_not == "new title"
|
||||
end
|
||||
|
||||
it "should allow admin to edit unlocked attributes" do
|
||||
@asmnt.description = "new description"
|
||||
@asmnt.updating_user = @admin
|
||||
@asmnt.save!
|
||||
|
||||
@asmnt.reload
|
||||
@asmnt.description.should == "new description"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
def setup_assignment_with_group
|
||||
|
|
|
@ -194,6 +194,73 @@ describe "assignments" do
|
|||
driver.find_element(:css, 'h2.title').should include_text(assignment_name + ' edit')
|
||||
end
|
||||
|
||||
context "frozen assignments" do
|
||||
|
||||
append_before (:each) do
|
||||
@att_map = {"lock_at" => "yes",
|
||||
"assignment_group" => "yes",
|
||||
"title" => "no",
|
||||
"assignment_group_id" => "yes",
|
||||
"submission_types" => "yes",
|
||||
"points_possible" => "yes",
|
||||
"description" => "yes",
|
||||
"peer_reviews" => "yes",
|
||||
"grading_type" => "yes"}
|
||||
PluginSetting.stubs(:settings_for_plugin).returns(@att_map)
|
||||
|
||||
@asmnt = @course.assignments.create!(
|
||||
:name => "frozen",
|
||||
:due_at => Time.now.utc + 2.days,
|
||||
:assignment_group => @course.assignment_groups.create!(:name => "default"),
|
||||
:freeze_on_copy => true
|
||||
)
|
||||
@asmnt.copied = true
|
||||
@asmnt.save!
|
||||
end
|
||||
|
||||
def run_assignment_edit
|
||||
orig_title = @asmnt.title
|
||||
|
||||
get "/courses/#{@course.id}/assignments"
|
||||
|
||||
expect_new_page_load { driver.find_element(:link, orig_title).click }
|
||||
driver.find_element(:css, '.edit_full_assignment_link').click
|
||||
driver.find_element(:css, '.more_options_link').click
|
||||
|
||||
yield
|
||||
|
||||
# title isn't locked, should allow editing
|
||||
driver.find_element(:id, 'assignment_title').send_keys(' edit')
|
||||
|
||||
#save changes
|
||||
driver.find_element(:id, 'edit_assignment_form').submit
|
||||
wait_for_ajaximations
|
||||
driver.find_elements(:css, '.loading_image_holder').length.should eql 0
|
||||
driver.find_element(:css, 'h2.title').should include_text(orig_title + ' edit')
|
||||
end
|
||||
|
||||
it "should respect frozen attributes for teacher" do
|
||||
skip_if_ie('Out of memory')
|
||||
|
||||
run_assignment_edit do
|
||||
f('#assignment_assignment_group_id').should be_nil
|
||||
f('#edit_assignment_form #assignment_peer_reviews').should be_nil
|
||||
f('#edit_assignment_form #assignment_description').should be_nil
|
||||
end
|
||||
end
|
||||
|
||||
it "should not be locked for admin" do
|
||||
skip_if_ie('Out of memory')
|
||||
course_with_admin_logged_in(:course => @course, :name => "admin user")
|
||||
|
||||
run_assignment_edit do
|
||||
f('#assignment_assignment_group_id').should_not be_nil
|
||||
f('#edit_assignment_form #assignment_peer_reviews').should_not be_nil
|
||||
f('#edit_assignment_form #assignment_description').should_not be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "should show a \"more errors\" errorBox if any invalid fields are hidden" do
|
||||
assignment_name = 'first test assignment'
|
||||
@group = @course.assignment_groups.create!(:name => "default")
|
||||
|
|
Loading…
Reference in New Issue