cloudinary 1.27.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +8 -14
- data/CHANGELOG.md +126 -0
- data/README.md +7 -5
- data/cloudinary.gemspec +25 -50
- data/lib/active_storage/service/cloudinary_service.rb +63 -8
- data/lib/cloudinary/account_api.rb +94 -25
- data/lib/cloudinary/analytics.rb +157 -0
- data/lib/cloudinary/api.rb +110 -44
- data/lib/cloudinary/auth_token.rb +1 -5
- data/lib/cloudinary/base_api.rb +36 -31
- data/lib/cloudinary/carrier_wave/storage.rb +3 -9
- data/lib/cloudinary/carrier_wave.rb +0 -5
- data/lib/cloudinary/helper.rb +3 -11
- data/lib/cloudinary/migrator.rb +70 -71
- data/lib/cloudinary/railtie.rb +3 -1
- data/lib/cloudinary/search.rb +18 -3
- data/lib/cloudinary/uploader.rb +72 -104
- data/lib/cloudinary/utils.rb +53 -48
- data/lib/cloudinary/version.rb +1 -1
- data/lib/cloudinary/video_helper.rb +3 -2
- data/lib/cloudinary.rb +3 -9
- data/lib/tasks/cloudinary/fetch_assets.rake +9 -3
- data/vendor/assets/javascripts/cloudinary/jquery.cloudinary.js +3 -3
- metadata +179 -38
- data/lib/cloudinary/ostruct2.rb +0 -284
data/lib/cloudinary/utils.rb
CHANGED
@@ -4,15 +4,14 @@
|
|
4
4
|
require 'digest/sha1'
|
5
5
|
require 'zlib'
|
6
6
|
require 'uri'
|
7
|
-
require 'aws_cf_signer'
|
8
7
|
require 'json'
|
9
8
|
require 'cgi'
|
9
|
+
require 'faraday'
|
10
|
+
require 'faraday/multipart'
|
10
11
|
require 'cloudinary/auth_token'
|
11
12
|
require 'cloudinary/responsive'
|
12
13
|
|
13
14
|
class Cloudinary::Utils
|
14
|
-
# @deprecated Use Cloudinary::SHARED_CDN
|
15
|
-
SHARED_CDN = Cloudinary::SHARED_CDN
|
16
15
|
MODE_DOWNLOAD = "download"
|
17
16
|
DEFAULT_RESPONSIVE_WIDTH_TRANSFORMATION = {:width => :auto, :crop => :limit}
|
18
17
|
CONDITIONAL_OPERATORS = {
|
@@ -98,7 +97,6 @@ class Cloudinary::Utils
|
|
98
97
|
secure_distribution
|
99
98
|
shorten
|
100
99
|
sign_url
|
101
|
-
ssl_detected
|
102
100
|
type
|
103
101
|
url_suffix
|
104
102
|
use_root_path
|
@@ -155,7 +153,7 @@ class Cloudinary::Utils
|
|
155
153
|
zoom
|
156
154
|
].map(&:to_sym)
|
157
155
|
|
158
|
-
REMOTE_URL_REGEX = %r(^ftp:|^https?:|^s3:|^gs:|^data:([\w-]+\/[\w-]+(\+[\w-]+)?)?(;[\w-]+=[\w-]+)*;base64,([a-zA-Z0-9\/+\n=]+)$)
|
156
|
+
REMOTE_URL_REGEX = %r(^ftp:|^https?:|^s3:|^gs:|^data:([\w-]+\/[\w-]+(\.[\w-]+)*(\+[\w-]+)?)?(;[\w-]+=[\w-]+)*;base64,([a-zA-Z0-9\/+\n=]+)$)
|
159
157
|
|
160
158
|
LONG_URL_SIGNATURE_LENGTH = 32
|
161
159
|
SHORT_URL_SIGNATURE_LENGTH = 8
|
@@ -513,7 +511,7 @@ class Cloudinary::Utils
|
|
513
511
|
end
|
514
512
|
|
515
513
|
# Warning: options are being destructively updated!
|
516
|
-
def self.
|
514
|
+
def self.cloudinary_url(source, options = {})
|
517
515
|
|
518
516
|
patch_fetch_format(options)
|
519
517
|
type = options.delete(:type)
|
@@ -530,7 +528,6 @@ class Cloudinary::Utils
|
|
530
528
|
|
531
529
|
sign_url = config_option_consume(options, :sign_url)
|
532
530
|
secret = config_option_consume(options, :api_secret)
|
533
|
-
sign_version = config_option_consume(options, :sign_version) # Deprecated behavior
|
534
531
|
url_suffix = options.delete(:url_suffix)
|
535
532
|
use_root_path = config_option_consume(options, :use_root_path)
|
536
533
|
auth_token = config_option_consume(options, :auth_token)
|
@@ -578,7 +575,7 @@ class Cloudinary::Utils
|
|
578
575
|
transformation = transformation.gsub(%r(([^:])//), '\1/')
|
579
576
|
if sign_url && ( !auth_token || auth_token.empty?)
|
580
577
|
raise(CloudinaryException, "Must supply api_secret") if (secret.nil? || secret.empty?)
|
581
|
-
to_sign = [transformation,
|
578
|
+
to_sign = [transformation, source_to_sign].reject(&:blank?).join("/")
|
582
579
|
to_sign = fully_unescape(to_sign)
|
583
580
|
signature_algorithm = long_url_signature ? ALGO_SHA256 : signature_algorithm
|
584
581
|
hash = hash("#{to_sign}#{secret}", signature_algorithm)
|
@@ -590,12 +587,22 @@ class Cloudinary::Utils
|
|
590
587
|
prefix = build_distribution_domain(options)
|
591
588
|
|
592
589
|
source = [prefix, resource_type, type, signature, transformation, version, source].reject(&:blank?).join("/")
|
590
|
+
|
591
|
+
token = nil
|
593
592
|
if sign_url && auth_token && !auth_token.empty?
|
594
593
|
auth_token[:url] = URI.parse(source).path
|
595
594
|
token = Cloudinary::AuthToken.generate auth_token
|
596
|
-
source += "?#{token}"
|
597
595
|
end
|
598
596
|
|
597
|
+
analytics = config_option_consume(options, :analytics, true)
|
598
|
+
analytics_token = nil
|
599
|
+
if analytics && ! original_source.include?("?") # Disable analytics for public IDs containing query params.
|
600
|
+
analytics_token = Cloudinary::Analytics.sdk_analytics_query_param
|
601
|
+
end
|
602
|
+
|
603
|
+
query_params = [token, analytics_token].compact.join("&")
|
604
|
+
|
605
|
+
source += "?#{query_params}" unless query_params.empty?
|
599
606
|
source
|
600
607
|
end
|
601
608
|
|
@@ -678,7 +685,7 @@ class Cloudinary::Utils
|
|
678
685
|
shared_domain = !private_cdn
|
679
686
|
|
680
687
|
if secure
|
681
|
-
if secure_distribution.nil?
|
688
|
+
if secure_distribution.nil?
|
682
689
|
secure_distribution = private_cdn ? "#{cloud_name}-res.cloudinary.com" : Cloudinary::SHARED_CDN
|
683
690
|
end
|
684
691
|
shared_domain ||= secure_distribution == Cloudinary::SHARED_CDN
|
@@ -705,9 +712,7 @@ class Cloudinary::Utils
|
|
705
712
|
cloud_name = config_option_consume(options, :cloud_name) || raise(CloudinaryException, "Must supply cloud_name in tag or in configuration")
|
706
713
|
|
707
714
|
source = options.delete(:source)
|
708
|
-
secure = options
|
709
|
-
ssl_detected = options.delete(:ssl_detected)
|
710
|
-
secure = ssl_detected || Cloudinary.config.secure if secure.nil?
|
715
|
+
secure = config_option_consume(options, :secure, true)
|
711
716
|
private_cdn = config_option_consume(options, :private_cdn)
|
712
717
|
secure_distribution = config_option_consume(options, :secure_distribution)
|
713
718
|
cname = config_option_consume(options, :cname)
|
@@ -726,8 +731,9 @@ class Cloudinary::Utils
|
|
726
731
|
def self.base_api_url(path, options = {})
|
727
732
|
cloudinary = options[:upload_prefix] || Cloudinary.config.upload_prefix || UPLOAD_PREFIX
|
728
733
|
cloud_name = options[:cloud_name] || Cloudinary.config.cloud_name || raise(CloudinaryException, 'Must supply cloud_name')
|
734
|
+
api_version = options[:api_version] || Cloudinary.config.api_version || 'v1_1'
|
729
735
|
|
730
|
-
[cloudinary,
|
736
|
+
[cloudinary, api_version, cloud_name, path].join('/')
|
731
737
|
end
|
732
738
|
|
733
739
|
def self.cloudinary_api_url(action = 'upload', options = {})
|
@@ -796,14 +802,6 @@ class Cloudinary::Utils
|
|
796
802
|
return Cloudinary::Utils.cloudinary_api_url("download", options) + "?" + hash_query_params(cloudinary_params)
|
797
803
|
end
|
798
804
|
|
799
|
-
# Utility method that uses the deprecated ZIP download API.
|
800
|
-
# @deprecated Replaced by {download_zip_url} that uses the more advanced and robust archive generation and download API
|
801
|
-
def self.zip_download_url(tag, options = {})
|
802
|
-
warn "zip_download_url is deprecated. Please use download_zip_url instead."
|
803
|
-
cloudinary_params = sign_request({:timestamp=>Time.now.to_i, :tag=>tag, :transformation=>generate_transformation_string(options)}, options)
|
804
|
-
return Cloudinary::Utils.cloudinary_api_url("download_tag.zip", options) + "?" + hash_query_params(cloudinary_params)
|
805
|
-
end
|
806
|
-
|
807
805
|
# Returns a URL that when invokes creates an archive and returns it.
|
808
806
|
# @param options [Hash]
|
809
807
|
# @option options [String|Symbol] :resource_type The resource type of files to include in the archive. Must be one of :image | :video | :raw
|
@@ -853,31 +851,6 @@ class Cloudinary::Utils
|
|
853
851
|
download_archive_url(options.merge(:resource_type => resource_type, :prefixes => folder_path))
|
854
852
|
end
|
855
853
|
|
856
|
-
def self.signed_download_url(public_id, options = {})
|
857
|
-
aws_private_key_path = options[:aws_private_key_path] || Cloudinary.config.aws_private_key_path
|
858
|
-
if aws_private_key_path
|
859
|
-
aws_key_pair_id = options[:aws_key_pair_id] || Cloudinary.config.aws_key_pair_id || raise(CloudinaryException, "Must supply aws_key_pair_id")
|
860
|
-
authenticated_distribution = options[:authenticated_distribution] || Cloudinary.config.authenticated_distribution || raise(CloudinaryException, "Must supply authenticated_distribution")
|
861
|
-
@signers ||= Hash.new{|h,k| path, id = k; h[k] = AwsCfSigner.new(path, id)}
|
862
|
-
signer = @signers[[aws_private_key_path, aws_key_pair_id]]
|
863
|
-
url = Cloudinary::Utils.unsigned_download_url(public_id, {:type=>:authenticated}.merge(options).merge(:secure=>true, :secure_distribution=>authenticated_distribution, :private_cdn=>true))
|
864
|
-
expires_at = options[:expires_at] || (Time.now+3600)
|
865
|
-
return signer.sign(url, :ending => expires_at)
|
866
|
-
else
|
867
|
-
return Cloudinary::Utils.unsigned_download_url( public_id, options)
|
868
|
-
end
|
869
|
-
|
870
|
-
end
|
871
|
-
|
872
|
-
def self.cloudinary_url(public_id, options = {})
|
873
|
-
if options[:type].to_s == 'authenticated' && !options[:sign_url]
|
874
|
-
result = signed_download_url(public_id, options)
|
875
|
-
else
|
876
|
-
result = unsigned_download_url(public_id, options)
|
877
|
-
end
|
878
|
-
return result
|
879
|
-
end
|
880
|
-
|
881
854
|
def self.asset_file_name(path)
|
882
855
|
data = Cloudinary.app_root.join(path).read(:mode=>"rb")
|
883
856
|
ext = path.extname
|
@@ -998,6 +971,12 @@ class Cloudinary::Utils
|
|
998
971
|
option_value.nil? ? default_value : option_value
|
999
972
|
end
|
1000
973
|
|
974
|
+
def self.config_option_fetch(options, option_name, default_value = nil)
|
975
|
+
return options.fetch(option_name) if options.include?(option_name)
|
976
|
+
option_value = Cloudinary.config.send(option_name)
|
977
|
+
option_value.nil? ? default_value : option_value
|
978
|
+
end
|
979
|
+
|
1001
980
|
def self.as_bool(value)
|
1002
981
|
case value
|
1003
982
|
when nil then nil
|
@@ -1250,7 +1229,8 @@ class Cloudinary::Utils
|
|
1250
1229
|
# @param options url and transformation options. This argument may be changed by the function!
|
1251
1230
|
#
|
1252
1231
|
def self.patch_fetch_format(options={})
|
1253
|
-
|
1232
|
+
use_fetch_format = config_option_consume(options, :use_fetch_format)
|
1233
|
+
if options[:type] === :fetch || use_fetch_format
|
1254
1234
|
format_arg = options.delete(:format)
|
1255
1235
|
options[:fetch_format] ||= format_arg
|
1256
1236
|
end
|
@@ -1295,6 +1275,31 @@ class Cloudinary::Utils
|
|
1295
1275
|
}
|
1296
1276
|
end
|
1297
1277
|
|
1278
|
+
# Handles file parameter.
|
1279
|
+
#
|
1280
|
+
# @param [Pathname, StringIO, File, String, int, _ToPath] file
|
1281
|
+
# @return [StringIO, File, String] A File object.
|
1282
|
+
#
|
1283
|
+
# @private
|
1284
|
+
def self.handle_file_param(file, options = {})
|
1285
|
+
original_filename = options[:original_filename] || "cloudinaryfile"
|
1286
|
+
content_type = options[:content_type] || "application/octet-stream"
|
1287
|
+
if file.is_a?(Pathname)
|
1288
|
+
return Faraday::FilePart.new(file.to_s, content_type)
|
1289
|
+
elsif file.is_a?(Cloudinary::Blob)
|
1290
|
+
return Faraday::FilePart.new(file, file.content_type, file.original_filename)
|
1291
|
+
elsif file.is_a?(StringIO)
|
1292
|
+
file.rewind
|
1293
|
+
return Faraday::FilePart.new(file, content_type, original_filename)
|
1294
|
+
elsif file.respond_to?(:read)
|
1295
|
+
return Faraday::FilePart.new(file, content_type, original_filename)
|
1296
|
+
elsif Cloudinary::Utils.is_remote?(file)
|
1297
|
+
return file.to_s
|
1298
|
+
end
|
1299
|
+
# we got file path
|
1300
|
+
Faraday::FilePart.new(file.to_s, content_type)
|
1301
|
+
end
|
1302
|
+
|
1298
1303
|
# The returned url should allow downloading the backedup asset based on the version and asset id
|
1299
1304
|
#
|
1300
1305
|
# asset and version id are returned with resource(<PUBLIC_ID1>, { versions: true })
|
data/lib/cloudinary/version.rb
CHANGED
@@ -53,7 +53,7 @@ module CloudinaryHelper
|
|
53
53
|
# }
|
54
54
|
# ])
|
55
55
|
def cl_video_tag(source, options = {}, &block)
|
56
|
-
source = strip_known_ext(source)
|
56
|
+
source = strip_known_ext(source) unless Cloudinary::Utils.config_option_fetch(options, :use_fetch_format)
|
57
57
|
video_attributes = [:autoplay,:controls,:loop,:muted,:poster, :preload]
|
58
58
|
options = Cloudinary::Utils.deep_symbolize_keys(DEFAULT_VIDEO_OPTIONS.merge(options))
|
59
59
|
|
@@ -155,8 +155,9 @@ module CloudinaryHelper
|
|
155
155
|
content_tag('video', tag_options.merge(video_options)) do
|
156
156
|
source_tags = sources.map do |source|
|
157
157
|
type = source[:type]
|
158
|
+
options[:format] = type
|
158
159
|
transformation = source[:transformations] || {}
|
159
|
-
cloudinary_tag(
|
160
|
+
cloudinary_tag(source_name, options.merge(transformation)) do |url, _tag_options|
|
160
161
|
mime_type = "video/#{(type == 'ogv' ? 'ogg' : type)}"
|
161
162
|
if source[:codecs]
|
162
163
|
codecs = source[:codecs].is_a?(Array) ? source[:codecs].join(", ") : source[:codecs]
|
data/lib/cloudinary.rb
CHANGED
@@ -1,10 +1,6 @@
|
|
1
1
|
# Copyright Cloudinary
|
2
|
-
if RUBY_VERSION > "2"
|
3
|
-
require "ostruct"
|
4
|
-
else
|
5
|
-
require "cloudinary/ostruct2"
|
6
|
-
end
|
7
2
|
|
3
|
+
require "ostruct"
|
8
4
|
require "pathname"
|
9
5
|
require "yaml"
|
10
6
|
require "uri"
|
@@ -29,11 +25,9 @@ module Cloudinary
|
|
29
25
|
autoload :CarrierWave, "cloudinary/carrier_wave"
|
30
26
|
autoload :Search, "cloudinary/search"
|
31
27
|
autoload :SearchFolders, "cloudinary/search_folders"
|
28
|
+
autoload :Analytics, "cloudinary/analytics"
|
32
29
|
|
33
|
-
|
34
|
-
AKAMAI_SHARED_CDN = "res.cloudinary.com"
|
35
|
-
OLD_AKAMAI_SHARED_CDN = "cloudinary-a.akamaihd.net"
|
36
|
-
SHARED_CDN = AKAMAI_SHARED_CDN
|
30
|
+
SHARED_CDN = "res.cloudinary.com"
|
37
31
|
|
38
32
|
USER_AGENT = "CloudinaryRuby/#{VERSION} (Ruby #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL})"
|
39
33
|
@@user_platform = defined?(Rails.version) ? "Rails/#{Rails.version}" : ""
|
@@ -1,7 +1,9 @@
|
|
1
1
|
require 'tmpdir'
|
2
|
-
require '
|
2
|
+
require 'faraday'
|
3
|
+
require 'faraday/follow_redirects'
|
3
4
|
require 'json'
|
4
5
|
require 'rubygems/package'
|
6
|
+
require 'stringio'
|
5
7
|
|
6
8
|
unless Rake::Task.task_defined?('cloudinary:fetch_assets') # prevent double-loading/execution
|
7
9
|
namespace :cloudinary do
|
@@ -11,7 +13,7 @@ unless Rake::Task.task_defined?('cloudinary:fetch_assets') # prevent double-load
|
|
11
13
|
processing_files = %w[canvas-to-blob.min.js load-image.all.min.js jquery.fileupload-process.js jquery.fileupload-image.js jquery.fileupload-validate.js]
|
12
14
|
files = index_files + processing_files
|
13
15
|
|
14
|
-
release = JSON(
|
16
|
+
release = JSON(Faraday.get("https://api.github.com/repos/cloudinary/cloudinary_js/releases/latest").body)
|
15
17
|
|
16
18
|
FileUtils.rm_rf 'vendor/assets'
|
17
19
|
html_folder = 'vendor/assets/html'
|
@@ -20,7 +22,11 @@ unless Rake::Task.task_defined?('cloudinary:fetch_assets') # prevent double-load
|
|
20
22
|
FileUtils.mkdir_p js_folder
|
21
23
|
|
22
24
|
puts "Fetching cloudinary_js version #{release["tag_name"]}\n\n"
|
23
|
-
|
25
|
+
conn = Faraday.new do |faraday|
|
26
|
+
faraday.response :follow_redirects
|
27
|
+
faraday.adapter Faraday.default_adapter
|
28
|
+
end
|
29
|
+
sio = StringIO.new(conn.get(release["tarball_url"]).body)
|
24
30
|
file = Zlib::GzipReader.new(sio)
|
25
31
|
tar = Gem::Package::TarReader.new(file)
|
26
32
|
tar.each_entry do |entry|
|
@@ -422,7 +422,7 @@ var slice = [].slice,
|
|
422
422
|
* @returns {boolean} true if item is empty
|
423
423
|
*/
|
424
424
|
isEmpty = function(item) {
|
425
|
-
return (item == null) || (
|
425
|
+
return (item == null) || (Array.isArray(item) || Util.isString(item)) && item.length === 0 || (jQuery.isPlainObject(item) && jQuery.isEmptyObject(item));
|
426
426
|
};
|
427
427
|
|
428
428
|
/**
|
@@ -565,7 +565,7 @@ var slice = [].slice,
|
|
565
565
|
setData: setData,
|
566
566
|
width: width,
|
567
567
|
isString: isString,
|
568
|
-
isArray:
|
568
|
+
isArray: Array.isArray,
|
569
569
|
isEmpty: isEmpty,
|
570
570
|
|
571
571
|
/**
|
@@ -4728,7 +4728,7 @@ var slice = [].slice,
|
|
4728
4728
|
return k + '=' + v;
|
4729
4729
|
}).join('|');
|
4730
4730
|
} else if (Util.isArray(value)) {
|
4731
|
-
if (value.length > 0 &&
|
4731
|
+
if (value.length > 0 && Array.isArray(value[0])) {
|
4732
4732
|
upload_params[key] = jQuery.map(value, function(array_value) {
|
4733
4733
|
return array_value.join(',');
|
4734
4734
|
}).join('|');
|