Google Drive Collaborations & Submissions

force users to auth google drive when the plugin is enabled

Add support for google drive in collaborations and homework submissions
Everything still looks like google docs to the user.

fixes PLAT-892
fixes PLAT-893
fixes PLAT-894

Test plan:

create a google docs integration
and enable the google drive plugin
when you visit the colaborations page it should ask you to authorize canvas to use google drive

Regression test homework submissions with google doc and drive
Regression test collaborations with google doc and drive

Change-Id: I79bdbdcae915b08a19cc9a078a64b49ef5f34796
Reviewed-on: https://gerrit.instructure.com/48583
Tested-by: Jenkins
Reviewed-by: Brad Humphrey <brad@instructure.com>
QA-Review: August Thornton <august@instructure.com>
Product-Review: Brad Horrocks <bhorrocks@instructure.com>
This commit is contained in:
Nathan Mills 2015-02-09 15:13:53 -07:00 committed by Brad Horrocks
parent c1b82356ab
commit a99c397662
21 changed files with 484 additions and 84 deletions

View File

@ -16,7 +16,7 @@ group :development do
unless ENV['DISABLE_RUBY_DEBUGGING']
gem 'byebug', '3.5.1', :platforms => [:ruby_20, :ruby_21, :ruby_22]
gem 'columnize', '0.9.0', :platforms => [:ruby_20, :ruby_21, :ruby_22]
gem 'columnize', '0.9.0', :platforms => [:ruby_20, :ruby_21, :ruby_22]
gem 'debugger', '1.6.6', :platforms => :ruby_19
end
end

View File

@ -1819,14 +1819,36 @@ class ApplicationController < ActionController::Base
end
def google_drive_connection
return unless Canvas::Plugin.find(:google_drive).try(:settings)
## @real_current_user first ensures that a masquerading user never sees the
## masqueradee's files, but in general you may want to block access to google
## docs for masqueraders earlier in the request
if logged_in_user
refresh_token, access_token = Rails.cache.fetch(['google_drive_tokens', logged_in_user].cache_key) do
service = logged_in_user.user_services.where(service: "google_drive").first
service && [service.token, service.secret]
end
else
refresh_token = session[:oauth_gdrive_refresh_token]
access_token = session[:oauth_gdrive_access_token]
end
GoogleDocs::DriveConnection.new(refresh_token, access_token) if refresh_token && access_token
end
def google_service_connection
google_drive_connection || google_docs_connection
end
def google_drive_user_client
if logged_in_user
refresh_token, access_token = Rails.cache.fetch(['google_drive_tokens', logged_in_user].cache_key) do
service = logged_in_user.user_services.where(service: "google_drive").first
service && [service.token, service.access_token]
end
else
refresh_token = session[:oauth_gdrive_access_token]
access_token = session[:oauth_gdrive_refresh_token]
refresh_token = session[:oauth_gdrive_refresh_token]
access_token = session[:oauth_gdrive_access_token]
end
google_drive_client(refresh_token, access_token)
end

View File

@ -129,12 +129,14 @@ class AssignmentsController < ApplicationController
end
begin
google_docs = google_docs_connection
google_docs = google_service_connection
@google_service = google_docs.service_type
@google_docs_token = google_docs.retrieve_access_token
rescue GoogleDocs::NoTokenError
#do nothing
end
add_crumb(@assignment.title, polymorphic_url([@context, @assignment]))
log_asset_access(@assignment, "assignments", @assignment.assignment_group)
@ -164,8 +166,7 @@ class AssignmentsController < ApplicationController
if assignment.allow_google_docs_submission? && @real_current_user.blank?
docs = {}
begin
google_docs = google_docs_connection
docs = google_docs.list_with_extension_filter(assignment.allowed_extensions)
docs = google_service_connection.list_with_extension_filter(assignment.allowed_extensions)
rescue GoogleDocs::NoTokenError
#do nothing
rescue => e

View File

