added ui for course exports
refs #3396 Change-Id: I07c30535e042dada7c7d4174a7bb8148b7f6b1fb Reviewed-on: https://gerrit.instructure.com/3039 Tested-by: Hudson <hudson@instructure.com> Reviewed-by: Brian Palmer <brianp@instructure.com>
This commit is contained in:
parent
53fc924ced
commit
fc4cc9ff46
|
@ -237,6 +237,8 @@ class ApplicationController < ActionController::Base
|
|||
elsif request.path.match(/\A\/profile/) || request.path == '/' || request.path.match(/\A\/dashboard\/files/) || request.path.match(/\A\/calendar/) || request.path.match(/\A\/assignments/) || request.path.match(/\A\/files/)
|
||||
@context = @current_user
|
||||
@context_membership = @context
|
||||
elsif params[:content_export_id]
|
||||
@context = ContentExport.find(params[:content_export_id])
|
||||
end
|
||||
if @context.try_rescue(:only_wiki_is_public) && params[:controller].match(/wiki/) && !@current_user && (!@context.is_a?(Course) || session[:enrollment_uuid_course_id] != @context.id)
|
||||
@show_left_side = false
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
class ContentExportsController < ApplicationController
|
||||
before_filter :require_context
|
||||
before_filter { |c| c.active_tab = "settings" }
|
||||
|
||||
def index
|
||||
if authorized_action(@context, @current_user, :manage)
|
||||
@exports = @context.content_exports.active
|
||||
@current_export_id = nil
|
||||
if export = @context.content_exports.running.first
|
||||
@current_export_id = export.id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def show
|
||||
if authorized_action(@context, @current_user, :manage)
|
||||
if export = @context.content_exports.find_by_id(params[:id])
|
||||
render_export(export)
|
||||
else
|
||||
render :json => {:errors => {:base => "Export does not exist"}}.to_json, :status => :bad_request
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
if authorized_action(@context, @current_user, :manage)
|
||||
if @context.content_exports.running.count == 0
|
||||
export = ContentExport.new
|
||||
export.course = @context
|
||||
export.user = @current_user
|
||||
export.workflow_state = 'created'
|
||||
export.export_type = 'common_cartridge'
|
||||
export.progress = 0
|
||||
if export.save
|
||||
export.export_course
|
||||
render_export(export)
|
||||
else
|
||||
render :json => {:error_message => "Couldn't create course export."}.to_json
|
||||
end
|
||||
else
|
||||
# an export is already running, just return it
|
||||
export = @context.content_exports.running.first
|
||||
render_export(export)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
if authorized_action(@context, @current_user, :manage)
|
||||
if export = @context.content_exports.find_by_id(params[:id])
|
||||
export.destroy
|
||||
render :json => {:success=>'true'}.to_json
|
||||
else
|
||||
render :json => {:errors => {:base => "Export does not exist"}}.to_json, :status => :bad_request
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def xml_schema
|
||||
file = nil
|
||||
if params[:version]
|
||||
file = Rails.root + "lib/canvas/cc/xsd/#{params[:version]}.xsd"
|
||||
end
|
||||
|
||||
if File.exists?(file)
|
||||
send_file(file, :type => 'text/xml', :disposition => 'inline')
|
||||
else
|
||||
render :template => 'shared/errors/404_message', :status => :bad_request
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def render_export(export)
|
||||
render :json => export.to_json(:only => [:id, :progress, :workflow_state],:methods => [:download_url, :error_message])
|
||||
end
|
||||
end
|
|
@ -1,3 +1,6 @@
|
|||
You need to register any new notifications in the DB. To do this edit the rake
|
||||
task db:load_notifications in lib/tasks/db_load_data.rake
|
||||
|
||||
Since messages are parsed in a different thread than the request, they don't
|
||||
know the time_zone of the user by default. If you want to display formatted
|
||||
versions of a date or time, you could call force_zone from within the template
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
<% define_content :link do %>
|
||||
http://<%= HostUrl.context_host(asset.context) %>/<%= asset.context.class.to_s.downcase.pluralize %>/<%= asset.context_id %>/content_exports
|
||||
<% end %>
|
||||
|
||||
<% define_content :subject do %>
|
||||
Course Export Failed: <%= asset.context.name %>
|
||||
<% end %>
|
||||
|
||||
There was a problem exporting the course "<%= asset.context.name %>".
|
||||
Please notify your system administrator, and give them the following export identifier: "ContentExport:<%= asset.id %>".
|
||||
|
||||
<%= content :link %>
|
|
@ -0,0 +1,11 @@
|
|||
<% define_content :link do %>
|
||||
http://<%= HostUrl.context_host(asset.context) %>/<%= asset.context.class.to_s.downcase.pluralize %>/<%= asset.context_id %>/content_exports
|
||||
<% end %>
|
||||
|
||||
<% define_content :subject do %>
|
||||
Course Export Finished: <%= asset.context.name %>
|
||||
<% end %>
|
||||
|
||||
Your course export for "<%= asset.context.name %>" has finished.
|
||||
|
||||
<%= content :link %>
|
|
@ -0,0 +1,111 @@
|
|||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
class ContentExport < ActiveRecord::Base
|
||||
include Workflow
|
||||
belongs_to :course
|
||||
belongs_to :user
|
||||
belongs_to :attachment
|
||||
has_many :attachments, :as => :context, :dependent => :destroy
|
||||
has_a_broadcast_policy
|
||||
serialize :settings
|
||||
|
||||
workflow do
|
||||
state :created
|
||||
state :exporting
|
||||
state :exported
|
||||
state :failed
|
||||
state :deleted
|
||||
end
|
||||
|
||||
set_broadcast_policy do |p|
|
||||
p.dispatch :content_export_finished
|
||||
p.to { [user] }
|
||||
p.whenever {|record|
|
||||
record.changed_state(:exported)
|
||||
}
|
||||
|
||||
p.dispatch :content_export_failed
|
||||
p.to { [user] }
|
||||
p.whenever {|record|
|
||||
record.changed_state(:failed)
|
||||
}
|
||||
end
|
||||
|
||||
def export_course
|
||||
self.workflow_state = 'exporting'
|
||||
self.save
|
||||
begin
|
||||
if Canvas::CC::CCExporter.export(self)
|
||||
self.workflow_state = 'exported'
|
||||
else
|
||||
self.workflow_state = 'failed'
|
||||
end
|
||||
rescue
|
||||
message = $!.to_s
|
||||
stack = "#{$!}: #{$!.backtrace.join("\n")}"
|
||||
add_error(message, stack)
|
||||
self.workflow_state = 'failed'
|
||||
ensure
|
||||
self.save
|
||||
end
|
||||
end
|
||||
handle_asynchronously :export_course, :priority => Delayed::LOW_PRIORITY
|
||||
|
||||
def download_url
|
||||
self.attachment ? self.attachment.authenticated_s3_url : nil
|
||||
end
|
||||
|
||||
def error_message
|
||||
self.settings[:last_error]
|
||||
end
|
||||
|
||||
def add_error(message, stack)
|
||||
self.settings[:error_messages] ||= []
|
||||
self.settings[:error_messages] << [message, stack]
|
||||
self.settings[:last_error] = message
|
||||
end
|
||||
|
||||
def root_account
|
||||
self.course.root_account
|
||||
end
|
||||
|
||||
def running?
|
||||
['created', 'exporting'].member? self.workflow_state
|
||||
end
|
||||
|
||||
alias_method :destroy!, :destroy
|
||||
def destroy
|
||||
self.workflow_state = 'deleted'
|
||||
self.attachment.destroy! if self.attachment
|
||||
save!
|
||||
end
|
||||
|
||||
def settings
|
||||
read_attribute(:settings) || write_attribute(:settings,{}.with_indifferent_access)
|
||||
end
|
||||
|
||||
def fast_update_progress(val)
|
||||
self.progress = val
|
||||
ContentExport.update_all({:progress=>val}, "id=#{self.id}")
|
||||
end
|
||||
|
||||
named_scope :active, {:conditions => ['workflow_state != ?', 'deleted']}
|
||||
named_scope :running, {:conditions => ['workflow_state IN (?)', ['created', 'exporting']]}
|
||||
|
||||
end
|
|
@ -107,6 +107,7 @@ class Course < ActiveRecord::Base
|
|||
has_many :media_objects, :as => :context
|
||||
has_many :page_views, :as => :context
|
||||
has_many :role_overrides, :as => :context
|
||||
has_many :content_exports
|
||||
attr_accessor :import_source
|
||||
|
||||
before_save :assign_uuid
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
<% add_crumb "Course Exports" %>
|
||||
<% content_for :page_title do %>Course Exports<% end %>
|
||||
|
||||
<% content_for :page_header do %>
|
||||
<h1>Course Exports</h1>
|
||||
<% end %>
|
||||
|
||||
<div>
|
||||
<h2>Course Exports</h2>
|
||||
|
||||
<div class="export_messages" style="<%= hidden %>">
|
||||
<div class="ui-state-error error_message"></div>
|
||||
</div>
|
||||
|
||||
<div id="exports">
|
||||
<% @exports.each do |export| %>
|
||||
<% unless export.running? %>
|
||||
<% if export.workflow_state == 'exported' #&& export.attachment %>
|
||||
<p>
|
||||
Course Export from <span class="created_at time_ago_date"><%= datetime_string export.created_at %></span>:
|
||||
<a href="<%= export.download_url %>">Click here to download</a>
|
||||
</p>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="form" style="<%= hidden if @current_export_id %>">
|
||||
<% form_tag course_content_exports_path(@context.id), :id => "exporter_form" do %>
|
||||
<div style="float: left; margin-left: 5px;">
|
||||
<button type="submit" class="submit_button button big-button">Create a Course Export</button>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="progress_bar_holder" style="<%= hidden unless @current_export_id %> margin-top: 10px;">
|
||||
<div class="export_progress"></div>
|
||||
<div class="progress_message">
|
||||
The export process has started. This can take awhile for large courses.
|
||||
<em>You can leave the page</em> and you'll get an email when the export is complete.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% if @current_export_id %>
|
||||
<div id="current_export_id" style="<%= hidden %>"><%= @current_export_id %></div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<% jammit_js :content_exports %>
|
|
@ -33,6 +33,7 @@
|
|||
<a href="<%= context_url(@context, :context_confirm_action_url, :event => 'conclude') %>" class="delete_course_link button button-sidebar-wide"><%= image_tag "delete.png" %>End this Course</a>
|
||||
<a href="<%= context_url(@context, :context_start_copy_url) %>" class="button button-sidebar-wide"><%= image_tag "file_multiple.png" %>Copy this Course</a>
|
||||
<a href="<%= context_url(@context, :context_imports_url) %>" class="button button-sidebar-wide"><%= image_tag "file_multiple.png" %>Import Content into this Course</a>
|
||||
<a href="<%= context_url(@context, :course_content_exports_url) %>" class="button button-sidebar-wide"><%= image_tag "file_multiple.png" %>Export this Course</a>
|
||||
<% end %>
|
||||
<table class="summary" style="margin-top: 20px;">
|
||||
<thead>
|
||||
|
|
|
@ -182,6 +182,8 @@ javascripts:
|
|||
- public/javascripts/link_enrollment.js
|
||||
plugins:
|
||||
- public/javascripts/plugins.js
|
||||
content_exports:
|
||||
- public/javascripts/content_exports.js
|
||||
|
||||
stylesheets:
|
||||
common:
|
||||
|
|
|
@ -219,6 +219,7 @@ ActionController::Routing::Routes.draw do |map|
|
|||
m.last_redirect 'items/last', :controller => 'context_modules', :action => 'module_redirect', :last => 1
|
||||
m.first_redirect 'items/first', :controller => 'context_modules', :action => 'module_redirect', :first => 1
|
||||
end
|
||||
course.resources :content_exports, :only => %w(create index destroy show)
|
||||
course.context_modules_assignment_info 'modules/items/assignment_info', :controller => 'context_modules', :action => 'content_tag_assignment_data', :conditions => {:method => :get}
|
||||
course.context_modules_item_redirect 'modules/items/:id', :controller => 'context_modules', :action => 'item_redirect', :conditions => {:method => :get}
|
||||
course.context_modules_item_details 'modules/items/sequence/:id', :controller => 'context_modules', :action => 'item_details', :conditions => {:method => :get}
|
||||
|
@ -600,6 +601,14 @@ ActionController::Routing::Routes.draw do |map|
|
|||
map.saml_logout "saml_logout", :controller => "pseudonym_sessions", :action => "saml_logout"
|
||||
map.saml_meta_data "saml_meta_data", :controller => 'accounts', :action => 'saml_meta_data'
|
||||
|
||||
# Routes for course exports
|
||||
map.connect 'xsd/:version.xsd', :controller => 'content_exports', :action => 'xml_schema'
|
||||
map.resources :content_exports do |ce|
|
||||
ce.resources :files do |file|
|
||||
file.download 'download', :controller => 'files', :action => 'show', :download => '1'
|
||||
end
|
||||
end
|
||||
|
||||
Jammit::Routes.draw(map)
|
||||
|
||||
# API routes
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
class AddContentExport < ActiveRecord::Migration
|
||||
def self.up
|
||||
create_table :content_exports do |t|
|
||||
t.integer :user_id, :limit => 8
|
||||
t.integer :course_id, :limit => 8
|
||||
t.integer :attachment_id, :limit => 8
|
||||
t.string :export_type
|
||||
t.text :settings
|
||||
t.float :progress
|
||||
t.string :workflow_state
|
||||
t.timestamps
|
||||
end
|
||||
add_index :content_exports, [:course_id]
|
||||
add_index :content_exports, [:user_id]
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table :content_exports
|
||||
end
|
||||
end
|
|
@ -20,17 +20,20 @@ module Canvas::CC
|
|||
|
||||
attr_accessor :course, :user, :export_dir, :manifest, :zip_file
|
||||
|
||||
def initialize(course, user, opts={})
|
||||
@course = course
|
||||
@user = user
|
||||
def initialize(content_export, opts={})
|
||||
@content_export = content_export
|
||||
@course = opts[:course] || @content_export.course
|
||||
@user = opts[:user] || @content_export.user
|
||||
@export_dir = nil
|
||||
@manifest = nil
|
||||
@zip_file = nil
|
||||
@zip_name = nil
|
||||
@logger = Rails.logger
|
||||
@migration_config = Setting.from_config('external_migration')
|
||||
end
|
||||
|
||||
def self.export(course, user, opts={})
|
||||
exporter = CCExporter.new(course, user, opts)
|
||||
def self.export(content_export, opts={})
|
||||
exporter = CCExporter.new(content_export, opts)
|
||||
exporter.export
|
||||
end
|
||||
|
||||
|
@ -41,25 +44,53 @@ module Canvas::CC
|
|||
@manifest = Manifest.new(self)
|
||||
@manifest.create_document
|
||||
@manifest.close
|
||||
#copy all folder contents into zip file
|
||||
#create attachment from zip file
|
||||
#delete directory
|
||||
rescue => e
|
||||
#todo error handling
|
||||
@logger.error e
|
||||
#delete directory?
|
||||
copy_all_to_zip
|
||||
@zip_file.close
|
||||
|
||||
if @content_export && File.exists?(@zip_path)
|
||||
att = Attachment.new
|
||||
att.context = @content_export
|
||||
att.user = @content_export.user
|
||||
up_data = ActionController::TestUploadedFile.new(@zip_path, Attachment.mimetype(@zip_path))
|
||||
att.uploaded_data = up_data
|
||||
if att.save
|
||||
@content_export.attachment = att
|
||||
@content_export.save
|
||||
end
|
||||
end
|
||||
rescue
|
||||
message = $!.to_s
|
||||
stack = "#{$!}: #{$!.backtrace.join("\n")}"
|
||||
@content_export.add_error(message, stack) if @content_export
|
||||
@logger.error $!
|
||||
return false
|
||||
ensure
|
||||
@zip_file.close if @zip_file
|
||||
if !@migration_config[:keep_after_complete] && File.directory?(@export_dir)
|
||||
FileUtils::rm_rf(@export_dir)
|
||||
end
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def set_progress(progress)
|
||||
@content_export.fast_update_progress(progress) if @content_export
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def copy_all_to_zip
|
||||
Dir["#{@export_dir}/**/**"].each do |file|
|
||||
next if File.basename(file) == @zip_name
|
||||
file_path = file.sub(@export_dir+'/', '')
|
||||
@zip_file.add(file_path, file)
|
||||
end
|
||||
end
|
||||
|
||||
def create_export_dir
|
||||
slug = "common_cartridge_#{@course.id}_user_#{@user.id}"
|
||||
config = Setting.from_config('external_migration')
|
||||
if config && config[:data_folder]
|
||||
folder = config[:data_folder]
|
||||
if @migration_config && @migration_config[:data_folder]
|
||||
folder = @migration_config[:data_folder]
|
||||
else
|
||||
folder = Dir.tmpdir
|
||||
end
|
||||
|
@ -76,8 +107,9 @@ module Canvas::CC
|
|||
end
|
||||
|
||||
def create_zip_file
|
||||
path = File.join(@export_dir, "#{@course.name.to_url}-export.#{CCHelper::CC_EXTENSION}")
|
||||
@zip_file = Zip::ZipFile.new(path, Zip::ZipFile::CREATE)
|
||||
@zip_name = "#{@course.name.to_url}-export.#{CCHelper::CC_EXTENSION}"
|
||||
@zip_path = File.join(@export_dir, @zip_name)
|
||||
@zip_file = Zip::ZipFile.new(@zip_path, Zip::ZipFile::CREATE)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
module Canvas::CC
|
||||
module CCHelper
|
||||
|
||||
CANVAS_NAMESPACE = 'http://www.instructure.com/xsd/cccv0p1'
|
||||
XSD_URI = 'cccv0p1.xsd'
|
||||
CANVAS_NAMESPACE = 'http://canvas.instructure.com/xsd/cccv1p0'
|
||||
XSD_URI = 'http://canvas.instructure.com/xsd/cccv1p0.xsd'
|
||||
|
||||
# IMS formats/types
|
||||
# The IMS documentation for Common Cartridge has conflicting values
|
||||
|
|
|
@ -62,8 +62,10 @@ module Canvas::CC
|
|||
manifest_node.metadata do |md|
|
||||
create_metadata(md)
|
||||
end
|
||||
set_progress(5)
|
||||
|
||||
Organization.create_organizations(self, manifest_node)
|
||||
set_progress(10)
|
||||
|
||||
Resource.create_resources(self, manifest_node)
|
||||
|
||||
|
@ -96,5 +98,9 @@ module Canvas::CC
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
def set_progress(progress)
|
||||
@exporter.set_progress(progress)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -44,15 +44,26 @@ module Canvas::CC
|
|||
@manifest_node.resources do |resources|
|
||||
@resources = resources
|
||||
add_canvas_non_cc_data
|
||||
set_progress(15)
|
||||
add_wiki_pages
|
||||
set_progress(30)
|
||||
add_assignments
|
||||
set_progress(35)
|
||||
add_topics
|
||||
add_web_links
|
||||
set_progress(40)
|
||||
add_course_files
|
||||
set_progress(70)
|
||||
QTI::QTIGenerator.generate_qti(@manifest, resources)
|
||||
set_progress(90)
|
||||
create_basic_lti_links
|
||||
#todo download kaltura videos?
|
||||
end
|
||||
end
|
||||
|
||||
def set_progress(progress)
|
||||
@manifest.set_progress(progress)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xs:schema targetNamespace="http://www.instructure.com/xsd/cccv0p1"
|
||||
xmlns="http://www.instructure.com/xsd/cccv0p1"
|
||||
<xs:schema targetNamespace="http://canvas.instructure.com/xsd/cccv1p0"
|
||||
xmlns="http://canvas.instructure.com/xsd/cccv1p0"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
elementFormDefault="qualified">
|
||||
<!-- todo: useful comments as documentation ;) -->
|
||||
|
@ -74,10 +74,10 @@
|
|||
<xs:element name="unlock_at" type="xs:dateTime" minOccurs="0"/>
|
||||
<xs:element name="peer_reviews_due_at" type="xs:dateTime" minOccurs="0"/>
|
||||
<xs:element name="all_day_date" type="xs:date" minOccurs="0"/>
|
||||
<xs:element name="assignment_group_identifierref" type="xs:IDREF" minOccurs="0"/>
|
||||
<xs:element name="grading_standard_identifierref" type="xs:IDREF" minOccurs="0"/>
|
||||
<xs:element name="rubric_identifierref" type="xs:IDREF" minOccurs="0"/>
|
||||
<xs:element name="quiz_identifierref" type="xs:IDREF" minOccurs="0"/>
|
||||
<xs:element name="assignment_group_identifierref" type="xs:string" minOccurs="0"/>
|
||||
<xs:element name="grading_standard_identifierref" type="xs:string" minOccurs="0"/>
|
||||
<xs:element name="rubric_identifierref" type="xs:string" minOccurs="0"/>
|
||||
<xs:element name="quiz_identifierref" type="xs:string" minOccurs="0"/>
|
||||
<xs:element name="allowed_extensions" type="xs:string" minOccurs="0"/>
|
||||
<xs:element name="points_possible" type="xs:float" minOccurs="0"/>
|
||||
<xs:element name="min_score" type="xs:float" minOccurs="0"/>
|
||||
|
@ -140,7 +140,7 @@
|
|||
<xs:complexType>
|
||||
<xs:all minOccurs="0">
|
||||
<xs:element name="title" type="xs:string" minOccurs="0"/>
|
||||
<xs:element name="identifierref" type="xs:IDREF" minOccurs="0"/>
|
||||
<xs:element name="identifierref" type="xs:string" minOccurs="0"/>
|
||||
</xs:all>
|
||||
<xs:attribute name="type" type="xs:string" use="required"/>
|
||||
</xs:complexType>
|
||||
|
@ -156,7 +156,7 @@
|
|||
<xs:complexType>
|
||||
<xs:all minOccurs="0">
|
||||
<xs:element name="content_type" type="xs:string" minOccurs="1"/>
|
||||
<xs:element name="identifierref" type="xs:IDREFS" minOccurs="0"/>
|
||||
<xs:element name="identifierref" type="xs:string" minOccurs="0"/>
|
||||
<xs:element name="url" type="xs:string" minOccurs="0"/>
|
||||
<xs:element name="position" type="xs:integer" minOccurs="0"/>
|
||||
<xs:element name="indent" type="xs:integer" minOccurs="0"/>
|
||||
|
@ -174,7 +174,7 @@
|
|||
<xs:element name="completionRequirement">
|
||||
<xs:complexType>
|
||||
<xs:all minOccurs="0">
|
||||
<xs:element name="identifierref" type="xs:IDREF" minOccurs="1"/>
|
||||
<xs:element name="identifierref" type="xs:string" minOccurs="1"/>
|
||||
<xs:element name="min_score" type="xs:integer" minOccurs="0"/>
|
||||
<xs:element name="max_score" type="xs:integer" minOccurs="0"/>
|
||||
</xs:all>
|
||||
|
@ -258,7 +258,7 @@
|
|||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element name="identifierref" type="xs:IDREF" minOccurs="0"/>
|
||||
<xs:element name="identifierref" type="xs:string" minOccurs="0"/>
|
||||
<xs:element name="drop_count" type="xs:integer" minOccurs="0"/>
|
||||
</xs:all>
|
||||
</xs:complexType>
|
||||
|
@ -364,7 +364,7 @@
|
|||
<xs:element name="points" type="xs:float" minOccurs="0"/>
|
||||
<xs:element name="mastery_points" type="xs:float" minOccurs="0"/>
|
||||
<xs:element name="ignore_for_scoring" type="xs:boolean" minOccurs="0"/>
|
||||
<xs:element name="learning_outcome_identifierref" type="xs:IDREF" minOccurs="0"/>
|
||||
<xs:element name="learning_outcome_identifierref" type="xs:string" minOccurs="0"/>
|
||||
<xs:element name="ratings" type="ratingsType" minOccurs="0"/>
|
||||
</xs:all>
|
||||
</xs:complexType>
|
|
@ -188,6 +188,28 @@ namespace :db do
|
|||
}, %{
|
||||
There was a problem importing <%= asset.migration_settings[:course_name] %> into <%= asset.context.name %>. Please notify your system administrator, and give them the following error code: "ContentMigration:<%= asset.id %>:<%= asset.progress %>".
|
||||
}
|
||||
|
||||
create_notification 'ContentExport', 'Message', 0,
|
||||
'http://<%= HostUrl.default_host %>', %{
|
||||
Content Export Finished
|
||||
|
||||
Course Export Finished: <%= asset.context.name %>
|
||||
|
||||
Your course export for "<%= asset.context.name %>" has finished.
|
||||
}, %{
|
||||
Your course export for "<%= asset.context.name %>" has finished.
|
||||
}
|
||||
|
||||
create_notification 'ContentExport', 'Message', 0,
|
||||
'http://<%= HostUrl.default_host %>', %{
|
||||
Content Export Failed
|
||||
|
||||
Course Export failed: <%= asset.context.name %>
|
||||
|
||||
There was a problem exporting the course "<%= asset.context.name %>".
|
||||
}, %{
|
||||
There was a problem exporting the course "<%= asset.context.name %>".
|
||||
}
|
||||
|
||||
create_notification 'User', 'Other', 0,
|
||||
'http://<%= HostUrl.default_host %>', %{
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
/**
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
$(document).ready(function(event) {
|
||||
var state = 'nothing';
|
||||
var current_id = null;
|
||||
|
||||
function startPoll() {
|
||||
$("#exporter_form").html("Processing<div style='font-size: 0.8em;'>this may take a bit...</div>")
|
||||
.attr('disabled', true);
|
||||
$(".instruction").hide();
|
||||
$(".progress_bar_holder").slideDown();
|
||||
$(".export_progress").progressbar();
|
||||
state = "nothing";
|
||||
var fakeTickCount = 0;
|
||||
var tick = function() {
|
||||
if(state == "nothing") {
|
||||
fakeTickCount++;
|
||||
var progress = ($(".export_progress").progressbar('option', 'value') || 0) + 0.25;
|
||||
if(fakeTickCount < 10) {
|
||||
$(".export_progress").progressbar('option', 'value', progress);
|
||||
}
|
||||
setTimeout(tick, 2000);
|
||||
} else {
|
||||
state = "nothing";
|
||||
fakeTickCount = 0;
|
||||
setTimeout(tick, 10000);
|
||||
}
|
||||
};
|
||||
var checkup = function() {
|
||||
var lastProgress = null;
|
||||
var waitTime = 1500;
|
||||
$.ajaxJSON(location.href + "/" + current_id, 'GET', {}, function(data) {
|
||||
state = "updating";
|
||||
var content_export = data.content_export;
|
||||
var progress = 0;
|
||||
if(content_export) {
|
||||
progress = Math.max($(".export_progress").progressbar('option', 'value') || 0, content_export.progress);
|
||||
$(".export_progress").progressbar('option', 'value', progress);
|
||||
}
|
||||
if(content_export.workflow_state == 'exported') {
|
||||
$("#exporter_form").hide();
|
||||
$(".export_progress").progressbar('option', 'value', 100);
|
||||
$(".progress_message").html("The course has been exported.");
|
||||
$("#exports").append('<p>New Course Export: <a href="' + content_export.download_url + '">Click here to download</a> </p>')
|
||||
} else if(content_export.workflow_state == 'failed') {
|
||||
code = "content_export_" + content_export.id;
|
||||
$(".progress_bar_holder").hide();
|
||||
$("#exporter_form").hide();
|
||||
var message = "There was an error exporting your course. Please notify your system administrator and give them the following export identifier: \"" + code + "\"";
|
||||
$(".export_messages .error_message").html(message);
|
||||
$(".export_messages").show();
|
||||
} else {
|
||||
if(progress == lastProgress) {
|
||||
waitTime = Math.max(waitTime + 500, 30000);
|
||||
} else {
|
||||
waitTime = 1500;
|
||||
}
|
||||
lastProgress = progress;
|
||||
setTimeout(checkup, 1500);
|
||||
}
|
||||
}, function() {
|
||||
setTimeout(checkup, 3000);
|
||||
});
|
||||
};
|
||||
setTimeout(checkup, 2000);
|
||||
setTimeout(tick, 1000)
|
||||
}
|
||||
|
||||
$("#exporter_form").formSubmit({
|
||||
success: function(data) {
|
||||
if(data && data.content_export) {
|
||||
current_id = data.content_export.id
|
||||
startPoll();
|
||||
} else {
|
||||
//show error message
|
||||
$(".export_messages .error_message").text(data.error_message);
|
||||
$(".export_messages").show();
|
||||
}
|
||||
},
|
||||
error: function(data) {
|
||||
$(this).find(".submit_button").attr('disabled', false).text("Process Data");
|
||||
}
|
||||
});
|
||||
|
||||
function check_if_exporting() {
|
||||
//state = "checking";
|
||||
if( $('#current_export_id').size() ){
|
||||
//state = "nothing";
|
||||
current_id = $('#current_export_id').text()
|
||||
startPoll();
|
||||
}
|
||||
}
|
||||
check_if_exporting();
|
||||
|
||||
});
|
|
@ -238,7 +238,7 @@ module Instructure #:nodoc:
|
|||
def broadcast_notifications
|
||||
return if @broadcasted
|
||||
@broadcasted = true
|
||||
raise ArgumentError, "Block not supplied for #{self.class.to_s}" unless self.class.broadcast_policy_block
|
||||
raise ArgumentError, "Broadcast Policy block not supplied for #{self.class.to_s}" unless self.class.broadcast_policy_block
|
||||
self.instance_eval &self.class.broadcast_policy_block
|
||||
self.broadcast_policy_list.each {|p| p.broadcast(self) }
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue