allow module items to be published/unpublished through API

adds undocumented arguments to context module items API
update action, identical to the context modules API

test plan:
* to publish a module item, update through the API
 with the argument 'module_item[published]=1'
* to unpublish an item, use
 'module_item[published]=0'

* NOTE: the published/unpublished status of an item
will not affect whether the item is viewable/usable by students

* confirm that publishing/unpublishing an item syncs the
state with the associated content (e.g. a wiki page's
published/unpublished state)

closes #CNVS-5943

Change-Id: I80c45a787a5cf8ac7b3bc569056d9590ab2f74bc
Reviewed-on: https://gerrit.instructure.com/20633
Reviewed-by: Jeremy Stanley <jeremy@instructure.com>
Tested-by: Jenkins <jenkins@instructure.com>
QA-Review: Clare Strong <clare@instructure.com>
Product-Review: Bracken Mosbacker <bracken@instructure.com>
This commit is contained in:
James Williams 2013-05-14 13:53:14 -06:00
parent 80fd695001
commit 2ecc8bc16b
10 changed files with 282 additions and 48 deletions

View File

@ -87,7 +87,7 @@ class ContextModuleItemsApiController < ApplicationController
if authorized_action(@context, @current_user, :read)
mod = @context.modules_visible_to(@current_user).find(params[:module_id])
route = polymorphic_url([:api_v1, @context, mod, :items])
scope = mod.content_tags.active
scope = mod.content_tags_visible_to(@current_user)
items = Api.paginate(scope, self, route)
prog = @context.grants_right?(@current_user, session, :participate_as_student) ? mod.evaluate_for(@current_user) : nil
render :json => items.map { |item| module_item_json(item, @current_user, session, mod, prog) }
@ -106,7 +106,7 @@ class ContextModuleItemsApiController < ApplicationController
def show
if authorized_action(@context, @current_user, :read)
mod = @context.modules_visible_to(@current_user).find(params[:module_id])
item = mod.content_tags.active.find(params[:id])
item = mod.content_tags_visible_to(@current_user).find(params[:id])
prog = @context.grants_right?(@current_user, session, :participate_as_student) ? mod.evaluate_for(@current_user) : nil
render :json => module_item_json(item, @current_user, session, mod, prog)
end
@ -182,6 +182,10 @@ class ContextModuleItemsApiController < ApplicationController
item_params[:url] = params[:module_item][:external_url]
if (@tag = @module.add_item(item_params)) && set_position && set_completion_requirement
if @domain_root_account.enable_draft?
@tag.workflow_state = 'unpublished'
@tag.save
end
@module.touch
render :json => module_item_json(@tag, @current_user, session, @module, nil)
elsif @tag
@ -207,6 +211,7 @@ class ContextModuleItemsApiController < ApplicationController
# "must_submit", "min_score": Only apply to "Assignment" and "Quiz" types
# Inapplicable types will be ignored
# @argument module_item[completion_requirement][min_score] [Required for completion_requirement type 'min_score'] minimum score required to complete
# @undocumented @argument module_item[published] [Optional] Whether the module item is published and visible to students
#
# @example_request
#
@ -220,7 +225,7 @@ class ContextModuleItemsApiController < ApplicationController
#
# @returns Module Item
def update
@tag = @context.context_module_tags.find(params[:id])
@tag = @context.context_module_tags.not_deleted.find(params[:id])
if authorized_action(@tag.context_module, @current_user, :update)
return render :json => {:message => "missing module item parameter"}, :status => :bad_request unless params[:module_item]
@ -229,6 +234,16 @@ class ContextModuleItemsApiController < ApplicationController
@tag.indent = params[:module_item][:indent] if params[:module_item][:indent]
@tag.new_tab = value_to_boolean(params[:module_item][:new_tab]) if params[:module_item][:new_tab]
if params[:module_item].has_key?(:published)
if value_to_boolean(params[:module_item][:published])
@tag.publish
else
@tag.unpublish
end
@tag.update_asset_workflow_state!
@tag.context_module.save
end
if @tag.save && set_position && set_completion_requirement
@tag.update_asset_name! if params[:module_item][:title]
render :json => module_item_json(@tag, @current_user, session, @tag.context_module, nil)
@ -250,7 +265,7 @@ class ContextModuleItemsApiController < ApplicationController
#
# @returns Module Item
def destroy
@tag = @context.context_module_tags.find(params[:id])
@tag = @context.context_module_tags.not_deleted.find(params[:id])
if authorized_action(@tag.context_module, @current_user, :update)
@module = @tag.context_module
@tag.destroy
@ -263,7 +278,7 @@ class ContextModuleItemsApiController < ApplicationController
return true unless @tag && params[:module_item][:position]
@tag.reload
if @tag.insert_at_position(params[:module_item][:position], @tag.context_module.content_tags.active)
if @tag.insert_at_position(params[:module_item][:position], @tag.context_module.content_tags.not_deleted)
# see ContextModulesController#reorder_items
@tag.touch_context_module
ContentTag.update_could_be_locked(@tag.context_module.content_tags)
@ -276,6 +291,7 @@ class ContextModuleItemsApiController < ApplicationController
return false
end
end
protected :set_position
def set_completion_requirement
return true unless @tag && params[:module_item][:completion_requirement]
@ -296,4 +312,5 @@ class ContextModuleItemsApiController < ApplicationController
@module.completion_requirements = reqs
@module.save
end
protected :set_completion_requirement
end