@ -67,8 +67,11 @@ class CollaborationsController < ApplicationController
@collaborations = @context.collaborations.active
log_asset_access("collaborations:#{@context.asset_string}", "collaborations", "other")
@google_drive_upgrade = logged_in_user && Canvas::Plugin.find(:google_drive).try(:settings) &&
(!logged_in_user.user_services.where(service: 'google_drive').first || !(google_drive_connection.verify_access_token rescue false))
@google_docs_authorized = !@google_drive_upgrade && google_service_connection.verify_access_token rescue false
@google_docs_authorized = google_docs_connection.verify_access_token rescue false
js_env :TITLE_MAX_LEN => Collaboration::TITLE_MAX_LENGTH,
:collaboration_types => Collaboration.collaboration_types
end
@ -135,7 +138,7 @@ class CollaborationsController < ApplicationController
def destroy
@collaboration = @context.collaborations.find(params[:id])
if authorized_action(@collaboration, @current_user, :delete)
@collaboration.delete_document if params[:delete_doc]
@collaboration.delete_document if value_to_boolean(params[:delete_doc])
@collaboration.destroy
respond_to do |format|
format.html { redirect_to named_context_url(@context, :collaborations_url) }
@ -180,5 +183,6 @@ class CollaborationsController < ApplicationController
return false
end
end
end

View File

@ -476,11 +476,11 @@ class SubmissionsController < ApplicationController
def submit_google_doc(document_id)
# fetch document from google
google_docs = google_docs_connection
google_docs = google_service_connection
document_response, display_name, file_extension = google_docs.download(document_id)
# error handling
unless document_response.try(:is_a?, Net::HTTPOK)
unless document_response.try(:is_a?, Net::HTTPOK) || document_response.status == 200
flash[:error] = t('errors.assignment_submit_fail', 'Assignment failed to submit')
end

View File

@ -238,6 +238,7 @@ class UsersController < ApplicationController
if logged_in_user
service = UserService.where(user_id: @current_user, service: 'google_drive', service_domain: 'drive.google.com').first_or_initialize
service.service_user_id = user_info['permissionId']
service.service_user_name = user_info['emailAddress']
service.token = client.authorization.refresh_token
service.secret = client.authorization.access_token
service.save

View File

@ -308,6 +308,7 @@ class Collaboration < ActiveRecord::Base
users_to_remove.uniq!
end
# make real user objects, instead of just ids, cause that's what this code expects
users_to_remove.reject! {|id| id == self.user.id}
users_to_remove = users_to_remove.map { |id| User.send(:instantiate, 'id' => id) }
remove_users_from_document(users_to_remove)
end

View File

