Remove Google Docs plugin

Google drive relevant code has been moved to the gems/google_drive
Actually test google drive with mocked api responses
cleaned up lots of code

Fixes PLAT-1301

Test Plan:
*** You'll need to use 2 separate google accounts to fully test ***
** Its helpful to have multiple chrome profiles so you dont have to log in and
** out a bunch https://support.google.com/chrome/answer/2364824?hl=en

BEFORE SWITCHING TO THIS PATCH SET
  - Make sure you have some google doc collaborations
    - Directions for creating one are below
  - Disable your google docs plugin

Switch to the patch set
Enable google drive. (if not already)
  - You should be able to paste in a client_secrets.json
  - Update Redirect URI to point to your local instance (The URI should exist in the JSON)

As an admin/teacher
  - Goto `profile/settings` and add the google drive user service
    - Your email should be displayed with the service
  - Goto Collaborations
    - Make sure existing collaborations still work
    - Click "Start new collaboration"
    - give it a nice name (Created by admin)
    - select your student
    - click start collaborating
    - You should be redirected to the new doc in google
    - create another collaboration without the student (Created by admin, no users)
  - Create an assignment with
    Submission Type: Online
    Online Entry Options: File Uploads
  - Make sure your student has access to the course/assignment
  - Goto Account settings -> Users
    - Click View User Groups in the top right
    - create a new group set with a group in it
    - add yourself and your student to the group
  - Goto the Group's Collaborations (Courses & Groups -> [group name] -> Collaborations)
    - Click "Start new collaboration"
    - give it a nice name (Created by admin for group)
    - select your student
    - click start collaborating
    - You should be redirected to the new doc in google

As a student
  - Goto `profile/settings` and add the google drive user service
    - Your email should be displayed with the service
  - Goto the assignment
    - Submit the assignment
    - You should have a "Google Doc" tab
    - Choose a document and submit it
    - After it submitted, click download on the right side
      just to make sure its correct
  - Goto Collaborations
    - Make sure existing collaborations still work
    - make sure the "created by admin" collaboration works
    - make sure you can't see "Created by admin, no users"
    - Click "Start new collaboration"
    - give it a nice name (created by a student)
    - click start collaborating
    - You should be redirected to the new doc in google
    - Switch to your admin/teacher and make sure they can't access it
    - Edit the collaboration add the teacher, click Save
    - Switch to your admin/teacher and make sure they can access it
  - Goto `profile/settings` and REMOVE the google drive user service
  - Go back to collaborations
  - You should be forced to add the google service in order to use a collaboration
  - Goto the Group's Collaborations (Courses & Groups -> [group name] -> Collaborations)
    - Make sure you can access "Created by admin for group"
    - Click "Start new collaboration"
    - select your admin/teacher
    - click start collaborating
    - You should be redirected to the new doc in google
    - make sure your admin can access it to

As a site admin
  - Disable Google Drive
  - Then with your student
    - Make sure you can still do file upload submissions on the assignment
    - Collaborations should no longer show up (unless you have etherpad enabled)
  - Google drive should no longer show up in your profile settings as a registered service
Change-Id: I4dfaff6f5262743c044aadd12266fd0bd85a60e1
Reviewed-on: https://gerrit.instructure.com/69078
Reviewed-by: Andrew Butterfield <abutterfield@instructure.com>
Tested-by: Jenkins
QA-Review: August Thornton <august@instructure.com>
Product-Review: Brad Horrocks <bhorrocks@instructure.com>
This commit is contained in:
Brad Horrocks 2015-12-16 14:53:11 -07:00
parent 3da893f521
commit 3fee901ac0
82 changed files with 4871 additions and 2153 deletions

View File

@ -128,7 +128,6 @@ gem 'canvas_text_helper', path: 'gems/canvas_text_helper'
gem 'canvas_time', path: 'gems/canvas_time'
gem 'canvas_unzip', path: 'gems/canvas_unzip'
gem 'csv_diff', path: 'gems/csv_diff'
gem 'google_docs', path: 'gems/google_docs'
gem 'google_drive', path: 'gems/google_drive'
gem 'html_text_helper', path: 'gems/html_text_helper'
gem 'incoming_mail_processor', path: 'gems/incoming_mail_processor'

View File

@ -1504,8 +1504,6 @@ class ApplicationController < ActionController::Base
!!LinkedIn::Connection.config
elsif feature == :diigo
!!Diigo::Connection.config
elsif feature == :google_docs
!!GoogleDocs::Connection.config
elsif feature == :google_drive
Canvas::Plugin.find(:google_drive).try(:enabled?)
elsif feature == :etherpad
@ -1957,29 +1955,13 @@ class ApplicationController < ActionController::Base
end
end
def google_docs_connection
## @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
service_token, service_secret = Rails.cache.fetch(['google_docs_tokens', logged_in_user].cache_key) do
service = logged_in_user.user_services.where(service: "google_docs").first
service && [service.token, service.secret]
end
raise GoogleDocs::NoTokenError unless service_token && service_secret
google_docs = GoogleDocs::Connection.new(service_token, service_secret)
else
google_docs = GoogleDocs::Connection.new(session[:oauth_gdocs_access_token_token], session[:oauth_gdocs_access_token_secret])
end
google_docs
end
def self.google_drive_timeout
Setting.get('google_drive_timeout', 30).to_i
end
def google_drive_connection
return unless Canvas::Plugin.find(:google_drive).try(:settings)
return @google_drive_connection if @google_drive_connection
## @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
@ -1993,24 +1975,7 @@ class ApplicationController < ActionController::Base
access_token = session[:oauth_gdrive_access_token]
end
GoogleDocs::DriveConnection.new(refresh_token, access_token, ApplicationController.google_drive_timeout) 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_refresh_token]
access_token = session[:oauth_gdrive_access_token]
end
google_drive_client(refresh_token, access_token)
@google_drive_connection = GoogleDrive::Connection.new(refresh_token, access_token, ApplicationController.google_drive_timeout)
end
def google_drive_client(refresh_token=nil, access_token=nil)
@ -2023,6 +1988,9 @@ class ApplicationController < ActionController::Base
GoogleDrive::Client.create(client_secrets, refresh_token, access_token)
end
def user_has_google_drive
@user_has_google_drive ||= google_drive_connection.authorized?
end
def twitter_connection
if @current_user

View File

@ -128,16 +128,8 @@ class AssignmentsController < ApplicationController
@current_student_submissions = @assignment.submissions.where("submissions.submission_type IS NOT NULL").where(:user_id => visible_student_ids).to_a
end
begin
google_docs = google_service_connection
@google_service = google_docs.service_type
@google_docs_token = google_service_connection.verify_access_token && google_docs.retrieve_access_token rescue false
rescue GoogleDocs::NoTokenError
# Just fail I guess.
end
@google_drive_upgrade = !!(logged_in_user && Canvas::Plugin.find(:google_drive).try(:settings) &&
(!logged_in_user.user_services.where(service: 'google_drive').first || !(google_docs.verify_access_token rescue false)))
# this will set @user_has_google_drive
user_has_google_drive
add_crumb(@assignment.title, polymorphic_url([@context, @assignment]))
@ -185,8 +177,10 @@ class AssignmentsController < ApplicationController
if assignment.allow_google_docs_submission? && @real_current_user.blank?
docs = {}
begin
docs = google_service_connection.list_with_extension_filter(assignment.allowed_extensions)
rescue GoogleDocs::NoTokenError => e
docs = google_drive_connection.list_with_extension_filter(assignment.allowed_extensions)
rescue GoogleDrive::NoTokenError => e
Canvas::Errors.capture_exception(:oauth, e)
rescue Google::APIClient::AuthorizationError => e
Canvas::Errors.capture_exception(:oauth, e)
rescue ArgumentError => e
Canvas::Errors.capture_exception(:oauth, e)

View File

@ -68,20 +68,8 @@ class CollaborationsController < ApplicationController
@collaborations = @context.collaborations.active
log_asset_access([ "collaborations", @context ], "collaborations", "other")
safe_token_valid = lambda do |service|
begin
self.send(service).verify_access_token
rescue => e
Canvas::Errors.capture(e, { source: 'rescue nil' })
false
end
end
@google_drive_upgrade = logged_in_user && Canvas::Plugin.find(:google_drive).try(:settings) &&
(!logged_in_user.user_services.where(service: 'google_drive').first ||
!safe_token_valid.call(:google_drive_connection))
@google_docs_authorized = !@google_drive_upgrade && safe_token_valid.call(:google_service_connection)
# this will set @user_has_google_drive
user_has_google_drive
@sunsetting_etherpad = EtherpadCollaboration.config.try(:[], :domain) == "etherpad.instructure.com/p"
@has_etherpad_collaborations = @collaborations.any? {|c| c.collaboration_type == 'EtherPad'}
@ -108,7 +96,7 @@ class CollaborationsController < ApplicationController
flash[:error] = t 'errors.cannot_load_collaboration', "Cannot load collaboration"
redirect_to named_context_url(@context, :context_collaborations_url)
end
rescue GoogleDocs::DriveConnectionException => drive_exception
rescue GoogleDrive::ConnectionException => drive_exception
Canvas::Errors.capture(drive_exception)
flash[:error] = t 'errors.cannot_load_collaboration', "Cannot load collaboration"
redirect_to named_context_url(@context, :context_collaborations_url)
@ -163,7 +151,7 @@ class CollaborationsController < ApplicationController
format.json { render :json => @collaboration.errors, :status => :bad_request }
end
end
rescue GoogleDocs::DriveConnectionException => error
rescue GoogleDrive::ConnectionException => error
Rails.logger.warn error
flash[:error] = t 'errors.update_failed', "Collaboration update failed" # generic failure message
if error.message.include?('File not found')

View File

@ -495,10 +495,9 @@ class SubmissionsController < ApplicationController
def submit_google_doc(document_id)
# fetch document from google
google_docs = google_service_connection
# since google drive can have many different export types, we need to send along our preferred extensions
document_response, display_name, file_extension = google_docs.download(document_id, @assignment.allowed_extensions)
document_response, display_name, file_extension, content_type = google_drive_connection.download(document_id,
@assignment.allowed_extensions)
# error handling
unless document_response.try(:is_a?, Net::HTTPOK) || document_response.status == 200
@ -525,7 +524,7 @@ class SubmissionsController < ApplicationController
end
@attachment = @assignment.attachments.new(
uploaded_data: Rack::Test::UploadedFile.new(path, document_response.content_type, true),
uploaded_data: Rack::Test::UploadedFile.new(path, content_type, true),
display_name: display_name, user: @current_user
)
@attachment.save!

View File

@ -213,19 +213,7 @@ class UsersController < ApplicationController
return redirect_to(user_profile_url(@current_user))
end
return_to_url = params[:return_to] || user_profile_url(@current_user)
if params[:service] == "google_docs"
request_token = GoogleDocs::Connection.request_token(oauth_success_url(:service => 'google_docs'))
OauthRequest.create(
:service => 'google_docs',
:token => request_token.token,
:secret => request_token.secret,
:user_secret => CanvasSlug.generate(nil, 16),
:return_url => return_to_url,
:user => @real_current_user || @current_user,
:original_host_with_port => request.host_with_port
)
redirect_to request_token.authorize_url
elsif params[:service] == "google_drive"
if params[:service] == "google_drive"
redirect_uri = oauth_success_url(:service => 'google_drive')
session[:oauth_gdrive_nonce] = SecureRandom.hex
state = Canvas::Security.create_jwt(redirect_uri: redirect_uri, return_to_url: return_to_url, nonce: session[:oauth_gdrive_nonce])
@ -273,7 +261,12 @@ class UsersController < ApplicationController
client = google_drive_client
client.authorization.code = params[:code]
client.authorization.fetch_access_token!
drive = client.discovered_api('drive', 'v2')
# we should look into consolidating this and connection.rb
drive = Rails.cache.fetch(['google_drive_v2'].cache_key) do
client.discovered_api('drive', 'v2')
end
result = client.execute!(:api_method => drive.about.get)
if result.status == 200
@ -318,32 +311,7 @@ class UsersController < ApplicationController
url = url_for request.parameters.merge(:host => oauth_request.original_host_with_port, :only_path => false)
redirect_to url
else
if params[:service] == "google_docs"
begin
access_token = GoogleDocs::Connection.get_access_token(oauth_request.token, oauth_request.secret, params[:oauth_verifier])
google_docs = GoogleDocs::Connection.new(oauth_request.token, oauth_request.secret)
service_user_id, service_user_name = google_docs.get_service_user_info access_token
if oauth_request.user
UserService.register(
:service => "google_docs",
:access_token => access_token,
:user => oauth_request.user,
:service_domain => "google.com",
:service_user_id => service_user_id,
:service_user_name => service_user_name
)
oauth_request.destroy
else
session[:oauth_gdocs_access_token_token] = access_token.token
session[:oauth_gdocs_access_token_secret] = access_token.secret
end
flash[:notice] = t('google_docs_added', "Google Docs access authorized!")
rescue => e
Canvas::Errors.capture_exception(:oauth, e)
flash[:error] = t('google_docs_fail', "Google Docs authorization failed. Please try again")
end
elsif params[:service] == "linked_in"
if params[:service] == "linked_in"
begin
linkedin_connection = LinkedIn::Connection.new
token = session.delete(:oauth_linked_in_request_token_token)
@ -967,8 +935,8 @@ class UsersController < ApplicationController
def delete_user_service
deleted = @current_user.user_services.find(params[:id]).destroy
if deleted.service == "google_docs"
Rails.cache.delete(['google_docs_tokens', @current_user].cache_key)
if deleted.service == "google_drive"
Rails.cache.delete(['google_drive_tokens', @current_user].cache_key)
end
render :json => {:deleted => true}
end

View File

@ -150,7 +150,17 @@ class Collaboration < ActiveRecord::Base
# Returns an array of type hashes w/ 'name' and 'type' keys.
def self.collaboration_types
Canvas::Plugin.all_for_tag(:collaborations).select(&:enabled?).map do |plugin|
HashWithIndifferentAccess.new({ 'name' => plugin.name, 'type' => plugin.id })
# google_drive is really a google_docs_collaboration
# eventually this will go away. baby steps...
if plugin.id == 'google_drive'
type = 'google_docs'
name = 'Google Docs'
else
type = plugin.id
name = plugin.name
end
HashWithIndifferentAccess.new({ 'name' => name, 'type' => type })
end
end
@ -219,7 +229,7 @@ class Collaboration < ActiveRecord::Base
#
# Returns a title string.
def title
read_attribute(:title) || self.parse_data.title
read_attribute(:title) || self.parse_data["title"]
rescue NoMethodError
t('#collaboration.default_title', 'Unnamed Collaboration')
end

View File

@ -16,10 +16,7 @@
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
require 'atom'
class GoogleDocsCollaboration < Collaboration
GOOGLE_DOC_SERVICE = "google.com"
GOOGLE_DRIVE_SERVICE = "drive.google.com"
def style_class
@ -34,7 +31,7 @@ class GoogleDocsCollaboration < Collaboration
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)
doc = self.document_id
google_adapter_for_user.delete_doc(doc)
end
end
@ -47,16 +44,10 @@ class GoogleDocsCollaboration < Collaboration
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.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
self.url = result.data.alternateLink
end
end
end
@ -75,7 +66,7 @@ class GoogleDocsCollaboration < Collaboration
if collaborator.authorized_service_user_id != service_user_id
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
user_param = service_user_id
google_adapter_for_user.acl_add(self.document_id, [user_param])
collaborator.update_attributes(:authorized_service_user_id => service_user_id)
end
@ -86,10 +77,8 @@ class GoogleDocsCollaboration < Collaboration
end
def remove_users_from_document(users_to_remove)
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
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
google_adapter_for_user.acl_remove(self.document_id, users_to_remove) if self.document_id
@ -103,24 +92,20 @@ class GoogleDocsCollaboration < Collaboration
nil
end
if is_google_drive
user_ids = new_users.map do |user|
google_user_service(user, GOOGLE_DRIVE_SERVICE).service_user_id rescue nil
end.compact
else
user_ids = new_users
end
user_ids = new_users.map do |user|
google_user_service(user, GOOGLE_DRIVE_SERVICE).service_user_id rescue nil
end.compact
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)
@entry_data ||= JSON.parse(self.data)
end
def self.config
GoogleDocs::Connection.config
GoogleDrive::Connection.config
end
# Internal: Update collaborators with the given groups.
@ -142,43 +127,25 @@ class GoogleDocsCollaboration < Collaboration
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)
def google_user_service(user, service_domain=GOOGLE_DRIVE_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
service && [service.token, service.secret]
end
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, ApplicationController.google_drive_timeout)
raise GoogleDrive::NoTokenError unless refresh_token && access_token
GoogleDrive::Connection.new(refresh_token, access_token, ApplicationController.google_drive_timeout)
end
def google_adapter_for_user
return google_drive_for_user if is_google_drive
google_docs_for_user
google_drive_for_user
end
def google_adapter_user_service(user)
google_user_service(user, GOOGLE_DRIVE_SERVICE) || google_user_service(user)
google_user_service(user)
end
end