View File

@ -136,7 +136,10 @@ class ContextModulesApiController < ApplicationController
modules.each do |mod|
case event
when 'publish'
mod.publish unless mod.active?
unless mod.active?
mod.publish
mod.publish_items!
end
when 'unpublish'
mod.unpublish unless mod.unpublished?
when 'delete'
@ -207,8 +210,7 @@ class ContextModulesApiController < ApplicationController
# @argument module[require_sequential_progress] [Optional] Whether module items must be unlocked in order
# @argument module[prerequisite_module_ids][] [Optional] IDs of Modules that must be completed before this one is unlocked
# Prerequisite modules must precede this module (i.e. have a lower position value), otherwise they will be ignored
# @undocumented @argument module[publish] [Optional] Set to publish the module
# @undocumented @argument module[unpublish] [Optional] Set to unpublish the module
# @undocumented @argument module[published] [Optional] Whether the module is published and visible to students
#
# @example_request
#
@ -235,10 +237,13 @@ class ContextModulesApiController < ApplicationController
end
end
if params[:module].delete :publish
@module.publish
elsif params[:module].delete :unpublish
@module.unpublish
if params[:module].has_key?(:published)
if value_to_boolean(params[:module][:published])
@module.publish
@module.publish_items!
else
@module.unpublish
end
end
if @module.update_attributes(module_parameters) && set_position

View File

@ -245,7 +245,7 @@ class ContextModulesController < ApplicationController
@module = @context.modules_visible_to(@current_user).find(params[:id])
respond_to do |format|
format.html { redirect_to named_context_url(@context, :context_context_modules_url, :anchor => "module_#{params[:id]}") }
format.json { render :json => (@module.content_tags.active.to_json) }
format.json { render :json => (@module.content_tags_visible_to(@current_user).to_json) }
end
end
@ -330,7 +330,7 @@ class ContextModulesController < ApplicationController
end
def remove_item
@tag = @context.context_module_tags.find(params[:id])
@tag = @context.context_module_tags.not_deleted.find(params[:id])
if authorized_action(@tag.context_module, @current_user, :update)
@module = @tag.context_module
@tag.destroy
@ -340,7 +340,7 @@ class ContextModulesController < ApplicationController
end
def update_item
@tag = @context.context_module_tags.find(params[:id])
@tag = @context.context_module_tags.not_deleted.find(params[:id])
if authorized_action(@tag.context_module, @current_user, :update)
@tag.title = params[:content_tag][:title] if params[:content_tag] && params[:content_tag][:title]
@tag.url = params[:content_tag][:url] if %w(ExternalUrl ContextExternalTool).include?(@tag.content_type) && params[:content_tag] && params[:content_tag][:url]
@ -378,6 +378,7 @@ class ContextModulesController < ApplicationController
if authorized_action(@module, @current_user, :update)
if params.delete :publish
@module.publish
@module.publish_items!
elsif params.delete :unpublish
@module.unpublish
end

View File