@ -17,84 +17,120 @@
#
class GoogleDocsCollaboration < Collaboration
GOOGLE_DOC_SERVICE = "google.com"
GOOGLE_DRIVE_SERVICE = "drive.google.com"
def style_class
'google_docs'
end
def service_name
"Google Docs"
end
def delete_document
if !self.document_id && self.user
google_docs = google_docs_for_user
google_docs.delete_doc(GoogleDocEntry.new(self.data))
if self.document_id && self.user
# google docs expected an object
# drive just wants an id
doc = is_google_drive ? self.document_id : GoogleDocs::Entry.new(self.data)
google_adapter_for_user.delete_doc(doc)
end
end
def initialize_document
if !self.document_id && self.user
name = self.title
name = nil if name && name.empty?
name ||= I18n.t('lib.google_docs.default_document_name', "Instructure Doc")
google_docs = google_docs_for_user
file = google_docs.create_doc(name, google_docs.retrieve_access_token)
self.document_id = file.document_id
self.data = file.entry.to_xml
self.url = file.alternate_url.to_s
result = google_adapter_for_user.create_doc(name)
if result
if is_google_drive
self.document_id = result.data.id
self.data = result.data.to_json
self.url = result.data.alternateLink
else
self.document_id = result.document_id
self.data = result.entry.to_xml
self.url = result.alternate_url.to_s
end
end
end
end
def user_can_access_document_type?(user)
if self.user && user
google_services = user.user_services.where(service_domain: "google.com").to_a
!!google_services.find{|s| s.service_user_id}
else
false
end
return !!google_user_service(user) || !!google_user_service(user, GOOGLE_DRIVE_SERVICE) if self.user && user
false
end
def authorize_user(user)
return unless self.document_id
google_services = user.user_services.where(service_domain: "google.com").to_a
service_user_id = google_services.find{|s| s.service_user_id}.service_user_id rescue nil
service_user_id = google_adapter_user_service(user).service_user_id rescue nil
collaborator = self.collaborators.where(user_id: user).first
if collaborator && collaborator.authorized_service_user_id != service_user_id
google_docs = google_docs_for_user
google_docs.acl_remove(self.document_id, [collaborator.authorized_service_user_id]) if collaborator.authorized_service_user_id
google_docs.acl_add(self.document_id, [user])
google_adapter_for_user.acl_remove(self.document_id, [collaborator.authorized_service_user_id]) if collaborator.authorized_service_user_id
user_param = is_google_drive ? service_user_id : user
google_adapter_for_user.acl_add(self.document_id, [user_param])
collaborator.update_attributes(:authorized_service_user_id => service_user_id)
end
end
def remove_users_from_document(users_to_remove)
google_docs = google_docs_for_user
google_docs.acl_remove(self.document_id, users_to_remove) if self.document_id
if is_google_drive
users_to_remove = users_to_remove.map do |user|
user_service = google_user_service(user, GOOGLE_DRIVE_SERVICE) and user_service.service_user_id
end
end
google_adapter_for_user.acl_remove(self.document_id, users_to_remove) if self.document_id
end
def add_users_to_document(new_users)
domain = if context.root_account.feature_enabled?(:google_docs_domain_restriction)
context.root_account.settings[:google_docs_domain]
else
nil
end
if document_id
google_docs = google_docs_for_user
google_docs.acl_add(self.document_id, new_users, domain)
domain = if context.root_account.feature_enabled?(:google_docs_domain_restriction)
context.root_account.settings[:google_docs_domain]
else
nil
end
if is_google_drive
user_ids = new_users.map do |user|
google_adapter_user_service(user).service_user_id rescue nil
end.compact
else
user_ids = new_users
end
google_adapter_for_user.acl_add(self.document_id, user_ids, domain)
end
end
def parse_data
@entry_data ||= Atom::Entry.load_entry(self.data)
end
def self.config
GoogleDocs::Connection.config
end
private
##
# Check to see if this collaboration can use google drive
def is_google_drive(user=self.user)
return unless Canvas::Plugin.find(:google_drive).try(:settings)
@google_drive ||= {}
@google_drive[user.id] ||= !!google_user_service(user, GOOGLE_DRIVE_SERVICE)
end
def google_user_service(user, service_domain=GOOGLE_DOC_SERVICE)
google_services = user.user_services.where(service_domain: service_domain).to_a
google_services.find{|s| s.service_user_id}
end
def google_docs_for_user
service_token, service_secret = Rails.cache.fetch(['google_docs_tokens', self.user].cache_key) do
service = self.user.user_services.where(service: "google_docs").first
@ -103,4 +139,22 @@ class GoogleDocsCollaboration < Collaboration
raise GoogleDocs::NoTokenError unless service_token && service_secret
GoogleDocs::Connection.new(service_token, service_secret)
end
def google_drive_for_user
refresh_token, access_token = Rails.cache.fetch(['google_drive_tokens', self.user].cache_key) do
service = self.user.user_services.where(service: "google_drive").first
service && [service.token, service.secret]
end
raise GoogleDocs::NoTokenError unless refresh_token && access_token
GoogleDocs::DriveConnection.new(refresh_token, access_token)
end
def google_adapter_for_user
return google_drive_for_user if is_google_drive
google_docs_for_user
end
def google_adapter_user_service(user)
google_user_service(user, GOOGLE_DRIVE_SERVICE) || google_user_service(user)
end
end

View File

@ -271,11 +271,13 @@
<div id="submit_google_doc_form">
<%= t 'messages.google_docs_auth_required', "Before you can submit assignments directly from Google Docs you need to authorize Canvas to access your Google Docs account:" %>
<div style="font-size: 1.1em; text-align: center; margin: 10px;">
<a class="btn" href="<%= oauth_url(:service => :google_docs, :return_to => (request.url + "#submit_google_doc_form")) %>"><%= t 'links.authorize_google_docs', "Authorize Google Docs Access" %></a>
<%# TODO: do the right stuff %>
<a class="btn" href="<%= oauth_url(:service => :google_drive, :return_to => (request.url + "#submit_google_doc_form")) %>"><%= t 'links.authorize_google_docs', "Authorize Google Docs Access" %></a>
</div>
</div>
<% end %>
<% end %>
<% if @assignment.submission_types && @assignment.submission_types.match(/media_recording/) %>
<% if !feature_enabled?(:kaltura) %>
<div id="submit_media_recording_form">

