canvadocs

This commit adds support for document previews from a Box View
compatible API.

closes CNVS-12416

Test plan:
* NOTE: check everywhere for previews (speedgrader, files list,
  individual files page, etc?)
* Enable Scribd and Crocodoc
  * attachments should be previewable in scribd or crocodoc (submission
    attachments should go to Crocodoc)
* Enable Canvadocs
  * Files that previously previewed in scribd should now go to
    Canvadocs.  Nothing should ever go to scribd.
  * New submission attachments should continue to use Crocodoc (for
    supported file types)
  * Other attachments should preview in Canvadocs

Change-Id: I173e4fabc0ae677cdddd4bd065777a90360c1f34
Reviewed-on: https://gerrit.instructure.com/34535
Reviewed-by: Simon Williams <simon@instructure.com>
Tested-by: Jenkins <jenkins@instructure.com>
Product-Review: Matt Fairbourn <mfairbourn@instructure.com>
QA-Review: Amber Taniuchi <amber@instructure.com>
This commit is contained in:
Cameron Matheson 2014-05-07 18:05:05 -06:00
parent b8fc5f3ec6
commit 33b4eb55f9
20 changed files with 676 additions and 20 deletions

View File

@ -0,0 +1,43 @@
#
# Copyright (C) 2014 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 CanvadocSessionsController < ApplicationController
before_filter :require_user
def show
unless Canvas::Security.verify_hmac_sha1(params[:hmac], params[:blob])
render :text => 'unauthorized', :status => :unauthorized
return
end
blob = JSON.parse(params[:blob])
attachment = Attachment.find(blob["attachment_id"])
unless @current_user.global_id == blob["user_id"]
render :text => 'unauthorized', :status => :unauthorized
return
end
if attachment.canvadocable?
attachment.submit_to_canvadocs unless attachment.canvadoc_available?
redirect_to attachment.canvadoc.session_url
else
render :text => "Not found", :status => :not_found
end
end
end

View File

@ -221,7 +221,11 @@ class FoldersController < ApplicationController
res = {
:actual_folder => @folder.as_json(folders_options),
:sub_folders => sub_folders_scope.by_position.map { |f| f.as_json(folders_options) },
:files => files.map { |f| f.as_json(files_options)}
:files => files.map { |f|
f.as_json(files_options).tap { |json|
json['attachment']['canvadoc_session_url'] = f.canvadoc_url(@current_user)
}
}
}
format.json { render :json => res }
end

View File

@ -27,6 +27,13 @@ module AttachmentHelper
rescue => e
ErrorReport.log_exception('crocodoc', e)
end
elsif attachment.canvadocable?
blob = {
user_id: @current_user.global_id,
attachment_id: attachment.global_id,
}.to_json
hmac = Canvas::Security.hmac_sha1(blob)
attrs[:canvadoc_session_url] = canvadoc_session_path(blob: blob, hmac: hmac)
elsif attachment.scribdable? && scribd_doc = attachment.scribd_doc
begin
attrs[:scribd_doc_id] = scribd_doc.doc_id

View File

@ -1229,7 +1229,7 @@ class Assignment < ActiveRecord::Base
a.as_json(
:only => attachment_fields,
:methods => [:view_inline_ping_url, :scribd_render_url]
)
).tap { |json| json[:attachment][:canvadoc_url] = a.canvadoc_url(user) }
end
end
end

View File

