discussions: separate group discussions from group assignments
fixes CNVS-4475 pre-checkout plan: * create a course * open people->groups * create a student group with multiple students * open discussions * create two topics * make one topic a group assignment test plan: * open the topics you created * verify that a "group discussion" option replaced "group assignment" * verify that your group assignment is a group discussion * verify that your group discussion uses the same group set * verify that the old behavior of presenting child topics is retained * verify that your non-group topic is not a group discussion * verify that the "group discussion" flag cannot be changed for topics with replies * create a new discussion topic * verify that "group discussion" is available for ungraded topics * verify that changes to the field persist * create a new graded group discussion topic * post to one of the subtopics as a student in its group * open the gradebook as the teacher * verify that the student who posted has a submission but the other group members do not * verify that group members can be graded individually Change-Id: Iada30628f0abdb7d7df4267d695553baf8b952d3 Reviewed-on: https://gerrit.instructure.com/33253 Tested-by: Jenkins <jenkins@instructure.com> QA-Review: Trevor deHaan <tdehaan@instructure.com> Reviewed-by: Jon Willesen <jonw@instructure.com> Product-Review: Braden Anderson <banderson@instructure.com>
This commit is contained in:
parent
b735558ff8
commit
4fafdda484
|
@ -183,6 +183,8 @@ define [
|
|||
return @get( 'group_category_id' ) unless arguments.length > 0
|
||||
@set 'group_category_id', id
|
||||
|
||||
canGroup: -> !@get('has_submitted_submissions')
|
||||
|
||||
gradingStandardId: (id) =>
|
||||
return @get('grading_standard_id') unless arguments.length > 0
|
||||
@set 'grading_standard_id', id
|
||||
|
|
|
@ -166,3 +166,9 @@ define [
|
|||
locked: @get('locked')
|
||||
@set('position', null)
|
||||
@updatePartial(data)
|
||||
|
||||
groupCategoryId: (id) =>
|
||||
return @get( 'group_category_id' ) unless arguments.length > 0
|
||||
@set 'group_category_id', id
|
||||
|
||||
canGroup: -> @get('can_group')
|
||||
|
|
|
@ -42,6 +42,11 @@ htmlEscape, DiscussionTopic, Announcement, Assignment, $, preventDefault, Missin
|
|||
'change #use_for_grading' : 'toggleAvailabilityOptions'
|
||||
)
|
||||
|
||||
messages:
|
||||
group_category_section_label: I18n.t('group_discussion_title', 'Group Discussion')
|
||||
group_category_field_label: I18n.t('this_is_a_group_discussion', 'This is a Group Discussion')
|
||||
group_locked_message: I18n.t('group_discussion_locked', 'Students have already submitted to this discussion, so group settings cannot be changed.')
|
||||
|
||||
@optionProperty 'permissions'
|
||||
|
||||
initialize: (options) ->
|
||||
|
@ -126,9 +131,12 @@ htmlEscape, DiscussionTopic, Announcement, Assignment, $, preventDefault, Missin
|
|||
renderGroupCategoryOptions: =>
|
||||
@groupCategorySelector = new GroupCategorySelector
|
||||
el: '#group_category_options'
|
||||
parentModel: @assignment
|
||||
parentModel: @model
|
||||
groupCategories: ENV.GROUP_CATEGORIES
|
||||
nested: true
|
||||
hideGradeIndividually: true
|
||||
sectionLabel: @messages.group_category_section_label
|
||||
fieldLabel: @messages.group_category_field_label
|
||||
lockedMessage: @messages.group_locked_message
|
||||
|
||||
@groupCategorySelector.render()
|
||||
|
||||
|
@ -145,6 +153,8 @@ htmlEscape, DiscussionTopic, Announcement, Assignment, $, preventDefault, Missin
|
|||
data.title ||= I18n.t 'default_discussion_title', 'No Title'
|
||||
data.discussion_type = if data.threaded is '1' then 'threaded' else 'side_comment'
|
||||
data.podcast_has_student_posts = false unless data.podcast_enabled is '1'
|
||||
unless ENV?.IS_LARGE_ROSTER
|
||||
data = @groupCategorySelector.filterFormData data
|
||||
|
||||
assign_data = data.assignment
|
||||
delete data.assignment
|
||||
|
@ -169,8 +179,6 @@ htmlEscape, DiscussionTopic, Announcement, Assignment, $, preventDefault, Missin
|
|||
data
|
||||
|
||||
updateAssignment: (data) =>
|
||||
unless ENV?.IS_LARGE_ROSTER
|
||||
data = @groupCategorySelector.filterFormData data
|
||||
@dueDateOverrideView.updateOverrides()
|
||||
defaultDate = @dueDateOverrideView.getDefaultDueDate()
|
||||
data.lock_at = defaultDate?.get('lock_at') or null
|
||||
|
|
|
@ -31,6 +31,10 @@ define [
|
|||
@optionProperty 'parentModel'
|
||||
@optionProperty 'groupCategories'
|
||||
@optionProperty 'nested'
|
||||
@optionProperty 'hideGradeIndividually'
|
||||
@optionProperty 'sectionLabel'
|
||||
@optionProperty 'fieldLabel'
|
||||
@optionProperty 'lockedMessage'
|
||||
|
||||
showGroupCategoryCreateDialog: =>
|
||||
if @$groupCategoryID.val() == 'new'
|
||||
|
@ -52,19 +56,24 @@ define [
|
|||
@showGroupCategoryCreateDialog()
|
||||
|
||||
toJSON: =>
|
||||
frozenAttributes = @parentModel.frozenAttributes()
|
||||
frozenAttributes = @parentModel.frozenAttributes?() || []
|
||||
groupCategoryFrozen = _.include frozenAttributes, 'group_category_id'
|
||||
groupCategoryLocked = @parentModel.attributes.has_submitted_submissions
|
||||
groupCategoryLocked = !@parentModel.canGroup()
|
||||
|
||||
groupCategoryId: @parentModel.groupCategoryId()
|
||||
groupCategories: @groupCategories
|
||||
gradeGroupStudentsIndividually: @parentModel.gradeGroupStudentsIndividually()
|
||||
hideGradeIndividually: @hideGradeIndividually
|
||||
gradeGroupStudentsIndividually: !@hideGradeIndividually && @parentModel.gradeGroupStudentsIndividually()
|
||||
groupCategoryLocked: groupCategoryLocked
|
||||
|
||||
hasGroupCategoryDisabled: groupCategoryFrozen || groupCategoryLocked
|
||||
gradeIndividuallyDisabled: groupCategoryFrozen
|
||||
groupCategoryIdDisabled: groupCategoryFrozen || groupCategoryLocked
|
||||
|
||||
sectionLabel: @sectionLabel
|
||||
fieldLabel: @fieldLabel
|
||||
lockedMessage: @lockedMessage
|
||||
|
||||
nested: @nested
|
||||
prefix: 'assignment' if @nested
|
||||
|
||||
|
|
|
@ -554,8 +554,8 @@ class DiscussionTopicsApiController < ApplicationController
|
|||
def visible_topics(topic)
|
||||
# conflate entries from all child topics for groups the user can access
|
||||
topics = [topic]
|
||||
if topic.for_group_assignment? && !topic.child_topics.empty?
|
||||
groups = topic.assignment.group_category.groups.active.select do |group|
|
||||
if topic.for_group_discussion? && !topic.child_topics.empty?
|
||||
groups = topic.group_category.groups.active.select do |group|
|
||||
group.grants_right?(@current_user, session, :read)
|
||||
end
|
||||
topic.child_topics.each{ |t| topics << t if groups.include?(t.context) }
|
||||
|
|
|
@ -197,6 +197,10 @@
|
|||
# ]
|
||||
# }
|
||||
# },
|
||||
# "group_category_id": {
|
||||
# "description": "The unique identifier of the group category if the topic is a group discussion, otherwise null.",
|
||||
# "type": "integer"
|
||||
# },
|
||||
# "attachments": {
|
||||
# "description": "Array of file attachments.",
|
||||
# "type": "array",
|
||||
|
@ -348,6 +352,7 @@ class DiscussionTopicsController < ApplicationController
|
|||
hash[:ATTRIBUTES] = discussion_topic_api_json(@topic, @context, @current_user, session, override_dates: false)
|
||||
end
|
||||
(hash[:ATTRIBUTES] ||= {})[:is_announcement] = @topic.is_announcement
|
||||
hash[:ATTRIBUTES][:can_group] = @topic.can_group?
|
||||
handle_assignment_edit_params(hash[:ATTRIBUTES])
|
||||
|
||||
if @topic.assignment.present?
|
||||
|
@ -395,8 +400,8 @@ class DiscussionTopicsController < ApplicationController
|
|||
@locked = @topic.locked_for?(@current_user, :check_policies => true, :deep_check_if_needed => true) || @topic.locked?
|
||||
@unlock_at = @topic.available_from_for(@current_user)
|
||||
@topic.change_read_state('read', @current_user)
|
||||
if @topic.for_group_assignment?
|
||||
@groups = @topic.assignment.group_category.groups.active.select{ |g| g.grants_right?(@current_user, session, :read) }
|
||||
if @topic.for_group_discussion?
|
||||
@groups = @topic.group_category.groups.active.select{ |g| g.grants_right?(@current_user, session, :read) }
|
||||
topics = @topic.child_topics.to_a
|
||||
topics = topics.select{|t| @groups.include?(t.context) } unless @topic.grants_right?(@current_user, session, :update)
|
||||
@group_topics = @groups.map do |group|
|
||||
|
@ -430,7 +435,7 @@ class DiscussionTopicsController < ApplicationController
|
|||
|
||||
},
|
||||
:PERMISSIONS => {
|
||||
:CAN_REPLY => @locked ? false : !(@topic.for_group_assignment? || @topic.locked_for?(@current_user)), # Can reply
|
||||
:CAN_REPLY => @locked ? false : !(@topic.for_group_discussion? || @topic.locked_for?(@current_user)), # Can reply
|
||||
:CAN_ATTACH => @locked ? false : @topic.grants_right?(@current_user, session, :attach), # Can attach files on replies
|
||||
:CAN_MANAGE_OWN => @context.user_can_manage_own_discussion_posts?(@current_user), # Can moderate their own topics
|
||||
:MODERATE => user_can_moderate # Can moderate any topic
|
||||
|
@ -528,6 +533,10 @@ class DiscussionTopicsController < ApplicationController
|
|||
# can pass the id of another topic to have this one show up after the other
|
||||
# when they are listed.
|
||||
#
|
||||
# @argument group_category_id [Optional, Integer]
|
||||
# If present, the topic will become a group discussion assigned
|
||||
# to the group.
|
||||
#
|
||||
# @example_request
|
||||
# curl https://<canvas>/api/v1/courses/<course_id>/discussion_topics \
|
||||
# -F title='my topic' \
|
||||
|
@ -648,7 +657,8 @@ class DiscussionTopicsController < ApplicationController
|
|||
end
|
||||
|
||||
API_ALLOWED_TOPIC_FIELDS = %w(title message discussion_type delayed_post_at lock_at podcast_enabled
|
||||
podcast_has_student_posts require_initial_post is_announcement pinned)
|
||||
podcast_has_student_posts require_initial_post is_announcement pinned
|
||||
group_category_id)
|
||||
def process_discussion_topic(is_new = false)
|
||||
@errors = {}
|
||||
discussion_topic_hash = params.slice(*API_ALLOWED_TOPIC_FIELDS)
|
||||
|
@ -675,6 +685,7 @@ class DiscussionTopicsController < ApplicationController
|
|||
process_lock_parameters(discussion_topic_hash)
|
||||
end
|
||||
process_published_parameters(discussion_topic_hash)
|
||||
process_group_parameters(discussion_topic_hash)
|
||||
process_pin_parameters(discussion_topic_hash)
|
||||
|
||||
if @errors.present?
|
||||
|
@ -767,6 +778,21 @@ class DiscussionTopicsController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def process_group_parameters(discussion_topic_hash)
|
||||
if params[:assignment] && params[:assignment].has_key?(:group_category_id)
|
||||
id = params[:assignment].delete(:group_category_id)
|
||||
discussion_topic_hash[:group_category_id] ||= id
|
||||
end
|
||||
return unless params[:group_category_id].to_s != @topic.group_category_id.to_s
|
||||
if @topic.is_announcement
|
||||
@errors[:group] = t(:error_group_announcement, "You cannot use grouped discussion on an announcement.")
|
||||
return
|
||||
end
|
||||
if !@topic.can_group?
|
||||
@errors[:group] = t(:error_group_change, "You cannot change grouping on a discussion with replies.")
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: upgrade acts_as_list after rails3
|
||||
# check_scope will probably handle this
|
||||
def process_pin_parameters(discussion_topic_hash)
|
||||
|
@ -828,6 +854,7 @@ class DiscussionTopicsController < ApplicationController
|
|||
|
||||
elsif (@assignment = @topic.assignment || @topic.restore_old_assignment || (@topic.assignment = @context.assignments.build)) &&
|
||||
@assignment.grants_right?(@current_user, session, :update)
|
||||
params[:assignment][:group_category_id] = nil unless @topic.group_category_id || @assignment.has_submitted_submissions?
|
||||
update_api_assignment(@assignment, params[:assignment].merge(@topic.attributes.slice('title')))
|
||||
@assignment.submission_types = 'discussion_topic'
|
||||
@assignment.saved_by = :discussion_topic
|
||||
|
|
|
@ -30,7 +30,8 @@ class DiscussionTopic < ActiveRecord::Base
|
|||
|
||||
attr_accessible :title, :message, :user, :delayed_post_at, :lock_at, :assignment,
|
||||
:plaintext_message, :podcast_enabled, :podcast_has_student_posts,
|
||||
:require_initial_post, :threaded, :discussion_type, :context, :pinned, :locked
|
||||
:require_initial_post, :threaded, :discussion_type, :context, :pinned, :locked,
|
||||
:group_category, :group_category_id
|
||||
|
||||
module DiscussionTypes
|
||||
SIDE_COMMENT = 'side_comment'
|
||||
|
@ -51,6 +52,7 @@ class DiscussionTopic < ActiveRecord::Base
|
|||
belongs_to :editor, :class_name => 'User'
|
||||
belongs_to :old_assignment, :class_name => 'Assignment'
|
||||
belongs_to :root_topic, :class_name => 'DiscussionTopic'
|
||||
belongs_to :group_category
|
||||
has_many :child_topics, :class_name => 'DiscussionTopic', :foreign_key => :root_topic_id, :dependent => :destroy
|
||||
has_many :discussion_topic_participants, :dependent => :destroy
|
||||
has_many :discussion_entry_participants, :through => :discussion_entries
|
||||
|
@ -117,13 +119,28 @@ class DiscussionTopic < ActiveRecord::Base
|
|||
if self.assignment_id
|
||||
self.assignment_id = nil unless (self.assignment && self.assignment.context == self.context) || (self.root_topic && self.root_topic.assignment_id == self.assignment_id)
|
||||
self.old_assignment_id = self.assignment_id if self.assignment_id
|
||||
if self.assignment && self.assignment.submission_types == 'discussion_topic' && self.assignment.has_group_category?
|
||||
self.subtopics_refreshed_at ||= Time.parse("Jan 1 2000")
|
||||
end
|
||||
end
|
||||
if self.has_group_category?
|
||||
self.subtopics_refreshed_at ||= Time.parse("Jan 1 2000")
|
||||
end
|
||||
end
|
||||
protected :default_values
|
||||
|
||||
# TODO: These overrides for assignment's group_category can be removed after the migration.
|
||||
alias_method :raw_group_category, :group_category
|
||||
|
||||
def group_category
|
||||
return raw_group_category || (self.assignment && self.assignment.group_category)
|
||||
end
|
||||
|
||||
def legacy_group_category_id
|
||||
return self.group_category.id if self.group_category
|
||||
end
|
||||
|
||||
def has_group_category?
|
||||
self.group_category.present?
|
||||
end
|
||||
|
||||
def set_schedule_delayed_transitions
|
||||
@should_schedule_delayed_post = self.delayed_post_at? && self.delayed_post_at_changed?
|
||||
@should_schedule_lock_at = self.lock_at && self.lock_at_changed?
|
||||
|
@ -141,14 +158,14 @@ class DiscussionTopic < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def update_subtopics
|
||||
if !self.deleted? && self.assignment && self.assignment.submission_types == 'discussion_topic' && self.assignment.has_group_category?
|
||||
if !self.deleted? && self.has_group_category?
|
||||
send_later_if_production :refresh_subtopics
|
||||
end
|
||||
end
|
||||
|
||||
def refresh_subtopics
|
||||
return if self.deleted?
|
||||
category = self.assignment.try(:group_category)
|
||||
category = self.group_category
|
||||
return unless category && self.root_topic_id.blank?
|
||||
category.groups.active.each do |group|
|
||||
group.shard.activate do
|
||||
|
@ -158,6 +175,7 @@ class DiscussionTopic < ActiveRecord::Base
|
|||
topic.message = self.message
|
||||
topic.title = "#{self.title} - #{group.name}"
|
||||
topic.assignment_id = self.assignment_id
|
||||
topic.group_category_id = self.group_category_id
|
||||
topic.user_id = self.user_id
|
||||
topic.discussion_type = self.discussion_type
|
||||
topic.workflow_state = self.workflow_state
|
||||
|
@ -214,7 +232,7 @@ class DiscussionTopic < ActiveRecord::Base
|
|||
def is_announcement; false end
|
||||
|
||||
def root_topic?
|
||||
!self.root_topic_id && self.assignment_id && self.assignment.has_group_category?
|
||||
!self.root_topic_id && self.has_group_category?
|
||||
end
|
||||
|
||||
# only the root level entries
|
||||
|
@ -231,8 +249,8 @@ class DiscussionTopic < ActiveRecord::Base
|
|||
self.assignment && self.assignment.submission_types =~ /discussion_topic/
|
||||
end
|
||||
|
||||
def for_group_assignment?
|
||||
self.for_assignment? && self.context == self.assignment.context && self.assignment.has_group_category?
|
||||
def for_group_discussion?
|
||||
self.has_group_category? && self.root_topic?
|
||||
end
|
||||
|
||||
def plaintext_message=(val)
|
||||
|
@ -552,17 +570,25 @@ class DiscussionTopic < ActiveRecord::Base
|
|||
def can_unpublish?
|
||||
if self.assignment
|
||||
!self.assignment.has_student_submissions?
|
||||
elsif self.for_group_discussion?
|
||||
self.child_topics.all? do |child|
|
||||
child.discussion_subentry_count == 0
|
||||
end
|
||||
else
|
||||
self.discussion_subentry_count == 0
|
||||
end
|
||||
end
|
||||
|
||||
def can_group?
|
||||
can_unpublish?
|
||||
end
|
||||
|
||||
def should_send_to_stream
|
||||
if self.not_available_yet?
|
||||
false
|
||||
elsif self.cloned_item_id
|
||||
false
|
||||
elsif self.assignment && self.root_topic_id && self.assignment.has_group_category?
|
||||
elsif self.root_topic_id && self.has_group_category?
|
||||
false
|
||||
elsif self.assignment && self.assignment.submission_types == 'discussion_topic' && (!self.assignment.due_at || self.assignment.due_at > 1.week.from_now) # TODO: vdd
|
||||
false
|
||||
|
|
|
@ -23,22 +23,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<% if @topic.for_group_assignment? %>
|
||||
<p>
|
||||
<%= t :separated_conversation_notice, "Since this is a group assignment, each group has its own conversation for this topic. Here are the ones you have access to:" %>
|
||||
<ul>
|
||||
<% @group_topics.each do |group_and_topic| %>
|
||||
<li>
|
||||
<a href="<%= context_url(group_and_topic[:group], :context_discussion_topics_url, :root_discussion_topic_id => @topic.id) %>"><%= group_and_topic[:group].name %></a>
|
||||
<%= render :partial => 'new_and_total_badge', :locals => {
|
||||
:unread_count => group_and_topic[:topic].unread_count(@current_user),
|
||||
:reply_count => group_and_topic[:topic].discussion_entries.active.size } if group_and_topic[:topic] %>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
<% if @presenter.can_grade? %>
|
||||
<div class="row-fluid">
|
||||
<div class="span12 hidden due_date_wrapper">
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<div class="discussion-section alert alert-info">
|
||||
<p>
|
||||
<%= t :separated_conversation_notice, "Since this is a group discussion, each group has its own conversation for this topic. Here are the ones you have access to:" %>
|
||||
<ul>
|
||||
<% @group_topics.each do |group_and_topic| %>
|
||||
<li>
|
||||
<a href="<%= context_url(group_and_topic[:group], :context_discussion_topics_url, :root_discussion_topic_id => @topic.id) %>"><%= group_and_topic[:group].name %></a>
|
||||
<%= render :partial => 'new_and_total_badge', :locals => {
|
||||
:unread_count => group_and_topic[:topic].unread_count(@current_user),
|
||||
:reply_count => group_and_topic[:topic].discussion_entries.active.size } if group_and_topic[:topic] %>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</p>
|
||||
</div>
|
|
@ -142,6 +142,9 @@
|
|||
<% if @topic.for_assignment? %>
|
||||
<%= render :partial => 'assignment_details' %>
|
||||
<% end %>
|
||||
<% if @topic.for_group_discussion? %>
|
||||
<%= render :partial => 'group_discussion' %>
|
||||
<% end %>
|
||||
<header class="discussion-section clearfix">
|
||||
<%= avatar((@topic.user), context_code: @context.asset_string) if @topic.user %>
|
||||
<div class="discussion-header-content right-of-avatar">
|
||||
|
|
|
@ -134,6 +134,10 @@
|
|||
</fieldset>
|
||||
|
||||
{{#if isTopic}}
|
||||
{{#unless isLargeRoster}}
|
||||
<div id="group_category_options" class="control-group"></div>
|
||||
{{/unless}}
|
||||
|
||||
{{#if contextIsCourse}}
|
||||
<div id="availability_options" style="{{hiddenIf set_assignment}}">
|
||||
<fieldset>
|
||||
|
@ -185,10 +189,6 @@
|
|||
|
||||
<div id="assignment_group_options" class="control-group"></div>
|
||||
|
||||
{{#unless isLargeRoster}}
|
||||
<div id="group_category_options" class="control-group"></div>
|
||||
{{/unless}}
|
||||
|
||||
{{#unless isLargeRoster}}
|
||||
<div id="peer_review_options" class="control-group"></div>
|
||||
{{/unless}}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<div class="form-column-left">
|
||||
{{#t 'group_assignment_header'}}Group Assignment{{/t}}
|
||||
{{#if sectionLabel}}{{sectionLabel}}{{else}}{{#t 'group_assignment_header'}}Group Assignment{{/t}}{{/if}}
|
||||
</div>
|
||||
|
||||
<div class="form-column-right">
|
||||
|
@ -11,12 +11,13 @@
|
|||
prefix=prefix
|
||||
checked=groupCategoryId
|
||||
disabled=hasGroupCategoryDisabled}}
|
||||
{{#t "is_group_assignment"}}This is a Group Assignment{{/t}}
|
||||
{{#if fieldLabel}}{{fieldLabel}}{{else}}{{#t "is_group_assignment"}}This is a Group Assignment{{/t}}{{/if}}
|
||||
</label>
|
||||
|
||||
{{!-- Individual grading? --}}
|
||||
<div class="nested">
|
||||
<div id="group_category_options" style="{{hiddenUnless groupCategoryId}}">
|
||||
{{#unless hideGradeIndividually}}
|
||||
<label class="checkbox" for="assignment_grade_students_individually">
|
||||
{{checkbox "grade_group_students_individually"
|
||||
id="assignment_grade_students_individually"
|
||||
|
@ -27,6 +28,7 @@
|
|||
Assign Grades to Each Student Individually
|
||||
{{/t}}
|
||||
</label>
|
||||
{{/unless}}
|
||||
|
||||
{{!-- Group selection --}}
|
||||
<label for="group_category_id">
|
||||
|
@ -50,9 +52,11 @@
|
|||
{{#if groupCategoryLocked}}
|
||||
<div class="group_category_locked_explanation alert"
|
||||
style="margin: 15px 20px 0">
|
||||
{{#t "group_category_locked_explanation"}}Students have already
|
||||
submitted homework on this assignment, so group settings cannot be
|
||||
changed.{{/t}}
|
||||
{{#if lockedMessage}}{{lockedMessage}}{{else}}
|
||||
{{#t "group_category_locked_explanation"}}Students have already
|
||||
submitted homework on this assignment, so group settings cannot be
|
||||
changed.{{/t}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<% if @assignment.submission_types == 'discussion_topic' && @assignment.discussion_topic %>
|
||||
<div style="width: 600px; margin: 10px auto;">
|
||||
<p>
|
||||
<% if @assignment.discussion_topic.for_group_assignment? && (group = @assignment.group_students(@submission.user)[0]) %>
|
||||
<% if @assignment.discussion_topic.for_group_discussion? && (group = @assignment.discussion_topic.group_category.group_for(@user)) %>
|
||||
<%= t('group_discussion_submission_description',
|
||||
"The submissions for this assignment are posts in the assignment's discussion for this group. Below are the discussion posts for %{user}, or you can *view the full group discussion*.",
|
||||
:user => context_user_name(@context, @submission.user),
|
||||
|
@ -17,7 +17,7 @@
|
|||
|
||||
</p>
|
||||
<% @entries = @assignment.discussion_topic.discussion_entries.active.for_user(@user) %>
|
||||
<% if @assignment.has_group_category? %>
|
||||
<% if @assignment.discussion_topic.for_group_discussion? %>
|
||||
<% @entries = @assignment.discussion_topic.child_topics.map{|t| t.discussion_entries.active.for_user(@user) }.flatten.sort_by{|e| e.created_at} %>
|
||||
<% end %>
|
||||
<% @entries.each do |entry| %>
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
class AddGroupCategoryIdToDiscussionTopic < ActiveRecord::Migration
|
||||
tag :predeploy
|
||||
|
||||
def self.up
|
||||
add_column :discussion_topics, :group_category_id, :integer, :limit => 8
|
||||
add_foreign_key :discussion_topics, :group_categories
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_foreign_key :discussion_topics, :group_categories
|
||||
remove_column :discussion_topics, :group_category_id
|
||||
end
|
||||
end
|
|
@ -0,0 +1,7 @@
|
|||
class PopulateGroupCategoryOnDiscussionTopics < ActiveRecord::Migration
|
||||
tag :postdeploy
|
||||
|
||||
def up
|
||||
DataFixup::PopulateGroupCategoryOnDiscussionTopics.send_later_if_production_enqueue_args(:run, :priority => Delayed::LOW_PRIORITY, :max_attempts => 1)
|
||||
end
|
||||
end
|
|
@ -105,7 +105,8 @@ module Api::V1::DiscussionTopics
|
|||
subscribed: topic.subscribed?(user), topic_children: topic.child_topics.pluck(:id),
|
||||
attachments: attachments, published: topic.published?, can_unpublish: topic.can_unpublish?,
|
||||
locked: topic.locked?, author: user_display_json(topic.user, topic.context),
|
||||
html_url: html_url, url: html_url, pinned: !!topic.pinned }
|
||||
html_url: html_url, url: html_url, pinned: !!topic.pinned,
|
||||
group_category_id: topic.legacy_group_category_id, can_group: topic.can_group? }
|
||||
end
|
||||
|
||||
# Public: Serialize discussion entries for returning a JSON response. This method,
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
module DataFixup::PopulateGroupCategoryOnDiscussionTopics
|
||||
def self.run
|
||||
Assignment.where('submission_types = ? AND group_category_id IS NOT NULL', 'discussion_topic').find_ids_in_ranges do |min_id, max_id|
|
||||
DiscussionTopic.where(assignment_id: min_id..max_id).joins(:assignment).update_all('group_category_id = assignments.group_category_id')
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1383,6 +1383,8 @@ describe AssignmentsApiController, type: :request do
|
|||
'attachments' => [],
|
||||
'permissions' => {'delete' => true, 'attach' => true, 'update' => true},
|
||||
'discussion_type' => 'side_comment',
|
||||
'group_category_id' => nil,
|
||||
'can_group' => true,
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -290,7 +290,10 @@ describe DiscussionTopicsController, type: :request do
|
|||
"locked"=>false,
|
||||
"locked_for_user"=>false,
|
||||
"author" => user_display_json(@topic.user, @topic.context).stringify_keys!,
|
||||
"permissions" => { "delete"=>true, "attach"=>true, "update"=>true }}
|
||||
"permissions" => { "delete"=>true, "attach"=>true, "update"=>true },
|
||||
"group_category_id" => nil,
|
||||
"can_group" => true,
|
||||
}
|
||||
end
|
||||
|
||||
describe "GET 'index'" do
|
||||
|
@ -718,6 +721,21 @@ describe DiscussionTopicsController, type: :request do
|
|||
@assignment.should be_deleted
|
||||
end
|
||||
|
||||
it "should transfer assignment group category to the discussion" do
|
||||
group_category = @course.group_categories.create(:name => 'watup')
|
||||
group = group_category.groups.create!(:name => "group1", :context => @course)
|
||||
group.add_user(@user)
|
||||
api_call(:put, "/api/v1/courses/#{@course.id}/discussion_topics/#{@topic.id}",
|
||||
{ :controller => "discussion_topics", :action => "update", :format => "json", :course_id => @course.to_param, :topic_id => @topic.to_param },
|
||||
{ :assignment => { :group_category_id => group_category.id } })
|
||||
@topic.reload
|
||||
|
||||
@topic.title.should == "Topic 1"
|
||||
@topic.group_category.should == group_category
|
||||
@topic.assignment.should be_present
|
||||
@topic.assignment.group_category.should be_nil
|
||||
end
|
||||
|
||||
it "should allow pinning a topic" do
|
||||
api_call(:put, "/api/v1/courses/#{@course.id}/discussion_topics/#{@topic.id}",
|
||||
{ controller: 'discussion_topics', action: 'update', format: 'json', course_id: @course.to_param, topic_id: @topic.to_param },
|
||||
|
@ -892,7 +910,9 @@ describe DiscussionTopicsController, type: :request do
|
|||
"permissions" => {"delete"=>true, "attach"=>true, "update"=>true},
|
||||
"locked" => false,
|
||||
"locked_for_user" => false,
|
||||
"author" => user_display_json(gtopic.user, gtopic.context).stringify_keys!
|
||||
"author" => user_display_json(gtopic.user, gtopic.context).stringify_keys!,
|
||||
"group_category_id" => nil,
|
||||
"can_group" => true,
|
||||
}
|
||||
json.should == expected
|
||||
end
|
||||
|
|
|
@ -169,7 +169,7 @@ describe DiscussionTopicsController do
|
|||
response.should be_success
|
||||
end
|
||||
|
||||
it "should assign groups from the topic's assignment's category if the topic is for a group assignment" do
|
||||
it "should assign groups from the topic's category if the topic is a group discussion" do
|
||||
course_with_teacher_logged_in(:active_all => true)
|
||||
course_topic(:with_assignment => true)
|
||||
|
||||
|
@ -179,8 +179,8 @@ describe DiscussionTopicsController do
|
|||
@course.groups.create!(:group_category => group_category1)
|
||||
@course.groups.create!(:group_category => group_category1)
|
||||
@course.groups.create!(:group_category => group_category2)
|
||||
@topic.assignment.group_category = group_category1
|
||||
@topic.assignment.save!
|
||||
@topic.group_category = group_category1
|
||||
@topic.save!
|
||||
|
||||
get 'show', :course_id => @course.id, :id => @topic.id
|
||||
assigns[:groups].size.should eql(2)
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
#
|
||||
# Copyright (C) 2012 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
|
||||
|
||||
describe DataFixup::PopulateGroupCategoryOnDiscussionTopics do
|
||||
it 'should copy assignment.group_category onto discussion topics' do
|
||||
# set up data
|
||||
course(:active_all => true, :name => 'Test course')
|
||||
group_category = @course.group_categories.create(:name => "category")
|
||||
@group = @course.groups.create(:name => "group", :group_category => group_category)
|
||||
|
||||
assignment1 = course.assignments.build(:submission_types => 'discussion_topic', :title => '')
|
||||
assignment1.group_category = group_category
|
||||
assignment1.save
|
||||
topic1 = @course.discussion_topics.create(:title => "topic 1")
|
||||
topic1.assignment = assignment1
|
||||
topic1.save
|
||||
|
||||
assignment2 = course.assignments.build(:submission_types => 'discussion_topic', :title => '')
|
||||
topic2 = @course.discussion_topics.create(:title => "topic 2")
|
||||
topic2.assignment = assignment2
|
||||
topic2.save
|
||||
|
||||
topic3 = @course.discussion_topics.create(:title => "topic 1")
|
||||
|
||||
# run the fix
|
||||
DataFixup::PopulateGroupCategoryOnDiscussionTopics.run
|
||||
|
||||
# verify the results
|
||||
topic1.reload.group_category.should == group_category
|
||||
topic1.assignment.reload.group_category.should == group_category
|
||||
topic2.reload.group_category.should be_nil
|
||||
topic3.reload.group_category.should be_nil
|
||||
end
|
||||
end
|
|
@ -163,9 +163,9 @@ describe DiscussionTopic do
|
|||
it "should not be visible when no delayed_post but assignment unlock date in future" do
|
||||
@topic.delayed_post_at = nil
|
||||
group_category = @course.group_categories.create(:name => "category")
|
||||
@topic.group_category = group_category
|
||||
@topic.assignment = @course.assignments.build(:submission_types => 'discussion_topic',
|
||||
:title => @topic.title,
|
||||
:group_category => group_category,
|
||||
:unlock_at => 10.days.from_now,
|
||||
:lock_at => 30.days.from_now)
|
||||
@topic.assignment.infer_times
|
||||
|
@ -372,16 +372,14 @@ describe DiscussionTopic do
|
|||
end
|
||||
|
||||
context "sub-topics" do
|
||||
it "should default subtopics_refreshed_at on save if a group assignment" do
|
||||
it "should default subtopics_refreshed_at on save if a group discussion" do
|
||||
course_with_student(:active_all => true)
|
||||
group_category = @course.group_categories.create(:name => "category")
|
||||
@group = @course.groups.create(:name => "group", :group_category => group_category)
|
||||
@topic = @course.discussion_topics.create(:title => "topic")
|
||||
@topic.subtopics_refreshed_at.should be_nil
|
||||
|
||||
@topic.assignment = @course.assignments.build(:submission_types => 'discussion_topic', :title => @topic.title, :group_category => @group.group_category)
|
||||
@topic.assignment.infer_times
|
||||
@topic.assignment.saved_by = :discussion_topic
|
||||
@topic.group_category = group_category
|
||||
@topic.save
|
||||
@topic.subtopics_refreshed_at.should_not be_nil
|
||||
end
|
||||
|
@ -405,7 +403,7 @@ describe DiscussionTopic do
|
|||
end
|
||||
|
||||
context "refresh_subtopics" do
|
||||
it "should be a no-op unless there's an assignment and it has a group_category" do
|
||||
it "should be a no-op unless it has a group_category" do
|
||||
course_with_student(:active_all => true)
|
||||
@topic = @course.discussion_topics.create(:title => "topic")
|
||||
@topic.refresh_subtopics
|
||||
|
@ -453,11 +451,13 @@ describe DiscussionTopic do
|
|||
course_with_student(:active_all => true)
|
||||
group_category = @course.group_categories.create(:name => "category")
|
||||
@parent_topic = @course.discussion_topics.create(:title => "parent topic")
|
||||
@parent_topic.group_category = group_category
|
||||
@subtopic = @parent_topic.child_topics.build(:title => "subtopic")
|
||||
@assignment = @course.assignments.build(:submission_types => 'discussion_topic', :title => @subtopic.title, :group_category => group_category)
|
||||
@assignment = @course.assignments.build(:submission_types => 'discussion_topic', :title => @subtopic.title)
|
||||
@assignment.infer_times
|
||||
@assignment.saved_by = :discussion_topic
|
||||
@subtopic.assignment = @assignment
|
||||
@subtopic.group_category = group_category
|
||||
@subtopic.save
|
||||
|
||||
@subtopic.should_not be_root_topic
|
||||
|
@ -470,7 +470,7 @@ describe DiscussionTopic do
|
|||
@topic.should_not be_root_topic
|
||||
end
|
||||
|
||||
it "should be false unless the topic's assignment has a group_category" do
|
||||
it "should be false unless the topic has a group_category" do
|
||||
# topic has no root topic and has an assignment, but the assignment has no group_category
|
||||
course_with_student(:active_all => true)
|
||||
@topic = @course.discussion_topics.create(:title => "topic")
|
||||
|
@ -488,7 +488,8 @@ describe DiscussionTopic do
|
|||
course_with_student(:active_all => true)
|
||||
group_category = @course.group_categories.create(:name => "category")
|
||||
@topic = @course.discussion_topics.create(:title => "topic")
|
||||
@assignment = @course.assignments.build(:submission_types => 'discussion_topic', :title => @topic.title, :group_category => group_category)
|
||||
@topic.group_category = group_category
|
||||
@assignment = @course.assignments.build(:submission_types => 'discussion_topic', :title => @topic.title)
|
||||
@assignment.infer_times
|
||||
@assignment.saved_by = :discussion_topic
|
||||
@topic.assignment = @assignment
|
||||
|
@ -512,23 +513,22 @@ describe DiscussionTopic do
|
|||
end
|
||||
end
|
||||
|
||||
context "for_assignment?/for_group_assignment?" do
|
||||
it "should not be for_assignment?/for_group_assignment? unless it has an assignment" do
|
||||
context "for_assignment?" do
|
||||
it "should not be for_assignment? unless it has an assignment" do
|
||||
course_with_student(:active_all => true)
|
||||
@topic = @course.discussion_topics.create(:title => "topic")
|
||||
@topic.should_not be_for_assignment
|
||||
@topic.should_not be_for_group_assignment
|
||||
|
||||
group_category = @course.group_categories.build(:name => "category")
|
||||
@topic.assignment = @course.assignments.build(:submission_types => 'discussion_topic', :title => @topic.title, :group_category => group_category)
|
||||
@topic.assignment = @course.assignments.build(:submission_types => 'discussion_topic', :title => @topic.title)
|
||||
@topic.assignment.infer_times
|
||||
@topic.assignment.saved_by = :discussion_topic
|
||||
@topic.save
|
||||
@topic.should be_for_assignment
|
||||
@topic.should be_for_group_assignment
|
||||
end
|
||||
end
|
||||
|
||||
it "should not be for_group_assignment? unless the assignment has a group_category" do
|
||||
context "for_group_discussion?" do
|
||||
it "should not be for_group_discussion? unless it has a group_category" do
|
||||
course_with_student(:active_all => true)
|
||||
@topic = @course.discussion_topics.build(:title => "topic")
|
||||
@assignment = @course.assignments.build(:submission_types => 'discussion_topic', :title => @topic.title)
|
||||
|
@ -536,12 +536,11 @@ describe DiscussionTopic do
|
|||
@assignment.saved_by = :discussion_topic
|
||||
@topic.assignment = @assignment
|
||||
@topic.save
|
||||
@topic.should be_for_assignment
|
||||
@topic.should_not be_for_group_assignment
|
||||
@topic.should_not be_for_group_discussion
|
||||
|
||||
@assignment.group_category = @course.group_categories.create(:name => "category")
|
||||
@assignment.save
|
||||
@topic.reload.should be_for_group_assignment
|
||||
@topic.group_category = @course.group_categories.create(:name => "category")
|
||||
@topic.save
|
||||
@topic.should be_for_group_discussion
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -562,12 +561,15 @@ describe DiscussionTopic do
|
|||
@topic.should_send_to_stream.should be_true
|
||||
end
|
||||
|
||||
it "should be true for the parent topic only in group discussion assignments, not the subtopics" do
|
||||
it "should be true for the parent topic only in group discussions, not the subtopics" do
|
||||
course_with_student(:active_all => true)
|
||||
group_category = @course.group_categories.create(:name => "category")
|
||||
@parent_topic = @course.discussion_topics.create(:title => "parent topic")
|
||||
@parent_topic.group_category = group_category
|
||||
@parent_topic.save
|
||||
@subtopic = @parent_topic.child_topics.build(:title => "subtopic")
|
||||
@assignment = @course.assignments.build(:submission_types => 'discussion_topic', :title => @subtopic.title, :group_category => group_category, :due_at => 1.day.from_now)
|
||||
@subtopic.group_category = group_category
|
||||
@assignment = @course.assignments.build(:submission_types => 'discussion_topic', :title => @subtopic.title, :due_at => 1.day.from_now)
|
||||
@assignment.saved_by = :discussion_topic
|
||||
@subtopic.assignment = @assignment
|
||||
@subtopic.save
|
||||
|
|
|
@ -1113,7 +1113,7 @@ describe "discussions" do
|
|||
click_option("#assignment_group_category_id", group_cat.name)
|
||||
|
||||
expect_new_page_load { f('.form-actions button[type=submit]').click }
|
||||
topic.reload.assignment.group_category_id.should == group_cat.id
|
||||
topic.reload.group_category_id.should == group_cat.id
|
||||
end
|
||||
|
||||
it "should allow editing the peer review" do
|
||||
|
|
|
@ -857,7 +857,8 @@ end
|
|||
@group2 = course.groups.create!(:name => "group 2", :group_category => group_category)
|
||||
|
||||
@topic = course.discussion_topics.build(:title => "topic")
|
||||
@assignment = course.assignments.build(:submission_types => 'discussion_topic', :title => @topic.title, :group_category => @group1.group_category)
|
||||
@topic.group_category = group_category
|
||||
@assignment = course.assignments.build(:submission_types => 'discussion_topic', :title => @topic.title)
|
||||
@assignment.infer_times
|
||||
@assignment.saved_by = :discussion_topic
|
||||
@topic.assignment = @assignment
|
||||
|
|
Loading…
Reference in New Issue