View File

@ -19,7 +19,7 @@
<% end %>
</select>
</td>
</tr><tr id="google_docs_description" style="display: none;" class="collaboration_type <%= 'unauthorized' unless @google_docs_authorized %>">
</tr><tr id="google_docs_description" style="display: none;" class="collaboration_type <%= 'unauthorized' unless @google_docs_authorized%>">
<td colspan="2" style="padding: 5px 20px 10px">
<%= image_tag "google_docs_icon.png", :style => "float: right; margin-left: 15px;" %>
<%= mt 'descriptions.google_docs', "Google Docs is a great place to collaborate on a group project. It's like Microsoft Word, but lets you work together with others on the same file at the same time without having to email it around. \n \n**Warning**: you (and all your collaborators) will need a Google account in order to participate in any Google Docs collaborations." %>
@ -56,12 +56,21 @@ HEREDOC
</div>
</div>
<div id="collaborate_authorize_google_docs" class="collaboration_authorization" style="display: none; margin: 20px;">
<%= t '#instructions.authorize_google_docs', "Before you can collaborate on documents, you need to authorize Canvas
to access your Google Docs account:" %>
<div class="button-container">
<a class="btn button-default-action" href="<%= oauth_url(:service => :google_docs, :return_to => (request.url + "#add_collaboration")) %>"><%= t '#buttons.authorize_google_docs', "Authorize Google Docs Access" %></a>
<button type="button" class="btn button-secondary cancel_button"><%= t '#buttons.cancel', "Cancel" %></button>
</div>
<% if @google_drive_upgrade %>
<%= t '#instructions.authorize_google_drive', "Before you can collaborate on documents, you need to authorize Canvas
to access your Google Drive account:" %>
<div class="button-container">
<a class="btn button-default-action" href="<%= oauth_url(:service => :google_drive, :return_to => (request.url + "#add_collaboration")) %>"><%= t '#buttons.authorize_google_drive', "Authorize Google Drive Access" %></a>
<button type="button" class="btn button-secondary cancel_button"><%= t '#buttons.cancel', "Cancel" %></button>
</div>
<% else %>
<%= t '#instructions.authorize_google_docs', "Before you can collaborate on documents, you need to authorize Canvas
to access your Google Docs account:" %>
<div class="button-container">
<a class="btn button-default-action" href="<%= oauth_url(:service => :google_docs, :return_to => (request.url + "#add_collaboration")) %>"><%= t '#buttons.authorize_google_docs', "Authorize Google Docs Access" %></a>
<button type="button" class="btn button-secondary cancel_button"><%= t '#buttons.cancel', "Cancel" %></button>
</div>
<% end %>
</div>
</td>
</tr>
@ -72,8 +81,8 @@ HEREDOC
<%= t 'messages.delete_google_doc_as_well', "This collaboration is being stored as a Google Doc. Did you want to delete it just
from Canvas, or remove it from Google Docs as well?" %>
<div style="margin-top: 15px;">
<button type="button" class="btn delete_button delete_document_button"><%= mt 'buttons.delete_from_canvas', "Just Delete \nfrom Canvas" %></button>
<button type="button" class="btn delete_button"><%= mt 'buttons.delete_from_google_docs', "Also Delete From \nGoogle Docs" %></button>
<button type="button" class="btn delete_button"><%= mt 'buttons.delete_from_canvas', "Just Delete \nfrom Canvas" %></button>
<button type="button" class="btn delete_button delete_document_button"><%= mt 'buttons.delete_from_google_docs', "Also Delete From \nGoogle Docs" %></button>
</div>
</div>
<%= form_tag(context_url(@context, :context_collaboration_url, "{{ id }}"), {:id => "edit_collaboration_form", :method => :put, :style => "display: none; margin-top: 10px;", :class => 'collaboration communication_message'}) do %>

View File