View File

@ -18,7 +18,7 @@
class UserService < ActiveRecord::Base
include Workflow
belongs_to :user
attr_accessor :password
attr_accessible :user, :service, :protocol, :token, :secret, :service_user_url, :service_user_id, :service_user_name, :service_domain, :visible
@ -29,11 +29,11 @@ class UserService < ActiveRecord::Base
after_save :assert_relations
after_save :touch_user
after_destroy :remove_related_channels
def should_have_communication_channel?
[CommunicationChannel::TYPE_TWITTER, CommunicationChannel::TYPE_YO].include?(service) && self.user
end
def assert_relations
if should_have_communication_channel?
cc = self.user.communication_channels.where(path_type: service).first_or_initialize
@ -47,7 +47,7 @@ class UserService < ActiveRecord::Base
end
true
end
def remove_related_channels
# should this include twitter?
if [CommunicationChannel::TYPE_YO].include?(self.service) && self.user
@ -56,25 +56,25 @@ class UserService < ActiveRecord::Base
end
true
end
def assert_communication_channel
# why is twitter getting special treatment?
self.touch if should_have_communication_channel? && !self.user.communication_channels.where(path_type: CommunicationChannel::TYPE_TWITTER).first
end
def infer_defaults
self.refresh_at ||= Time.now.utc
end
protected :infer_defaults
workflow do
state :active do
event :failed_request, :transitions_to => :failed
end
state :failed
end
scope :of_type, lambda { |type| where(:type => type.to_s) }
scope :to_be_polled, -> { where("refresh_at<", Time.now.utc).order(:refresh_at).limit(1) }
@ -84,20 +84,20 @@ class UserService < ActiveRecord::Base
where(:service => service.to_s)
}
scope :visible, -> { where("visible") }
def service_name
self.service.titleize rescue ""
end
def password=(password)
self.crypted_password, self.password_salt = Canvas::Security.encrypt_password(password, 'instructure_user_service')
end
def decrypted_password
return nil unless self.password_salt && self.crypted_password
Canvas::Security.decrypt_password(self.crypted_password, self.password_salt, 'instructure_user_service')
end
def self.register(opts={})
raise "User required" unless opts[:user]
token = opts[:access_token] ? opts[:access_token].token : opts[:token]
@ -117,7 +117,7 @@ class UserService < ActiveRecord::Base
user_service.save!
user_service
end
def self.register_from_params(user, params={})
opts = {}
opts[:user] = user
@ -153,19 +153,17 @@ class UserService < ActiveRecord::Base
end
register(opts)
end
def has_profile_link?
service != 'google_docs'
true
end
def has_readable_user_name?
service == 'google_docs'
service == 'google_drive'
end
def self.sort_position(type)
case type
when 'google_docs'
1
when 'google_drive'
2
when 'skype'
@ -184,11 +182,9 @@ class UserService < ActiveRecord::Base
999
end
end
def self.short_description(type)
case type
when 'google_docs'
t '#user_service.descriptions.google_docs', 'Students can use Google Docs to collaborate on group projects. Google Docs allows for real-time collaborative editing of documents, spreadsheets and presentations.'
when 'google_drive'
t '#user_service.descriptions.google_drive', 'Students can use Google Drive to collaborate on group projects. Google Drive allows for real-time collaborative editing of documents, spreadsheets and presentations.'
when 'google_calendar'
@ -209,11 +205,9 @@ class UserService < ActiveRecord::Base
''
end
end
def self.registration_url(type)
case type
when 'google_docs'
'http://docs.google.com'
when 'google_drive'
'https://www.google.com/drive/'
when 'google_calendar'
@ -234,11 +228,9 @@ class UserService < ActiveRecord::Base
nil
end
end
def service_user_link
case service
when 'google_docs'
'http://docs.google.com'
when 'google_drive'
'https://myaccount.google.com/?pli=1'
when 'google_calendar'
@ -259,15 +251,15 @@ class UserService < ActiveRecord::Base
'http://www.instructure.com'
end
end
def self.configured_services
[:google_docs, :google_drive, :twitter, :yo, :linked_in, :diigo]
[:google_drive, :twitter, :yo, :linked_in, :diigo]
end
def self.configured_service?(service)
configured_services.include?((service || "").to_sym)
end
def self.service_type(type)
if type == 'google_docs' || type == 'google_drive'
'DocumentService'

View File

