cloudinary 1.18.0 → 1.21.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/.github/ISSUE_TEMPLATE/bug_report.md +15 -14
- data/.github/ISSUE_TEMPLATE/feature_request.md +1 -1
- data/.github/pull_request_template.md +18 -11
- data/CHANGELOG.md +69 -0
- data/cloudinary.gemspec +8 -3
- data/lib/active_storage/service/cloudinary_service.rb +23 -3
- data/lib/cloudinary.rb +54 -64
- data/lib/cloudinary/account_api.rb +226 -0
- data/lib/cloudinary/account_config.rb +30 -0
- data/lib/cloudinary/api.rb +65 -78
- data/lib/cloudinary/auth_token.rb +4 -0
- data/lib/cloudinary/base_api.rb +79 -0
- data/lib/cloudinary/base_config.rb +70 -0
- data/lib/cloudinary/config.rb +43 -0
- data/lib/cloudinary/search.rb +40 -7
- data/lib/cloudinary/uploader.rb +52 -23
- data/lib/cloudinary/utils.rb +216 -29
- data/lib/cloudinary/version.rb +1 -1
- data/vendor/assets/javascripts/cloudinary/jquery.cloudinary.js +16 -4
- metadata +17 -12
data/lib/cloudinary/search.rb
CHANGED
@@ -1,9 +1,14 @@
|
|
1
1
|
class Cloudinary::Search
|
2
|
+
SORT_BY = :sort_by
|
3
|
+
AGGREGATE = :aggregate
|
4
|
+
WITH_FIELD = :with_field
|
5
|
+
KEYS_WITH_UNIQUE_VALUES = [SORT_BY, AGGREGATE, WITH_FIELD].freeze
|
6
|
+
|
2
7
|
def initialize
|
3
8
|
@query_hash = {
|
4
|
-
|
5
|
-
|
6
|
-
|
9
|
+
SORT_BY => {},
|
10
|
+
AGGREGATE => {},
|
11
|
+
WITH_FIELD => {}
|
7
12
|
}
|
8
13
|
end
|
9
14
|
|
@@ -28,23 +33,51 @@ class Cloudinary::Search
|
|
28
33
|
self
|
29
34
|
end
|
30
35
|
|
36
|
+
# Sets the `sort_by` field.
|
37
|
+
#
|
38
|
+
# @param [String] field_name The field to sort by. You can specify more than one sort_by parameter;
|
39
|
+
# results will be sorted according to the order of the fields provided.
|
40
|
+
# @param [String] dir Sort direction. Valid sort directions are 'asc' or 'desc'. Default: 'desc'.
|
41
|
+
#
|
42
|
+
# @return [Cloudinary::Search]
|
31
43
|
def sort_by(field_name, dir = 'desc')
|
32
|
-
@query_hash[
|
44
|
+
@query_hash[SORT_BY][field_name] = { field_name => dir }
|
33
45
|
self
|
34
46
|
end
|
35
47
|
|
48
|
+
# The name of a field (attribute) for which an aggregation count should be calculated and returned in the response.
|
49
|
+
#
|
50
|
+
# You can specify more than one aggregate parameter.
|
51
|
+
#
|
52
|
+
# @param [String] value Supported values: resource_type, type, pixels (only the image assets in the response are
|
53
|
+
# aggregated), duration (only the video assets in the response are aggregated), format, and
|
54
|
+
# bytes. For aggregation fields without discrete values, the results are divided into
|
55
|
+
# categories.
|
56
|
+
# @return [Cloudinary::Search]
|
36
57
|
def aggregate(value)
|
37
|
-
@query_hash[
|
58
|
+
@query_hash[AGGREGATE][value] = value
|
38
59
|
self
|
39
60
|
end
|
40
61
|
|
62
|
+
# The name of an additional asset attribute to include for each asset in the response.
|
63
|
+
#
|
64
|
+
# @param [String] value Possible value: context, tags, and for Tier 2 also image_metadata, and image_analysis.
|
65
|
+
#
|
66
|
+
# @return [Cloudinary::Search]
|
41
67
|
def with_field(value)
|
42
|
-
@query_hash[
|
68
|
+
@query_hash[WITH_FIELD][value] = value
|
43
69
|
self
|
44
70
|
end
|
45
71
|
|
72
|
+
# Returns the query as an hash.
|
73
|
+
#
|
74
|
+
# @return [Hash]
|
46
75
|
def to_h
|
47
|
-
@query_hash.
|
76
|
+
@query_hash.each_with_object({}) do |(key, value), query|
|
77
|
+
next if value.nil? || ((value.is_a?(Array) || value.is_a?(Hash)) && value.blank?)
|
78
|
+
|
79
|
+
query[key] = KEYS_WITH_UNIQUE_VALUES.include?(key) ? value.values : value
|
80
|
+
end
|
48
81
|
end
|
49
82
|
|
50
83
|
def execute(options = {})
|
data/lib/cloudinary/uploader.rb
CHANGED
@@ -42,6 +42,7 @@ class Cloudinary::Uploader
|
|
42
42
|
:faces => Cloudinary::Utils.as_safe_bool(options[:faces]),
|
43
43
|
:folder => options[:folder],
|
44
44
|
:format => options[:format],
|
45
|
+
:filename_override => options[:filename_override],
|
45
46
|
:headers => build_custom_headers(options[:headers]),
|
46
47
|
:image_metadata => Cloudinary::Utils.as_safe_bool(options[:image_metadata]),
|
47
48
|
:invalidate => Cloudinary::Utils.as_safe_bool(options[:invalidate]),
|
@@ -158,7 +159,9 @@ class Cloudinary::Uploader
|
|
158
159
|
:from_public_id => from_public_id,
|
159
160
|
:to_public_id => to_public_id,
|
160
161
|
:to_type => options[:to_type],
|
161
|
-
:invalidate => Cloudinary::Utils.as_safe_bool(options[:invalidate])
|
162
|
+
:invalidate => Cloudinary::Utils.as_safe_bool(options[:invalidate]),
|
163
|
+
:context => options[:context],
|
164
|
+
:metadata => options[:metadata]
|
162
165
|
}
|
163
166
|
end
|
164
167
|
end
|
@@ -206,17 +209,42 @@ class Cloudinary::Uploader
|
|
206
209
|
end
|
207
210
|
end
|
208
211
|
|
209
|
-
|
212
|
+
SLIDESHOW_PARAMS = [:notification_url, :public_id, :upload_preset]
|
213
|
+
|
214
|
+
# Creates auto-generated video slideshow.
|
215
|
+
#
|
216
|
+
# @param [Hash] options Additional options.
|
217
|
+
#
|
218
|
+
# @return [Hash] Hash with meta information URLs of generated slideshow resources.
|
219
|
+
def self.create_slideshow(options = {})
|
220
|
+
options[:resource_type] ||= :video
|
221
|
+
|
222
|
+
call_api("create_slideshow", options) do
|
223
|
+
params = {
|
224
|
+
:timestamp => Time.now.to_i,
|
225
|
+
:transformation => Cloudinary::Utils.build_eager(options[:transformation]),
|
226
|
+
:manifest_transformation => Cloudinary::Utils.build_eager(options[:manifest_transformation]),
|
227
|
+
:manifest_json => options[:manifest_json] && options[:manifest_json].to_json,
|
228
|
+
:tags => options[:tags] && Cloudinary::Utils.build_array(options[:tags]).join(","),
|
229
|
+
:overwrite => Cloudinary::Utils.as_safe_bool(options[:overwrite])
|
230
|
+
}
|
231
|
+
SLIDESHOW_PARAMS.each { |k| params[k] = options[k] unless options[k].nil? }
|
232
|
+
|
233
|
+
params
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
# Generates sprites by merging multiple images into a single large image.
|
238
|
+
#
|
239
|
+
# @param [String|Hash] tag Treated as additional options when hash is passed, otherwise as a tag
|
240
|
+
# @param [Hash] options Additional options. Should be omitted when +tag_or_options+ is a Hash
|
241
|
+
#
|
242
|
+
# @return [Hash] Hash with meta information URLs of generated sprite resources
|
243
|
+
def self.generate_sprite(tag, options = {})
|
210
244
|
version_store = options.delete(:version_store)
|
211
245
|
|
212
246
|
result = call_api("sprite", options) do
|
213
|
-
|
214
|
-
:timestamp => (options[:timestamp] || Time.now.to_i),
|
215
|
-
:tag => tag,
|
216
|
-
:async => options[:async],
|
217
|
-
:notification_url => options[:notification_url],
|
218
|
-
:transformation => Cloudinary::Utils.generate_transformation_string(options.merge(:fetch_format => options[:format]))
|
219
|
-
}
|
247
|
+
Cloudinary::Utils.build_multi_and_sprite_params(tag, options)
|
220
248
|
end
|
221
249
|
|
222
250
|
if version_store == :file && result && result["version"]
|
@@ -228,16 +256,15 @@ class Cloudinary::Uploader
|
|
228
256
|
return result
|
229
257
|
end
|
230
258
|
|
231
|
-
|
259
|
+
# Creates either a single animated image, video or a PDF.
|
260
|
+
#
|
261
|
+
# @param [String|Hash] tag Treated as additional options when hash is passed, otherwise as a tag
|
262
|
+
# @param [Hash] options Additional options. Should be omitted when +tag_or_options+ is a Hash
|
263
|
+
#
|
264
|
+
# @return [Hash] Hash with meta information URLs of the generated file
|
265
|
+
def self.multi(tag, options = {})
|
232
266
|
call_api("multi", options) do
|
233
|
-
|
234
|
-
:timestamp => (options[:timestamp] || Time.now.to_i),
|
235
|
-
:tag => tag,
|
236
|
-
:format => options[:format],
|
237
|
-
:async => options[:async],
|
238
|
-
:notification_url => options[:notification_url],
|
239
|
-
:transformation => Cloudinary::Utils.generate_transformation_string(options.clone)
|
240
|
-
}
|
267
|
+
Cloudinary::Utils.build_multi_and_sprite_params(tag, options)
|
241
268
|
end
|
242
269
|
end
|
243
270
|
|
@@ -341,11 +368,13 @@ class Cloudinary::Uploader
|
|
341
368
|
non_signable ||= []
|
342
369
|
|
343
370
|
unless options[:unsigned]
|
344
|
-
api_key
|
345
|
-
api_secret
|
346
|
-
|
347
|
-
params[:
|
371
|
+
api_key = options[:api_key] || Cloudinary.config.api_key || raise(CloudinaryException, "Must supply api_key")
|
372
|
+
api_secret = options[:api_secret] || Cloudinary.config.api_secret || raise(CloudinaryException, "Must supply api_secret")
|
373
|
+
signature_algorithm = options[:signature_algorithm]
|
374
|
+
params[:signature] = Cloudinary::Utils.api_sign_request(params.reject { |k, v| non_signable.include?(k) }, api_secret, signature_algorithm)
|
375
|
+
params[:api_key] = api_key
|
348
376
|
end
|
377
|
+
proxy = options[:api_proxy] || Cloudinary.config.api_proxy
|
349
378
|
timeout = options.fetch(:timeout) { Cloudinary.config.to_h.fetch(:timeout, 60) }
|
350
379
|
|
351
380
|
result = nil
|
@@ -355,7 +384,7 @@ class Cloudinary::Uploader
|
|
355
384
|
headers['Content-Range'] = options[:content_range] if options[:content_range]
|
356
385
|
headers['X-Unique-Upload-Id'] = options[:unique_upload_id] if options[:unique_upload_id]
|
357
386
|
headers.merge!(options[:extra_headers]) if options[:extra_headers]
|
358
|
-
RestClient::Request.execute(:method => :post, :url => api_url, :payload => params.reject { |k, v| v.nil? || v=="" }, :timeout => timeout, :headers => headers) do
|
387
|
+
RestClient::Request.execute(:method => :post, :url => api_url, :payload => params.reject { |k, v| v.nil? || v=="" }, :timeout => timeout, :headers => headers, :proxy => proxy) do
|
359
388
|
|response, request, tmpresult|
|
360
389
|
raise CloudinaryException, "Server returned unexpected status code - #{response.code} - #{response.body}" unless [200, 400, 401, 403, 404, 500].include?(response.code)
|
361
390
|
begin
|
data/lib/cloudinary/utils.rb
CHANGED
@@ -13,6 +13,7 @@ require 'cloudinary/responsive'
|
|
13
13
|
class Cloudinary::Utils
|
14
14
|
# @deprecated Use Cloudinary::SHARED_CDN
|
15
15
|
SHARED_CDN = Cloudinary::SHARED_CDN
|
16
|
+
MODE_DOWNLOAD = "download"
|
16
17
|
DEFAULT_RESPONSIVE_WIDTH_TRANSFORMATION = {:width => :auto, :crop => :limit}
|
17
18
|
CONDITIONAL_OPERATORS = {
|
18
19
|
"=" => 'eq',
|
@@ -32,19 +33,34 @@ class Cloudinary::Utils
|
|
32
33
|
|
33
34
|
PREDEFINED_VARS = {
|
34
35
|
"aspect_ratio" => "ar",
|
36
|
+
"aspectRatio" => "ar",
|
35
37
|
"current_page" => "cp",
|
38
|
+
"currentPage" => "cp",
|
36
39
|
"face_count" => "fc",
|
40
|
+
"faceCount" => "fc",
|
37
41
|
"height" => "h",
|
38
42
|
"initial_aspect_ratio" => "iar",
|
43
|
+
"initialAspectRatio" => "iar",
|
44
|
+
"trimmed_aspect_ratio" => "tar",
|
45
|
+
"trimmedAspectRatio" => "tar",
|
39
46
|
"initial_height" => "ih",
|
47
|
+
"initialHeight" => "ih",
|
40
48
|
"initial_width" => "iw",
|
49
|
+
"initialWidth" => "iw",
|
41
50
|
"page_count" => "pc",
|
51
|
+
"pageCount" => "pc",
|
42
52
|
"page_x" => "px",
|
53
|
+
"pageX" => "px",
|
43
54
|
"page_y" => "py",
|
55
|
+
"pageY" => "py",
|
44
56
|
"tags" => "tags",
|
45
57
|
"initial_duration" => "idu",
|
58
|
+
"initialDuration" => "idu",
|
46
59
|
"duration" => "du",
|
47
|
-
"width" => "w"
|
60
|
+
"width" => "w",
|
61
|
+
"illustration_score" => "ils",
|
62
|
+
"illustrationScore" => "ils",
|
63
|
+
"context" => "ctx"
|
48
64
|
}
|
49
65
|
|
50
66
|
SIMPLE_TRANSFORMATION_PARAMS = {
|
@@ -144,6 +160,16 @@ class Cloudinary::Utils
|
|
144
160
|
LONG_URL_SIGNATURE_LENGTH = 32
|
145
161
|
SHORT_URL_SIGNATURE_LENGTH = 8
|
146
162
|
|
163
|
+
UPLOAD_PREFIX = 'https://api.cloudinary.com'
|
164
|
+
|
165
|
+
ALGO_SHA1 = :sha1
|
166
|
+
ALGO_SHA256 = :sha256
|
167
|
+
|
168
|
+
ALGORITHM_SIGNATURE = {
|
169
|
+
ALGO_SHA1 => Digest::SHA1,
|
170
|
+
ALGO_SHA256 => Digest::SHA256,
|
171
|
+
}
|
172
|
+
|
147
173
|
def self.extract_config_params(options)
|
148
174
|
options.select{|k,v| URL_KEYS.include?(k)}
|
149
175
|
end
|
@@ -309,16 +335,16 @@ class Cloudinary::Utils
|
|
309
335
|
"if_" + normalize_expression(if_value) unless if_value.to_s.empty?
|
310
336
|
end
|
311
337
|
|
312
|
-
EXP_REGEXP = Regexp.new('(
|
338
|
+
EXP_REGEXP = Regexp.new('(\$_*[^_ ]+)|(?<![\$:])('+PREDEFINED_VARS.keys.join("|")+')'+'|('+CONDITIONAL_OPERATORS.keys.reverse.map { |k| Regexp.escape(k) }.join('|')+')(?=[ _])')
|
313
339
|
EXP_REPLACEMENT = PREDEFINED_VARS.merge(CONDITIONAL_OPERATORS)
|
314
340
|
|
315
341
|
def self.normalize_expression(expression)
|
316
342
|
if expression.nil?
|
317
|
-
|
343
|
+
nil
|
318
344
|
elsif expression.is_a?( String) && expression =~ /^!.+!$/ # quoted string
|
319
345
|
expression
|
320
346
|
else
|
321
|
-
expression.to_s.gsub(EXP_REGEXP
|
347
|
+
expression.to_s.gsub(EXP_REGEXP) { |match| EXP_REPLACEMENT[match] || match }.gsub(/[ _]+/, "_")
|
322
348
|
end
|
323
349
|
end
|
324
350
|
|
@@ -405,6 +431,8 @@ class Cloudinary::Utils
|
|
405
431
|
]
|
406
432
|
|
407
433
|
def self.text_style(layer)
|
434
|
+
return layer[:text_style] if layer[:text_style].present?
|
435
|
+
|
408
436
|
font_family = layer[:font_family]
|
409
437
|
font_size = layer[:font_size]
|
410
438
|
keywords = []
|
@@ -433,9 +461,9 @@ class Cloudinary::Utils
|
|
433
461
|
params_to_sign.map{|k,v| [k.to_s, v.is_a?(Array) ? v.join(",") : v]}.reject{|k,v| v.nil? || v == ""}.sort_by(&:first).map{|k,v| "#{k}=#{v}"}.join("&")
|
434
462
|
end
|
435
463
|
|
436
|
-
def self.api_sign_request(params_to_sign, api_secret)
|
464
|
+
def self.api_sign_request(params_to_sign, api_secret, signature_algorithm = nil)
|
437
465
|
to_sign = api_string_to_sign(params_to_sign)
|
438
|
-
|
466
|
+
hash("#{to_sign}#{api_secret}", signature_algorithm, :hexdigest)
|
439
467
|
end
|
440
468
|
|
441
469
|
# Returns a JSON array as String.
|
@@ -501,6 +529,7 @@ class Cloudinary::Utils
|
|
501
529
|
use_root_path = config_option_consume(options, :use_root_path)
|
502
530
|
auth_token = config_option_consume(options, :auth_token)
|
503
531
|
long_url_signature = config_option_consume(options, :long_url_signature)
|
532
|
+
signature_algorithm = config_option_consume(options, :signature_algorithm)
|
504
533
|
unless auth_token == false
|
505
534
|
auth_token = Cloudinary::AuthToken.merge_auth_token(Cloudinary.config.auth_token, auth_token)
|
506
535
|
end
|
@@ -545,7 +574,10 @@ class Cloudinary::Utils
|
|
545
574
|
raise(CloudinaryException, "Must supply api_secret") if (secret.nil? || secret.empty?)
|
546
575
|
to_sign = [transformation, sign_version && version, source_to_sign].reject(&:blank?).join("/")
|
547
576
|
to_sign = fully_unescape(to_sign)
|
548
|
-
|
577
|
+
signature_algorithm = long_url_signature ? ALGO_SHA256 : signature_algorithm
|
578
|
+
hash = hash("#{to_sign}#{secret}", signature_algorithm)
|
579
|
+
signature = Base64.urlsafe_encode64(hash)
|
580
|
+
signature = "s--#{signature[0, long_url_signature ? LONG_URL_SIGNATURE_LENGTH : SHORT_URL_SIGNATURE_LENGTH ]}--"
|
549
581
|
end
|
550
582
|
|
551
583
|
prefix = unsigned_download_url_prefix(source, cloud_name, private_cdn, cdn_subdomain, secure_cdn_subdomain, cname, secure, secure_distribution)
|
@@ -661,22 +693,72 @@ class Cloudinary::Utils
|
|
661
693
|
prefix
|
662
694
|
end
|
663
695
|
|
696
|
+
# Creates a base URL for the cloudinary api
|
697
|
+
#
|
698
|
+
# @param [Object] path Resource name
|
699
|
+
# @param [Hash] options Additional options
|
700
|
+
#
|
701
|
+
# @return [String]
|
702
|
+
def self.base_api_url(path, options = {})
|
703
|
+
cloudinary = options[:upload_prefix] || Cloudinary.config.upload_prefix || UPLOAD_PREFIX
|
704
|
+
cloud_name = options[:cloud_name] || Cloudinary.config.cloud_name || raise(CloudinaryException, 'Must supply cloud_name')
|
705
|
+
|
706
|
+
[cloudinary, 'v1_1', cloud_name, path].join('/')
|
707
|
+
end
|
708
|
+
|
664
709
|
def self.cloudinary_api_url(action = 'upload', options = {})
|
665
|
-
|
666
|
-
|
667
|
-
resource_type
|
668
|
-
return [cloudinary, "v1_1", cloud_name, resource_type, action].join("/")
|
710
|
+
resource_type = options[:resource_type] || 'image'
|
711
|
+
|
712
|
+
base_api_url([resource_type, action], options)
|
669
713
|
end
|
670
714
|
|
671
715
|
def self.sign_request(params, options={})
|
672
716
|
api_key = options[:api_key] || Cloudinary.config.api_key || raise(CloudinaryException, "Must supply api_key")
|
673
717
|
api_secret = options[:api_secret] || Cloudinary.config.api_secret || raise(CloudinaryException, "Must supply api_secret")
|
718
|
+
signature_algorithm = options[:signature_algorithm]
|
674
719
|
params = params.reject{|k, v| self.safe_blank?(v)}
|
675
|
-
params[:signature] =
|
720
|
+
params[:signature] = api_sign_request(params, api_secret, signature_algorithm)
|
676
721
|
params[:api_key] = api_key
|
677
722
|
params
|
678
723
|
end
|
679
724
|
|
725
|
+
# Helper method for generating download URLs
|
726
|
+
#
|
727
|
+
# @param [String] action @see Cloudinary::Utils.cloudinary_api_url
|
728
|
+
# @param [Hash] params Query parameters in generated URL
|
729
|
+
# @param [Hash] options Additional options
|
730
|
+
# @yield [query_parameters] Invokes the block with query parameters to override how to encode them
|
731
|
+
#
|
732
|
+
# @return [String]
|
733
|
+
def self.cloudinary_api_download_url(action, params, options = {})
|
734
|
+
cloudinary_params = sign_request(params.merge(mode: MODE_DOWNLOAD), options)
|
735
|
+
|
736
|
+
"#{Cloudinary::Utils.cloudinary_api_url(action, options)}?#{hash_query_params(cloudinary_params)}"
|
737
|
+
end
|
738
|
+
private_class_method :cloudinary_api_download_url
|
739
|
+
|
740
|
+
# Return a signed URL to the 'generate_sprite' endpoint with 'mode=download'.
|
741
|
+
#
|
742
|
+
# @param [String|Hash] tag Treated as additional options when hash is passed, otherwise as a tag
|
743
|
+
# @param [Hash] options Additional options. Should be omitted when +tag_or_options+ is a Hash
|
744
|
+
#
|
745
|
+
# @return [String] The signed URL to download sprite
|
746
|
+
def self.download_generated_sprite(tag, options = {})
|
747
|
+
params = build_multi_and_sprite_params(tag, options)
|
748
|
+
cloudinary_api_download_url("sprite", params, options)
|
749
|
+
end
|
750
|
+
|
751
|
+
# Return a signed URL to the 'multi' endpoint with 'mode=download'.
|
752
|
+
#
|
753
|
+
# @param [String|Hash] tag Treated as additional options when hash is passed, otherwise as a tag
|
754
|
+
# @param [Hash] options Additional options. Should be omitted when +tag_or_options+ is a Hash
|
755
|
+
#
|
756
|
+
# @return [String] The signed URL to download multi
|
757
|
+
def self.download_multi(tag, options = {})
|
758
|
+
params = build_multi_and_sprite_params(tag, options)
|
759
|
+
cloudinary_api_download_url("multi", params, options)
|
760
|
+
end
|
761
|
+
|
680
762
|
def self.private_download_url(public_id, format, options = {})
|
681
763
|
cloudinary_params = sign_request({
|
682
764
|
:timestamp=>Time.now.to_i,
|
@@ -725,11 +807,10 @@ class Cloudinary::Utils
|
|
725
807
|
# @option options [String] :keep_derived (false) keep the derived images used for generating the archive
|
726
808
|
# @return [String] archive url
|
727
809
|
def self.download_archive_url(options = {})
|
728
|
-
|
729
|
-
|
810
|
+
params = Cloudinary::Utils.archive_params(options)
|
811
|
+
cloudinary_api_download_url("generate_archive", params, options)
|
730
812
|
end
|
731
813
|
|
732
|
-
|
733
814
|
# Returns a URL that when invokes creates an zip archive and returns it.
|
734
815
|
# @see download_archive_url
|
735
816
|
def self.download_zip_url(options = {})
|
@@ -1149,23 +1230,129 @@ class Cloudinary::Utils
|
|
1149
1230
|
REMOTE_URL_REGEX === url
|
1150
1231
|
end
|
1151
1232
|
|
1152
|
-
#
|
1153
|
-
#
|
1154
|
-
# @param [String]
|
1155
|
-
# @param [
|
1156
|
-
#
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
1233
|
+
# Build params for multi, download_multi, generate_sprite, and download_generated_sprite methods
|
1234
|
+
#
|
1235
|
+
# @param [String|Hash] tag_or_options Treated as additional options when hash is passed, otherwise as a tag
|
1236
|
+
# @param [Hash] options Additional options. Should be omitted when +tag_or_options+ is a Hash
|
1237
|
+
#
|
1238
|
+
# @return [Hash]
|
1239
|
+
#
|
1240
|
+
# @private
|
1241
|
+
def self.build_multi_and_sprite_params(tag_or_options, options)
|
1242
|
+
if tag_or_options.is_a?(Hash)
|
1243
|
+
if options.blank?
|
1244
|
+
options = tag_or_options
|
1245
|
+
tag_or_options = nil
|
1163
1246
|
else
|
1164
|
-
|
1247
|
+
raise "First argument must be a tag when additional options are passed"
|
1165
1248
|
end
|
1249
|
+
end
|
1250
|
+
urls = options.delete(:urls)
|
1166
1251
|
|
1167
|
-
|
1252
|
+
if tag_or_options.blank? && urls.blank?
|
1253
|
+
raise "Either tag or urls are required"
|
1254
|
+
end
|
1255
|
+
|
1256
|
+
{
|
1257
|
+
:tag => tag_or_options,
|
1258
|
+
:urls => urls,
|
1259
|
+
:transformation => Cloudinary::Utils.generate_transformation_string(options.merge(:fetch_format => options[:format])),
|
1260
|
+
:notification_url => options[:notification_url],
|
1261
|
+
:format => options[:format],
|
1262
|
+
:async => options[:async],
|
1263
|
+
:mode => options[:mode],
|
1264
|
+
:timestamp => (options[:timestamp] || Time.now.to_i)
|
1265
|
+
}
|
1266
|
+
end
|
1267
|
+
|
1268
|
+
# The returned url should allow downloading the backedup asset based on the version and asset id
|
1269
|
+
#
|
1270
|
+
# asset and version id are returned with resource(<PUBLIC_ID1>, { versions: true })
|
1271
|
+
#
|
1272
|
+
# @param [String] asset_id Asset identifier
|
1273
|
+
# @param [String] version_id Specific version of asset to download
|
1274
|
+
# @param [Hash] options Additional options
|
1275
|
+
#
|
1276
|
+
# @return [String] An url for downloading a file
|
1277
|
+
def self.download_backedup_asset(asset_id, version_id, options = {})
|
1278
|
+
params = Cloudinary::Utils.sign_request({
|
1279
|
+
:timestamp => (options[:timestamp] || Time.now.to_i),
|
1280
|
+
:asset_id => asset_id,
|
1281
|
+
:version_id => version_id
|
1282
|
+
}, options)
|
1283
|
+
|
1284
|
+
"#{Cloudinary::Utils.base_api_url("download_backup", options)}?#{Cloudinary::Utils.hash_query_params((params))}"
|
1285
|
+
end
|
1286
|
+
|
1287
|
+
# Format date in a format accepted by the usage API (e.g., 31-12-2020) if
|
1288
|
+
# passed value is of type Date, otherwise return the string representation of
|
1289
|
+
# the input.
|
1290
|
+
#
|
1291
|
+
# @param [Date|Object] date
|
1292
|
+
# @return [String]
|
1293
|
+
def self.to_usage_api_date_format(date)
|
1294
|
+
if date.is_a?(Date)
|
1295
|
+
date.strftime('%d-%m-%Y')
|
1296
|
+
else
|
1297
|
+
date.to_s
|
1298
|
+
end
|
1299
|
+
end
|
1300
|
+
|
1301
|
+
# Verifies the authenticity of an API response signature.
|
1302
|
+
#
|
1303
|
+
# @param [String] public_id he public id of the asset as returned in the API response
|
1304
|
+
# @param [Fixnum] version The version of the asset as returned in the API response
|
1305
|
+
# @param [String] signature Actual signature. Can be retrieved from the X-Cld-Signature header
|
1306
|
+
# @param [Symbol|nil] signature_algorithm Algorithm to use for computing hash
|
1307
|
+
# @param [Hash] options
|
1308
|
+
# @option options [String] :api_secret API secret, if not passed taken from global config
|
1309
|
+
#
|
1310
|
+
# @return [Boolean]
|
1311
|
+
def self.verify_api_response_signature(public_id, version, signature, signature_algorithm = nil, options = {})
|
1312
|
+
api_secret = options[:api_secret] || Cloudinary.config.api_secret || raise("Must supply api_secret")
|
1313
|
+
|
1314
|
+
parameters_to_sign = {
|
1315
|
+
:public_id => public_id,
|
1316
|
+
:version => version
|
1317
|
+
}
|
1318
|
+
|
1319
|
+
signature == api_sign_request(parameters_to_sign, api_secret, signature_algorithm)
|
1320
|
+
end
|
1321
|
+
|
1322
|
+
# Verifies the authenticity of a notification signature.
|
1323
|
+
#
|
1324
|
+
# @param [String] body JSON of the request's body
|
1325
|
+
# @param [Fixnum] timestamp Unix timestamp. Can be retrieved from the X-Cld-Timestamp header
|
1326
|
+
# @param [String] signature Actual signature. Can be retrieved from the X-Cld-Signature header
|
1327
|
+
# @param [Fixnum] valid_for The desired time in seconds for considering the request valid
|
1328
|
+
# @param [Symbol|nil] signature_algorithm Algorithm to use for computing hash
|
1329
|
+
# @param [Hash] options
|
1330
|
+
# @option options [String] :api_secret API secret, if not passed taken from global config
|
1331
|
+
#
|
1332
|
+
# @return [Boolean]
|
1333
|
+
def self.verify_notification_signature(body, timestamp, signature, valid_for = 7200, signature_algorithm = nil, options = {})
|
1334
|
+
api_secret = options[:api_secret] || Cloudinary.config.api_secret || raise("Must supply api_secret")
|
1335
|
+
raise("Body should be of String type") unless body.is_a?(String)
|
1336
|
+
# verify that signature is valid for the given timestamp
|
1337
|
+
return false if timestamp < (Time.now - valid_for).to_i
|
1338
|
+
|
1339
|
+
payload_hash = hash("#{body}#{timestamp}#{api_secret}", signature_algorithm, :hexdigest)
|
1340
|
+
|
1341
|
+
signature == payload_hash
|
1342
|
+
end
|
1343
|
+
|
1344
|
+
# Computes hash from input string using specified algorithm.
|
1345
|
+
#
|
1346
|
+
# @param [String] input String which to compute hash from
|
1347
|
+
# @param [Symbol|nil] signature_algorithm Algorithm to use for computing hash
|
1348
|
+
# @param [Symbol] hash_method Hash method applied to a signature algorithm (:digest or :hexdigest)
|
1349
|
+
#
|
1350
|
+
# @return [String] Computed hash value
|
1351
|
+
def self.hash(input, signature_algorithm = nil, hash_method = :digest)
|
1352
|
+
signature_algorithm ||= Cloudinary.config.signature_algorithm || ALGO_SHA1
|
1353
|
+
algorithm = ALGORITHM_SIGNATURE[signature_algorithm] || raise("Unsupported algorithm '#{signature_algorithm}'")
|
1354
|
+
algorithm.public_send(hash_method, input)
|
1168
1355
|
end
|
1169
1356
|
|
1170
|
-
private_class_method :
|
1357
|
+
private_class_method :hash
|
1171
1358
|
end
|