@ -57,7 +57,7 @@ HEREDOC
</div>
<div id="collaborations">
<% @collaborations.each do |collaboration| %>
<% if can_do(collaboration, @current_user, :read) %>
<% if can_do(collaboration, @current_user, :read) && !collaboration.is_a?(GoogleDocsCollaboration) || (collaboration.is_a?(GoogleDocsCollaboration) && !@google_drive_upgrade) %>
<div class="collaboration <%= collaboration.style_class %> collaboration_<%= collaboration.id %>" data-id="<%= collaboration.id %>">
<% if can_do(collaboration, @current_user, :delete) %>
<div class="links">
@ -76,6 +76,19 @@ HEREDOC
:at => datetime_string(collaboration.created_at) %>
</span>
</div>
<% elsif @google_drive_upgrade %>
<div class="collaboration <%= collaboration.style_class %> collaboration_<%= collaboration.id %>" data-id="<%= collaboration.id %>">
<h3><%= collaboration.title %></h3>
<div style="margin-bottom: 5px;" class="description">
<a href="<%= oauth_url(:service => :google_drive, :return_to => (request.url)) %>"><%= t 'google.drive.upgrade.description', "To access this collaboration you must authorize Canvas to access your Google Drive account" %></a>
</div>
<span <%= context_sensitive_datetime_title(collaboration.created_at, @context) %> style="font-size: 0.8em;">
<%= t :started_by, "Started by *%{user}*, %{at}",
:user => context_user_name(@context, collaboration.user),
:wrapper => "<a href=\"#{context_url(@context, :context_user_url, collaboration.user_id)}\" class=\"collaborator_link\">\\1</a>",
:at => datetime_string(collaboration.created_at) %>
</span>
</div>
<% end %>
<% end %>
</div>

View File

@ -1,5 +1,10 @@
GoogleDocs::DriveConnection.config = Proc.new do
Canvas::Plugin.find(:google_drive).try(:settings) || ConfigFile.load('google_drive')
end
GoogleDocs::Connection.config = Proc.new do
Canvas::Plugin.find(:google_docs).try(:settings) || ConfigFile.load('google_docs')
end
GoogleDocs::Entry.extension_looker_upper = ScribdMimeType
GoogleDocs::Entry.extension_looker_upper = ScribdMimeType

View File

@ -4,6 +4,7 @@ require 'uri'
module GoogleDocs
require "google_docs/connection"
require "google_docs/drive_connection"
require "google_docs/entry"
require "google_docs/folder"
require "google_docs/no_token_error"

View File

@ -31,7 +31,11 @@ module GoogleDocs
@access_token ||= OAuth::AccessToken.new(consumer, @oauth_gdocs_access_token, @oauth_gdocs_access_token_secret)
end
def get_service_user_info(access_token)
def service_type
:google_docs
end
def get_service_user_info(access_token=retrieve_access_token)
doc = create_doc("Temp Doc: #{Time.now.strftime("%d %b %Y, %I:%M %p")}", access_token)
delete_doc(doc, access_token)
service_user_id = doc.entry.authors[0].email rescue nil
@ -211,7 +215,7 @@ module GoogleDocs
add_extension_namespace :gAcl, 'http://schemas.google.com/acl/2007'
end
def create_doc(name, access_token)
def create_doc(name, access_token=retrieve_access_token)
url = "https://docs.google.com/feeds/documents/private/full"
entry = Atom::Entry.new do |entry|
entry.title = name

View File