@ -67,6 +67,7 @@ class Attachment < ActiveRecord::Base
has_one :thumbnail, :foreign_key => "parent_id", :conditions => {:thumbnail => "thumb"}
has_many :thumbnails, :foreign_key => "parent_id"
has_one :crocodoc_document
has_one :canvadoc
before_save :infer_display_name
before_save :default_values
@ -1284,12 +1285,22 @@ class Attachment < ActiveRecord::Base
CrocodocDocument::MIME_TYPES.include?(content_type)
end
def canvadocable?
Canvadocs.enabled? && Canvadoc::MIME_TYPES.include?(content_type)
end
def self.submit_to_scribd(ids)
Attachment.find_all_by_id(ids).compact.each do |attachment|
attachment.submit_to_scribd! rescue nil
end
end
def self.submit_to_canvadocs(ids)
Attachment.find_each(ids).each do |a|
a.submit_to_canvadocs
end
end
def self.skip_3rd_party_submits(skip=true)
@skip_3rd_party_submits = skip
end
@ -1309,11 +1320,12 @@ class Attachment < ActiveRecord::Base
end
def needs_scribd_doc?
if self.scribd_attempts >= MAX_SCRIBD_ATTEMPTS
if Canvadocs.enabled?
return false
elsif self.scribd_attempts >= MAX_SCRIBD_ATTEMPTS
self.mark_errored
false
end
if self.scribd_doc?
elsif self.scribd_doc?
Scribd::API.instance.user = scribd_user
begin
status = self.scribd_doc.conversion_status
@ -1386,6 +1398,31 @@ class Attachment < ActiveRecord::Base
end
end
def submit_to_canvadocs(attempt = 1, opts = {})
# ... or crocodoc (this will go away soon)
return if Attachment.skip_3rd_party_submits?
if opts[:wants_annotation] && crocodocable?
submit_to_crocodoc(attempt)
elsif canvadocable?
doc = canvadoc || create_canvadoc
doc.upload
update_attribute(:workflow_state, 'processing')
end
rescue => e
update_attribute(:workflow_state, 'errored')
ErrorReport.log_exception(:canvadocs, e, :attachment_id => id)
if attempt <= Setting.get('max_canvadocs_attempts', '5').to_i
send_later_enqueue_args :submit_to_canvadocs, {
:n_strand => 'canvadocs_retries',
:run_at => (5 * attempt).minutes.from_now,
:max_attempts => 1,
:priority => Delayed::LOW_PRIORITY,
}, attempt + 1, opts
end
end
def submit_to_crocodoc(attempt = 1)
if crocodocable? && !Attachment.skip_3rd_party_submits?
crocodoc = crocodoc_document || create_crocodoc_document
@ -1696,6 +1733,10 @@ class Attachment < ActiveRecord::Base
crocodoc_document.try(:available?)
end
def canvadoc_available?
canvadoc.try(:available?)
end
def view_inline_ping_url
"/#{context_url_prefix}/files/#{self.id}/inline_view"
end
@ -1717,6 +1758,16 @@ class Attachment < ActiveRecord::Base
end
end
def canvadoc_url(user)
return unless canvadocable?
blob = {
user_id: user.global_id,
attachment_id: id,
}.to_json
hmac = Canvas::Security.hmac_sha1(blob)
"/canvadoc_session?blob=#{URI.encode blob}&hmac=#{URI.encode hmac}"
end
def check_rerender_scribd_doc
if scribd_doc_missing?
attachment = root_attachment || self

69
app/models/canvadoc.rb Normal file
View File

@ -0,0 +1,69 @@
#
# Copyright (C) 2014 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 Canvadoc < ActiveRecord::Base
attr_accessible :document_id, :process_state
belongs_to :attachment
MIME_TYPES = %w(
application/excel
application/msword
application/pdf
application/vnd.ms-excel
application/vnd.ms-powerpoint
application/vnd.openxmlformats-officedocument.presentationml.presentation
application/vnd.openxmlformats-officedocument.wordprocessingml.document
)
def upload
return if document_id.present?
url = attachment.authenticated_s3_url(:expires => 1.day)
response = Canvas.timeout_protection("canvadocs") {
canvadocs_api.upload(url)
}
if response && response['id']
update_attributes document_id: response['id'], process_state: response['status']
elsif response.nil?
raise "no response received (request timed out?)"
else
raise response.inspect
end
end
def session_url
Canvas.timeout_protection("canvadocs") do
session = canvadocs_api.session(document_id)
canvadocs_api.view(session["id"])
end
end
def available?
!!(document_id && process_state != 'error' && Canvadocs.enabled?)
end
def canvadocs_api
raise "Canvadocs isn't enabled" unless Canvadocs.enabled?
Canvadocs::API.new(token: Canvadocs.config['api_key'],
base_url: Canvadocs.config['base_url'])
end
private :canvadocs_api
end

View File

@ -156,9 +156,16 @@ class CrocodocDocument < ActiveRecord::Base
if error_uuids.present?
error_docs = CrocodocDocument.where(:uuid => error_uuids)
attachment_ids = error_docs.map(&:attachment_id)
Attachment.send_later_enqueue_args :submit_to_scribd,
{:n_strand => 'scribd', :max_attempts => 1},
attachment_ids = error_docs.pluck(:attachment_id)
if Canvadocs.enabled?
method = :submit_to_canvadocs
strand = "canvadocs"
else
method = :submit_to_scribd
strand = "scribd"
end
Attachment.send_later_enqueue_args method,
{:n_strand => strand, :max_attempts => 1},
attachment_ids
end
end

View File

@ -113,7 +113,7 @@ class Submission < ActiveRecord::Base
after_save :touch_user
after_save :update_assignment
after_save :update_attachment_associations
after_save :submit_attachments_to_crocodoc
after_save :submit_attachments_to_canvadocs
after_save :queue_websnap
after_save :update_final_score
after_save :submit_to_turnitin_later
@ -472,14 +472,15 @@ class Submission < ActiveRecord::Base
end
private :attachment_fake_belongs_to_group
def submit_attachments_to_crocodoc
def submit_attachments_to_canvadocs
if attachment_ids_changed?
attachments = attachment_associations.map(&:attachment)
attachments.each do |a|
a.send_later_enqueue_args :submit_to_crocodoc,
:n_strand => 'crocodoc',
a.send_later_enqueue_args :submit_to_canvadocs, {
:n_strand => 'canvadocs',
:max_attempts => 1,
:priority => Delayed::LOW_PRIORITY
}, 1, wants_annotation: true
end
end
end

View File

@ -0,0 +1,20 @@
<% settings[:base_url] = "https://view-api.box.com/1" if settings[:base_url].blank? %>
<%= fields_for :settings, OpenObject.new(settings) do |f| %>
<table style="width: 500px;" class="formtable">
<tr>
<td colspan="2">
<p><%= t :description, "This plugin integrates with Canvadocs (or any
Box View compatible API) to provide an HTML5 document previewer." %></p>
</td>
</tr>
<tr>
<td><%= f.blabel :api_key, :en => "API Key" %></td>
<td><%= f.text_field :api_key %></td>
</tr>
<tr>
<td><%= f.blabel :base_url, :en => "Base URL" %></td>
<td><%= f.text_field :base_url %></td>
</tr>
</table>
<% end %>

View File

@ -406,6 +406,7 @@ routes.draw do
match '/submissions/:submission_id/attachments/:attachment_id/crocodoc_sessions' => 'crocodoc_sessions#create', :via => :post
match '/attachments/:attachment_id/crocodoc_sessions' => 'crocodoc_sessions#create', :via => :post
match '/canvadoc_session' => 'canvadoc_sessions#show', :via => :get, :as => :canvadoc_session
resources :page_views, :only => [:update]
match 'media_objects' => 'context#create_media_object', :as => :create_media_object, :via => :post

View File

@ -0,0 +1,20 @@
class CreateCanvadocsTable < ActiveRecord::Migration
tag :predeploy
def self.up
create_table :canvadocs do |t|
t.string :document_id
t.string :process_state
t.integer :attachment_id, limit: 8, null: false
t.timestamps
end
add_index :canvadocs, :document_id, :unique => true
add_index :canvadocs, :attachment_id
add_index :canvadocs, :process_state
add_foreign_key :canvadocs, :attachments
end
def self.down
drop_table :canvadocs
end
end