@ -56,11 +56,21 @@ class ContentTag < ActiveRecord::Base
end
workflow do
state :active
state :active do
event :unpublish, :transitions_to => :unpublished
end
state :unpublished do
event :publish, :transitions_to => :active
end
state :deleted
end
scope :active, where("content_tags.workflow_state<>'deleted'")
# Note: right now, being unpublished won't affect any
# behavior with the content tag itself.
# The next step (#CNVS-5491) will be to replace this line with the following:
#scope :active, where(:workflow_state => 'active')
scope :not_deleted, where("content_tags.workflow_state<>'deleted'")
attr_accessor :skip_touch
def touch_context_module
@ -161,7 +171,7 @@ class ContentTag < ActiveRecord::Base
end
def update_asset_name!
return if !self.sync_title_to_asset_title?
return unless self.sync_title_to_asset_title?
correct_context = self.content && self.content.respond_to?(:context) && self.content.context == self.context
if correct_context
if self.content.respond_to?("name=") && self.content.respond_to?("name") && self.content.name != self.title
@ -174,6 +184,29 @@ class ContentTag < ActiveRecord::Base
end
end
def update_asset_workflow_state!
return unless self.sync_workflow_state_to_asset?
correct_context = self.content && self.content.respond_to?(:context) && self.content.context == self.context
if correct_context
asset_workflow_state = nil
if self.unpublished? && self.content.respond_to?(:unpublished?)
asset_workflow_state = 'unpublished'
elsif self.active?
if self.content.respond_to?(:active?)
asset_workflow_state = 'active'
elsif self.content.respond_to?(:available?)
asset_workflow_state = 'available'
elsif self.content.respond_to?(:published?)
asset_workflow_state = 'published'
end
end
if asset_workflow_state
self.content.update_attribute(:workflow_state, asset_workflow_state)
self.class.update_for(self.content)
end
end
end
def self.delete_for(asset)
ContentTag.find_all_by_content_id_and_content_type(asset.id, asset.class.to_s).each{|t| t.destroy }
ContentTag.find_all_by_context_id_and_context_type(asset.id, asset.class.to_s).each{|t| t.destroy }
@ -226,21 +259,39 @@ class ContentTag < ActiveRecord::Base
end
def self.update_for(asset)
tags = ContentTag.where(:content_id => asset, :content_type => asset.class.to_s).select([:id, :tag_type, :content_type, :context_module_id]).all
tag_ids = tags.select{|t| t.sync_title_to_asset_title? }.map(&:id)
tags = ContentTag.where(:content_id => asset, :content_type => asset.class.to_s).not_deleted.select([:id, :tag_type, :content_type, :context_module_id]).all
module_ids = tags.select{|t| t.context_module_id }.map(&:context_module_id)
# update title
tag_ids = tags.select{|t| t.sync_title_to_asset_title? }.map(&:id)
attr_hash = {:updated_at => Time.now.utc}
{:display_name => :title, :name => :title, :title => :title}.each do |attr, val|
attr_hash[val] = asset.send(attr) if asset.respond_to?(attr)
end
attr_hash[:workflow_state] = 'deleted' if asset.respond_to?(:workflow_state) && asset.workflow_state == 'deleted'
ContentTag.where(:id => tag_ids).update_all(attr_hash)
# update workflow_state
tag_ids = tags.select{|t| t.sync_workflow_state_to_asset? }.map(&:id)
attr_hash = {:updated_at => Time.now.utc}
if asset.respond_to?(:workflow_state)
if ['active', 'available', 'published'].include?(asset.workflow_state)
attr_hash[:workflow_state] = 'active'
elsif ['unpublished', 'deleted'].include?(asset.workflow_state)
attr_hash[:workflow_state] = asset.workflow_state
end
end
ContentTag.where(:id => tag_ids).update_all(attr_hash) if attr_hash[:workflow_state]
ContentTag.touch_context_modules(module_ids)
end
def sync_title_to_asset_title?
self.tag_type != "learning_outcome_association" && !['ContextExternalTool', 'Attachment'].member?(self.content_type)
end
def sync_workflow_state_to_asset?
['Assignment', 'WikiPage'].include?(self.content_type)
end
def context_module_action(user, action, points=nil)
self.context_module.update_for(user, action, self, points) if self.context_module

View File