@ -0,0 +1,214 @@
#
# 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/>.
#
# See Google Docs API documentation here:
# http://code.google.com/apis/documents/docs/2.0/developers_guide_protocol.html
module GoogleDocs
class DriveConnectionException < RuntimeError
end
class DriveConnection
def initialize(refresh_token, access_token)
@refresh_token = refresh_token
@access_token = access_token
end
def retrieve_access_token
@access_token || api_client
end
def service_type
:google_drive
end
def download(document_id)
response = api_client.execute!(
:api_method => drive.files.get,
:parameters => { :fileId => document_id }
)
file = response.data
file_info = get_file_info(file)
result = api_client.execute(:uri => file_info[:url])
if result.status == 200
# hack to make it seem like the old object
result.define_singleton_method(:content_type) do
result.headers['Content-Type']
end
# TODO: get extension from response header
[result, file.title, file_info[:ext]]
else
raise DriveConnectionException
end
end
def list_with_extension_filter(extensions)
list extensions
end
def create_doc(name)
file_data = {
:title => name,
:mimeType => 'application/vnd.google-apps.document'
}
api_client.authorization.update_token!
file = drive.files.insert.request_schema.new(file_data)
result = api_client.execute(
:api_method => drive.files.insert,
:body_object => file
)
if result.status == 200
result
else
raise DriveConnectionException
end
end
def delete_doc(document_id)
api_client.authorization.update_token!
result = api_client.execute(
:api_method => drive.files.delete,
:parameters => { :fileId => document_id })
if result.error? && !result.error_message.include?('File not found')
raise DriveConnectionException
end
end
def acl_remove(document_id, users)
api_client.authorization.update_token!
users.each do |user_id|
next if user_id.blank?
result = api_client.execute(
:api_method => drive.permissions.delete,
:parameters => {
:fileId => document_id,
:permissionId => user_id })
if result.error?
raise DriveConnectionException, result
end
end
end
# Public: Add users to a Google Doc ACL list.
#
# document_id - The id of the Google Doc to add users to.
# users - An array of user objects.
# domain - The string domain to restrict additions to (e.g. "example.com").
# Accounts not on this domain will be ignored.
#
# Returns nothing.
def acl_add(document_id, users, domain = nil)
# TODO: support domain
api_client.authorization.update_token!
users.each do |user_id|
new_permission = drive.permissions.insert.request_schema.new({
:id => user_id,
:type => 'user',
:role => 'writer'
})
result = api_client.execute(
:api_method => drive.permissions.insert,
:body_object => new_permission,
:parameters => { :fileId => document_id }
)
if result.error?
raise DriveConnectionException
end
end
end
def verify_access_token
api_client.authorization.update_token!
return api_client.execute(:api_method => drive.about.get).status == 200
end
def self.config_check(settings)
raise DriveConnectionException("No config check")
end
def self.config=(config)
unless config.is_a?(Proc)
raise 'Config must be a Proc'
end
@config = config
end
def self.config
@config.call
end
private
def list(extensions)
documents = api_client.execute!(:api_method => drive.files.list).data.to_hash
{
:name => '/',
:folders => [],
:files => documents['items'].map do |doc|
doc_info = get_file_info(doc)
if extensions.include?(doc_info[:ext])
{
:name => doc['title'],
:document_id => doc['id'],
:extension => doc_info[:ext],
:alternate_url => {
:href => doc_info[:url]
}
}
end
end.compact!
}
end
def api_client
return nil if GoogleDocs::DriveConnection.config.nil?
@api_client ||= GoogleDrive::Client.create(GoogleDocs::DriveConnection.config, @refresh_token, @access_token)
end
def drive
api_client.discovered_api('drive', 'v2')
end
def get_file_info(file)
file_ext = {
".docx" => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
".xlsx" => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
".pdf" => 'application/pdf',
}
e = file_ext.find {|extension, mime_type| file['exportLinks'] && file['exportLinks'][mime_type] }
if e.present?
{
url: file['exportLinks'][e.last],
ext: e.first
}
else
{
url: file['downloadUrl'],
ext: ".none"
}
end
end
end
end

View File

@ -10,7 +10,7 @@ module GoogleDrive
# @param [String] access_token
# Optional access_token
def self.create(client_secrets, refresh_token = nil, access_token = nil)
client = Google::APIClient.new
client = Google::APIClient.new(application_name: "Instructure Google Drive", application_version: "0.0.1")
client.authorization.client_id = client_secrets['client_id']
client.authorization.client_secret = client_secrets['client_secret']
client.authorization.redirect_uri = client_secrets['redirect_uri']

View File