180
lib/canvadocs.rb Normal file
View File

@ -0,0 +1,180 @@
require 'cgi'
require 'net/http'
require 'net/https'
require 'json'
module Canvadocs
# Public: A small ruby client that wraps the Box View api.
#
# Examples
#
# Canvadocs::API.new(:token => <token>)
class API
attr_accessor :token, :http, :url
# Public: The base part of the url that is the same for all api requests.
BASE_URL = "https://view-api.box.com/1"
# Public: Initialize a Canvadocs api object
#
# opts - A hash of options with which to initialize the object
# :token - The api token to use to authenticate requests. Required.
#
# Examples
# crocodoc = Canvadocs::API.new(:token => <token>)
# # => <Canvadocs::API:<id>>
def initialize(opts)
self.token = opts[:token]
# setup the http object for ssl
@url = URI.parse(opts[:base_url] || BASE_URL)
@http = Net::HTTP.new(@url.host, @url.port)
@http.use_ssl = true
end
# -- Documents --
# Public: Create a document with the file at the given url.
#
# obj - a url string
#
# Examples
#
# upload("http://www.example.com/test.doc")
# # => { "id": 1234, "status": "queued" }
#
# Returns a hash containing the document's id and status
def upload(obj)
params = if obj.is_a?(File)
{ :file => obj }
raise Canvadocs::Error, "TODO: support raw files"
else
{ :url => obj.to_s }
end
raw_body = api_call(:post, "documents", params)
JSON.parse(raw_body)
end
# Public: Delete a document.
#
# id - a single document id to delete
#
def delete(id)
api_call(:delete, "documents/#{id}")
end
# -- Sessions --
# Public: Create a session, which is a unique id with which you can view
# the document. Sessions expire 60 minutes after they are generated.
#
# id - The id of the document for the session
#
# Examples
#
# session(1234)
# # => { "id": "CFAmd3Qjm_2ehBI7HyndnXKsDrQXJ7jHCuzcRv" }
#
# Returns a hash containing the session id
def session(document_id, opts={})
raw_body = api_call(:post, "sessions",
opts.merge(:document_id => document_id))
JSON.parse(raw_body)
end
# Public: Get the url for the viewer for a session.
#
# session_id - The id of the session (see #session)
#
# Examples
# view("CFAmd3Qjm_2ehBI7HyndnXKsDrQXJ7jHCuzcRv_V4FAgbSmaBkF")
# # => https://view-api.box.com/1/sessions/#{session_id}/view?theme=dark"
#
# Returns a url string for viewing the session
def view(session_id)
"#{@url.to_s}/sessions/#{session_id}/view?theme=dark"
end
# -- API Glue --
# Internal: Setup the api call, format the parameters, send the request,
# parse the response and return it.
#
# method - The http verb to use, currently :get or :post
# endpoint - The api endpoint to hit. this is the part after
# +base_url+. please do not include a beginning slash.
# params - Parameters to send with the api call
#
# Examples
#
# api_call(:post,
# "documents",
# { url: "http://www.example.com/test.doc" })
# # => { "id": 1234 }
#
# Returns the json parsed response body of the call
def api_call(method, endpoint, params={})
# dispatch to the right method, with the full path (/api/v2 + endpoint)
request = self.send("format_#{method}", "#{@url.path}/#{endpoint}", params)
request["Authorization"] = "Token #{token}"
response = @http.request(request)
unless response.code =~ /\A20./
raise Canvadocs::Error, "HTTP Error #{response.code}: #{response.body}"
end
response.body
end
# Internal: Format and create a Net::HTTP get request, with query
# parameters.
#
# path - the path to get
# params - the params to add as query params to the path
#
# Examples
#
# format_get("/api/v2/document/status",
# { :token => <token>, :uuids => <uuids> })
# # => <Net::HTTP::Get:<id>> for
# # "/api/v2/document/status?token=<token>&uuids=<uuids>"
#
# Returns a Net::HTTP::Get object for the path with query params
def format_get(path, params)
query = params.map { |k,v| "#{k}=#{CGI::escape(v.to_s)}" }.join("&")
Net::HTTP::Get.new("#{path}?#{query}")
end
# Internal: Format and create a Net::HTTP post request, with form
# parameters.
#
# path - the path to get
# params - the params to add as form params to the path
#
# Examples
#
# format_post("/api/v2/document/upload",
# { :token => <token>, :url => <url> })
# # => <Net::HTTP::Post:<id>>
#
# Returns a Net::HTTP::Post object for the path with json-formatted params
def format_post(path, params)
Net::HTTP::Post.new(path).tap { |req|
req["Content-Type"] = "application/json"
req.body = params.to_json
}
end
end
class Error < StandardError; end
def self.config
PluginSetting.settings_for_plugin(:canvadocs)
end
def self.enabled?
!!config
end
end

