give content_exports polymorphic context
test plan: * content export regressions closes #CNVS-13468 Change-Id: I44dcf077967d4273c0ae9056074b03853d66a5dc Reviewed-on: https://gerrit.instructure.com/35745 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Cody Cutrer <cody@instructure.com> Reviewed-by: Jon Willesen <jonw@instructure.com> QA-Review: Clare Strong <clare@instructure.com> Product-Review: James Williams <jamesw@instructure.com>
This commit is contained in:
parent
8611d9ce09
commit
3fa2e9c3db
|
@ -127,8 +127,7 @@ class ContentExportsApiController < ApplicationController
|
|||
def create
|
||||
if authorized_action(@context, @current_user, :read_as_admin)
|
||||
return render json: { message: 'invalid export_type' }, status: :bad_request unless %w(qti common_cartridge).include?(params[:export_type])
|
||||
export = ContentExport.new
|
||||
export.course = @context
|
||||
export = @context.content_exports.build
|
||||
export.user = @current_user
|
||||
export.workflow_state = 'created'
|
||||
|
||||
|
|
|
@ -44,8 +44,7 @@ class ContentExportsController < ApplicationController
|
|||
return render_unauthorized_action unless @context.grants_all_rights?(@current_user, :read, :read_as_admin)
|
||||
|
||||
if @context.content_exports.running.count == 0
|
||||
export = ContentExport.new
|
||||
export.course = @context
|
||||
export = @context.content_exports.build
|
||||
export.user = @current_user
|
||||
export.workflow_state = 'created'
|
||||
if params[:export_type] == 'qti'
|
||||
|
@ -60,7 +59,7 @@ class ContentExportsController < ApplicationController
|
|||
export.export_course
|
||||
render_export(export)
|
||||
else
|
||||
render :json => {:error_message => t('errors.couldnt_create', "Couldn't create course export.")}
|
||||
render :json => {:error_message => t('errors.couldnt_create', "Couldn't create content export.")}
|
||||
end
|
||||
else
|
||||
# an export is already running, just return it
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<% define_content :link do %>
|
||||
<%= HostUrl.protocol %>://<%= HostUrl.context_host(asset.course) %>/<%= asset.course.class.to_s.downcase.pluralize %>/<%= asset.course_id %>/content_exports
|
||||
<%= HostUrl.protocol %>://<%= HostUrl.context_host(asset.context) %>/<%= asset.context.class.to_s.downcase.pluralize %>/<%= asset.context_id %>/content_exports
|
||||
<% end %>
|
||||
|
||||
<% define_content :subject do %>
|
||||
<%= t :subject, "Course Export Failed: %{course}", :course => asset.course.name %>
|
||||
<%= t :subject, "Course Export Failed: %{course}", :course => asset.context.name %>
|
||||
<% end %>
|
||||
|
||||
<%= t :body, <<-BODY, :course => asset.course.name, :id => %{"ContentExport:#{asset.id}"}
|
||||
<%= t :body, <<-BODY, :course => asset.context.name, :id => %{"ContentExport:#{asset.id}"}
|
||||
There was a problem exporting the course "%{course}".
|
||||
Please notify your system administrator, and give them the following export identifier: %{id}.
|
||||
BODY
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<% define_content :link do %>
|
||||
<%= HostUrl.protocol %>://<%= HostUrl.context_host(asset.course) %>/<%= asset.course.class.to_s.downcase.pluralize %>/<%= asset.course_id %>/content_exports
|
||||
<%= HostUrl.protocol %>://<%= HostUrl.context_host(asset.context) %>/<%= asset.context.class.to_s.downcase.pluralize %>/<%= asset.context_id %>/content_exports
|
||||
<% end %>
|
||||
|
||||
<% define_content :subject do %>
|
||||
<%= t :subject, "Course Export Failed: %{course}", :course => asset.course.name %>
|
||||
<%= t :subject, "Course Export Failed: %{course}", :course => asset.context.name %>
|
||||
<% end %>
|
||||
|
||||
<% define_content :footer_link do %>
|
||||
|
@ -13,7 +13,7 @@
|
|||
<% end %>
|
||||
|
||||
<p>
|
||||
<%= t :body2, <<-BODY, :course => asset.course.name, :wrapper => '<em>\1</em>'
|
||||
<%= t :body2, <<-BODY, :course => asset.context.name, :wrapper => '<em>\1</em>'
|
||||
There was a problem exporting the course *%{course}*.
|
||||
Please notify your system administrator, and give them the following export identifier:
|
||||
BODY
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<% define_content :link do %>
|
||||
<%= HostUrl.protocol %>://<%= HostUrl.context_host(asset.course) %>/<%= asset.course.class.to_s.downcase.pluralize %>/<%= asset.course_id %>/content_exports
|
||||
<%= HostUrl.protocol %>://<%= HostUrl.context_host(asset.context) %>/<%= asset.context.class.to_s.downcase.pluralize %>/<%= asset.context_id %>/content_exports
|
||||
<% end %>
|
||||
|
||||
<% define_content :subject do %>
|
||||
<%= t :subject, "Course Export Finished: %{course}", :course => asset.course.name %>
|
||||
<%= t :subject, "Course Export Finished: %{course}", :course => asset.context.name %>
|
||||
<% end %>
|
||||
|
||||
<%= t :body, %{Your course export for "%{course}" has finished.}, :course => asset.course.name %>
|
||||
<%= t :body, %{Your content export for "%{course}" has finished.}, :course => asset.context.name %>
|
||||
|
||||
<%= content :link %>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<% define_content :link do %>
|
||||
<%= HostUrl.protocol %>://<%= HostUrl.context_host(asset.course) %>/<%= asset.course.class.to_s.downcase.pluralize %>/<%= asset.course_id %>/content_exports
|
||||
<%= HostUrl.protocol %>://<%= HostUrl.context_host(asset.context) %>/<%= asset.context.class.to_s.downcase.pluralize %>/<%= asset.context_id %>/content_exports
|
||||
<% end %>
|
||||
<% define_content :subject do %>
|
||||
<%= t :subject, "Course Export Finished: %{course}", :course => asset.course.name %>
|
||||
<%= t :subject, "Course Export Finished: %{course}", :course => asset.context.name %>
|
||||
<% end %>
|
||||
<p><%= t :body2, %{Your course export for *%{course}* has finished.}, :course => asset.course.name, :wrapper => '<em>\1</em>' %></p>
|
||||
<p><%= t :body2, %{Your course export for *%{course}* has finished.}, :course => asset.context.name, :wrapper => '<em>\1</em>' %></p>
|
||||
|
||||
<p><a href="<%= content :link %>"><%= t :link, "Click to view exports" %></a></p>
|
||||
|
|
|
@ -18,24 +18,25 @@
|
|||
|
||||
class ContentExport < ActiveRecord::Base
|
||||
include Workflow
|
||||
belongs_to :course
|
||||
belongs_to :context, :polymorphic => true
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :attachment
|
||||
belongs_to :content_migration
|
||||
has_many :attachments, :as => :context, :dependent => :destroy
|
||||
has_a_broadcast_policy
|
||||
serialize :settings
|
||||
attr_accessible :course
|
||||
validates_presence_of :course_id, :workflow_state
|
||||
has_one :job_progress, :class_name => 'Progress', :as => :context
|
||||
attr_accessible :context
|
||||
validates_presence_of :context_id, :workflow_state
|
||||
validates_inclusion_of :context_type, :in => ['Course']
|
||||
|
||||
alias_method :context, :course
|
||||
has_one :job_progress, :class_name => 'Progress', :as => :context
|
||||
|
||||
#export types
|
||||
COMMON_CARTRIDGE = 'common_cartridge'
|
||||
COURSE_COPY = 'course_copy'
|
||||
QTI = 'qti'
|
||||
|
||||
|
||||
workflow do
|
||||
state :created
|
||||
state :exporting
|
||||
|
@ -49,21 +50,25 @@ class ContentExport < ActiveRecord::Base
|
|||
p.dispatch :content_export_finished
|
||||
p.to { [user] }
|
||||
p.whenever {|record|
|
||||
record.changed_state(:exported) && self.content_migration.blank?
|
||||
record.context_type == 'Course' && record.changed_state(:exported) && self.content_migration.blank?
|
||||
}
|
||||
|
||||
p.dispatch :content_export_failed
|
||||
p.to { [user] }
|
||||
p.whenever {|record|
|
||||
record.changed_state(:failed) && self.content_migration.blank?
|
||||
record.context_type == 'Course' && record.changed_state(:failed) && self.content_migration.blank?
|
||||
}
|
||||
end
|
||||
|
||||
set_policy do
|
||||
given { |user, session| self.course.grants_right?(user, session, :manage_files) }
|
||||
given { |user, session| self.context.grants_right?(user, session, :manage_files) }
|
||||
can :manage_files and can :read
|
||||
end
|
||||
|
||||
def course
|
||||
raise "Use context instead"
|
||||
end
|
||||
|
||||
def export_course(opts={})
|
||||
self.workflow_state = 'exporting'
|
||||
self.save
|
||||
|
@ -185,7 +190,7 @@ class ContentExport < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def root_account
|
||||
self.course.root_account
|
||||
self.context.try_rescue(:root_account)
|
||||
end
|
||||
|
||||
def running?
|
||||
|
|
|
@ -195,7 +195,7 @@ class Course < ActiveRecord::Base
|
|||
has_many :asset_user_accesses, :as => :context
|
||||
has_many :role_overrides, :as => :context
|
||||
has_many :content_migrations, :as => :context
|
||||
has_many :content_exports
|
||||
has_many :content_exports, :as => :context
|
||||
has_many :course_imports
|
||||
has_many :alerts, :as => :context, :include => :criteria
|
||||
has_many :appointment_group_contexts, :as => :context
|
||||
|
|
|
@ -26,6 +26,7 @@ class ActiveRecord::Base
|
|||
'assignments' => %w(sequence_position minimum_required_blog_posts minimum_required_blog_comments reminders_created_for_due_at publishing_reminder_sent previously_published before_quiz_submission_types),
|
||||
'attachments' => %w(enrollment_id cached_s3_url s3_url_cached_at scribd_account_id scribd_user),
|
||||
'calendar_events' => %w(calendar_event_repeat_id for_repeat_on),
|
||||
'content_exports' => %w(course_id),
|
||||
'content_tags' => %w(sequence_position context_module_association_id),
|
||||
'context_modules' => %w(downstream_modules),
|
||||
'conversation_messages' => %w(context_message_id),
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
class AddContextToContentExports < ActiveRecord::Migration
|
||||
tag :predeploy
|
||||
|
||||
def self.up
|
||||
add_column :content_exports, :context_type, :string
|
||||
add_column :content_exports, :context_id, :integer, :limit => 8
|
||||
|
||||
remove_foreign_key :content_exports, :courses
|
||||
|
||||
if connection.adapter_name == 'PostgreSQL'
|
||||
create_trigger("content_export_after_insert_row_when_context_id_is_null__tr", :generated => true).
|
||||
on("content_exports").
|
||||
after(:insert).
|
||||
where("NEW.context_id IS NULL") do
|
||||
<<-SQL_ACTIONS
|
||||
UPDATE content_exports
|
||||
SET context_id = NEW.course_id
|
||||
WHERE id = NEW.id
|
||||
SQL_ACTIONS
|
||||
end
|
||||
|
||||
execute("ALTER TABLE content_exports ALTER context_type SET DEFAULT 'Course'")
|
||||
end
|
||||
|
||||
while ContentExport.where("context_id IS NULL AND course_id IS NOT NULL").limit(1000).
|
||||
update_all("context_id = course_id, context_type = 'Course'") > 0; end
|
||||
end
|
||||
|
||||
def self.down
|
||||
while ContentExport.where("course_id IS NULL AND context_id IS NOT NULL AND context_type = ?", "Course").
|
||||
limit(1000).update_all("course_id = context_id") > 0; end
|
||||
|
||||
add_foreign_key :content_exports, :courses
|
||||
|
||||
remove_column :content_exports, :context_type
|
||||
remove_column :content_exports, :context_id
|
||||
end
|
||||
end
|
|
@ -0,0 +1,16 @@
|
|||
class RemoveCourseIdFromContentExports < ActiveRecord::Migration
|
||||
tag :postdeploy
|
||||
|
||||
def self.up
|
||||
if connection.adapter_name == 'PostgreSQL'
|
||||
drop_trigger("content_export_after_insert_row_when_context_id_is_null__tr", "content_exports", :generated => true)
|
||||
execute("ALTER TABLE content_exports ALTER context_type DROP DEFAULT")
|
||||
end
|
||||
|
||||
remove_column :content_exports, :course_id
|
||||
end
|
||||
|
||||
def self.down
|
||||
add_column :content_exports, :course_id, :integer, :limit => 8
|
||||
end
|
||||
end
|
|
@ -21,7 +21,8 @@ module Api::V1::ContentExport
|
|||
include Api::V1::Attachment
|
||||
|
||||
def content_export_json(export, current_user, session)
|
||||
json = api_json(export, current_user, session, :only => %w(id user_id created_at course_id workflow_state export_type))
|
||||
json = api_json(export, current_user, session, :only => %w(id user_id created_at workflow_state export_type))
|
||||
json['course_id'] = export.context_id if export.context_type == 'Course'
|
||||
if export.attachment && !export.for_course_copy?
|
||||
json[:attachment] = attachment_json(export.attachment, current_user, {}, {:can_manage_files => true})
|
||||
end
|
||||
|
|
|
@ -27,11 +27,11 @@ class Canvas::Migration::Worker::CourseCopyWorker < Struct.new(:migration_id)
|
|||
cm.job_progress.start
|
||||
|
||||
begin
|
||||
source = cm.source_course || Course.find(cm.migration_settings[:source_course_id])
|
||||
ce = ContentExport.new
|
||||
ce.context = source
|
||||
ce.content_migration = cm
|
||||
ce.selected_content = cm.copy_options
|
||||
source = cm.source_course || Course.find(cm.migration_settings[:source_course_id])
|
||||
ce.course = source
|
||||
ce.export_type = ContentExport::COURSE_COPY
|
||||
ce.user = cm.user
|
||||
ce.save!
|
||||
|
|
|
@ -28,7 +28,8 @@ module CC
|
|||
|
||||
def initialize(content_export, opts={})
|
||||
@content_export = content_export
|
||||
@course = opts[:course] || @content_export.course
|
||||
@course = opts[:course] || @content_export.context
|
||||
raise "CCExporter supports only Courses" unless @course.is_a?(Course) # a Course is a Course, of course, of course
|
||||
@user = opts[:user] || @content_export.user
|
||||
@export_dir = nil
|
||||
@manifest = nil
|
||||
|
|
|
@ -54,7 +54,7 @@ describe ContentExportsApiController, type: :request do
|
|||
def course_copy_export
|
||||
export = t_course.content_exports.create
|
||||
export.export_type = 'course_copy'
|
||||
export.save
|
||||
export.save!
|
||||
export
|
||||
end
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ describe "Common Cartridge exporting" do
|
|||
message = "fail"
|
||||
course.stubs(:wiki).raises(message)
|
||||
content_export = ContentExport.new
|
||||
content_export.course = course
|
||||
content_export.context = course
|
||||
content_export.user = user
|
||||
content_export.save!
|
||||
|
||||
|
@ -25,9 +25,8 @@ describe "Common Cartridge exporting" do
|
|||
|
||||
before do
|
||||
course_with_teacher
|
||||
@ce = ContentExport.new
|
||||
@ce = @course.content_exports.build
|
||||
@ce.export_type = ContentExport::COURSE_COPY
|
||||
@ce.course = @course
|
||||
@ce.user = @user
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
#
|
||||
# Copyright (C) 2011 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')
|
||||
require 'db/migrate/20140530195058_add_context_to_content_exports.rb'
|
||||
require 'db/migrate/20140530195059_remove_course_id_from_content_exports.rb'
|
||||
|
||||
describe 'AddContextToContentExports' do
|
||||
describe "up" do
|
||||
it "should populate all content exports with course context type and context id" do
|
||||
pending("PostgreSQL specific") unless ContentExport.connection.adapter_name == 'PostgreSQL'
|
||||
course1 = course
|
||||
course2 = course
|
||||
|
||||
RemoveCourseIdFromContentExports.down
|
||||
AddContextToContentExports.down
|
||||
|
||||
ContentExport.connection.execute "INSERT INTO content_exports(course_id, workflow_state, created_at, updated_at) VALUES(#{course1.id}, '', '2014-07-07', '2014-07-07')"
|
||||
|
||||
AddContextToContentExports.up
|
||||
ContentExport.connection.execute "INSERT INTO content_exports(course_id, workflow_state, created_at, updated_at) VALUES(#{course2.id}, '', '2014-07-07', '2014-07-07')"
|
||||
RemoveCourseIdFromContentExports.up
|
||||
|
||||
ce1 = course1.content_exports.first
|
||||
ce2 = course2.content_exports.first
|
||||
ce1.context_type.should == 'Course'
|
||||
ce1.context_id = course1.id
|
||||
ce2.context_type.should == 'Course'
|
||||
ce2.context_id.should == course2.id
|
||||
end
|
||||
end
|
||||
end
|
|
@ -22,7 +22,8 @@ describe ContentExport do
|
|||
|
||||
context "export_object?" do
|
||||
before :once do
|
||||
@ce = ContentExport.new(course: Account.default.courses.create!)
|
||||
course = Account.default.courses.create!
|
||||
@ce = course.content_exports.create!
|
||||
end
|
||||
|
||||
it "should return true for everything if there are no copy options" do
|
||||
|
@ -62,7 +63,8 @@ describe ContentExport do
|
|||
|
||||
context "add_item_to_export" do
|
||||
before :once do
|
||||
@ce = ContentExport.new(course: Account.default.courses.create!)
|
||||
course = Account.default.courses.create!
|
||||
@ce = course.content_exports.create!
|
||||
end
|
||||
|
||||
it "should not add nil" do
|
||||
|
@ -98,7 +100,7 @@ describe ContentExport do
|
|||
context "notifications" do
|
||||
before :once do
|
||||
course_with_teacher(:active_all => true)
|
||||
@ce = ContentExport.create! { |ce| ce.user = @user; ce.course = @course }
|
||||
@ce = @course.content_exports.create! { |ce| ce.user = @user }
|
||||
|
||||
Notification.create!(:name => 'Content Export Finished', :category => 'Migration')
|
||||
Notification.create!(:name => 'Content Export Failed', :category => 'Migration')
|
||||
|
|
|
@ -41,10 +41,9 @@ describe ContentMigration do
|
|||
end
|
||||
|
||||
it "should show correct progress" do
|
||||
ce = ContentExport.new
|
||||
ce = @course.content_exports.build
|
||||
ce.export_type = ContentExport::COMMON_CARTRIDGE
|
||||
ce.content_migration = @cm
|
||||
ce.course = @course
|
||||
@cm.content_export = ce
|
||||
ce.save!
|
||||
|
||||
|
|
Loading…
Reference in New Issue