@ -82,7 +82,7 @@ Canvas::Plugin.register('google_docs', :collaborations, {
:settings_partial => 'plugins/google_docs_settings',
:validator => 'GoogleDocsValidator'
})
Canvas::Plugin.register('google_drive', :collaborations, {
Canvas::Plugin.register('google_drive', nil, {
:name => lambda{ t :name, 'Google Drive' },
:description => lambda{ t :description, 'Google Drive file sharing' },
:website => 'http://drive.google.com',

View File

@ -132,6 +132,69 @@ describe ApplicationController do
end
it "uses @real_current_user first" do
mock_real_current_user = mock()
mock_current_user = mock()
controller.instance_variable_set(:@real_current_user, mock_real_current_user)
controller.instance_variable_set(:@current_user, mock_current_user)
session[:oauth_gdrive_refresh_token] = "session_token"
session[:oauth_gdrive_access_token] = "sesion_secret"
Rails.cache.expects(:fetch).with(['google_drive_tokens', mock_real_current_user].cache_key).returns(["real_current_user_token", "real_current_user_secret"])
GoogleDocs::DriveConnection.expects(:new).with("real_current_user_token", "real_current_user_secret")
controller.send(:google_drive_connection)
end
it "uses @current_user second" do
mock_current_user = mock()
controller.instance_variable_set(:@real_current_user, nil)
controller.instance_variable_set(:@current_user, mock_current_user)
session[:oauth_gdrive_refresh_token] = "session_token"
session[:oauth_gdrive_access_token] = "sesion_secret"
Rails.cache.expects(:fetch).with(['google_drive_tokens', mock_current_user].cache_key).returns(["current_user_token", "current_user_secret"])
GoogleDocs::DriveConnection.expects(:new).with("current_user_token", "current_user_secret")
controller.send(:google_drive_connection)
end
it "queries user services if token isn't in the cache" do
mock_current_user = mock()
controller.instance_variable_set(:@real_current_user, nil)
controller.instance_variable_set(:@current_user, mock_current_user)
session[:oauth_gdrive_refresh_token] = "session_token"
session[:oauth_gdrive_access_token] = "sesion_secret"
mock_user_services = mock("mock_user_services")
mock_current_user.expects(:user_services).returns(mock_user_services)
mock_user_services.expects(:where).with(service: "google_drive").returns(stub(first: mock(token: "user_service_token", secret: "user_service_secret")))
GoogleDocs::DriveConnection.expects(:new).with("user_service_token", "user_service_secret")
controller.send(:google_drive_connection)
end
it "uses the session values if no users are set" do
controller.instance_variable_set(:@real_current_user, nil)
controller.instance_variable_set(:@current_user, nil)
session[:oauth_gdrive_refresh_token] = "session_token"
session[:oauth_gdrive_access_token] = "sesion_secret"
GoogleDocs::DriveConnection.expects(:new).with("session_token", "sesion_secret")
controller.send(:google_drive_connection)
end
end
describe "#google_drive_user_client" do
before :each do
settings_mock = mock()
settings_mock.stubs(:settings).returns({})
Canvas::Plugin.stubs(:find).returns(settings_mock)
end
it "uses @real_current_user first" do
mock_real_current_user = mock()
mock_current_user = mock()
@ -140,7 +203,7 @@ describe ApplicationController do
Rails.cache.expects(:fetch).with(['google_drive_tokens', mock_real_current_user].cache_key).returns(["real_current_user_refresh_token", "real_current_user_access_token"])
GoogleDrive::Client.expects(:create).with({},"real_current_user_refresh_token", "real_current_user_access_token")
controller.send(:google_drive_connection)
controller.send(:google_drive_user_client)
end
it "uses @current_user second" do
@ -149,7 +212,7 @@ describe ApplicationController do
controller.instance_variable_set(:@current_user, mock_current_user)
Rails.cache.expects(:fetch).with(['google_drive_tokens', mock_current_user].cache_key).returns(["current_user_refresh_token", "current_user_access_token"])
GoogleDrive::Client.expects(:create).with({},"current_user_refresh_token", "current_user_access_token")
controller.send(:google_drive_connection)
controller.send(:google_drive_user_client)
end
it "queries user services if token isn't in the cache" do
@ -165,7 +228,7 @@ describe ApplicationController do
GoogleDrive::Client.expects(:create).with({}, "user_refresh_token", "user_access_token")
controller.send(:google_drive_connection)
controller.send(:google_drive_user_client)
end
it "uses the session values if no users are set" do
@ -174,9 +237,8 @@ describe ApplicationController do
session[:oauth_gdrive_access_token] = "access_token"
session[:oauth_gdrive_refresh_token] = "refresh_token"
GoogleDrive::Client.expects(:create).with({}, "access_token", "refresh_token")
controller.send(:google_drive_connection)
GoogleDrive::Client.expects(:create).with({}, "refresh_token", "access_token")
controller.send(:google_drive_user_client)
end
end

View File

@ -42,10 +42,7 @@ describe CollaborationsController do
it "should assign variables" do
user_session(@student)
mock_user_service = mock()
@user.expects(:user_services).returns(mock_user_service)
mock_user_service.expects(:where).with(service: "google_docs").returns(stub(first: mock(token: "token", secret: "secret")))
GoogleDocs::Connection.any_instance.expects(:verify_access_token).returns(true)
controller.stubs(:google_docs_connection).returns(mock(verify_access_token:true))
get 'index', :course_id => @course.id
@ -55,9 +52,7 @@ describe CollaborationsController do
it "should handle users without google authorized" do
user_session(@student)
mock_user_service = mock()
@user.expects(:user_services).returns(mock_user_service)
mock_user_service.expects(:where).with(service: "google_docs").returns(stub(first: mock(token: nil, secret: nil)))
controller.stubs(:google_docs_connection).returns(mock(verify_access_token:false))
get 'index', :course_id => @course.id
@ -67,10 +62,9 @@ describe CollaborationsController do
it "should assign variables when verify raises" do
user_session(@student)
mock_user_service = mock()
@user.expects(:user_services).returns(mock_user_service)
mock_user_service.expects(:where).with(service: "google_docs").returns(stub(first: mock(token: "token", secret: "secret")))
GoogleDocs::Connection.any_instance.expects(:verify_access_token).raises("Error")
google_docs_connection_mock = mock()
google_docs_connection_mock.expects(:verify_access_token).raises("Error")
controller.stubs(:google_docs_connection).returns(google_docs_connection_mock)
get 'index', :course_id => @course.id
@ -78,6 +72,19 @@ describe CollaborationsController do
expect(assigns(:google_docs_authorized)).to eq false
end
it 'handles users that need to upgrade to google_drive' do
user_session(@student)
plugin = Canvas::Plugin.find(:google_drive)
plugin_setting = PluginSetting.find_by_name(plugin.id) || PluginSetting.new(:name => plugin.id, :settings => plugin.default_settings)
plugin_setting.posted_settings = {}
plugin_setting.save!
get 'index', :course_id => @course.id
expect(response).to be_success
expect(assigns(:google_docs_authorized)).to be_falsey
expect(assigns(:google_drive_upgrade)).to be_truthy
end
it "should not allow the student view student to access collaborations" do
course_with_teacher_logged_in(:active_user => true)
expect(@course).not_to be_available
@ -94,9 +101,7 @@ describe CollaborationsController do
group = gc.groups.create!(:context => @course)
group.add_user(@student, 'accepted')
mock_user_service = mock()
@user.expects(:user_services).returns(mock_user_service)
mock_user_service.expects(:where).with(service: "google_docs").returns(stub(first: mock(token: "token", secret: "secret")))
#controller.stubs(:google_docs_connection).returns(mock(verify_access_token:false))
get 'index', :group_id => group.id
expect(response).to be_success

View File

@ -208,7 +208,7 @@ describe SubmissionsController do
flag.state = 'on'
flag.save!
mock_user_service = mock()
@user.expects(:user_services).returns(mock_user_service)
@user.stubs(:user_services).returns(mock_user_service)
mock_user_service.expects(:where).with(service: "google_docs").returns(stub(first: mock(token: "token", secret: "secret")))
end

View File

@ -26,9 +26,11 @@ describe GoogleDocsCollaboration do
google_docs_collaboration.title = "title"
google_docs_collaboration.user = user
google_doc_connection = stub(retrieve_access_token: "asdf123")
Canvas::Plugin.stubs(:find).with(:google_drive).returns(nil)
GoogleDocs::Connection.expects(:new).returns(google_doc_connection)
file = stub(document_id: 1, entry: stub(to_xml: "<xml></xml>"), alternate_url: "http://google.com")
google_doc_connection.expects(:create_doc).with("title", "asdf123").returns(file)
google_doc_connection.expects(:create_doc).with("title").returns(file)
Rails.cache.expects(:fetch).returns(["token", "secret"])
google_docs_collaboration.initialize_document