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:
parent
b8fc5f3ec6
commit
33b4eb55f9
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 %>
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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' },
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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,13 +157,15 @@ describe Attachment do
|
|||
|
||||
end
|
||||
|
||||
context "crocodoc" do
|
||||
before do
|
||||
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 { configure_crocodoc }
|
||||
|
||||
it "crocodocable?" do
|
||||
crocodocable_attachment_model
|
||||
@attachment.should be_crocodocable
|
||||
|
@ -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
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue