diff --git a/.travis.yml b/.travis.yml index 34f1a320..836cc74c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,16 +14,20 @@ before_install: - "if [ $(phantomjs --version) != '2.1.1' ]; then tar -xvf $PWD/travis_phantomjs/phantomjs-2.1.1-linux-x86_64.tar.bz2 -C $PWD/travis_phantomjs; fi" - "phantomjs --version" rvm: - - 2.2 + - "2.2.2" +# rack 2 requires Ruby version >= 2.2.2 env: global: - NOKOGIRI_USE_SYSTEM_LIBRARIES=true matrix: - "RAILS_VERSION=4.1.10" - "RAILS_VERSION=4.2.1" + - "RAILS_VERSION=5.0.0.1" matrix: include: + - rvm: "2.3.0" + env: "RAILS_VERSION=5.0.0.1" - rvm: 2.1 env: "RAILS_VERSION=4.2.1" - rvm: 2.0 diff --git a/app/controllers/browse_everything_controller.rb b/app/controllers/browse_everything_controller.rb index c19b7abc..0b4d78a0 100644 --- a/app/controllers/browse_everything_controller.rb +++ b/app/controllers/browse_everything_controller.rb @@ -4,8 +4,8 @@ class BrowseEverythingController < ActionController::Base layout 'browse_everything' helper BrowseEverythingHelper - after_filter {session["#{provider_name}_token"] = provider.token unless provider.nil? } - + after_filter { session["#{provider_name}_token"] = provider.token unless provider.nil? } + def index render :layout => !request.xhr? end @@ -13,22 +13,21 @@ def index def show render :layout => !request.xhr? end - + def auth - code = params[:code] - session["#{provider_name}_token"] = provider.connect(params,session["#{provider_name}_data"]) + session["#{provider_name}_token"] = provider.connect(params, session["#{provider_name}_data"]) end def resolve selected_files = params[:selected_files] || [] - @links = selected_files.collect { |file| - p,f = file.split(/:/) + @links = selected_files.collect { |file| + p,f = file.split(/:/) (url,extra) = browser.providers[p].link_for(f) result = { url: url } result.merge!(extra) unless extra.nil? result } - respond_to do |format| + respond_to do |format| format.html { render :layout => false } format.json { render :json => @links } end @@ -40,7 +39,7 @@ def auth_link @auth_link ||= if provider.present? link, data = provider.auth_link session["#{provider_name}_data"] = data - link = "#{link}&state=#{provider.key}" unless link.include?("state") + link = "#{link}&state=#{provider.key}" unless link.to_s.include?('state') link else nil diff --git a/app/helpers/browse_everything_helper.rb b/app/helpers/browse_everything_helper.rb index ba6467a8..44fd0cb6 100644 --- a/app/helpers/browse_everything_helper.rb +++ b/app/helpers/browse_everything_helper.rb @@ -1,36 +1,25 @@ module BrowseEverythingHelper - - def array_to_hidden_fields(array,key) + def array_to_hidden_fields(array, key) fields = array.to_query(key).split(Rack::Utils::DEFAULT_SEP).collect do |pair| - key,value=pair.split('=', 2).map { |str| Rack::Utils.unescape(str) } - hidden_field_tag(key,value) + key, value = pair.split('=', 2).map { |str| Rack::Utils.unescape(str) } + hidden_field_tag(key, value) end fields.join("\n").html_safe end - # Extracted from Rack::Mime 1.5.2 for use with earlier versions - # of Rack/Rails + # Extracted from Rack::Mime 1.5.2 for use with earlier versions of Rack/Rails + # @param [String] value + # @param [String] matcher + # @return [TrueClass,FalseClass] def mime_match?(value, matcher) v1, v2 = value.split('/', 2) m1, m2 = matcher.split('/', 2) - - if m1 == '*' - if m2.nil? || m2 == '*' - return true - elsif m2 == v2 - return true - else - return false - end - end - - return false if v1 != m1 - - return true if m2.nil? || m2 == '*' - - m2 == v2 + return false if m1 != '*' && v1 != m1 + m2.nil? || m2 == '*' || m2 == v2 end + # @param [BrowseEverything::FileEntry] file + # @return [TrueClass,FalseClass] def is_acceptable?(file) acceptable = params[:accept] || '*/*' acceptable_types = acceptable.split(/,\s*/) diff --git a/app/views/browse_everything/_file.html.erb b/app/views/browse_everything/_file.html.erb index 9620e486..8bea9020 100644 --- a/app/views/browse_everything/_file.html.erb +++ b/app/views/browse_everything/_file.html.erb @@ -1,13 +1,10 @@ -<% unless file.relative_parent_path? %> - - <% if file.container? || provider.config[:max_upload_file_size].blank? #never disable a folder or if no maximum is set - disabled = false - else - max_size = provider.config[:max_upload_file_size].to_i - disabled = file.size > max_size - end - %> - +<% if file.container? || provider.config[:max_upload_file_size].blank? # never disable a folder or if no maximum is set + disabled = false + else + max_size = provider.config[:max_upload_file_size].to_i + disabled = file.size > max_size + end +%> <% if disabled %> @@ -36,4 +33,3 @@ <%= file.mtime.strftime('%F %R') %> -<% end %> diff --git a/app/views/browse_everything/_files.html.erb b/app/views/browse_everything/_files.html.erb index 4fdf8b79..8b76872d 100644 --- a/app/views/browse_everything/_files.html.erb +++ b/app/views/browse_everything/_files.html.erb @@ -1,4 +1,3 @@ -<% if provider.present? %>
100% Complete @@ -14,11 +13,9 @@ Modified - <% provider.contents(browse_path).each_with_index do |file,index| %> - <% if is_acceptable?(file) %> - <%= render :partial => 'file', :locals => { :file => file, :index => index, - :path => browse_everything_engine.contents_path(provider_name,file.id), :parent => params[:parent] } %> - <% end %> + <% provider.contents(browse_path).each_with_index do |file, index| %> + <% next if file.relative_parent_path? %> + <%= render :partial => 'file', :locals => { :file => file, :index => index, + :path => browse_everything_engine.contents_path(provider_name, file.id), :parent => params[:parent] } %> <% end %> -<% end %> diff --git a/app/views/browse_everything/index.html.erb b/app/views/browse_everything/index.html.erb index c4583db0..181f802b 100644 --- a/app/views/browse_everything/index.html.erb +++ b/app/views/browse_everything/index.html.erb @@ -8,7 +8,7 @@ diff --git a/browse-everything.gemspec b/browse-everything.gemspec index 32bb7550..a67baeaf 100644 --- a/browse-everything.gemspec +++ b/browse-everything.gemspec @@ -26,7 +26,8 @@ Gem::Specification.new do |spec| spec.add_dependency "sass-rails" spec.add_dependency "bootstrap-sass" spec.add_dependency "font-awesome-rails" - spec.add_dependency "google-api-client", "~> 0.8.6" + spec.add_dependency "google-api-client", "~> 0.9" + spec.add_dependency "signet" spec.add_dependency "httparty" spec.add_development_dependency "rspec", "~> 3.0" spec.add_development_dependency "rspec-rails" diff --git a/lib/browse_everything/driver/base.rb b/lib/browse_everything/driver/base.rb index 4997632c..35d63df6 100644 --- a/lib/browse_everything/driver/base.rb +++ b/lib/browse_everything/driver/base.rb @@ -5,7 +5,7 @@ class Base attr_reader :config, :name attr_accessor :token - + def initialize(config,session_info={}) @config = config validate_config @@ -42,6 +42,7 @@ def authorized? false end + # @return [Array{URI,Object}] 2 elements: the URI, and session data to store under "#{provider_name}_data" def auth_link [] end diff --git a/lib/browse_everything/driver/google_drive.rb b/lib/browse_everything/driver/google_drive.rb index c33c871a..b23fcbd7 100644 --- a/lib/browse_everything/driver/google_drive.rb +++ b/lib/browse_everything/driver/google_drive.rb @@ -1,8 +1,8 @@ module BrowseEverything module Driver class GoogleDrive < Base - - require 'google/api_client' + require 'google/apis/drive_v3' + require 'signet' def icon 'google-plus-sign' @@ -17,114 +17,90 @@ def validate_config end end - def contents(path='') - default_params = { } + def contents(path = '') + return to_enum(:contents, path) unless block_given? + default_params = { + order_by: 'folder,modifiedTime desc,name', + fields: 'nextPageToken,files(name,id,mimeType,size,modifiedTime,parents,web_content_link)' + # page_size: 100 + } page_token = nil - files = [] begin - unless path.blank? - default_params[:q] = "'#{path}' in parents" - end - unless page_token.blank? - default_params[:pageToken] = page_token - end - api_result = oauth_client.execute( api_method: drive.files.list, parameters: default_params ) - response = JSON.parse(api_result.response.body) - page_token = response["nextPageToken"] - response["items"].select do |file| - path.blank? ? (file["parents"].blank? or file["parents"].any?{|p| p["isRoot"] }) : true + default_params[:q] = "'#{path}' in parents" unless path.blank? + default_params[:page_token] = page_token unless page_token.blank? + response = drive.list_files(default_params) + page_token = response.next_page_token + response.files.select do |file| + path.blank? ? (file.parents.blank? || file.parents.any?{|p| p == 'root' }) : true end.each do |file| - files << details(file, path) + d = details(file, path) + yield d if d end end while !page_token.blank? - files.compact end - def details(file, path='') - if file["downloadUrl"] or file["mimeType"] == "application/vnd.google-apps.folder" - BrowseEverything::FileEntry.new( - file["id"], - "#{self.key}:#{file["id"]}", - file["title"], - (file["fileSize"] || 0), - Time.parse(file["modifiedDate"]), - file["mimeType"] == "application/vnd.google-apps.folder", - file["mimeType"] == "application/vnd.google-apps.folder" ? - "directory" : - file["mimeType"] - ) - end + def details(file, path = '') + mime_folder = file.mime_type == 'application/vnd.google-apps.folder' + BrowseEverything::FileEntry.new( + file.id, + "#{self.key}:#{file.id}", + file.name, + file.size.to_i, + file.modified_time || DateTime.new, + mime_folder, + mime_folder ? 'directory' : file.mime_type + ) end def link_for(id) - api_method = drive.files.get - api_result = oauth_client.execute(api_method: api_method, parameters: {fileId: id}) - download_url = JSON.parse(api_result.response.body)["downloadUrl"] - auth_header = {'Authorization' => "Bearer #{oauth_client.authorization.access_token.to_s}"} - extras = { + file = drive.get_file(id) + auth_header = { 'Authorization' => "Bearer #{auth_client.access_token}" } + extras = { auth_header: auth_header, - expires: 1.hour.from_now, - file_name: api_result.data.title, - file_size: api_result.data.fileSize.to_i + expires: 1.hour.from_now, + file_name: file.name, + file_size: file.size.to_i } - [download_url, extras] + [file.web_content_link, extras] end def auth_link - oauth_client.authorization.authorization_uri.to_s + auth_client.authorization_uri end def authorized? - @token.present? + token.present? end def connect(params, data) - oauth_client.authorization.code = params[:code] - @token = oauth_client.authorization.fetch_access_token! + auth_client.code = params[:code] + self.token = auth_client.fetch_access_token! end def drive - oauth_client.discovered_api('drive', 'v2') + @drive ||= Google::Apis::DriveV3::DriveService.new.tap do |s| + s.authorization = authorization + end end private - #As per issue http://stackoverflow.com/questions/12572723/rails-google-client-api-unable-to-exchange-a-refresh-token-for-access-token - - #patch start - def token_expired?(token) - client=@client - result = client.execute( api_method: drive.files.list, parameters: {} ) - (result.status != 200) - end - - def exchange_refresh_token( refresh_token ) - client=oauth_client - client.authorization.grant_type = 'refresh_token' - client.authorization.refresh_token = refresh_token - client.authorization.fetch_access_token! - client.authorization - client + def authorization + return @auth_client unless @auth_client.nil? + return nil unless token.present? + auth_client.update_token!(token) + self.token = auth_client.fetch_access_token! if auth_client.expired? + auth_client end - #patch end - def oauth_client - if @client.nil? - @client = Google::APIClient.new - @client.authorization.client_id = config[:client_id] - @client.authorization.client_secret = config[:client_secret] - @client.authorization.scope = "https://www.googleapis.com/auth/drive" - @client.authorization.redirect_uri = callback - @client.authorization.update_token!(@token) if @token.present? - #Patch start - @client = exchange_refresh_token(@token["refresh_token"]) if @token.present? && token_expired?(@token) - #Patch end - end - #todo error checking here - @client + def auth_client + @auth_client ||= Signet::OAuth2::Client.new token_credential_uri: 'https://www.googleapis.com/oauth2/v3/token', + authorization_uri: 'https://accounts.google.com/o/oauth2/auth', + scope: 'https://www.googleapis.com/auth/drive', + client_id: config[:client_id], + client_secret: config[:client_secret], + redirect_uri: callback end - end - end end diff --git a/lib/browse_everything/file_entry.rb b/lib/browse_everything/file_entry.rb index 4ac375ce..1be4c0ca 100644 --- a/lib/browse_everything/file_entry.rb +++ b/lib/browse_everything/file_entry.rb @@ -9,13 +9,13 @@ def initialize(id, location, name, size, mtime, container, type=nil) @size = size @mtime = mtime @container = container - @type = type || @container ? 'application/x-directory' : Rack::Mime.mime_type(File.extname(name)) + @type = type || (@container ? 'application/x-directory' : Rack::Mime.mime_type(File.extname(name))) end def relative_parent_path? name =~ /^\.\.?$/ ? true : false end - + def container? @container end diff --git a/spec/features/select_files_spec.rb b/spec/features/select_files_spec.rb index 4e945cd5..1cf94303 100644 --- a/spec/features/select_files_spec.rb +++ b/spec/features/select_files_spec.rb @@ -13,7 +13,7 @@ it "selects files from the filesystem" do click_button('Browse') sleep(5) - click_link("README.rdoc") + click_link('Gemfile.lock') within(".modal-footer") do expect(page).to have_selector("span", text: "1 file selected") click_button("Submit")