@ -109,6 +109,13 @@ class ContextModule < ActiveRecord::Base
scope :unpublished, where(:workflow_state => 'unpublished')
scope :not_deleted, where("context_modules.workflow_state<>'deleted'")
def publish_items!
self.content_tags.select{|t| t.unpublished?}.each do |tag|
tag.publish
tag.update_asset_workflow_state!
end
end
def update_student_progressions(user=nil)
# modules are ordered by position, so running through them in order will
# automatically handle issues with dependencies loading in the correct
@ -240,6 +247,14 @@ class ContextModule < ActiveRecord::Base
write_attribute(:completion_requirements, val)
end
def content_tags_visible_to(user)
if self.grants_right?(user, :update)
self.content_tags.not_deleted
else
self.content_tags.active
end
end
def add_item(params, added_item=nil, opts={})
params[:type] = params[:type].underscore if params[:type]
position = opts[:position] || (self.content_tags.active.map(&:position).compact.max || 0) + 1
@ -254,6 +269,8 @@ class ContextModule < ActiveRecord::Base
elsif params[:type] == "quiz"
item = opts[:quiz] || self.context.quizzes.active.find_by_id(params[:id])
end
workflow_state = item.workflow_state if item && item.respond_to?(:workflow_state) && ['active', 'unpublished'].include?(item.workflow_state)
workflow_state ||= 'active'
if params[:type] == 'external_url'
title = params[:title]
added_item ||= self.content_tags.build(:context => self.context)
@ -268,7 +285,7 @@ class ContextModule < ActiveRecord::Base
added_item.content_type = 'ExternalUrl'
added_item.context_module_id = self.id
added_item.indent = params[:indent] || 0
added_item.workflow_state = 'active'
added_item.workflow_state = workflow_state
added_item.save
added_item
elsif params[:type] == 'context_external_tool' || params[:type] == 'external_tool'
@ -290,7 +307,7 @@ class ContextModule < ActiveRecord::Base
}
added_item.context_module_id = self.id
added_item.indent = params[:indent] || 0
added_item.workflow_state = 'active'
added_item.workflow_state = workflow_state
added_item.save
added_item
elsif params[:type] == 'context_module_sub_header' || params[:type] == 'sub_header'
@ -306,7 +323,7 @@ class ContextModule < ActiveRecord::Base
added_item.content_type = 'ContextModuleSubHeader'
added_item.context_module_id = self.id
added_item.indent = params[:indent] || 0
added_item.workflow_state = 'active'
added_item.workflow_state = workflow_state
added_item.save
added_item
else
@ -322,7 +339,7 @@ class ContextModule < ActiveRecord::Base
}
added_item.context_module_id = self.id
added_item.indent = params[:indent] || 0
added_item.workflow_state = 'active'
added_item.workflow_state = workflow_state
added_item.save
added_item
end

View File

@ -123,8 +123,12 @@ class WikiPage < ActiveRecord::Base
after_save :remove_changed_flag
workflow do
state :active
state :unpublished
state :active do
event :unpublish, :transitions_to => :unpublished
end
state :unpublished do
event :publish, :transitions_to => :active
end
state :post_delayed do
event :delayed_post, :transitions_to => :active
end

View File

@ -19,7 +19,7 @@ module Api::V1::ContextModule
include Api::V1::Json
include Api::V1::User
MODULE_JSON_ATTRS = %w(id position name unlock_at workflow_state)
MODULE_JSON_ATTRS = %w(id position name unlock_at)
MODULE_ITEM_JSON_ATTRS = %w(id position title indent)
@ -32,6 +32,7 @@ module Api::V1::ContextModule
hash['state'] = progression.workflow_state
hash['completed_at'] = progression.completed_at
end
hash['published'] = context_module.active? if context_module.grants_right?(current_user, :update)
hash
end
@ -91,6 +92,8 @@ module Api::V1::ContextModule
hash['completion_requirement'] = ch
end
hash['published'] = content_tag.active? if context_module.grants_right?(current_user, :update)
hash
end
end

View File

@ -61,6 +61,7 @@ describe "Module Items API", :type => :integration do
end
it "should list module items" do
@assignment_tag.unpublish
json = api_call(:get, "/api/v1/courses/#{@course.id}/modules/#{@module1.id}/items",
:controller => "context_module_items_api", :action => "index", :format => "json",
:course_id => "#{@course.id}", :module_id => "#{@module1.id}")
@ -74,7 +75,8 @@ describe "Module Items API", :type => :integration do
"url" => "http://www.example.com/api/v1/courses/#{@course.id}/assignments/#{@assignment.id}",
"title" => @assignment_tag.title,
"indent" => 0,
"completion_requirement" => { "type" => "must_submit" }
"completion_requirement" => { "type" => "must_submit" },
"published" => false
},
{
"type" => "Quiz",
@ -85,7 +87,8 @@ describe "Module Items API", :type => :integration do
"position" => 2,
"title" => @quiz_tag.title,
"indent" => 0,
"completion_requirement" => { "type" => "min_score", "min_score" => 10 }
"completion_requirement" => { "type" => "min_score", "min_score" => 10 },
"published" => true
},
{
"type" => "Discussion",
@ -96,14 +99,16 @@ describe "Module Items API", :type => :integration do
"url" => "http://www.example.com/api/v1/courses/#{@course.id}/discussion_topics/#{@topic.id}",
"title" => @topic_tag.title,
"indent" => 0,
"completion_requirement" => { "type" => "must_contribute" }
"completion_requirement" => { "type" => "must_contribute" },
"published" => true
},
{
"type" => "SubHeader",
"id" => @subheader_tag.id,
"position" => 4,
"title" => @subheader_tag.title,
"indent" => 0
"indent" => 0,
"published" => true
},
{
"type" => "ExternalUrl",
@ -113,7 +118,8 @@ describe "Module Items API", :type => :integration do
"position" => 5,
"title" => @external_url_tag.title,
"indent" => 1,
"completion_requirement" => { "type" => "must_view" }
"completion_requirement" => { "type" => "must_view" },
"published" => true
}
]
end
@ -131,9 +137,11 @@ describe "Module Items API", :type => :integration do
"title" => @wiki_page_tag.title,
"indent" => 0,
"url" => "http://www.example.com/api/v1/courses/#{@course.id}/pages/#{@wiki_page.url}",
"page_url" => @wiki_page.url
"page_url" => @wiki_page.url,
"published" => true
}
@attachment_tag.unpublish
json = api_call(:get, "/api/v1/courses/#{@course.id}/modules/#{@module2.id}/items/#{@attachment_tag.id}",
:controller => "context_module_items_api", :action => "show", :format => "json",
:course_id => "#{@course.id}", :module_id => "#{@module2.id}",
@ -146,7 +154,8 @@ describe "Module Items API", :type => :integration do
"position" => 2,
"title" => @attachment_tag.title,
"indent" => 0,
"url" => "http://www.example.com/api/v1/files/#{@attachment.id}"
"url" => "http://www.example.com/api/v1/files/#{@attachment.id}",
"published" => false
}
end
@ -414,6 +423,53 @@ describe "Module Items API", :type => :integration do
req = @module1.completion_requirements.find{|h| h[:id] == json['id'].to_i}
req.should be_nil
end
it "should publish module items" do
course_with_student(:course => @course, :active_all => true)
@user = @teacher
@assignment.submit_homework(@student, :body => "done!")
@assignment_tag.unpublish
@assignment_tag.workflow_state.should == 'unpublished'
@module1.save
# Uncomment this after implenting stricter content_tags.active scope - #CNVS-5491
#@module1.evaluate_for(@student).workflow_state.should == 'unlocked'
json = api_call(:put, "/api/v1/courses/#{@course.id}/modules/#{@module1.id}/items/#{@assignment_tag.id}",
{:controller => "context_module_items_api", :action => "update", :format => "json",
:course_id => "#{@course.id}", :module_id => "#{@module1.id}", :id => "#{@assignment_tag.id}"},
{:module_item => {:published => '1'}}
)
json['published'].should == true
@assignment_tag.reload
@assignment_tag.workflow_state.should == 'active'
end
it "should unpublish module items" do
course_with_student(:course => @course, :active_all => true)
@user = @teacher
@assignment.submit_homework(@student, :body => "done!")
@module1.evaluate_for(@student).workflow_state.should == 'started'
json = api_call(:put, "/api/v1/courses/#{@course.id}/modules/#{@module1.id}/items/#{@assignment_tag.id}",
{:controller => "context_module_items_api", :action => "update", :format => "json",
:course_id => "#{@course.id}", :module_id => "#{@module1.id}", :id => "#{@assignment_tag.id}"},
{:module_item => {:published => '0'}}
)
json['published'].should == false
@assignment_tag.reload
@assignment_tag.workflow_state.should == 'unpublished'
@module1.reload
# Uncomment this after implenting stricter content_tags.active scope - #CNVS-5491
#@module1.evaluate_for(@student).workflow_state.should == 'unlocked'
end
end
it "should delete a module item" do
@ -434,11 +490,12 @@ describe "Module Items API", :type => :integration do
end
it "should list module items" do
@assignment_tag.unpublish
json = api_call(:get, "/api/v1/courses/#{@course.id}/modules/#{@module1.id}/items",
:controller => "context_module_items_api", :action => "index", :format => "json",
:course_id => "#{@course.id}", :module_id => "#{@module1.id}")
json.map{|item| item['id']}.sort.should == @module1.content_tags.map(&:id).sort
json.map{|item| item['id']}.sort.should == @module1.content_tags.active.map(&:id).sort
#also for locked modules that have completion requirements
@assignment2 = @course.assignments.create!(:name => "pls submit", :submission_types => ["online_text_entry"])
@ -471,6 +528,15 @@ describe "Module Items API", :type => :integration do
json['completion_requirement']['completed'].should be_true
end
it "should not show unpublished items" do
pending 'restricting content_tags.active scope - #CNVS-5491'
@assignment_tag.unpublish
json = api_call(:get, "/api/v1/courses/#{@course.id}/modules/#{@module1.id}/items/#{@assignment_tag.id}",
{:controller => "context_module_items_api", :action => "show", :format => "json",
:course_id => "#{@course.id}", :module_id => "#{@module1.id}",
:id => "#{@assignment_tag.id}"}, {}, {}, {:expected_status => 404})
end
it "should mark viewed and redirect external URLs" do
raw_api_call(:get, "/api/v1/courses/#{@course.id}/module_item_redirect/#{@external_url_tag.id}",
:controller => "context_module_items_api", :action => "redirect",

View File

@ -72,7 +72,7 @@ describe "Modules API", :type => :integration do
"require_sequential_progress" => false,
"prerequisite_module_ids" => [],
"id" => @module1.id,
"workflow_state" => "active"
"published" => true
},
{
"name" => @module2.name,
@ -81,7 +81,7 @@ describe "Modules API", :type => :integration do
"require_sequential_progress" => true,
"prerequisite_module_ids" => [@module1.id],
"id" => @module2.id,
"workflow_state" => "active"
"published" => true
},
{
"name" => @module3.name,
@ -90,7 +90,7 @@ describe "Modules API", :type => :integration do
"require_sequential_progress" => false,
"prerequisite_module_ids" => [],
"id" => @module3.id,
"workflow_state" => "unpublished"
"published" => false
}
]
end
@ -106,7 +106,7 @@ describe "Modules API", :type => :integration do
"require_sequential_progress" => true,
"prerequisite_module_ids" => [@module1.id],
"id" => @module2.id,
"workflow_state" => "active"
"published" => true
}
end
@ -121,7 +121,7 @@ describe "Modules API", :type => :integration do
"require_sequential_progress" => false,
"prerequisite_module_ids" => [],
"id" => @module3.id,
"workflow_state" => "unpublished"
"published" => false
}
end
@ -153,13 +153,23 @@ describe "Modules API", :type => :integration do
@test_modules[2..3].each { |m| m.update_attribute(:workflow_state , 'unpublished') }
@test_modules.map { |tm| tm.workflow_state }.should == %w(active active unpublished unpublished)
@modules_to_update = [@test_modules[1], @test_modules[3]]
@wiki_page = @course.wiki.wiki_page
@wiki_page.workflow_state = 'unpublished'; @wiki_page.save!
@wiki_page_tag = @test_modules[3].add_item(:id => @wiki_page.id, :type => 'wiki_page')
@ids_to_update = @modules_to_update.map(&:id)
end
it "should publish modules" do
it "should publish modules (and their tags)" do
json = api_call(:put, @path, @path_opts, { :event => 'publish', :module_ids => @ids_to_update })
json['completed'].sort.should == @ids_to_update
@test_modules.map { |tm| tm.reload.workflow_state }.should == %w(active active unpublished active)
@wiki_page_tag.reload
@wiki_page_tag.active?.should == true
@wiki_page.reload
@wiki_page.active?.should == true
end
it "should unpublish modules" do
@ -237,6 +247,11 @@ describe "Modules API", :type => :integration do
@module1 = @course.context_modules.create(:name => "unpublished")
@module1.workflow_state = 'unpublished'
@module1.save!
@wiki_page = @course.wiki.wiki_page
@wiki_page.workflow_state = 'unpublished'; @wiki_page.save!
@wiki_page_tag = @module1.add_item(:id => @wiki_page.id, :type => 'wiki_page')
@module2 = @course.context_modules.create!(:name => "published")
end
@ -286,24 +301,29 @@ describe "Modules API", :type => :integration do
@module2.position.should == 2
end
it "should publish modules" do
it "should publish modules (and their tags)" do
json = api_call(:put, "/api/v1/courses/#{@course.id}/modules/#{@module1.id}",
{:controller => "context_modules_api", :action => "update", :format => "json",
:course_id => "#{@course.id}", :id => "#{@module1.id}"},
{:module => {:publish => '1'}}
{:module => {:published => '1'}}
)
json['workflow_state'].should == 'active'
json['published'].should == true
@module1.reload
@module1.active?.should == true
@wiki_page_tag.reload
@wiki_page_tag.active?.should == true
@wiki_page.reload
@wiki_page.active?.should == true
end
it "should unpublish modules" do
json = api_call(:put, "/api/v1/courses/#{@course.id}/modules/#{@module2.id}",
{:controller => "context_modules_api", :action => "update", :format => "json",
:course_id => "#{@course.id}", :id => "#{@module2.id}"},
{:module => {:unpublish => '1'}}
{:module => {:published => '0'}}
)
json['workflow_state'].should == 'unpublished'
json['published'].should == false
@module2.reload
@module2.unpublished?.should == true
end
@ -485,7 +505,7 @@ describe "Modules API", :type => :integration do
:controller => "context_modules_api", :action => "index", :format => "json",
:course_id => "#{@course.id}")
json.length.should == 2
json.each{|cm| cm['workflow_state'].should == 'active'}
json.each{|cm| @course.context_modules.find(cm['id']).workflow_state.should == 'active'}
end
it "should not show a single unpublished module" do