View File

@ -227,7 +227,7 @@ Canvas::Plugin.register('assignment_freezer', nil, {
Canvas::Plugin.register('crocodoc', :previews, {
:name => lambda { t :name, 'Crocodoc' },
:description => lambda { t :description, 'Enabled Crocodoc as a document preview option' },
:description => lambda { t :description, 'Enable Crocodoc as a document preview option' },
:website => 'https://crocodoc.com/',
:author => 'Instructure',
:author_website => 'http://www.instructure.com',
@ -235,6 +235,17 @@ Canvas::Plugin.register('crocodoc', :previews, {
:settings_partial => 'plugins/crocodoc_settings',
:settings => nil
})
Canvas::Plugin.register('canvadocs', :previews, {
:name => lambda { t :name, 'Canvadocs' },
:description => lambda { t :description, 'Enable Canvadocs (compatible with Box View) as a document preview option' },
:author => 'Instructure',
:author_website => 'http://www.instructure.com',
:version => '1.0.0',
:settings_partial => 'plugins/canvadocs_settings',
:settings => nil
})
Canvas::Plugin.register('account_reports', nil, {
:name => lambda{ t :name, 'Account Reports' },
:description => lambda{ t :description, 'Select account reports' },

View File

@ -1586,6 +1586,7 @@ define([
attachment_id: data.id,
height: '100%',
crocodoc_session_url: data.crocodocSession,
canvadoc_session_url: data.canvadoc_session_url,
scribd_doc_id: data.scribd_doc && data.scribd_doc.attributes && data.scribd_doc.attributes.doc_id,
scribd_access_key: data.scribd_doc && data.scribd_doc.attributes && data.scribd_doc.attributes.access_key,
attachment_view_inline_ping_url: files.viewInlinePingUrl(data.context_string, data.id),
@ -1593,7 +1594,10 @@ define([
attachment_preview_processing: data.workflow_state == 'pending_upload' || data.workflow_state == 'processing'
});
};
if (data.permissions && data.permissions.download && $.isPreviewable(data.content_type)) {
if (data.canvadoc_session_url) {
showPreview();
}
else if (data.permissions && data.permissions.download && $.isPreviewable(data.content_type)) {
if (data['crocodoc_available?'] && !data.crocodocSession) {
$preview.disableWhileLoading(
$.ajaxJSON(

View File

@ -91,7 +91,7 @@ define([
// if I have a url to ping back to the app that I viewed this file inline, ping it.
if (opts.attachment_view_inline_ping_url) {
$.ajaxJSON(opts.attachment_view_inline_ping_url, 'POST', {}, function() { }, function() { });
$.trackEvent('Doc Previews', serviceUsed, JSON.stringify(opts, ['attachment_id', 'submission_id', 'mimetype', 'crocodoc_session_url', 'scribd_doc_id']));
$.trackEvent('Doc Previews', serviceUsed, JSON.stringify(opts, ['attachment_id', 'submission_id', 'mimetype', 'crocodoc_session_url', 'canvadoc_session_url', 'scribd_doc_id']));
}
}
@ -108,6 +108,20 @@ define([
opts.ready();
});
}
else if (opts.canvadoc_session_url) {
var iframe = $('<iframe/>', {
src: opts.canvadoc_session_url,
width: opts.width,
height: opts.height,
css: {border: 0}
});
iframe.appendTo($this);
iframe.load(function() {
tellAppIViewedThisInline('canvadocs');
if ($.isFunction(opts.ready))
opts.ready();
});
}
else if (!INST.disableScribdPreviews && opts.scribd_doc_id && opts.scribd_access_key &&
(scribdHtml5 || scribd && hasGoodEnoughFlash)) {
if (scribdHtml5) {

View File

@ -1136,6 +1136,7 @@ define([
$.each(submission.versioned_attachments || [], function(i,a){
var attachment = a.attachment;
if (attachment['crocodoc_available?'] ||
attachment['canvadocable?'] ||
(attachment.scribd_doc && attachment.scribd_doc.created) ||
$.isPreviewable(attachment.content_type, 'google')) {
inlineableAttachments.push(attachment);
@ -1337,6 +1338,11 @@ define([
}
));
}
else if (attachment && attachment.canvadoc_url) {
$iframe_holder.show().loadDocPreview($.extend(previewOptions, {
canvadoc_session_url: attachment.canvadoc_url
}));
}
else if ( attachment && (attachment['scribdable?'] || $.isPreviewable(attachment.content_type, 'google')) ) {
if (!INST.disableCrocodocPreviews) $no_annotation_warning.show();

View File

@ -0,0 +1,75 @@
#
# Copyright (C) 2014 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')
describe CanvadocSessionsController do
before do
PluginSetting.create! :name => 'canvadocs',
:settings => {"base_url" => "https://example.com"}
Canvadocs::API.any_instance.stubs(:upload).returns "id" => 1234
Canvadocs::API.any_instance.stubs(:session).returns 'id' => 'SESSION'
end
before do
course_with_teacher_logged_in(:active_all => true)
@attachment1 = attachment_model :content_type => 'application/pdf',
:context => @course
end
describe '#show' do
before do
@blob = {
attachment_id: @attachment1.global_id,
user_id: @teacher.global_id
}
end
it "works" do
get :show, blob: @blob.to_json, hmac: Canvas::Security.hmac_sha1(@blob.to_json)
response.should redirect_to("https://example.com/sessions/SESSION/view?theme=dark")
end
it "doesn't upload documents that are already uploaded" do
@attachment1.submit_to_canvadocs
Attachment.any_instance.expects(:submit_to_canvadocs).never
get :show, blob: @blob.to_json, hmac: Canvas::Security.hmac_sha1(@blob.to_json)
response.should redirect_to("https://example.com/sessions/SESSION/view?theme=dark")
end
it "needs a valid signed blob" do
hmac = Canvas::Security.hmac_sha1(@blob.to_json)
attachment2 = attachment_model :content_type => 'application/pdf',
:context => @course
@blob[:attachment_id] = attachment2.id
get :show, blob: @blob.to_json, hmac: hmac
assert_status(401)
end
it "needs to be run by the blob user" do
student_in_course
@blob[:user_id] = @student.global_id
blob = @blob.to_json
get :show, blob: blob, hmac: Canvas::Security.hmac_sha1(blob)
assert_status(401)
end
end
end

View File

@ -73,3 +73,5 @@ end
def crocodocable_attachment_model(opts={})
attachment_model({:content_type => 'application/pdf'}.merge(opts))
end
alias :canvadocable_attachment_model :crocodocable_attachment_model

View File

@ -95,6 +95,19 @@ describe Attachment do
@attachment.should be_scribdable
end
describe "needs_scribd_doc?" do
it "normally needs a scribd doc" do
scribdable_attachment_model
@attachment.needs_scribd_doc?.should be_true
end
it "doesn't need a scribd doc when canvadocs are configured" do
Canvadocs.stubs(:config).returns(api_key: "asdf")
scribdable_attachment_model
@attachment.needs_scribd_doc?.should be_false
end
end
context "authenticated_s3_url" do
before do
local_storage!
@ -144,12 +157,14 @@ describe Attachment do
end
def configure_crocodoc
PluginSetting.create! :name => 'crocodoc',
:settings => { :api_key => "blahblahblahblahblah" }
Crocodoc::API.any_instance.stubs(:upload).returns 'uuid' => '1234567890'
end
context "crocodoc" do
before do
PluginSetting.create! :name => 'crocodoc',
:settings => { :api_key => "blahblahblahblahblah" }
Crocodoc::API.any_instance.stubs(:upload).returns 'uuid' => '1234567890'
end
before { configure_crocodoc }
it "crocodocable?" do
crocodocable_attachment_model
@ -217,6 +232,52 @@ describe Attachment do
end
end
context "canvadocs" do
before do
PluginSetting.create! :name => 'canvadocs',
:settings => {"api_key" => "blahblahblahblahblah",
"base_url" => "http://example.com"}
Canvadocs::API.any_instance.stubs(:upload).returns "id" => 1234
end
describe "submit_to_canvadocs" do
it "submits canvadocable documents" do
a = canvadocable_attachment_model
a.submit_to_canvadocs
a.canvadoc.document_id.should_not be_nil
end
it "doesn't submit non-canvadocable documents" do
a = attachment_model
a.submit_to_canvadocs
a.canvadoc.should be_nil
end
it "tries again later when upload fails" do
Canvadocs::API.any_instance.stubs(:upload).returns(nil)
expects_job_with_tag('Attachment#submit_to_canvadocs') {
canvadocable_attachment_model.submit_to_canvadocs
}
end
it "prefers crocodoc when annotation is requested" do
configure_crocodoc
Canvadoc::MIME_TYPES << "application/blah"
crocodocable = crocodocable_attachment_model
canvadocable = canvadocable_attachment_model content_type: "application/blah"
crocodocable.submit_to_canvadocs 1, wants_annotation: true
crocodocable.canvadoc.should be_nil
crocodocable.crocodoc_document.should_not be_nil
canvadocable.submit_to_canvadocs 1, wants_annotation: true
canvadocable.canvadoc.should_not be_nil
canvadocable.crocodoc_document.should be_nil
end
end
end
it "should set the uuid" do
attachment_model
@attachment.uuid.should_not be_nil

View File

@ -0,0 +1,80 @@
#
# Copyright (C) 2014 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')
describe 'Canvadoc' do
before do
PluginSetting.create! :name => 'canvadocs',
:settings => {"api_key" => "blahblahblahblahblah",
"base_url" => "http://example.com"}
Canvadocs::API.any_instance.stubs(:upload).returns "id" => 123456,
"status" => "pending"
Canvadocs::API.any_instance.stubs(:session).returns "id" => "blah",
"status" => "pending"
@attachment = attachment_model(content_type: "application/pdf")
@doc = @attachment.create_canvadoc
end
def disable_canvadocs
Canvadocs.stubs(:enabled?).returns false
end
describe "#upload" do
it "uploads" do
@doc.upload
@doc.document_id.should == 123456
end
it "doesn't upload again" do
@doc.update_attribute :document_id, 999999
@doc.upload
@doc.document_id.should == 999999 # not 123456
end
it "doesn't upload when canvadocs isn't configured" do
disable_canvadocs
lambda {
@doc.upload
}.should raise_error
end
end
describe "#session_url" do
it "returns a session_url" do
@doc.upload
@doc.session_url.should == "http://example.com/sessions/blah/view?theme=dark"
end
end
describe "#available?" do
before { @doc.upload }
it "is available for documents that didn't fail" do
@doc.should be_available
@doc.update_attribute :process_state, "error"
@doc.should_not be_available
end
it "... unless canvadocs isn't configured" do
disable_canvadocs
@doc.should_not be_available
end
end
end