@ -2,7 +2,7 @@
show_google_docs = @assignment.allow_google_docs_submission? &&
@real_current_user.blank? &&
@domain_root_account &&
feature_and_service_enabled?(:google_docs)
feature_and_service_enabled?(:google_drive)
-%>
<%
js_bundle :submit_assignment
@ -204,7 +204,7 @@
}
</style>
<% if show_google_docs %>
<% if @google_docs_token and not @google_drive_upgrade%>
<% if @user_has_google_drive %>
<% if @domain_root_account.feature_enabled?(:google_docs_domain_restriction) &&
@domain_root_account.settings[:google_docs_domain] &&
!@current_user.gmail.match(%r{@#{@domain_root_account.settings[:google_docs_domain]}$}) %>
@ -268,20 +268,13 @@
</div>
<% end %>
<% end %>
<% elsif @google_drive_upgrade %>
<% else %>
<div id="submit_google_doc_form">
<%= t 'messages.google_drives_auth_required', "Before you can submit assignments directly from Google Drive you need to authorize Canvas to access your Google Drive account:" %>
<div style="font-size: 1.1em; text-align: center; margin: 10px;">
<a class="btn" href="<%= oauth_url(:service => :google_drive, :return_to => (request.url + "#submit_google_doc_form")) %>"><%= t 'links.authorize_google_drive', "Authorize Google Drive Access" %></a>
</div>
</div>
<% else %>
<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>
</div>
</div>
<% end %>
<% end %>

View File

@ -11,7 +11,7 @@
<% Collaboration.collaboration_types.each do |collab_type| %>
<% next if collab_type['type'] == 'etherpad' && @sunsetting_etherpad %>
<% if collab_type['type'] == 'google_docs' %>
<% if @domain_root_account && feature_and_service_enabled?(:google_docs) %>
<% if @domain_root_account && feature_and_service_enabled?(:google_drive) %>
<option value="<%= collab_type['name'] %>"><%= collab_type['name'] %></option>
<% end %>
<% elsif (Collaboration.collaboration_class(collab_type['type'].titleize.gsub(/\s/, "")).config rescue false) %>
@ -20,7 +20,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 @user_has_google_drive %>">
<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." %>
@ -57,20 +57,14 @@ HEREDOC
</div>
</div>
<div id="collaborate_authorize_google_docs" class="collaboration_authorization" style="display: none; margin: 20px;">
<% if @google_drive_upgrade %>
<% if !@user_has_google_drive %>
<%= 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>

View File

@ -56,7 +56,7 @@ HEREDOC
<div id="collaborations">
<% @collaborations.each do |collaboration| %>
<% if can_do(collaboration, @current_user, :read) %>
<% if !collaboration.is_a?(GoogleDocsCollaboration) || (collaboration.is_a?(GoogleDocsCollaboration) && !@google_drive_upgrade) %>
<% if !collaboration.is_a?(GoogleDocsCollaboration) || (collaboration.is_a?(GoogleDocsCollaboration) && @user_has_google_drive) %>
<div class="collaboration <%= collaboration.style_class %> collaboration_<%= collaboration.id %>" data-id="<%= collaboration.id %>">
<div class="collaboration-header clearfix">
<h2>
@ -81,7 +81,7 @@ HEREDOC
:at => datetime_string(collaboration.created_at) %>
</small>
</div>
<% elsif @google_drive_upgrade %>
<% elsif !@user_has_google_drive %>
<div class="collaboration <%= collaboration.style_class %> collaboration_<%= collaboration.id %>" data-id="<%= collaboration.id %>">
<h3><%= collaboration.title %></h3>
<div style="margin-bottom: 5px;" class="description">

View File

@ -229,6 +229,9 @@ TEXT
<% if service.has_profile_link? %>
<a href="<%= service.service_user_link %>">
<%= t('links.view_your_profile', "view your profile") %>
<% if service.has_readable_user_name? %>
<span><%= service.service_user_name %></span>
<% end %>
</a>
<% elsif service.has_readable_user_name? %>
<span><%= service.service_user_name %></span>
@ -252,26 +255,8 @@ TEXT
<%= before_label(:other_services, "Click any service below to register") %>
<% services = @user.user_services.map{|s| s.service} %>
<ul id="unregistered_services" class="unstyled_list">
<li id="unregistered_service_google_docs" class="service" style="<%= hidden if !feature_and_service_enabled?(:google_docs) || services.include?("google_docs") %>">
<a href="#" class="btn btn-small"><%= image_tag "google_docs_icon.png" %> <%= t('links.google_docs', "Google Docs") %></a>
<div style="display: none; text-align: left;" class="content" title="<%= t('titles.authorize_google_docs', "Authorize Google Docs") %>" id="unregistered_service_google_docs_dialog">
<div>
<%= image_tag "google_docs.png", :style => "float: left; padding-right: 5px;" %>
<div style="font-size: 1.2em; font-weight: bold;"><%= t('headers.google_docs_access', "Google Docs Access") %></div>
<%= t(:google_docs_description, <<-TEXT)
Once you authorize us to see your Google Docs you'll be able to submit your assignments
directly from Google Docs, and create and share documents with members of your classes.
TEXT
%>
<div class="clear"></div>
</div>
<div style="text-align: center; margin-top: 10px;">
<a class="btn" href="<%= oauth_url(:service => "google_docs", :return_to => settings_profile_url) %>"><%= t('buttons.authorize_google_docs', "Authorize Google Docs Access") %></a>
</div>
</div>
</li>
<li id="unregistered_service_google_drive" class="service" style="<%= hidden if !feature_enabled?(:google_drive) || !feature_and_service_enabled?(:google_docs) || services.include?("google_drive") %>">
<li id="unregistered_service_google_drive" class="service" style="<%= hidden if !feature_and_service_enabled?(:google_drive) || services.include?("google_drive") %>">
<a href="#" class="btn btn-small"><%= image_tag "google_drive_icon.png" %> <%= t('links.google_drive', "Google Drive") %></a>
<div style="display: none; text-align: left;" class="content" title="<%= t('titles.authorize_google_drive', "Authorize Google Drive") %>" id="unregistered_service_google_drive_dialog">
<div>

View File

@ -1,15 +0,0 @@
GoogleDocs::DriveConnection.config = Proc.new do
settings = Canvas::Plugin.find(:google_drive).try(:settings)
if settings
settings = settings.dup
settings[:client_secret] = settings[:client_secret_dec]
end
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

View File

@ -0,0 +1,9 @@
GoogleDrive::Connection.config = proc do
settings = Canvas::Plugin.find(:google_drive).try(:settings)
if settings
settings = settings.dup
settings[:client_secret] = settings[:client_secret_dec]
end
settings || ConfigFile.load('google_drive')
end

View File

@ -1,4 +0,0 @@
source 'https://rubygems.org'
# Specify your gem's dependencies in google_docs.gemspec
gemspec

View File

@ -1,25 +0,0 @@
# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
Gem::Specification.new do |spec|
spec.name = "google_docs"
spec.version = "1.0.0"
spec.authors = ["Ken Romney"]
spec.email = ["kromney@instructure.com"]
spec.summary = %q{Google Docs}
spec.files = Dir.glob("{lib,spec}/**/*") + %w(test.sh)
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ["lib"]
spec.add_dependency "ratom-nokogiri", "0.10.4"
spec.add_dependency "oauth-instructure", "0.4.10"
spec.add_development_dependency "bundler", "~> 1.5"
spec.add_development_dependency "rake"
spec.add_development_dependency "rspec", "2.99.0"
spec.add_development_dependency "mocha"
spec.add_development_dependency "timecop"
end

View File

@ -1,13 +0,0 @@
require "atom"
require "oauth"
require 'uri'
module GoogleDocs
require "google_docs/connection"
require "google_docs/drive_connection"
require "google_docs/drive_entry"
require "google_docs/drive_folder"
require "google_docs/entry"
require "google_docs/folder"
require "google_docs/no_token_error"
end

View File

@ -1,362 +0,0 @@
#
# Copyright (C) 2011 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
require 'atom'
require 'oauth'
# See Google Docs API documentation here:
# http://code.google.com/apis/documents/docs/2.0/developers_guide_protocol.html
module GoogleDocs
class Connection
def initialize(oauth_gdocs_access_token, oauth_gdocs_access_token_secret)
@oauth_gdocs_access_token = oauth_gdocs_access_token
@oauth_gdocs_access_token_secret = oauth_gdocs_access_token_secret
end
def retrieve_access_token
consumer = GoogleDocs::Connection.consumer
return nil unless consumer
@access_token ||= OAuth::AccessToken.new(consumer, @oauth_gdocs_access_token, @oauth_gdocs_access_token_secret)
end
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
service_user_name = doc.entry.authors[0].email rescue nil
return service_user_id, service_user_name
end
def self.get_access_token(token, secret, oauth_verifier)
consumer = GoogleDocs::Connection.consumer
request_token = OAuth::RequestToken.new(consumer,
token,
secret)
request_token.get_access_token(:oauth_verifier => oauth_verifier)
end
def self.request_token(oauth_callback)
consumer = GoogleDocs::Connection.consumer
consumer.get_request_token({:oauth_callback => oauth_callback}, {:scope => "https://docs.google.com/feeds/ https://spreadsheets.google.com/feeds/"})
end
def download(document_id, not_supported=nil)
access_token = retrieve_access_token
entry = fetch_list(access_token).entries.map { |e| GoogleDocs::Entry.new(e) }.find { |e| e.document_id == document_id }
if entry
response = fetch_entry(access_token, entry)
# some new google spreadsheets will not download as plain 'xls', so
# retry the download as 'xlsx'
if response.is_a?(Net::HTTPBadRequest) && entry.extension == 'xls'
entry.reset_extension_as_xlsx
response = fetch_entry(access_token, entry)
end
[response, entry.display_name, entry.extension]
else
[nil, nil, nil]
end
end
def fetch_entry(access_token, entry)
response = access_token.get(entry.download_url)
response = access_token.get(response['Location']) if response.is_a?(Net::HTTPFound)
response
end
def fetch_list(access_token)
response = access_token.get('https://docs.google.com/feeds/documents/private/full')
Atom::Feed.load_feed(response.body)
end
private :fetch_list
def folderize_list(docs)
root = Folder.new('/')
folders = {nil => root}
docs.entries.each do |entry|
entry = GoogleDocs::Entry.new(entry)
if !folders.has_key?(entry.folder)
folder = Folder.new(entry.folder)
root.add_folder folder
folders[entry.folder] = folder
else
folder = folders[entry.folder]
end
folder.add_file entry
end
return root
end
private :folderize_list
def list(access_token=nil)
access_token ||= retrieve_access_token
folderize_list(fetch_list(access_token))
end
private :list
def list_with_extension_filter(extensions, access_token=nil)
access_token ||= retrieve_access_token
docs = list(access_token)
if extensions && extensions.length > 0
docs = docs.select { |e| extensions.include?(e.extension) }
end
docs
end
def self.consumer(key = nil, secret = nil)
if key.nil? || secret.nil?
return nil if GoogleDocs::Connection.config.nil?
key ||= GoogleDocs::Connection.config['api_key']
secret ||= GoogleDocs::Connection.config['secret_key']
end
require 'oauth'
require 'oauth/consumer'
OAuth::Consumer.new(key, secret, {
:site => 'https://www.google.com',
:request_token_path => '/accounts/OAuthGetRequestToken',
:access_token_path => '/accounts/OAuthGetAccessToken',
:authorize_path => '/accounts/OAuthAuthorizeToken',
:signature_method => 'HMAC-SHA1'
})
end
class Google
class Google::Batch
class Google::Batch::Operation
attr_accessor :type
def initialize(operation_type="insert")
self.type = operation_type
end
def to_xml(builder, *_opts)
builder['batch'].operation(type: type)
end
end
end
class Google::GAcl
class Google::GAcl::Role
attr_accessor :role
def initialize()
self.role = "writer"
end
def to_xml(builder, *_opts)
builder['gAcl'].role(value: role)
end
end
class Google::GAcl::Scope
attr_accessor :type, :value
def initialize(email)
self.type = "user"
self.value = email
end
def to_xml(builder, *_opts)
builder['gAcl'].scope(type: type, value: value)
end
end
end
end
class Entry < Atom::Entry
namespace Atom::NAMESPACE
element "id"
element "batch:id"
element "batch:operation", :class => Google::Batch::Operation
element "gAcl:role", :class => Google::GAcl::Role
element "gAcl:scope", :class => Google::GAcl::Scope
elements :categories
add_extension_namespace :batch, 'http://schemas.google.com/gdata/batch'
add_extension_namespace :gAcl, 'http://schemas.google.com/acl/2007'
end
class Feed < Atom::Feed
namespace Atom::NAMESPACE
elements :entries
elements :categories
add_extension_namespace :batch, 'http://schemas.google.com/gdata/batch'
add_extension_namespace :gAcl, 'http://schemas.google.com/acl/2007'
end
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
entry.categories << Atom::Category.new do |category|
category.scheme = "http://schemas.google.com/g/2005#kind"
category.term = "http://schemas.google.com/docs/2007#document"
category.label = "document"
end
end
xml = entry.to_xml.to_s
begin
response = access_token.post(url, xml, {'Content-Type' => 'application/atom+xml'})
rescue => e
raise "Unable to post to Google API #{url}:\n#{xml}" +
"\n\n(" + e.to_s + ")\n"
end
begin
GoogleDocs::Entry.new(Atom::Entry.load_entry(response.body))
rescue => e
raise "Unable to load GoogleDocEntry from response: \n" + response.body +
"\n\n(" + e.to_s + ")\n"
end
end
def delete_doc(entry, access_token = retrieve_access_token)
access_token.delete(entry.edit_url, {'GData-Version' => '2', 'If-Match' => '*'})
end
def acl_remove(document_id, users)
access_token = retrieve_access_token
url = "https://docs.google.com/feeds/acl/private/full/#{document_id}/batch"
Struct.new('UserStruct', :id, :gmail, :google_docs_address)
users.each_with_index do |user, idx|
if user.is_a? String
users[idx] = Struct::UserStruct.new(user, user)
end
end
request_feed = Feed.new do |feed|
feed.categories << Atom::Category.new { |category|
category.scheme = "http://schemas.google.com/g/2005#kind"
category.term = "http://schemas.google.com/acl/2007#accessRule"
}
users.each do |user|
next unless user_identifier = user.google_docs_address || user.gmail
feed.entries << Entry.new do |entry|
entry.id = "https://docs.google.com/feeds/acl/private/full/#{CGI.escape(document_id)}/user%3A#{CGI.escape(user_identifier)}"
entry.batch_operation = Google::Batch::Operation.new('delete')
entry.gAcl_role = Google::GAcl::Role.new
entry.gAcl_scope = Google::GAcl::Scope.new(user_identifier)
end
end
end
response = access_token.post(url, request_feed.to_xml.to_s, {'Content-Type' => 'application/atom+xml'})
feed = Atom::Feed.load_feed(response.body)
res = []
feed.entries.each do |entry|
user = users.to_a.find { |u| u.id == entry['http://schemas.google.com/gdata/batch', 'id'][0].to_i }
res << user if user
end
res
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)
access_token = retrieve_access_token
url = "https://docs.google.com/feeds/acl/private/full/#{document_id}/batch"
domain_regex = domain ? %r{@#{domain}$} : /./
allowed_users = []
user_added = false
request_feed = Feed.new do |feed|
feed.categories << Atom::Category.new do |category|
category.scheme = "http://schemas.google.com/g/2005#kind"
category.term = "http://schemas.google.com/acl/2007#accessRule"
end
allowed_users = users.select do |user|
address = user.google_docs_address || user.gmail
address ? address.match(domain_regex) : nil
end
allowed_users.each do |user|
user_added = true
feed.entries << user_feed_entry(user)
end
end
return unless user_added
post_response = access_token.post(url, request_feed.to_xml.to_s, {'Content-Type' => 'application/atom+xml'})
feed = Atom::Feed.load_feed(post_response.body)
feed.entries.inject([]) do |response, entry|
user = allowed_users.find do |u|
u.id == entry['http://schemas.google.com/gdata/batch', 'id'][0].to_i
end
response << user if user
response
end
end
def user_feed_entry(user)
Entry.new do |entry|
entry.batch_id = user.id
entry.batch_operation = Google::Batch::Operation.new
entry.gAcl_role = Google::GAcl::Role.new
entry.gAcl_scope = Google::GAcl::Scope.new(user.google_docs_address || user.gmail)
end
end
private :user_feed_entry
def verify_access_token
access_token = retrieve_access_token
access_token.head("https://www.google.com/accounts/AuthSubTokenInfo").is_a? Net::HTTPSuccess
end
def self.config_check(settings)
consumer = GoogleDocs::Connection.consumer(settings[:api_key], settings[:secret_key])
token = consumer.get_request_token({}, {:scope => "https://docs.google.com/feeds/"}) rescue nil
token ? nil : "Configuration check failed, please check your settings"
end
def self.config=(config)
if !config.is_a?(Proc)
raise "Config must be a Proc"
end
@config = config
end
def self.config
@config.call()
end
end
end

View File

@ -1,41 +0,0 @@
module GoogleDocs
class DriveFolder
attr_reader :name, :folders, :files
def initialize(name, folders=[], files=[])
@name = name
@folders, @files = folders, files
end
def add_file(file)
@files << file
end
def add_folder(folder)
@folders << folder
end
def select(&block)
DriveFolder.new(@name,
@folders.map { |f| f.select(&block) }.select { |f| !f.files.empty? },
@files.select(&block))
end
def map(&block)
@folders.map { |f| f.map(&block) }.flatten +
@files.map(&block)
end
def flatten
@folders.flatten + @files
end
def to_hash
{
:name => @name,
:folders => @folders.map { |sf| sf.to_hash },
:files => @files.map { |f| f.to_hash }
}
end
end
end

View File

@ -1,112 +0,0 @@
#
# Copyright (C) 2011 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
require 'atom'
module GoogleDocs
class Entry
def self.extension_looker_upper
@extension_looker_upper
end
def self.extension_looker_upper=(extension_looker_upper)
@extension_looker_upper=extension_looker_upper
end
attr_reader :document_id, :folder, :entry
def initialize(entry)
if entry.is_a?(String)
@entry = Atom::Entry.load_entry(entry)
else
@entry = entry
end
set_document_id_from @entry
@folder = @entry.categories.find { |c| c.scheme.match(/\Ahttp:\/\/schemas.google.com\/docs\/2007\/folders/) }.label rescue nil
end
def alternate_url
link = @entry.links.find { |link| link.rel == "alternate" && link.type == "text/html" }
link || "http://docs.google.com"
end
def edit_url
"https://docs.google.com/feeds/documents/private/full/#{@document_id}"
end
def content_type
@entry.content && @entry.content.type
end
def extension
if @extension.nil?
# first, try and chose and extension by content-types we can scribd
if !content_type.nil? && !content_type.strip.empty? && self.class.extension_looker_upper && mimetype = self.class.extension_looker_upper.find_by_name(content_type)
@extension = mimetype.extension
end
# second, look at the document id itself for any clues
if !@document_id.nil? && !@document_id.strip.empty?
@extension ||= case @document_id
when /\Aspreadsheet/ then
"xls"
when /\Apresentation/ then
"ppt"
when /\Adocument/ then
"doc"
end
end
# finally, just declare it unknown
@extension ||= "unknown"
end
@extension == "unknown" ? nil : @extension
end
def display_name
@entry.title || "google_doc.#{extension}"
end
def download_url
url = @entry.content.src
if url && (parsed_url = (URI.parse(url) rescue nil)) && (ext = extension)
parsed_url.query = [parsed_url.query, "exportFormat=#{ext}", "format=#{ext}"].compact.join("&")
url = parsed_url.to_s
end
url
end
def to_hash
{
"name" => display_name,
"document_id" => @document_id,
"extension" => extension,
"alternate_url" => alternate_url
}
end
def reset_extension_as_xlsx
@extension = 'xlsx'
end
private
def set_document_id_from(entry)
doc_id = entry.simple_extensions["{http://schemas.google.com/g/2005,resourceId}"]
@document_id = doc_id.first.to_s
end
end
end

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:batch="http://schemas.google.com/gdata/batch" xmlns:gAcl="http://schemas.google.com/acl/2007">
<entry>
<batch:id>192</batch:id>
<batch:operation type="insert"/>
<gAcl:role value="writer"/>
<gAcl:scope type="user" value="u_id"/>
</entry>
<category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/acl/2007#accessRule"/>
</feed>

View File

@ -1,4 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<entry xmlns='http://www.w3.org/2005/Atom' xmlns:gd='http://schemas.google.com/g/2005'>
<gd:resourceId />
</entry>

View File

@ -1,5 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<entry xmlns='http://www.w3.org/2005/Atom' xmlns:gd='http://schemas.google.com/g/2005'>
<gd:resourceId>document:deadbeef</gd:resourceId>
<content type='application/pdf' src='http://some-url/blah' />
</entry>

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="http://www.w3.org/2005/Atom">
<title>test document</title>
<category label="document" scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/docs/2007#document"/>
</entry>

View File

@ -1 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?><entry xmlns='http://www.w3.org/2005/Atom' xmlns:docs='http://schemas.google.com/docs/2007' xmlns:batch='http://schemas.google.com/gdata/batch' xmlns:gd='http://schemas.google.com/g/2005'><id>https://docs.google.com/feeds/documents/private/full/document%3A1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA</id><published>2011-10-31T22:23:06.375Z</published><updated>2011-10-31T22:23:06.993Z</updated><category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/docs/2007#document' label='document'/><category scheme='http://schemas.google.com/g/2005/labels' term='http://schemas.google.com/g/2005/labels#viewed' label='viewed'/><title type='text'>test document</title><content type='text/html' src='https://docs.google.com/feeds/download/documents/export/Export?id=1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA'/><link rel='alternate' type='text/html' href='https://docs.google.com/document/d/1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA/edit?hl=en_US'/><link rel='self' type='application/atom+xml' href='https://docs.google.com/feeds/documents/private/full/document%3A1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA'/><link rel='edit' type='application/atom+xml' href='https://docs.google.com/feeds/documents/private/full/document%3A1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA/gug1bunq'/><link rel='edit-media' type='text/html' href='https://docs.google.com/feeds/media/private/full/document%3A1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA/gug1bunq'/><author><name>instructure.test.2011</name><email>instructure.test.2011@gmail.com</email></author><gd:resourceId>document:1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA</gd:resourceId><gd:lastModifiedBy><name>instructure.test.2011</name><email>instructure.test.2011@gmail.com</email></gd:lastModifiedBy><gd:lastViewed>2011-10-31T22:23:06.652Z</gd:lastViewed><gd:quotaBytesUsed>0</gd:quotaBytesUsed><docs:writersCanInvite value='true'/><gd:feedLink rel='http://schemas.google.com/acl/2007#accessControlList' href='https://docs.google.com/feeds/acl/private/full/document%3A1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA'/></entry>

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="http://www.w3.org/2005/Atom">
<title>Temp Doc: 07 Apr 2014, 08:32 AM</title>
<category label="document" scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/docs/2007#document"/>
</entry>

View File

@ -1,94 +0,0 @@
<?xml version='1.0' encoding='utf-8'?>
<feed xmlns='http://www.w3.org/2005/Atom'
xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'
xmlns:docs='http://schemas.google.com/docs/2007'
xmlns:batch='http://schemas.google.com/gdata/batch'
xmlns:gd='http://schemas.google.com/g/2005'>
<id>https://docs.google.com/feeds/documents/private/full</id>
<updated>2013-01-29T23:11:36.156Z</updated>
<category scheme='http://schemas.google.com/g/2005#kind'
term='http://schemas.google.com/docs/2007#item' label='item' />
<title type='text'>Available Documents -
don@sterlingcooperdraperpryce.com</title>
<link rel='alternate' type='text/html'
href='https://docs.google.com' />
<link rel='http://schemas.google.com/g/2005#feed'
type='application/atom+xml'
href='https://docs.google.com/feeds/documents/private/full' />
<link rel='http://schemas.google.com/g/2005#post'
type='application/atom+xml'
href='https://docs.google.com/feeds/documents/private/full' />
<link rel='http://schemas.google.com/g/2005#batch'
type='application/atom+xml'
href='https://docs.google.com/feeds/documents/private/full/batch' />
<link rel='self' type='application/atom+xml'
href='https://docs.google.com/feeds/documents/private/full' />
<author>
<name>don draper</name>
<email>don@sterlingcooperdraperpryce.com</email>
</author>
<openSearch:totalResults>1</openSearch:totalResults>
<openSearch:startIndex>1</openSearch:startIndex>
<entry>
<id>
https://docs.google.com/feeds/documents/private/full/spreadsheet%12345</id>
<published>2012-09-05T15:21:42.872Z</published>
<updated>2013-01-29T21:56:31.800Z</updated>
<category scheme='http://schemas.google.com/g/2005/labels'
term='http://schemas.google.com/g/2005/labels#viewed'
label='viewed' />
<category scheme='http://schemas.google.com/g/2005#kind'
term='http://schemas.google.com/docs/2007#spreadsheet'
label='spreadsheet' />
<category scheme='http://schemas.google.com/g/2005/labels'
term='http://schemas.google.com/g/2005/labels#modified-by-me'
label='modified-by-me' />
<category scheme='http://schemas.google.com/g/2005/labels'
term='http://schemas.google.com/g/2005/labels#shared'
label='shared' />
<title type='text'>Lucky Strike Campaign</title>
<content type='text/html'
src='https://docs.google.com/feeds/download/spreadsheets/Export?key=12345' />
<link rel='alternate' type='text/html'
href='https://docs.google.com/a/instructure.com/spreadsheet/ccc?key=12345' />
<link rel='http://schemas.google.com/docs/2007#embed'
type='text/html'
href='https://docs.google.com/a/instructure.com/spreadsheet/ccc?key=12345&amp;output=html&amp;chrome=false&amp;widget=true' />
<link rel='http://schemas.google.com/docs/2007#icon'
type='image/png'
href='https://ssl.gstatic.com/docs/doclist/images/icon_11_spreadsheet_list.png' />
<link rel='http://schemas.google.com/spreadsheets/2006#worksheetsfeed'
type='application/atom+xml'
href='https://spreadsheets.google.com/feeds/worksheets/12345/private/full' />
<link rel='http://schemas.google.com/spreadsheets/2006#tablesfeed'
type='application/atom+xml'
href='https://spreadsheets.google.com/feeds/12345/tables' />
<link rel='self' type='application/atom+xml'
href='https://docs.google.com/feeds/documents/private/full/spreadsheet%12345' />
<link rel='edit' type='application/atom+xml'
href='https://docs.google.com/feeds/documents/private/full/spreadsheet%12345/hcjl6593' />
<link rel='edit-media' type='text/html'
href='https://docs.google.com/feeds/media/private/full/spreadsheet%12345/hcjl6593' />
<author>
<name>don draper</name>
<email>don@sterlingcooperdraperpryce.com</email>
</author>
<gd:resourceId>
spreadsheet:12345</gd:resourceId>
<docs:isShareable value='true' />
<docs:modifiedByMeDate>
2012-09-13T21:09:04.878Z</docs:modifiedByMeDate>
<docs:sharedWithMeDate>
2012-09-05T15:46:27.146Z</docs:sharedWithMeDate>
<gd:lastModifiedBy>
<name>peggy olson</name>
<email>peggy@sterlingcooperdraperpryce.com</email>
</gd:lastModifiedBy>
<gd:lastViewed>2012-09-13T21:08:09.688Z</gd:lastViewed>
<gd:quotaBytesUsed>0</gd:quotaBytesUsed>
<docs:writersCanInvite value='true' />
<docs:hasForm value='false' />
<gd:feedLink rel='http://schemas.google.com/acl/2007#accessControlList'
href='https://docs.google.com/feeds/acl/private/full/spreadsheet%12345' />
</entry>
</feed>

View File

@ -1 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:docs='http://schemas.google.com/docs/2007' xmlns:batch='http://schemas.google.com/gdata/batch' xmlns:gd='http://schemas.google.com/g/2005'><id>https://docs.google.com/feeds/documents/private/full</id><updated>2011-10-31T22:23:05.864Z</updated><category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/docs/2007#item' label='item'/><title type='text'>Available Documents - instructure.test.2011@gmail.com</title><link rel='alternate' type='text/html' href='http://docs.google.com'/><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='https://docs.google.com/feeds/documents/private/full'/><link rel='http://schemas.google.com/g/2005#post' type='application/atom+xml' href='https://docs.google.com/feeds/documents/private/full'/><link rel='http://schemas.google.com/g/2005#batch' type='application/atom+xml' href='https://docs.google.com/feeds/documents/private/full/batch'/><link rel='self' type='application/atom+xml' href='https://docs.google.com/feeds/documents/private/full'/><author><name>instructure.test.2011</name><email>instructure.test.2011@gmail.com</email></author><openSearch:totalResults>0</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex></feed>

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:docs='http://schemas.google.com/docs/2007' xmlns:batch='http://schemas.google.com/gdata/batch' xmlns:gd='http://schemas.google.com/g/2005'><id>https://docs.google.com/feeds/documents/private/full</id><updated>2011-10-31T22:23:07.920Z</updated><category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/docs/2007#item' label='item'/><title type='text'>Available Documents - instructure.test.2011@gmail.com</title><link rel='alternate' type='text/html' href='http://docs.google.com'/><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='https://docs.google.com/feeds/documents/private/full'/><link rel='http://schemas.google.com/g/2005#post' type='application/atom+xml' href='https://docs.google.com/feeds/documents/private/full'/><link rel='http://schemas.google.com/g/2005#batch' type='application/atom+xml' href='https://docs.google.com/feeds/documents/private/full/batch'/><link rel='self' type='application/atom+xml' href='https://docs.google.com/feeds/documents/private/full'/><author><name>instructure.test.2011</name><email>instructure.test.2011@gmail.com</email></author><openSearch:totalResults>1</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><entry><id>https://docs.google.com/feeds/documents/private/full/document%3A1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA</id><published>2011-10-31T22:23:06.375Z</published><updated>2011-10-31T22:23:06.993Z</updated><category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/docs/2007#document' label='document'/><category scheme='http://schemas.google.com/g/2005/labels' term='http://schemas.google.com/g/2005/labels#viewed' label='viewed'/><title type='text'>test document</title><content type='text/html' src='https://docs.google.com/feeds/download/documents/export/Export?id=1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA'/><link rel='alternate' type='text/html' href='https://docs.google.com/document/d/1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA/edit?hl=en_US'/><link rel='self' type='application/atom+xml' href='https://docs.google.com/feeds/documents/private/full/document%3A1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA'/><link rel='edit' type='application/atom+xml' href='https://docs.google.com/feeds/documents/private/full/document%3A1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA/gug1buow'/><link rel='edit-media' type='text/html' href='https://docs.google.com/feeds/media/private/full/document%3A1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA/gug1buow'/><author><name>instructure.test.2011</name><email>instructure.test.2011@gmail.com</email></author><gd:resourceId>document:1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA</gd:resourceId><gd:lastModifiedBy><name>instructure.test.2011</name><email>instructure.test.2011@gmail.com</email></gd:lastModifiedBy><gd:lastViewed>2011-10-31T22:23:07.015Z</gd:lastViewed><gd:quotaBytesUsed>0</gd:quotaBytesUsed><docs:writersCanInvite value='true'/><gd:feedLink rel='http://schemas.google.com/acl/2007#accessControlList' href='https://docs.google.com/feeds/acl/private/full/document%3A1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA'/></entry></feed>

View File

@ -1,5 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<entry xmlns='http://www.w3.org/2005/Atom' xmlns:gd='http://schemas.google.com/g/2005'>
<content type='application/pdf' src='http://some-url/blah' />
<gd:resourceId />
</entry>

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:batch="http://schemas.google.com/gdata/batch" xmlns:gAcl="http://schemas.google.com/acl/2007">
<entry>
<id>https://docs.google.com/feeds/acl/private/full/document%3A1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA/user%3Auser%40example.com</id>
<batch:operation type="delete"/>
<gAcl:role value="writer"/>
<gAcl:scope type="user" value="user@example.com"/>
</entry>
<category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/acl/2007#accessRule"/>
</feed>

View File

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<atom:feed xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:gAcl="http://schemas.google.com/acl/2007" xmlns:batch="http://schemas.google.com/gdata/batch">
<atom:id>https://docs.google.com/feeds/acl/private/full/document%3A10RabxaKMMU9aSvzfGyvmSvzwQpBPhRnl-zJvCVioJVs/batch/1362693661345</atom:id>
<atom:updated>2013-03-07T22:01:02.114Z</atom:updated>
<atom:title type="text">Batch Feed</atom:title>
<atom:entry>
<atom:id>https://docs.google.com/feeds/acl/private/full/document%3A10RabxaKMMU9aSvzfGyvmSvzwQpBPhRnl-zJvCVioJVs/user%3Auser%40example.com</atom:id>
<atom:updated>2013-03-07T22:01:02.114Z</atom:updated>
<atom:title type="text">Deleted</atom:title>
<atom:content type="text">Deleted</atom:content>
<batch:status code="200" reason="Success"/>
<batch:operation type="delete"/>
</atom:entry>
</atom:feed>

View File

@ -1,10 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns='http://www.w3.org/2005/Atom' xmlns:batch='http://schemas.google.com/gdata/batch' xmlns:gAcl='http://schemas.google.com/acl/2007'>
<entry xmlns:batch='http://schemas.google.com/gdata/batch' xmlns:gAcl='http://schemas.google.com/acl/2007'>
<id>https://docs.google.com/feeds/acl/private/full/document%3A1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA/user%3Auser%40example.com</id>
<batch:operation type='delete'/>
<gAcl:role value='writer'/>
<gAcl:scope type='user' value='user@example.com'/>
</entry>
<category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/acl/2007#accessRule'/>
</feed>

View File

@ -1,4 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<entry xmlns='http://www.w3.org/2005/Atom' xmlns:gd='http://schemas.google.com/g/2005'>
<gd:resourceId>spreadsheet:deadbeef</gd:resourceId>
</entry>

View File

@ -1,5 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<entry xmlns='http://www.w3.org/2005/Atom' xmlns:gd='http://schemas.google.com/g/2005'>
<content type='unknown' src='http://some-url/blah' />
<gd:resourceId />
</entry>

View File

@ -1,4 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<entry xmlns='http://www.w3.org/2005/Atom' xmlns:gd='http://schemas.google.com/g/2005'>
<gd:resourceId>unknown:deadbeef</gd:resourceId>
</entry>

View File

@ -1,393 +0,0 @@
#
# Copyright (C) 2011-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 'spec_helper'
DOCS_FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/google_docs/'
def load_fixture(filename)
File.read(DOCS_FIXTURES_PATH + filename)
end
describe GoogleDocs::Connection do
let(:xml_schema_id) { 'https://docs.google.com/feeds/documents/private/full' }
let(:xml_doc_list_empty) { load_fixture("doc_list_empty.xml") }
let(:xml_doc_list_one) { load_fixture("doc_list_one.xml") }
let(:xml_doc_list_many) { load_fixture("doc_list_many.xml") }
let(:xml_create_doc_request) { load_fixture("create_doc_request.xml") }
let(:xml_create_temp_doc_request) { load_fixture("create_temp_doc_request.xml") }
let(:xml_create_doc_response) { load_fixture("create_doc_response.xml") }
let(:xml_remove_doc_request) { load_fixture("remove_doc_request.xml") }
let(:xml_remove_doc_response) { load_fixture("remove_doc_response.xml") }
let(:xml_add_user_acl) { load_fixture("add_user_acl.xml") }
let(:token) { "token" }
let(:secret) { "secret" }
before do
config = {
"api_key" => "key",
"secret_key" => "secret",
}
GoogleDocs::Connection.config = Proc.new do
config
end
end
describe "#retrieve_access_token" do
it "should not error out if the google plugin is not configured" do
GoogleDocs::Connection.config = Proc.new do
nil
end
google_docs = GoogleDocs::Connection.new(token, secret)
google_docs.retrieve_access_token.should be_nil
end
it "news up an OAuth::AccessToken" do
access_token = mock_access_token
google_docs = GoogleDocs::Connection.new(token, secret)
google_docs.retrieve_access_token.should == access_token
end
end
describe "#get_service_user_info" do
it "returns service user id and name" do
known_time = Time.new(2014, 4, 7, 8, 32)
Timecop.freeze(known_time) do
prepare_mock_post(xml_schema_id, xml_create_temp_doc_request, xml_create_doc_response)
google_docs = GoogleDocs::Connection.new(token, secret)
prepare_mock_delete("#{xml_schema_id}/document:1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA")
user_service_id, user_service_name = google_docs.get_service_user_info google_docs.retrieve_access_token
user_service_id.should == "instructure.test.2011@gmail.com"
user_service_name.should == "instructure.test.2011@gmail.com"
end
end
end
describe ".get_access_token" do
it "returns the access token" do
mock_consumer = mock_consumer()
mock_request_token = mock()
OAuth::RequestToken.expects(:new).with(mock_consumer, token, secret).returns(mock_request_token)
mock_access_token = mock()
mock_request_token.expects(:get_access_token).with(oauth_verifier: "oauth-verifier").returns(mock_access_token)
access_token = GoogleDocs::Connection.get_access_token(token, secret, "oauth-verifier")
access_token.should == mock_access_token
end
end
describe ".request_token" do
it "returns a request token" do
consumer = mock_consumer
mock_request_token = mock()
consumer.expects(:get_request_token).with({:oauth_callback => "http://callback.example.com"}, {:scope => "https://docs.google.com/feeds/ https://spreadsheets.google.com/feeds/"}).returns(mock_request_token)
request_token = GoogleDocs::Connection.request_token("http://callback.example.com")
request_token.should == mock_request_token
end
end
describe "#list_with_extension_filter" do
context "with an empty list" do
before do
prepare_mock_get xml_doc_list_empty
@google_docs = GoogleDocs::Connection.new(token, secret)
end
it "handles an empty list" do
document_id_list = @google_docs.list_with_extension_filter(nil).files.map(&:document_id)
document_id_list.should == []
end
it "handles an empty list with extensions" do
document_id_list = @google_docs.list_with_extension_filter(["jpg"]).files.map(&:document_id)
document_id_list.should == []
end
end
context "with a single document" do
before do
@google_docs = GoogleDocs::Connection.new(token, secret)
end
it "and nil filter" do
prepare_mock_get xml_doc_list_one
list = @google_docs.list_with_extension_filter(nil)
document_id_list = list.files.map(&:document_id)
document_id_list.should == ["document:1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA"]
end
it "and an empty filter" do
prepare_mock_get xml_doc_list_one
list = @google_docs.list_with_extension_filter([])
document_id_list = list.files.map(&:document_id)
document_id_list.should == ["document:1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA"]
end
it "returns matches" do
prepare_mock_get xml_doc_list_one
list = @google_docs.list_with_extension_filter(['doc'])
document_id_list = list.files.map(&:document_id)
document_id_list.should == ["document:1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA"]
end
it "rejects non matching documents" do
prepare_mock_get xml_doc_list_one
list = @google_docs.list_with_extension_filter(['xls'])
document_id_list = list.files.map(&:document_id)
document_id_list.should == []
end
end
context "with multiple documents" do
before do
@google_docs = GoogleDocs::Connection.new(token, secret)
end
it "returns filesystem view of results" do
prepare_mock_get xml_doc_list_many
root_folder = @google_docs.list_with_extension_filter(nil)
root_folder.should be_a(GoogleDocs::Folder)
root_folder.name.should == '/'
root_folder.folders.size.should == 1
root_folder.folders.map { |f| f.name }.should == ["Good Stuff"]
root_folder.folders.first.files.size.should == 1
root_folder.folders.first.files.map(&:display_name).should == ["2012 Employee Review Form"]
root_folder.files.size.should == 10
end
it "rejects non matches" do
prepare_mock_get xml_doc_list_many
root_folder = @google_docs.list_with_extension_filter(['ppt', 'doc'])
root_folder.files.size.should == 6
document_id_list = root_folder.files.map(&:document_id)
document_id_list.should == ["document:15OmhdkR46iZnjFycN8__s6jVKcemzAxAGiFkr6UFxgw", "document:10jp_7QYXSN90iC6iKj_JieUiE72AuJOzLhfEvs0VGrU", "document:135mk8IhGEusw3-nG-GCHNefnlhzW8wH35ytT3EiytLo", "document:1Ohs0PlPbVsDVB0J-nJM7cSC6kvDnz8xRwH70xor4-W4", "document:1dMP-0Cr8xiuBVo86TBikxdv8uM4MOaN5ssYmNMx_xUc", "document:1yzywXxOorojl6mm0RQpgdwsX9B0K0IIn-efXhrVZVFI"]
end
it "accepts any of the extensions" do
prepare_mock_get xml_doc_list_many
list = @google_docs.list_with_extension_filter(['xls', 'doc'])
document_id_list = list.files.map(&:document_id)
document_id_list.should == ["spreadsheet:0AiN8C_VHrPxkdEF6YmQyc3p2Qm02ODhJWGJnUmJYY2c", "spreadsheet:0AqsakWbfzwqRdDN1RDhNQ1hDWXpiVXNKN3VMb2Zlamc", "spreadsheet:0AsOXCUtn3LUxdEh6RC1KZEhoMXNqSHczeDdsc3VyYUE", "document:15OmhdkR46iZnjFycN8__s6jVKcemzAxAGiFkr6UFxgw", "document:10jp_7QYXSN90iC6iKj_JieUiE72AuJOzLhfEvs0VGrU", "document:135mk8IhGEusw3-nG-GCHNefnlhzW8wH35ytT3EiytLo", "document:1Ohs0PlPbVsDVB0J-nJM7cSC6kvDnz8xRwH70xor4-W4", "document:1dMP-0Cr8xiuBVo86TBikxdv8uM4MOaN5ssYmNMx_xUc", "spreadsheet:0AsZU1aOHX2kSdGhQVG9CWWdWcTdVZVdBMXh6V0xlVUE", "document:1yzywXxOorojl6mm0RQpgdwsX9B0K0IIn-efXhrVZVFI"]
end
end
end
describe "#download" do
it "pulls the document out that matches the provided id" do
doc_id = 'spreadsheet:0AiN8C_VHrPxkdEF6YmQyc3p2Qm02ODhJWGJnUmJYY2c'
access_token = mock_access_token
document_response = mock()
access_token.expects(:get).returns(document_response)
response = mock()
response.expects(:body).returns(xml_doc_list_many)
access_token.expects(:get).with(xml_schema_id).returns(response)
google_docs = GoogleDocs::Connection.new(token, secret)
doc_array = google_docs.download(doc_id)
doc_array[0].should == document_response
doc_array[1].should == 'Sprint Teams'
doc_array[2].should == 'xls'
end
it "follows redirects" do
doc_id = 'spreadsheet:0AiN8C_VHrPxkdEF6YmQyc3p2Qm02ODhJWGJnUmJYY2c'
access_token = mock_access_token
document_response = mock()
redirect = Net::HTTPFound.new(1.0, 302, "FOUND")
redirect['Location'] = 'http://example.com/1234'
access_token.expects(:get).returns(redirect)
access_token.expects(:get).with('http://example.com/1234').returns(document_response)
response = mock()
response.expects(:body).returns(xml_doc_list_many)
access_token.expects(:get).with(xml_schema_id).returns(response)
google_docs = GoogleDocs::Connection.new(token, secret)
doc_array = google_docs.download(doc_id)
doc_array[0].should == document_response
doc_array[1].should == 'Sprint Teams'
doc_array[2].should == 'xls'
end
it "handles nonexistant entry" do
doc_id = 'spreadsheet:WRONG'
access_token = mock_access_token
response = mock()
response.expects(:body).returns(xml_doc_list_many)
access_token.expects(:get).with(xml_schema_id).returns(response)
google_docs = GoogleDocs::Connection.new(token, secret)
doc_array = google_docs.download(doc_id)
doc_array.should == [nil, nil, nil]
end
it "attempts as 'xlsx' if 'xls' fails" do
doc_id = 'spreadsheet:0AiN8C_VHrPxkdEF6YmQyc3p2Qm02ODhJWGJnUmJYY2c'
src = 'https://docs.google.com/feeds/download/spreadsheets/Export?key=0AiN8C_VHrPxkdEF6YmQyc3p2Qm02ODhJWGJnUmJYY2c'
xls_src = [src, "exportFormat=xls", "format=xls"].join('&')
xlsx_src = [src, "exportFormat=xlsx", "format=xlsx"].join('&')
access_token = mock_access_token
document_response = mock()
access_token.expects(:get).with(xlsx_src).returns(document_response)
bad_req = Net::HTTPBadRequest.new(1.0, 400, "Bad Request")
access_token.expects(:get).with(xls_src).returns(bad_req)
fetch_list_response = mock()
fetch_list_response.expects(:body).returns(xml_doc_list_many)
access_token.expects(:get).with(xml_schema_id).returns(fetch_list_response)
google_docs = GoogleDocs::Connection.new(token, secret)
doc_array = google_docs.download(doc_id)
doc_array[0].should == document_response
doc_array[1].should == 'Sprint Teams'
doc_array[2].should == 'xlsx'
end
end
describe "#create_doc" do
it "can be created" do
prepare_mock_post(xml_schema_id, xml_create_doc_request, xml_create_doc_response)
google_docs = GoogleDocs::Connection.new(token, secret)
new_document = google_docs.create_doc "test document", google_docs.retrieve_access_token
new_document.document_id.should == 'document:1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA'
new_document.extension.should == 'doc'
new_document.display_name.should == 'test document'
new_document.download_url.should include('id=1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA')
new_document.alternate_url.should be_a(Atom::Link)
new_document.alternate_url.href.should == 'https://docs.google.com/document/d/1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA/edit?hl=en_US'
end
end
describe "#delete_doc" do
it "can be deleted" do
prepare_mock_post(xml_schema_id, xml_create_doc_request, xml_create_doc_response)
google_docs = GoogleDocs::Connection.new(token, secret)
new_document = google_docs.create_doc("test document", google_docs.retrieve_access_token)
prepare_mock_delete "#{xml_schema_id}/document:1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA"
google_docs.delete_doc new_document
end
end
describe "#acl_remove" do
it "can be removed" do
prepare_mock_post('https://docs.google.com/feeds/acl/private/full/document:1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA/batch', xml_remove_doc_request, xml_remove_doc_response)
google_docs = GoogleDocs::Connection.new(token, secret)
result = google_docs.acl_remove 'document:1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA', ['user@example.com']
result.should == []
end
end
describe "#acl_add" do
let(:doc_response) { mock }
let(:doc_id) { "12345" }
let(:url) { "https://docs.google.com/feeds/acl/private/full/#{doc_id}/batch" }
it "should add users to a document" do
prepare_mock_post(url, xml_add_user_acl, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<feed xmlns=\"http://www.w3.org/2005/Atom\"/>\n")
google_docs = GoogleDocs::Connection.new(token, secret)
mock_user = mock()
mock_user.stubs(:id).returns(192)
mock_user.stubs(:google_docs_address).returns('u_id')
google_docs.acl_add('12345', [mock_user], nil)
end
it "should optionally filter by domain" do
access_token = mock_access_token
access_token.expects(:post).never
google_docs = GoogleDocs::Connection.new(token, secret)
mock_user = mock()
mock_user.stubs(:id).returns(192)
mock_user.stubs(:google_docs_address).returns('u_id')
google_docs.acl_add('12345', [mock_user], 'does-not-match.com')
end
end
# ----------------------------
# Helper methods for this spec
# ----------------------------
def google_doc_settings
{
'test_user_token' => 'u_token',
'test_user_secret' => 'u_secret',
'test_user_id' => 'u_id',
'test_user_name' => 'u_name',
'api_key' => 'key',
'secret_key' => 'secret'
}
end
def mock_consumer
consumer = mock()
OAuth::Consumer.expects(:new).at_least_once.with(
GoogleDocs::Connection.config["api_key"],
GoogleDocs::Connection.config["secret_key"], {
:signature_method => 'HMAC-SHA1',
:request_token_path => '/accounts/OAuthGetRequestToken',
:site => 'https://www.google.com',
:authorize_path => '/accounts/OAuthAuthorizeToken',
:access_token_path => '/accounts/OAuthGetAccessToken'}).returns(consumer)
return consumer
end
def mock_access_token
return @access_token if @access_token
@access_token = mock()
OAuth::AccessToken.expects(:new).at_least_once.with(mock_consumer(),
"token",
"secret").returns(@access_token)
return @access_token
end
def prepare_mock_get(response_xml)
response = mock()
mock_access_token.expects(:get).with(xml_schema_id).returns(response)
response.expects(:body).returns(response_xml)
end
def prepare_mock_post(url, request_xml, response_xml)
response = mock()
headers = {'Content-Type' => 'application/atom+xml'}
mock_access_token.expects(:post).
with(url, request_xml, headers).returns(response)
response.expects(:body).returns(response_xml)
end
def prepare_mock_delete(xml_schema_id)
headers = {'GData-Version' => '2', 'If-Match' => '*'}
mock_access_token.expects(:delete).with(xml_schema_id, headers).returns(mock())
end
end

View File

@ -1,82 +0,0 @@
#
# Copyright (C) 2011-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 'spec_helper'
DOCS_FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/google_docs/'
def load_fixture(filename)
File.read(DOCS_FIXTURES_PATH + filename)
end
describe GoogleDocs::Entry do
let(:entry_feed) { load_fixture("create_doc_response.xml") }
describe "#extension" do
context "with an extension looker upper" do
before do
extension_looker_upper = mock
extension_mock = mock
extension_looker_upper.expects(:find_by_name).with('text/html').returns(extension_mock)
extension_mock.expects(:extension).returns("whatever")
GoogleDocs::Entry.extension_looker_upper = extension_looker_upper
end
after do
GoogleDocs::Entry.extension_looker_upper = nil
end
it "checks the extension_looker_upper first" do
entry = GoogleDocs::Entry.new(entry_feed)
entry.extension.should == "whatever"
end
end
it "is 'doc' when document id matches 'document'" do
entry = GoogleDocs::Entry.new(entry_feed)
entry.extension.should == "doc"
end
it "can be reset/forced to 'xlsx'" do
entry = GoogleDocs::Entry.new(entry_feed)
entry.extension.should == "doc"
entry.reset_extension_as_xlsx
entry.extension.should == "xlsx"
end
end
describe '#download_url' do
it 'should add exportFormat and format parameters when applicable' do
entry = GoogleDocs::Entry.new(entry_feed)
entry.stubs(:extension).returns("xls")
url = URI.parse(entry.download_url)
url.scheme.should == "https"
url.host.should == "docs.google.com"
url.path.should == "/feeds/download/documents/export/Export"
params = url.query.split("&")
params.should include("id=1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA")
params.should include("exportFormat=xls")
params.should include("format=xls")
entry.stubs(:extension).returns(nil)
entry.download_url.should == "https://docs.google.com/feeds/download/documents/export/Export?id=1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA"
end
end
end

View File

@ -1,66 +0,0 @@
#
# Copyright (C) 2011-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 'spec_helper'
describe GoogleDocs::Folder do
class MockFile < Struct.new(:name); end
let(:folder) do
folder1 = GoogleDocs::Folder.new(
"one",
[],
[
MockFile.new("one-1"),
MockFile.new("one-2"),
MockFile.new("one-3")
]
)
folder2 = GoogleDocs::Folder.new(
"two",
[],
[
MockFile.new("two-1"),
MockFile.new("two-2")
]
)
GoogleDocs::Folder.new(
"root",
[
folder1,
folder2
]
)
end
it "can map files" do
names = folder.map{ |f| f.name }
names.should == ['one-1', 'one-2', 'one-3', 'two-1', 'two-2']
end
it "can select files" do
tree = folder.select{ |f| f.name =~ /one-[12]/ }
tree.name.should == 'root'
tree.folders.size.should == 1
tree.folders.first.name.should == 'one'
tree.folders.first.files.size.should == 2
tree.folders.first.files.map{ |f| f.name }.should == ['one-1', 'one-2']
end
end

View File

@ -1,14 +0,0 @@
require 'google_docs'
require 'mocha'
require 'timecop'
RSpec.configure do |config|
config.treat_symbols_as_metadata_keys_with_true_values = true
config.run_all_when_everything_filtered = true
config.filter_run :focus
config.order = 'random'
config.mock_framework = :mocha
end

View File

@ -1,6 +0,0 @@
#!/bin/bash
set -e
rm -f Gemfile.lock
bundle check || bundle install
bundle exec rspec spec

View File

@ -14,10 +14,14 @@ Gem::Specification.new do |spec|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ["lib"]
spec.add_dependency "rails", ">= 3.2", "< 4.2"
spec.add_runtime_dependency "google-api-client", "0.8.2"
spec.add_development_dependency "bundler", "~> 1.7"
spec.add_development_dependency "rake"
spec.add_development_dependency "rspec", "3.2.0"
spec.add_development_dependency "byebug"
spec.add_development_dependency "mocha"
spec.add_development_dependency "timecop"
spec.add_development_dependency "webmock"
end

View File

@ -1,4 +1,12 @@
require 'active_support'
require 'google/api_client'
module GoogleDrive
require 'google_drive/no_token_error'
require 'google_drive/connection_exception'
require 'google_drive/client'
end
require 'google_drive/connection'
require 'google_drive/entry'
require 'google_drive/folder'
end

View File

@ -20,7 +20,6 @@ module GoogleDrive
client
end
def self.auth_uri(client, state, login=nil)
auth_client = client.authorization
auth_client.update!
@ -32,8 +31,6 @@ module GoogleDrive
}
request_data[:login_hint] = login if login
auth_client.authorization_uri(request_data).to_s
end
end

View File

@ -16,13 +16,10 @@
# 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
# See Google Drive API documentation here:
# https://developers.google.com/drive/v2/web/about-sdk
module GoogleDrive
class Connection
def initialize(refresh_token, access_token, timeout = nil)
@refresh_token = refresh_token
@access_token = access_token
@ -40,7 +37,7 @@ module GoogleDocs
def with_timeout_protection
Timeout.timeout(@timeout || 30) { yield }
rescue Timeout::Error
raise DriveConnectionException, "Google Drive connection timed out"
raise ConnectionException, 'Google Drive connection timed out'
end
def client_execute(options)
@ -62,23 +59,19 @@ module GoogleDocs
)
file = response.data.to_hash
entry = GoogleDocs::DriveEntry.new(file, extensions)
entry = GoogleDrive::Entry.new(file, extensions)
result = client_execute(:uri => entry.download_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'].sub(/; charset=[^;]+/, '')
end
file_name = file['title']
name_extension = file_name[/\.([a-z]+$)/, 1]
file_extension = name_extension || file_extension_from_header(result.headers, entry)
# file_name should contain the file_extension
file_name += ".#{file_extension}" unless name_extension
[result, file_name, file_extension]
content_type = result.headers['Content-Type'].sub(/; charset=[^;]+/, '')
[result, file_name, file_extension, content_type]
else
raise DriveConnectionException, result.error_message
raise ConnectionException, result.error_message
end
end
@ -103,7 +96,7 @@ module GoogleDocs
if result.status == 200
result
else
raise DriveConnectionException, result.error_message
raise ConnectionException, result.error_message
end
end
@ -113,21 +106,23 @@ module GoogleDocs
:api_method => drive.files.delete,
:parameters => { :fileId => normalize_document_id(document_id) })
if result.error? && !result.error_message.include?('File not found')
raise DriveConnectionException, result.error_message
raise ConnectionException, result.error_message
end
end
def acl_remove(document_id, users)
force_token_update
users.each do |user_id|
next if user_id.blank? || /@/.match(user_id) # google drive ids are numeric, google docs are emails. if it is a google doc email just skip it
# google drive ids are numeric, google docs are emails. if it is a google doc email just skip it
# this is needed for legacy purposes
next if user_id.blank? || /@/.match(user_id)
result = client_execute(
:api_method => drive.permissions.delete,
:parameters => {
:fileId => normalize_document_id(document_id),
:permissionId => user_id })
if result.error? && !result.error_message.starts_with?("Permission not found")
raise DriveConnectionException, result.error_message
raise ConnectionException, result.error_message
end
end
end
@ -155,18 +150,20 @@ module GoogleDocs
:parameters => { :fileId => normalize_document_id(document_id) }
)
if result.error?
raise DriveConnectionException, result.error_message
raise ConnectionException, result.error_message
end
end
end
def verify_access_token
def authorized?
force_token_update
client_execute(:api_method => drive.about.get).status == 200
rescue ConnectionException, NoTokenError, Google::APIClient::AuthorizationError
false
end
def self.config_check(_settings)
raise DriveConnectionException("No config check")
raise ConnectionException("No config check")
end
def self.config=(config)
@ -196,20 +193,21 @@ module GoogleDocs
end
def folderize_list(documents, extensions)
root = GoogleDocs::DriveFolder.new('/')
root = GoogleDrive::Folder.new('/')
folders = {nil => root}
documents['items'].each do |doc_entry|
next unless doc_entry['downloadUrl'] || doc_entry['exportLinks']
entry = GoogleDocs::DriveEntry.new(doc_entry, extensions)
if folders.has_key?(entry.folder)
entry = GoogleDrive::Entry.new(doc_entry, extensions)
if folders.key?(entry.folder)
folder = folders[entry.folder]
else
folder = GoogleDocs::DriveFolder.new(get_folder_name_by_id(documents['items'], entry.folder))
folder = GoogleDrive::Folder.new(get_folder_name_by_id(documents['items'], entry.folder))
root.add_folder folder
folders[entry.folder] = folder
end
folder.add_file(entry) unless doc_entry['mimeType'] && doc_entry['mimeType'] == 'application/vnd.google-apps.folder'
is_folder = doc_entry['mimeType'] && doc_entry['mimeType'] == 'application/vnd.google-apps.folder'
folder.add_file(entry) unless is_folder
end
if extensions && extensions.length > 0
@ -227,26 +225,23 @@ module GoogleDocs
end
def api_client
return nil if GoogleDocs::DriveConnection.config.nil?
@api_client ||= GoogleDrive::Client.create(GoogleDocs::DriveConnection.config, @refresh_token, @access_token)
end
# override for specs because GoogleDrive is in a sibling project
# and not an actual declared dependency of this gem. That's
# probably a design mistake that should be corrected
def set_api_client(client)
@api_client = client
raise ConnectionException, "GoogleDrive is not configured" if GoogleDrive::Connection.config.nil?
raise NoTokenError unless @refresh_token && @access_token
@api_client ||= GoogleDrive::Client.create(GoogleDrive::Connection.config, @refresh_token, @access_token)
end
def drive
api_client.discovered_api('drive', 'v2')
@drive ||= Rails.cache.fetch('google_drive_v2') do
api_client.discovered_api('drive', 'v2')
end
end
def file_extension_from_header(headers, entry)
file_extension = entry.extension && !entry.extension.empty? && entry.extension || 'unknown'
if headers['content-disposition'] && headers['content-disposition'].match(/filename=[\"\']?[^;\"\'\.]+\.(?<file_extension>[^;\"\']+)[\"\']?/)
file_extension = Regexp.last_match[:file_extension]
if headers['content-disposition'] &&
headers['content-disposition'].match(/filename=[\"\']?[^;\"\'\.]+\.(?<file_extension>[^;\"\']+)[\"\']?/)
file_extension = Regexp.last_match[:file_extension]
end
file_extension

View File

@ -0,0 +1,4 @@
module GoogleDrive
class ConnectionException < RuntimeError
end
end

View File

@ -15,8 +15,8 @@
# 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/>.
#
module GoogleDocs
class DriveEntry
module GoogleDrive
class Entry
attr_reader :document_id, :folder, :entry
@ -25,7 +25,7 @@ module GoogleDocs
@document_id = @entry['id']
@preferred_extensions = preferred_extensions
parent = @entry['parents'].length > 0 ? @entry['parents'][0] : nil
@folder = (parent == nil || parent['isRoot'] ? nil : parent['id'])
@folder = (parent.nil? || parent['isRoot'] ? nil : parent['id'])
end
def alternate_url
@ -37,7 +37,7 @@ module GoogleDocs
end
def extension
get_file_data[:ext]
file_data[:ext]
end
def display_name
@ -45,7 +45,7 @@ module GoogleDocs
end
def download_url
get_file_data[:url]
file_data[:url]
end
def to_hash
@ -58,7 +58,7 @@ module GoogleDocs
end
private
def get_file_data()
def file_data
# First we check export links for our preferred formats
# then we fail over to the file properties
if @entry['exportLinks']
@ -66,10 +66,10 @@ module GoogleDocs
end
# we'll have to find the url and extensions some other place
extension ||= @entry['fileExtension'] if @entry.has_key? 'fileExtension'
extension ||= @entry['fileExtension'] if @entry.key? 'fileExtension'
extension ||= 'none'
url ||= @entry['downloadUrl'] if @entry.has_key? 'downloadUrl'
url ||= @entry['downloadUrl'] if @entry.key? 'downloadUrl'
{
url: url,
@ -78,10 +78,29 @@ module GoogleDocs
end
def preferred_export_link(preferred_extensions=nil)
preferred_urls = preferred_mime_types.map do |mime_type|
next unless @entry['exportLinks'][mime_type]
current_url = @entry['exportLinks'][mime_type]
current_extension = /([a-z]+)$/.match(current_url).to_s
has_preferred_extension = preferred_extensions && preferred_extensions.include?(current_extension)
# our extension is in the preferred list or we have no preferences
[current_url, current_extension] if has_preferred_extension || !preferred_extensions
end
url, extension = preferred_urls.find{ |i| i}
# if we dont have any "preferred extension" just return the default.
# they will be filtered out by the folderize method
return preferred_export_link if url.nil? && preferred_extensions
[url, extension]
end
def preferred_mime_types
# Order is important
# we return the first matching mime type
preferred_mime_types = %w{
%w{
application/vnd.openxmlformats-officedocument.wordprocessingml.document
application/vnd.oasis.opendocument.text
application/vnd.openxmlformats-officedocument.presentationml.presentation
@ -90,22 +109,6 @@ module GoogleDocs
application/pdf
application/zip
}
url, extension = preferred_mime_types.map do |mime_type|
next unless @entry['exportLinks'][mime_type]
current_url = @entry['exportLinks'][mime_type]
current_extension = /([a-z]+)$/.match(current_url).to_s
# our extension is in the preferred list or we have no preferences
[current_url, current_extension] if (preferred_extensions && preferred_extensions.include?(current_extension)) || !preferred_extensions
end.find{|i|i}
# if we dont have any "preferred extension" just return the default.
# they will be filtered out by the folderize method
return preferred_export_link if url == nil && preferred_extensions
[url, extension]
end
end
end

View File

@ -1,10 +1,9 @@
module GoogleDocs
module GoogleDrive
class Folder
attr_reader :name, :folders, :files
def initialize(name, folders=[], files=[])
@name = name
# File objects are GoogleDocEntry objects
@folders, @files = folders, files
end
@ -27,11 +26,15 @@ module GoogleDocs
@files.map(&block)
end
def flatten
@folders.flatten + @files
end
def to_hash
{
"name" => @name,
"folders" => @folders.map { |sf| sf.to_hash },
"files" => @files.map { |f| f.to_hash }
:name => @name,
:folders => @folders.map(&:to_hash),
:files => @files.map(&:to_hash)
}
end
end

View File

@ -1,4 +1,4 @@
module GoogleDocs
module GoogleDrive
class NoTokenError < StandardError
def initialize
super("User does not have a valid Google Docs token")

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,86 @@
{
"kind": "drive#file",
"id": "**file_id**",
"etag": "\"amKkzAMv_fUBF0Cxt1a1WaLm5Nk/MTQ1MDM3NTY4NDYwMg\"",
"selfLink": "https://www.googleapis.com/drive/v2/files/**file_id**",
"alternateLink": "https://docs.google.com/a/instructure.com/document/d/**file_id**/edit?usp=drivesdk",
"embedLink": "https://docs.google.com/a/instructure.com/document/d/**file_id**/preview",
"iconLink": "https://ssl.gstatic.com/docs/doclist/images/icon_11_document_list.png",
"title": "Biology 100 Collaboration",
"mimeType": "application/vnd.google-apps.document",
"labels": {
"starred": false,
"hidden": false,
"trashed": false,
"restricted": false,
"viewed": true
},
"createdDate": "2015-12-17T18:08:04.602Z",
"modifiedDate": "2015-12-17T18:08:04.602Z",
"modifiedByMeDate": "2015-12-17T18:08:04.602Z",
"lastViewedByMeDate": "2015-12-17T18:08:04.602Z",
"markedViewedByMeDate": "1970-01-01T00:00:00.000Z",
"version": "15483",
"parents": [
{
"kind": "drive#parentReference",
"id": "0ADvGR2OPwneAUk9PVA",
"selfLink": "https://www.googleapis.com/drive/v2/files/**file_id**/parents/0ADvGR2OPwneAUk9PVA",
"parentLink": "https://www.googleapis.com/drive/v2/files/0ADvGR2OPwneAUk9PVA",
"isRoot": true
}
],
"exportLinks": {
"application/rtf": "https://docs.google.com/feeds/download/documents/export/Export?id=**file_id**&exportFormat=rtf",
"application/vnd.oasis.opendocument.text": "https://docs.google.com/feeds/download/documents/export/Export?id=**file_id**&exportFormat=odt",
"text/html": "https://docs.google.com/feeds/download/documents/export/Export?id=**file_id**&exportFormat=html",
"application/pdf": "https://docs.google.com/feeds/download/documents/export/Export?id=**file_id**&exportFormat=pdf",
"application/zip": "https://docs.google.com/feeds/download/documents/export/Export?id=**file_id**&exportFormat=zip",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": "https://docs.google.com/feeds/download/documents/export/Export?id=**file_id**&exportFormat=docx",
"text/plain": "https://docs.google.com/feeds/download/documents/export/Export?id=**file_id**&exportFormat=txt"
},
"userPermission": {
"kind": "drive#permission",
"etag": "\"amKkzAMv_fUBF0Cxt1a1WaLm5Nk/y4y5UF9JzlFDfWgHoG5-AJ5lCDM\"",
"id": "me",
"selfLink": "https://www.googleapis.com/drive/v2/files/**file_id**/permissions/me",
"role": "owner",
"type": "user"
},
"quotaBytesUsed": "0",
"ownerNames": [
"Montgomery Montgomery"
],
"owners": [
{
"kind": "drive#user",
"displayName": "Montgomery Montgomery",
"picture": {
"url": "https://lh6.googleusercontent.com/-x0Cip_5Wrw0/AAAAAAAAAAI/AAAAAAAAAGM/**********/s64/photo.jpg"
},
"isAuthenticatedUser": true,
"permissionId": "0024957*******0999547",
"emailAddress": "montgomery@nowhere.com"
}
],
"lastModifyingUserName": "Montgomery Montgomery",
"lastModifyingUser": {
"kind": "drive#user",
"displayName": "Montgomery Montgomery",
"picture": {
"url": "https://lh6.googleusercontent.com/-x0Cip_5Wrw0/AAAAAAAAAAI/AAAAAAAAAGM/**********/s64/photo.jpg"
},
"isAuthenticatedUser": true,
"permissionId": "0024957*******0999547",
"emailAddress": "montgomery@nowhere.com"
},
"editable": true,
"copyable": true,
"writersCanShare": true,
"shared": false,
"explicitlyTrashed": false,
"appDataContents": false,
"spaces": [
"drive"
]
}

View File

@ -0,0 +1,87 @@
{
"kind": "drive#fileList",
"etag": "\"XTBboRWlttiWhZ_feOHVV3sFYJs/NldWXgcoxpDGqwSjdu9I3zgzcmU\"",
"selfLink": "https://www.googleapis.com/drive/v2/files?q=trashed%3Dfalse",
"nextPageToken": "fake_token",
"nextLink": "https://www.googleapis.com/drive/v2/files?pageToken=fake_token&q=trashed%3Dfalse",
"items": [
{
"kind": "drive#file",
"id": "1p0*******************MYS-kJYhmhvfw8",
"etag": "\"XTBboRWlttiWhZ_feOHVV3sFYJs/MTQ1MjcyMTI4NDUwOA\"",
"selfLink": "https://www.googleapis.com/drive/v2/files/1p0*******************MYS-kJYhmhvfw8",
"alternateLink": "https://docs.google.com/a/instructure.com/spreadsheets/d/1p0*******************MYS-kJYhmhvfw8/edit?usp=drivesdk",
"embedLink": "https://docs.google.com/a/instructure.com/spreadsheets/d/1p0*******************MYS-kJYhmhvfw8/htmlembed",
"openWithLinks": {
"1083656409722": "https://docs.google.com/a/instructure.com/spreadsheets/d/1p0*******************MYS-kJYhmhvfw8/edit?usp=drive_web"
},
"defaultOpenWithLink": "https://docs.google.com/a/instructure.com/spreadsheets/d/1p0*******************MYS-kJYhmhvfw8/edit?usp=drive_web",
"iconLink": "https://ssl.gstatic.com/docs/doclist/images/icon_11_spreadsheet_list.png",
"thumbnailLink": "https://docs.google.com/a/instructure.com/feeds/vt?gd=true&id=1p0*******************MYS-kJYhmhvfw8&v=0&s=AMedNn****************MCMDEc&sz=s220",
"title": "Top Secret Documents",
"mimeType": "application/vnd.google-apps.spreadsheet",
"labels": {
"starred": false,
"hidden": false,
"trashed": false,
"restricted": false,
"viewed": true
},
"createdDate": "2014-07-07T20:11:17.079Z",
"modifiedDate": "2016-01-13T21:41:24.508Z",
"modifiedByMeDate": "2015-01-16T21:20:07.945Z",
"lastViewedByMeDate": "2015-01-16T21:20:07.945Z",
"markedViewedByMeDate": "1970-01-01T00:00:00.000Z",
"version": "459452",
"parents": [],
"exportLinks": {
"text/csv": "https://docs.google.com/spreadsheets/export?id=1p0*******************MYS-kJYhmhvfw8&exportFormat=csv",
"application/x-vnd.oasis.opendocument.spreadsheet": "https://docs.google.com/spreadsheets/export?id=1p0*******************MYS-kJYhmhvfw8&exportFormat=ods",
"application/zip": "https://docs.google.com/spreadsheets/export?id=1p0*******************MYS-kJYhmhvfw8&exportFormat=zip",
"application/pdf": "https://docs.google.com/spreadsheets/export?id=1p0*******************MYS-kJYhmhvfw8&exportFormat=pdf",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "https://docs.google.com/spreadsheets/export?id=1p0*******************MYS-kJYhmhvfw8&exportFormat=xlsx"
},
"userPermission": {
"kind": "drive#permission",
"etag": "\"XTBboRWlttiWhZ_feOHVV3sFYJs/fAeh6NlrWb23aHSXwPR-vOdwE0o\"",
"id": "me",
"selfLink": "https://www.googleapis.com/drive/v2/files/1p0*******************MYS-kJYhmhvfw8/permissions/me",
"role": "writer",
"type": "user"
},
"quotaBytesUsed": "0",
"ownerNames": [
"Montgomery Montgomery"
],
"owners": [
{
"kind": "drive#user",
"displayName": "Montgomery Montgomery",
"picture": {
"url": "https://lh6.googleusercontent.com/-x0Cip_5Wrw0/AAAAAAAAAAI/AAAAAAAAAGM/**********/s64/photo.jpg"
},
"isAuthenticatedUser": false,
"permissionId": "0024957*******0999547",
"emailAddress": "montgomery@nowhere.com"
}
],
"lastModifyingUserName": "Frank Frank",
"lastModifyingUser": {
"kind": "drive#user",
"displayName": "Frank Frank",
"isAuthenticatedUser": false,
"permissionId": "0701907*******5319074",
"emailAddress": "frank@nowhere.com"
},
"editable": true,
"copyable": true,
"writersCanShare": true,
"shared": true,
"explicitlyTrashed": false,
"appDataContents": false,
"spaces": [
"drive"
]
}
]
}

View File

@ -18,130 +18,73 @@
require 'spec_helper'
describe GoogleDocs::DriveConnection do
describe GoogleDrive::Connection do
let(:token) { "token" }
let(:secret) { "secret" }
fake_client = Class.new do
attr_reader :token
attr_writer :responses
attr_accessor :inputs
def initialize(input=nil)
@input = input
@calls = 0
@token = 0
@responses = []
end
def insert
self
end
def request_schema
self.class
end
def discovered_api(_endpoint, _version)
self
end
def files
self
end
def authorization
self
end
def update_token!
@token += 1
end
def get
"/api_method"
end
def list
"/api_method"
end
def execute!(*args)
@inputs = args
response = @responses[@calls]
@calls += 1
response
end
def execute(*args)
execute!(*args)
end
end
before do
config = {
"api_key" => "key",
"secret_key" => "secret",
}
GoogleDocs::DriveConnection.config = proc do
GoogleDrive::Connection.config = proc do
config
end
end
describe "#file_extension from headers" do
it "should pull the file extension from the response header" do
google_docs = GoogleDocs::DriveConnection.new(token, secret)
google_docs = GoogleDrive::Connection.new(token, secret)
headers = {
'content-disposition' => 'attachment;filename="Testing.docx"'
}
entry = stub('DriveEntry', extension: "not")
file_extension = google_docs.send(:file_extension_from_header, headers, entry)
entry = stub('Entry', extension: "not")
file_extension = google_docs.send(:file_extension_from_header, headers, entry)
expect(file_extension).to eq("docx")
end
it "should pull the file extension from the entry if its not in the response header" do
google_docs = GoogleDocs::DriveConnection.new(token, secret)
google_docs = GoogleDrive::Connection.new(token, secret)
headers = {
'content-disposition' => 'attachment"'
}
entry = stub('DriveEntry', extension: "not")
file_extension = google_docs.send(:file_extension_from_header, headers, entry)
entry = stub('Entry', extension: "not")
file_extension = google_docs.send(:file_extension_from_header, headers, entry)
expect(file_extension).to eq("not")
end
it "should use unknown as a last resort file extension" do
google_docs = GoogleDocs::DriveConnection.new(token, secret)
google_docs = GoogleDrive::Connection.new(token, secret)
headers = {
'content-disposition' => 'attachment"'
}
entry = stub('DriveEntry', extension: "")
file_extension = google_docs.send(:file_extension_from_header, headers, entry)
entry = stub('Entry', extension: "")
file_extension = google_docs.send(:file_extension_from_header, headers, entry)
expect(file_extension).to eq("unknown")
end
it "should use unknown as file extension when extension is nil" do
google_docs = GoogleDocs::DriveConnection.new(token, secret)
google_docs = GoogleDrive::Connection.new(token, secret)
headers = {}
entry = stub('DriveEntry', extension: nil)
entry = stub('Entry', extension: nil)
file_extension = google_docs.send(:file_extension_from_header, headers, entry)
file_extension = google_docs.send(:file_extension_from_header, headers, entry)
expect(file_extension).to eq("unknown")
end
end
describe "#normalize_document_id" do
it "should remove prefixes" do
google_docs = GoogleDocs::DriveConnection.new(token, secret)
google_docs = GoogleDrive::Connection.new(token, secret)
spreadsheet_id = google_docs.send(:normalize_document_id, "spreadsheet:awesome-spreadsheet-id")
expect(spreadsheet_id).to eq("awesome-spreadsheet-id")
@ -151,8 +94,7 @@ describe GoogleDocs::DriveConnection do
end
it "shouldnt do anything to normalized ids" do
google_docs = GoogleDocs::DriveConnection.new(token, secret)
google_docs = GoogleDrive::Connection.new(token, secret)
spreadsheet_id = google_docs.send(:normalize_document_id, "awesome-spreadsheet-id")
expect(spreadsheet_id).to eq("awesome-spreadsheet-id")
@ -163,49 +105,58 @@ describe GoogleDocs::DriveConnection do
end
describe "API interaction" do
let(:connection){ GoogleDocs::DriveConnection.new(token, secret) }
let(:client){ fake_client.new }
let(:connection){ GoogleDrive::Connection.new(token, secret) }
before do
connection.send(:set_api_client, client)
stub_request(:get, "https://www.googleapis.com/discovery/v1/apis/drive/v2/rest").
to_return(
:status => 200,
:body => load_fixture('discovered_api.json'),
:headers => {'Content-Type' => 'application/json'}
)
end
describe "#list_with_extension_filter" do
before do
client.responses = [
stub(status: 200, data: { "items" => [] })
]
end
it "should submit `trashed = false` parameter" do
stub_request(
:get, "https://www.googleapis.com/drive/v2/files?maxResults=0&q=trashed=false"
).to_return(
:status => 200, :body => load_fixture('list.json'), :headers => {'Content-Type' => 'application/json'}
)
connection.list_with_extension_filter('.txt')
expect(client.inputs.last[:parameters][:q]).to eql 'trashed=false'
expect(WebMock).to have_requested(:get,
"https://www.googleapis.com/drive/v2/files?maxResults=0&q=trashed=false")
end
end
describe "#download" do
before do
client.responses = [
stub(status: 200, data:
{
'parents' => [],
'title' => "SomeFile.txt"
}
),
stub(status: 200, data: {})
]
end
it "requests a download from the api client" do
stub_request(
:get, "https://www.googleapis.com/drive/v2/files/42"
).to_return(
:status => 200, :body => load_fixture('file_data.json'), :headers => {'Content-Type' => 'application/json'}
)
stub_request(
:get, "https://docs.google.com/feeds/download/documents/export/Export?exportFormat=docx&id=**file_id**"
).to_return(
:status => 200, :body => "",
:headers => {
'Content-Type' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
}
)
output = connection.download("42", nil)
expect(output[1]).to eq("SomeFile.txt")
expect(output[2]).to eq("txt")
expect(output[1]).to eq("Biology 100 Collaboration.docx")
expect(output[2]).to eq("docx")
end
it "wraps a timeout in a drive connection exception" do
Timeout.stubs(:timeout).raises(Timeout::Error)
expect{ connection.download("42", nil) }.to(
raise_error(GoogleDocs::DriveConnectionException) do |e|
raise_error(GoogleDrive::ConnectionException) do |e|
expect(e.message).to eq("Google Drive connection timed out")
end
)
@ -213,26 +164,40 @@ describe GoogleDocs::DriveConnection do
end
describe "#create_doc" do
before do
client.responses = [
stub(status: 200, data: {})
]
end
it "forces a new refresh token" do
connection.create_doc("DocName")
expect(client.token).to eq(1)
end
it "wraps a timeout in a drive connection exception" do
Timeout.stubs(:timeout).raises(Timeout::Error)
expect{ connection.create_doc("Docname") }.to(
raise_error(GoogleDocs::DriveConnectionException) do |e|
raise_error(GoogleDrive::ConnectionException) do |e|
expect(e.message).to eq("Google Drive connection timed out")
end
)
end
end
describe "#authorized?" do
it "returns false when there ConnectionException" do
GoogleDrive::Connection.config = GoogleDrive::Connection.config = proc do
nil
end
expect(connection.authorized?).to be false
end
it "returns false when there NoTokenError" do
my_connection = GoogleDrive::Connection.new(nil, nil)
expect(my_connection.authorized?).to be false
end
it "returns false when there NoTokenError" do
stub_request(:get, "https://www.googleapis.com/drive/v2/about").
to_return(:status => 200, :body => "", :headers => {})
expect(connection.authorized?).to be true
end
end
end
end

View File

@ -1,5 +1,8 @@
require 'google_drive'
require 'byebug'
require 'mocha'
require 'timecop'
require 'webmock/rspec'
DRIVE_FIXTURES_PATH = File.dirname(__FILE__) + '/fixtures/google_drive/'
@ -7,11 +10,19 @@ def load_fixture(filename)
File.read(DRIVE_FIXTURES_PATH + filename)
end
WebMock.disable_net_connect!(allow_localhost: true)
RSpec.configure do |config|
config.treat_symbols_as_metadata_keys_with_true_values = true
config.run_all_when_everything_filtered = true
config.filter_run :focus
config.order = 'random'
config.mock_framework = :mocha
end
module Rails
def self.cache
@cache ||= ActiveSupport::Cache::MemoryStore.new
end
end

View File

@ -6,16 +6,11 @@ module AccountServices
def self.allowable_services
AllowedServicesHash.new.merge({
:google_docs => {
:name => I18n.t("Google Docs"),
:description => "",
:expose_to_ui => :service,
:expose_to_ui_proc => proc { !!GoogleDocs::Connection.config }
},
:google_drive => {
:name => I18n.t("Google Drive"),
:description => "",
:expose_to_ui => false
:expose_to_ui => :service,
:expose_to_ui_proc => proc { !!GoogleDrive::Connection.config }
},
:google_docs_previews => {
:name => I18n.t("Google Docs Preview"),

View File

@ -80,26 +80,18 @@ Canvas::Plugin.register('etherpad', :collaborations, {
:settings_partial => 'plugins/etherpad_settings',
:validator => 'EtherpadValidator'
})
Canvas::Plugin.register('google_docs', :collaborations, {
:name => lambda{ t :name, 'Google Docs' },
:description => lambda{ t :description, 'Google Docs document sharing' },
:website => 'http://docs.google.com',
:author => 'Instructure',
:author_website => 'http://www.instructure.com',
:version => '1.0.0',
:settings_partial => 'plugins/google_docs_settings',
:validator => 'GoogleDocsValidator'
})
Canvas::Plugin.register('google_drive', nil,
name: -> { t :name, 'Google Drive' },
description: -> { t :description, 'Google Drive file sharing' },
website: 'http://drive.google.com',
author: 'Instructure',
author_website: 'http://www.instructure.com',
version: '1.0.0',
settings_partial: 'plugins/google_drive_settings',
validator: 'GoogleDriveValidator',
encrypted_settings: [:client_secret]
Canvas::Plugin.register('google_drive', :collaborations,
{
name: -> { t :name, 'Google Drive' },
description: -> { t :description, 'Google Drive file sharing' },
website: 'http://drive.google.com',
author: 'Instructure',
author_website: 'http://www.instructure.com',
version: '1.0.0',
settings_partial: 'plugins/google_drive_settings',
validator: 'GoogleDriveValidator',
encrypted_settings: [:client_secret]
}
)
Canvas::Plugin.register('kaltura', nil, {
:name => lambda{ t :name, 'Kaltura' },

View File

@ -1,38 +0,0 @@
#
# 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/>.
#
module Canvas::Plugins::Validators::GoogleDocsValidator
def self.validate(settings, plugin_setting)
if settings.map(&:last).all?(&:blank?)
{}
else
if settings.map(&:last).any?(&:blank?)
plugin_setting.errors.add(:base, I18n.t('canvas.plugins.errors.all_fields_required', 'All fields are required'))
false
else
res = GoogleDocs::Connection.config_check(settings)
if res
plugin_setting.errors.add(:base, res)
false
else
settings.slice(:api_key, :secret_key)
end
end
end
end
end

View File

@ -52,80 +52,6 @@ describe ApplicationController do
end
end
describe "#google_docs_connection" do
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_gdocs_access_token_token] = "session_token"
session[:oauth_gdocs_access_token_secret] = "sesion_secret"
Rails.cache.expects(:fetch).with(['google_docs_tokens', mock_real_current_user].cache_key).returns(["real_current_user_token", "real_current_user_secret"])
GoogleDocs::Connection.expects(:new).with("real_current_user_token", "real_current_user_secret")
controller.send(:google_docs_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_gdocs_access_token_token] = "session_token"
session[:oauth_gdocs_access_token_secret] = "sesion_secret"
Rails.cache.expects(:fetch).with(['google_docs_tokens', mock_current_user].cache_key).returns(["current_user_token", "current_user_secret"])
GoogleDocs::Connection.expects(:new).with("current_user_token", "current_user_secret")
controller.send(:google_docs_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_gdocs_access_token_token] = "session_token"
session[:oauth_gdocs_access_token_secret] = "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_docs").returns(stub(first: mock(token: "user_service_token", secret: "user_service_secret")))
GoogleDocs::Connection.expects(:new).with("user_service_token", "user_service_secret")
controller.send(:google_docs_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_gdocs_access_token_token] = "session_token"
session[:oauth_gdocs_access_token_secret] = "sesion_secret"
GoogleDocs::Connection.expects(:new).with("session_token", "sesion_secret")
controller.send(:google_docs_connection)
end
it "raises a NoTokenError when the user exists but does not have a user service" do
mock_current_user = mock()
controller.instance_variable_set(:@real_current_user, nil)
controller.instance_variable_set(:@current_user, mock_current_user)
session[:oauth_gdocs_access_token_token] = "session_token"
session[:oauth_gdocs_access_token_secret] = "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_docs").returns(stub(first: nil))
expect {
controller.send(:google_docs_connection)
}.to raise_error(GoogleDocs::NoTokenError)
end
end
describe "#google_drive_connection" do
before :each do
settings_mock = mock()
@ -143,7 +69,7 @@ describe ApplicationController do
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", 30)
GoogleDrive::Connection.expects(:new).with("real_current_user_token", "real_current_user_secret", 30)
controller.send(:google_drive_connection)
end
@ -157,7 +83,7 @@ describe ApplicationController do
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", 30)
GoogleDrive::Connection.expects(:new).with("current_user_token", "current_user_secret", 30)
controller.send(:google_drive_connection)
end
@ -172,7 +98,7 @@ describe ApplicationController do
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", 30)
GoogleDrive::Connection.expects(:new).with("user_service_token", "user_service_secret", 30)
controller.send(:google_drive_connection)
end
@ -182,75 +108,12 @@ describe ApplicationController do
session[:oauth_gdrive_refresh_token] = "session_token"
session[:oauth_gdrive_access_token] = "sesion_secret"
GoogleDocs::DriveConnection.expects(:new).with("session_token", "sesion_secret", 30)
GoogleDrive::Connection.expects(:new).with("session_token", "sesion_secret", 30)
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
let(:empty_client_secrets) do
{
client_id: nil,
client_secret: nil,
redirect_uri: nil
}.with_indifferent_access
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)
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(empty_client_secrets,"real_current_user_refresh_token", "real_current_user_access_token")
controller.send(:google_drive_user_client)
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)
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(empty_client_secrets,"current_user_refresh_token", "current_user_access_token")
controller.send(:google_drive_user_client)
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)
mock_user_services = mock("mock_user_services")
mock_current_user.expects(:user_services).returns(mock_user_services)
service_mock = mock('service')
service_mock.stubs(first: mock(token: "user_refresh_token", access_token: "user_access_token"))
mock_user_services.expects(:where).with(service: "google_drive").returns(service_mock)
GoogleDrive::Client.expects(:create).with(empty_client_secrets, "user_refresh_token", "user_access_token")
controller.send(:google_drive_user_client)
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_access_token] = "access_token"
session[:oauth_gdrive_refresh_token] = "refresh_token"
GoogleDrive::Client.expects(:create).with(empty_client_secrets, "refresh_token", "access_token")
controller.send(:google_drive_user_client)
end
end
describe "js_env" do
before do
controller.stubs(:api_request?).returns(false)

View File

@ -184,30 +184,20 @@ describe AssignmentsController do
assert_require_login
end
it 'should not error out when google docs is not configured' do
GoogleDocs::Connection.stubs(:config).returns nil
user_session(@student)
a = @course.assignments.create(:title => "some assignment")
get 'show', :course_id => @course.id, :id => a.id
GoogleDocs::Connection.unstub(:config)
end
it 'should force users to use google drive if available' do
it 'should set user_has_google_drive' do
user_session(@student)
a = @course.assignments.create(:title => "some assignment")
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!
google_docs_mock = mock('google_docs')
google_docs_mock.stubs(:verify_access_token).returns(true)
google_docs_mock.stubs(:service_type).returns(nil)
google_docs_mock.stubs(:retrieve_access_token).returns(nil)
controller.stubs(:google_service_connection).returns(google_docs_mock)
google_drive_mock = mock('google_drive')
google_drive_mock.stubs(:authorized?).returns(true)
controller.stubs(:google_drive_connection).returns(google_drive_mock)
get 'show', :course_id => @course.id, :id => a.id
expect(response).to be_success
expect(assigns(:google_drive_upgrade)).to be_truthy
expect(assigns(:user_has_google_drive)).to be true
end
end
@ -366,7 +356,7 @@ describe AssignmentsController do
user_session(@teacher)
connection = stub()
connection.stubs(:list_with_extension_filter).raises(ArgumentError)
controller.stubs(:google_service_connection).returns(connection)
controller.stubs(:google_drive_connection).returns(connection)
Assignment.any_instance.stubs(:allow_google_docs_submission?).returns(true)
Canvas::Errors.expects(:capture_exception)
params = {course_id: @course.id, id: @assignment.id, format: 'json' }

View File

@ -42,34 +42,22 @@ describe CollaborationsController do
it "should assign variables" do
user_session(@student)
controller.stubs(:google_docs_connection).returns(mock(verify_access_token:true))
controller.stubs(:google_drive_connection).returns(mock(authorized?:true))
get 'index', :course_id => @course.id
expect(response).to be_success
expect(assigns(:google_docs_authorized)).to eq true
expect(assigns(:user_has_google_drive)).to eq true
end
it "should handle users without google authorized" do
user_session(@student)
controller.stubs(:google_docs_connection).returns(mock(verify_access_token:false))
controller.stubs(:google_drive_connection).returns(mock(authorized?:false))
get 'index', :course_id => @course.id
expect(response).to be_success
expect(assigns(:google_docs_authorized)).to eq false
end
it "should assign variables when verify raises" do
user_session(@student)
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
expect(response).to be_success
expect(assigns(:google_docs_authorized)).to eq false
expect(assigns(:user_has_google_drive)).to eq false
end
it 'handles users that need to upgrade to google_drive' do
@ -81,8 +69,7 @@ describe CollaborationsController do
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
expect(assigns(:user_has_google_drive)).to be false
end
it "should not allow the student view student to access collaborations" do
@ -101,7 +88,7 @@ describe CollaborationsController do
group = gc.groups.create!(:context => @course)
group.add_user(@student, 'accepted')
#controller.stubs(:google_docs_connection).returns(mock(verify_access_token:false))
#controller.stubs(:google_docs_connection).returns(mock(authorized?:false))
get 'index', :group_id => group.id
expect(response).to be_success

View File

@ -197,14 +197,35 @@ describe SubmissionsController do
end
it "should not send a comment to the entire group by default" do
post 'create', :course_id => @course.id, :assignment_id => @assignment.id, :submission => {:submission_type => 'online_text_entry', :body => 'blah', :comment => "some comment"}
post(
'create',
:course_id => @course.id,
:assignment_id => @assignment.id,
:submission => {
:submission_type => 'online_text_entry',
:body => 'blah',
:comment => "some comment"
}
)
subs = @assignment.submissions
expect(subs.size).to eq 2
expect(subs.to_a.sum{ |s| s.submission_comments.size }).to eql 1
end
it "should send a comment to the entire group if requested" do
post 'create', :course_id => @course.id, :assignment_id => @assignment.id, :submission => {:submission_type => 'online_text_entry', :body => 'blah', :comment => "some comment", :group_comment => '1'}
post(
'create',
:course_id => @course.id,
:assignment_id => @assignment.id,
:submission => {
:submission_type => 'online_text_entry',
:body => 'blah',
:comment => "some comment",
:group_comment => '1'
}
)
subs = @assignment.submissions
expect(subs.size).to eq 2
expect(subs.to_a.sum{ |s| s.submission_comments.size }).to eql 2
@ -226,12 +247,13 @@ describe SubmissionsController do
flag.save!
mock_user_service = mock()
@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")))
mock_user_service.expects(:where).with(service: "google_drive").
returns(stub(first: mock(token: "token", secret: "secret")))
end
it "should not save if domain restriction prevents it" do
google_docs = mock
GoogleDocs::Connection.expects(:new).returns(google_docs)
GoogleDrive::Connection.expects(:new).returns(google_docs)
google_docs.expects(:download).returns([Net::HTTPOK.new(200, {}, ''), 'title', 'pdf'])
post(:create, course_id: @course.id, assignment_id: @assignment.id,
@ -338,7 +360,8 @@ describe SubmissionsController do
it "should allow setting 'student_entered_grade'" do
course_with_student_logged_in(:active_all => true)
@assignment = @course.assignments.create!(:title => "some assignment", :submission_types => "online_url,online_upload")
@assignment = @course.assignments.create!(:title => "some assignment",
:submission_types => "online_url,online_upload")
@submission = @assignment.submit_homework(@user)
put 'update', {
:course_id => @course.id,
@ -353,7 +376,8 @@ describe SubmissionsController do
it "should round 'student_entered_grade'" do
course_with_student_logged_in(:active_all => true)
@assignment = @course.assignments.create!(:title => "some assignment", :submission_types => "online_url,online_upload")
@assignment = @course.assignments.create!(:title => "some assignment",
:submission_types => "online_url,online_upload")
@submission = @assignment.submit_homework(@user)
put 'update', {
:course_id => @course.id,

View File

@ -1352,26 +1352,6 @@ describe UsersController do
end
end
describe "oauth_success" do
it "should use the access token to get user info" do
user_with_pseudonym
admin = @user
user_session(admin)
mock_oauth_request = stub(token: 'token', secret: 'secret', original_host_with_port: 'test.host', user: @user, return_url: '/')
OauthRequest.expects(:where).with(token: 'token', service: 'google_docs').returns(stub(first: mock_oauth_request))
mock_access_token = stub(token: '123', secret: 'abc')
GoogleDocs::Connection.expects(:get_access_token).with('token', 'secret', 'oauth_verifier').returns(mock_access_token)
mock_google_docs = stub()
GoogleDocs::Connection.expects(:new).with('token', 'secret').returns(mock_google_docs)
mock_google_docs.expects(:get_service_user_info).with(mock_access_token)
get 'oauth_success', {oauth_token: 'token', service: 'google_docs', oauth_verifier: 'oauth_verifier'}, {host_with_port: 'test.host'}
end
end
describe 'GET media_download' do
let(:kaltura_client) do
kaltura_client = mock('CanvasKaltura::ClientV3').responds_like_instance_of(CanvasKaltura::ClientV3)

View File

@ -32,25 +32,6 @@ def valid_collaboration_attributes
:context => @course || course_model,
:url => "value for url",
:title => "My Collaboration",
:data => %{<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:ns1="http://schemas.google.com/g/2005" xmlns:ns2="http://schemas.google.com/docs/2007">
<title>Biology 100 Collaboration</title>
<id>http://docs.google.com/feeds/documents/private/full/document%3Adc3pjs4r_3hhc6fvcc</id>
<updated>2009-05-28T23:12:49Z</updated>
<published>2009-05-28T23:12:49Z</published>
<link href="http://docs.google.com/Doc?id=dc3pjs4r_3hhc6fvcc" rel="alternate" type="tex/html"/>
<link href="http://docs.google.com/feeds/documents/private/full/document%3Adc3pjs4r_3hhc6fvcc" rel="self" type="application/atom+xml"/>
<link href="http://docs.google.com/feeds/documents/private/full/document%3Adc3pjs4r_3hhc6fvcc/fva2z1b2" rel="edit" type="application/atom+xml"/>
<link href="http://docs.google.com/feeds/media/private/full/document%3Adc3pjs4r_3hhc6fvcc/fva2z1b2" rel="edit-media" type="text/html"/>
<author>
<name>davidlamontrichards</name>
<email>davidlamontrichards@gmail.com</email>
</author>
<category label="document" scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/docs/2007#document"/>
<content/>
<ns1:lastModifiedBy>&lt;name xmlns="http://www.w3.org/2005/Atom"&gt;davidlamontrichards&lt;/name&gt;&lt;email xmlns="http://www.w3.org/2005/Atom"&gt;davidlamontrichards@gmail.com&lt;/email&gt;</ns1:lastModifiedBy>
<ns1:feedLink/>
<ns1:resourceId>document:dc3pjs4r_3hhc6fvcc</ns1:resourceId>n <ns2:writersCanInvite/>
</entry>}
:data => File.read('gems/google_drive/spec/fixtures/google_drive/file_data.json')
}
end

View File

@ -27,11 +27,11 @@ describe CollaborationsController, type: :request do
course_with_teacher_logged_in :active_all => true, :name => "teacher 1"
UserService.register(
:service => "google_docs",
:service => "google_drive",
:token => "token",
:secret => "secret",
:user => @user,
:service_domain => "google.com",
:service_domain => "drive.google.com",
:service_user_id => "service_user_id",
:service_user_name => "service_user_name"
)

View File

@ -18,7 +18,7 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
%w{ Twitter GoogleDocs LinkedIn }.each do |integration|
%w{ Twitter LinkedIn }.each do |integration|
describe integration do
before do
course_with_student_logged_in(:active_all => true)
@ -28,8 +28,6 @@ describe integration do
UsersController.any_instance.expects(:feature_and_service_enabled?).with(integration.underscore).returns(true)
if integration == "LinkedIn"
LinkedIn::Connection.expects(:config).at_least_once.returns({})
elsif integration == "GoogleDocs"
GoogleDocs::Connection.expects(:config).at_least_once.returns({})
elsif integration == "Twitter"
Twitter::Connection.expects(:config).at_least_once.returns({})
else
@ -97,9 +95,7 @@ describe integration do
# mock up the response from the 3rd party service, so we don't actually contact it
OAuth::Consumer.any_instance.expects(:token_request).with(anything, anything, anything, has_entry(:oauth_verifier, "test_verifier"), anything).returns({:oauth_token => "test_token", :oauth_token_secret => "test_secret"})
if integration == "GoogleDocs"
GoogleDocs::Connection.any_instance.expects(:get_service_user_info).returns(["test_user_id", "test_user_name"])
elsif integration == "LinkedIn"
if integration == "LinkedIn"
LinkedIn::Connection.any_instance.expects(:get_service_user_info).with(instance_of(OAuth::AccessToken)).returns(["test_user_id", "test_user_name"])
elsif integration == "Twitter"
Twitter::Connection.any_instance.expects(:get_service_user).returns(["test_user_id", "test_user_name"])
@ -126,9 +122,7 @@ describe integration do
OAuth::Consumer.any_instance.expects(:token_request).with(anything, anything, anything, has_entry(:oauth_verifier, "test_verifier"), anything).returns({:oauth_token => "test_token", :oauth_token_secret => "test_secret"})
# pretend that somehow we think we got a valid auth token, but we actually didn't
if integration == "GoogleDocs"
GoogleDocs::Connection.any_instance.expects(:get_service_user_info).raises(RuntimeError, "Third-party service totally like, failed")
elsif integration == "LinkedIn"
if integration == "LinkedIn"
LinkedIn::Connection.any_instance.expects(:get_service_user_info).with(instance_of(OAuth::AccessToken)).raises(RuntimeError, "Third-party service totally like, failed")
elsif integration == "Twitter"
Twitter::Connection.any_instance.expects(:get_service_user).raises(RuntimeError, "Third-party service totally like, failed")

View File

@ -251,9 +251,17 @@ describe Account do
end
it "should not wipe out services that are substrings of each other" do
AccountServices.register_service(
:google_docs_prev,
{
:name => "My google docs prev", :description => "", :expose_to_ui => :service, :default => true
}
)
@a.disable_service('google_docs_previews')
@a.disable_service('google_docs')
expect(@a.allowed_services).to eq '-google_docs_previews,-google_docs'
@a.disable_service('google_docs_prev')
expect(@a.allowed_services).to eq '-google_docs_previews,-google_docs_prev'
end
describe "services_exposed_to_ui_hash" do

View File

@ -27,7 +27,7 @@ describe Collaboration do
it "should allow google docs collaborations" do
expect(Collaboration.collaboration_class('GoogleDocs')).to eql(nil)
plugin_setting = PluginSetting.new(:name => "google_docs", :settings => {})
plugin_setting = PluginSetting.new(:name => "google_drive", :settings => {})
plugin_setting.save!
expect(Collaboration.collaboration_class('GoogleDocs')).to eql(GoogleDocsCollaboration)
plugin_setting.disabled = true
@ -55,10 +55,9 @@ describe Collaboration do
google_docs_collaboration_model
end
it "should be able to parse the data stored as an Atom entry" do
it "should be able to parse the data stored as JSON" do
ae = @collaboration.parse_data
expect(ae).to be_is_a(Atom::Entry)
expect(ae.title).to eql('Biology 100 Collaboration')
expect(ae['title']).to eql('Biology 100 Collaboration')
end
it "should be able to get the title from the data" do

View File

@ -25,14 +25,11 @@ describe GoogleDocsCollaboration do
google_docs_collaboration = GoogleDocsCollaboration.new
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").returns(file)
google_drive_connection = stub(retrieve_access_token: "asdf123")
GoogleDrive::Connection.expects(:new).returns(google_drive_connection)
file = stub(data: stub(id: 1, to_json: "{id: 1}", alternateLink: "http://google.com"))
google_drive_connection.expects(:create_doc).with("title").returns(file)
Rails.cache.expects(:fetch).returns(["token", "secret"])
google_docs_collaboration.initialize_document
end
end

View File

@ -140,10 +140,6 @@ describe UserService do
end
context "service type disambiguation" do
it "should know that google_docs means 'DocumentService" do
expect(UserService.service_type('google_docs')).to eql('DocumentService')
end
it "should know that google_drive means 'DocumentService" do
expect(UserService.service_type('google_drive')).to eql('DocumentService')
end

View File

@ -12,7 +12,7 @@ describe "assignments" do
end
it "should not show google docs tab for masquerading admin" do
PluginSetting.create!(:name => 'google_docs', :settings => {})
PluginSetting.create!(:name => 'google_drive', :settings => {})
get "/users/#{@student.id}/masquerade"
expect_new_page_load { f('.masquerade_button').click }

View File

@ -206,10 +206,9 @@ describe "assignments" do
end
context "google drive" do
before(:each) do
PluginSetting.create!(:name => 'google_docs', :settings => {})
before do
PluginSetting.create!(:name => 'google_drive', :settings => {})
set_up_google_docs()
setup_google_drive()
end
it "should have a google doc tab if google docs is enabled", priority: "1", test_id: 161884 do
@ -224,16 +223,16 @@ describe "assignments" do
context "select file or folder" do
before(:each) do
# mock out function calls
google_service_connection = mock()
google_service_connection.stubs(:service_type).returns('google_drive')
google_service_connection.stubs(:retrieve_access_token).returns('access_token')
google_service_connection.stubs(:verify_access_token).returns(true)
google_drive_connection = mock()
google_drive_connection.stubs(:service_type).returns('google_drive')
google_drive_connection.stubs(:retrieve_access_token).returns('access_token')
google_drive_connection.stubs(:authorized?).returns(true)
# mock files to show up from "google drive"
file_list = create_file_list
google_service_connection.stubs(:list_with_extension_filter).returns(file_list)
google_drive_connection.stubs(:list_with_extension_filter).returns(file_list)
ApplicationController.any_instance.stubs(:google_service_connection).returns(google_service_connection)
ApplicationController.any_instance.stubs(:google_drive_connection).returns(google_drive_connection)
# create assignment
@assignment.update_attributes(:submission_types => 'online_upload')
@ -261,11 +260,11 @@ describe "assignments" do
it "forces users to authenticate", priority: "1", test_id: 161892 do
# stub out google drive
google_service_connection = mock()
google_service_connection.stubs(:service_type).returns('google_drive')
google_service_connection.stubs(:retrieve_access_token).returns(nil)
google_service_connection.stubs(:verify_access_token).returns(nil)
ApplicationController.any_instance.stubs(:google_service_connection).returns(google_service_connection)
google_drive_connection = mock()
google_drive_connection.stubs(:service_type).returns('google_drive')
google_drive_connection.stubs(:retrieve_access_token).returns(nil)
google_drive_connection.stubs(:authorized?).returns(nil)
ApplicationController.any_instance.stubs(:google_drive_connection).returns(google_drive_connection)
@assignment.update_attributes(:submission_types => 'online_upload')
get "/courses/#{@course.id}/assignments/#{@assignment.id}"

View File

@ -16,7 +16,7 @@ describe "collaborations" do
context "#{title} collaboration" do
before(:each) do
course_with_student_logged_in
set_up_google_docs
setup_google_drive
end
it 'should display the new collaboration form if there are no existing collaborations', priority: "1", test_id: 162354 do

View File

@ -16,7 +16,7 @@ describe "collaborations" do
context "#{title} collaboration" do
before(:each) do
course_with_teacher_logged_in
set_up_google_docs
setup_google_drive
end
it 'should display the new collaboration form if there are no existing collaborations', priority: "1", test_id: 132521 do

View File

@ -16,7 +16,7 @@ describe "collaborations" do
context "#{title} collaboration" do
before(:each) do
course_with_student_logged_in
set_up_google_docs
setup_google_drive
end
it 'should be editable', priority: "1", test_id: 158504 do
@ -39,7 +39,7 @@ describe "collaborations" do
context "Google Docs collaborations with google docs not having access" do
before(:each) do
course_with_teacher_logged_in
set_up_google_docs(false)
setup_google_drive(false, false)
end
it 'should not be editable if google drive does not have access to your account', priority: "1", test_id: 162363 do

View File

@ -16,7 +16,7 @@ describe "collaborations" do
context "#{title} collaboration" do
before(:each) do
course_with_teacher_logged_in
set_up_google_docs
setup_google_drive
end
it 'should be editable', priority: "1", test_id: 138612 do
@ -39,7 +39,7 @@ describe "collaborations" do
context "Google Docs collaborations with google docs not having access" do
before(:each) do
course_with_teacher_logged_in
set_up_google_docs(false)
setup_google_drive(false, false)
end
it 'should not be editable if google drive does not have access to your account', priority: "1", test_id: 160259 do

View File

@ -64,34 +64,6 @@ describe "default plugins" do
expect(settings[:name]).to eq 'asdf'
end
it "should allow configuring google docs plugin" do
settings = Canvas::Plugin.find(:google_docs).try(:settings)
expect(settings).to be_nil
GoogleDocs::Connection.stubs(:config_check).returns("Bad check")
get "/plugins/google_docs"
multiple_accounts_select
f("#plugin_setting_disabled").click
wait_for_ajaximations
f("#settings_api_key").send_keys("asdf")
f("#settings_secret_key").send_keys("asdf")
submit_form('#new_plugin_setting')
assert_flash_error_message /There was an error/
GoogleDocs::Connection.stubs(:config_check).returns(nil)
submit_form('#new_plugin_setting')
wait_for_ajax_requests
assert_flash_notice_message /successfully updated/
settings = Canvas::Plugin.find(:google_docs).try(:settings)
expect(settings).not_to be_nil
expect(settings[:api_key]).to eq 'asdf'
expect(settings[:secret_key]).to eq 'asdf'
end
it "should allow configuring linked in plugin" do
settings = Canvas::Plugin.find(:linked_in).try(:settings)
expect(settings).to be_nil

View File

@ -306,9 +306,8 @@ describe "groups" do
#-------------------------------------------------------------------------------------------------------------------
describe "collaborations page" do
before(:each) do
set_up_google_docs
unless PluginSetting.where(name: 'google_docs').exists?
PluginSetting.create!(name: 'google_docs', settings: {})
setup_google_drive
unless PluginSetting.where(name: 'google_drive').exists?
PluginSetting.create!(name: 'google_drive', settings: {})
end
end

View File

@ -48,15 +48,15 @@ module CollaborationsCommon
# title - The title of the new collaboration (default: "New collaboration").
#
# Returns a boolean.
def create_collaboration!(type, title = 'New collaboration')
unless PluginSetting.where(:name => type).exists?
PluginSetting.create!(:name => type, :settings => {})
PluginSetting.create!(:name => 'google_drive', :settings => {}) if type == "google_docs"
end
# PluginSetting.where(:name => type).destroy_all
# PluginSetting.where(:name => 'google_drive').destroy_all
def create_collaboration!(collaboration_type, title = 'New collaboration')
name = Collaboration.collaboration_types.detect{|t| t[:type] == type}[:name]
plugin_type = collaboration_type
plugin_type = 'google_drive' if plugin_type == 'google_docs'
unless PluginSetting.where(:name => plugin_type).exists?
PluginSetting.create!(:name => plugin_type, :settings => {})
end
name = Collaboration.collaboration_types.detect{|t| t[:type] == collaboration_type}[:name]
@collaboration = Collaboration.typed_collaboration_instance(name)
@collaboration.context = @course

View File

@ -1,6 +1,12 @@
module CollaborationsSpecsCommon
def new_collaborations_form(type)
def ensure_plugin(type)
type = 'google_drive' if type == 'google_docs'
PluginSetting.create!(:name => type, :settings => {})
end
def new_collaborations_form(type)
ensure_plugin(type)
validate_collaborations
end
@ -45,7 +51,7 @@ module CollaborationsSpecsCommon
end
def display_available_collaborators(type)
PluginSetting.create!(:name => type, :settings => {})
ensure_plugin(type)
student_in_course(:course => @course)
@student.update_attribute(:name, 'Don Draper')
@ -56,7 +62,7 @@ module CollaborationsSpecsCommon
end
def select_collaborators(type)
PluginSetting.create!(:name => type, :settings => {})
ensure_plugin(type)
student_in_course(:course => @course)
@student.update_attribute(:name, 'Don Draper')
@ -93,7 +99,7 @@ module CollaborationsSpecsCommon
end
def deselect_collaborators(type)
PluginSetting.create!(:name => type, :settings => {})
ensure_plugin(type)
student_in_course(:course => @course)
@student.update_attribute(:name, 'Don Draper')
@ -106,7 +112,7 @@ module CollaborationsSpecsCommon
end
def select_collaborators_and_look_for_start(type)
PluginSetting.create!(:name => type, :settings => {})
ensure_plugin(type)
collaboration_name = "StreetsOfRage"
manually_create_collaboration(collaboration_name)
@ -138,7 +144,7 @@ module CollaborationsSpecsCommon
end
def display_new_form_if_none_exist(type)
PluginSetting.create!(:name => type, :settings => {})
ensure_plugin(type)
validate_collaborations(%W{/courses/#{@course.id}/collaborations
/courses/#{@course.id}/collaborations#add_collaboration}, true)
end
@ -157,7 +163,7 @@ module CollaborationsSpecsCommon
end
def not_display_new_form_when_penultimate_collaboration_is_deleted(type, title)
PluginSetting.create!(:name => type, :settings => {})
ensure_plugin(type)
@collaboration1 = Collaboration.typed_collaboration_instance(title)
@collaboration1.context = @course

View File

@ -1,23 +1,20 @@
module GoogleDriveCommon
def set_up_google_docs(setupDrive = true)
def setup_google_drive(add_user_service=true, authorized=true)
UserService.register(
:service => "google_docs",
:service => "google_drive",
:token => "token",
:secret => "secret",
:user => @user,
:service_domain => "google.com",
:service_domain => "drive.google.com",
:service_user_id => "service_user_id",
:service_user_name => "service_user_name"
)
GoogleDocs::Connection.any_instance.
stubs(:verify_access_token).
returns(true)
# GoogleDocs::Connection.any_instance.
# stubs(:list).
# returns(nil)
) if add_user_service
GoogleDrive::Connection.any_instance.
stubs(:authorized?).
returns(authorized)
GoogleDocsCollaboration.any_instance.
stubs(:initialize_document).
@ -27,24 +24,5 @@ module GoogleDriveCommon
stubs(:delete_document).
returns(nil)
if setupDrive == true
set_up_drive_connection
end
end
def set_up_drive_connection
UserService.register(
:service => "google_drive",
:token => "token",
:secret => "secret",
:user => @user,
:service_domain => "drive.google.com",
:service_user_id => "service_user_id",
:service_user_name => "service_user_name"
)
GoogleDocs::DriveConnection.any_instance.
stubs(:verify_access_token).
returns(true)
end
end