View File

@ -160,6 +160,56 @@ describe ContentTag do
@assignment.reload
@assignment.title.should == 'some assignment (renamed)'
end
it "should publish/unpublish the tag if the linked wiki page is published/unpublished" do
course
@page = @course.wiki.wiki_pages.create!(:title => "some page")
@page.workflow_state = 'unpublished'
@page.save!
@module = @course.context_modules.create!(:name => "module")
@tag = @module.add_item({:type => 'WikiPage', :title => 'some page', :id => @page.id})
@tag.workflow_state.should == 'unpublished'
@page.reload
@page.workflow_state = 'active'
@page.save!
@tag.reload
@tag.workflow_state.should == 'active'
@page.reload
@page.workflow_state = 'unpublished'
@page.save!
@tag.reload
@tag.workflow_state.should == 'unpublished'
end
it "should publish/unpublish the linked wiki page (and its tags) if the tag is published/unpublished" do
course
@page = @course.wiki.wiki_pages.create!(:title => "some page")
@page.workflow_state = 'unpublished'
@page.save!
@module = @course.context_modules.create!(:name => "module")
@tag = @module.add_item({:type => 'WikiPage', :title => 'some page', :id => @page.id})
@tag2 = @module.add_item({:type => 'WikiPage', :title => 'some page', :id => @page.id})
@tag.reload
@tag.workflow_state = 'active'
@tag.save!
@tag.update_asset_workflow_state!
@page.reload
@page.workflow_state.should == 'active'
@tag2.reload
@tag2.workflow_state.should == 'active'
@tag.reload
@tag.workflow_state = 'unpublished'
@tag.save!
@tag.update_asset_workflow_state!
@page.reload
@page.workflow_state.should == 'unpublished'
@tag2.reload
@tag2.workflow_state.should == 'unpublished'
end
it "should not rename tag if linked attachment is renamed" do
course