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:
Bracken Mosbacker 2012-04-26 08:32:54 -06:00
parent a56a41367c
commit 98fe5ee5c0
21 changed files with 596 additions and 150 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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