spree_core 5.4.0.beta7 → 5.4.0.beta8
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/app/helpers/spree/base_helper.rb +5 -7
- data/app/mailers/spree/base_mailer.rb +1 -1
- data/app/models/concerns/spree/user_methods.rb +15 -0
- data/app/models/spree/allowed_origin.rb +44 -0
- data/app/models/spree/asset.rb +121 -3
- data/app/models/spree/image/configuration/active_storage.rb +2 -14
- data/app/models/spree/image.rb +2 -78
- data/app/models/spree/legacy_user.rb +1 -0
- data/app/models/spree/line_item.rb +3 -3
- data/app/models/spree/product.rb +45 -34
- data/app/models/spree/store.rb +31 -0
- data/app/models/spree/variant.rb +21 -12
- data/app/services/spree/seeds/all.rb +1 -0
- data/app/services/spree/seeds/allowed_origins.rb +14 -0
- data/app/views/spree/shared/_mailer_logo.html.erb +1 -1
- data/config/locales/en.yml +16 -0
- data/db/migrate/20260315000000_create_spree_allowed_origins.rb +14 -0
- data/db/migrate/20260315100000_add_product_media_support.rb +21 -0
- data/lib/spree/core/configuration.rb +3 -0
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/permitted_attributes.rb +5 -1
- data/lib/spree/testing_support/factories/allowed_origin_factory.rb +8 -0
- data/lib/spree/testing_support/factories/asset_factory.rb +6 -9
- data/lib/spree/testing_support/factories/image_factory.rb +3 -1
- data/lib/tasks/images.rake +11 -11
- data/lib/tasks/products.rake +4 -2
- metadata +9 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d8297e501dfb71ba02dbd003c3f2a6fa035585a07cca4cf3f7e039eb92bb55c6
|
|
4
|
+
data.tar.gz: 87546d3659afc8573cf33cd9b3b2ceac6e351f7f8e30969626991c7c39da8773
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8983ae8234112c480dc54e302bd7d56557b11418af19a0ca6d65aa182ed06195c828ae69dc122209c23057149febb28b4c9e410a460e9dba2b09978240788997
|
|
7
|
+
data.tar.gz: e62cdc311fe8a2a256442b38e84df024e5c145ef47dc3c2cf5981e923bf5e3558c3b6937c3a64d101eb0783f5c493fdfbc3e75db004740d45599fa9d8ddfc80a
|
|
@@ -114,10 +114,8 @@ module Spree
|
|
|
114
114
|
|
|
115
115
|
base_url = if options[:relative]
|
|
116
116
|
''
|
|
117
|
-
elsif store.respond_to?(:formatted_custom_domain) && store.formatted_custom_domain.present?
|
|
118
|
-
store.formatted_custom_domain
|
|
119
117
|
else
|
|
120
|
-
store.
|
|
118
|
+
store.storefront_url
|
|
121
119
|
end
|
|
122
120
|
|
|
123
121
|
localize = if options[:locale].present?
|
|
@@ -151,15 +149,15 @@ module Spree
|
|
|
151
149
|
# we should always try to render image of the default variant
|
|
152
150
|
# same as it's done on PDP
|
|
153
151
|
def default_image_for_product(product)
|
|
154
|
-
Spree::Deprecation.warn('BaseHelper#default_image_for_product is deprecated and will be removed in Spree
|
|
152
|
+
Spree::Deprecation.warn('BaseHelper#default_image_for_product is deprecated and will be removed in Spree 6.0. Please use product.primary_media instead')
|
|
155
153
|
|
|
156
|
-
product.
|
|
154
|
+
product.primary_media
|
|
157
155
|
end
|
|
158
156
|
|
|
159
157
|
def default_image_for_product_or_variant(product_or_variant)
|
|
160
|
-
Spree::Deprecation.warn('BaseHelper#default_image_for_product_or_variant is deprecated and will be removed in Spree
|
|
158
|
+
Spree::Deprecation.warn('BaseHelper#default_image_for_product_or_variant is deprecated and will be removed in Spree 6.0. Please use product_or_variant.primary_media instead')
|
|
161
159
|
|
|
162
|
-
product_or_variant.
|
|
160
|
+
product_or_variant.primary_media
|
|
163
161
|
end
|
|
164
162
|
|
|
165
163
|
def base_cache_key
|
|
@@ -37,7 +37,7 @@ module Spree
|
|
|
37
37
|
# this is only a fail-safe solution if developer didn't set this in environment files
|
|
38
38
|
# http://guides.rubyonrails.org/action_mailer_basics.html#generating-urls-in-action-mailer-views
|
|
39
39
|
def ensure_default_action_mailer_url_host(store_url = nil)
|
|
40
|
-
host_url = store_url.presence || current_store.try(:
|
|
40
|
+
host_url = store_url.presence || current_store.try(:storefront_url)
|
|
41
41
|
|
|
42
42
|
return if host_url.blank?
|
|
43
43
|
|
|
@@ -18,6 +18,13 @@ module Spree
|
|
|
18
18
|
# Enable lifecycle events for user models
|
|
19
19
|
publishes_lifecycle_events
|
|
20
20
|
|
|
21
|
+
# Password reset token (Rails 7.1+ signed token, no DB column needed)
|
|
22
|
+
# Token auto-invalidates when password changes (salt changes)
|
|
23
|
+
# Expiration is configurable via Spree::Config.customer_password_reset_expires_in (in minutes)
|
|
24
|
+
generates_token_for :password_reset, expires_in: Spree::Config.customer_password_reset_expires_in.minutes do
|
|
25
|
+
password_salt&.last(10) || encrypted_password&.last(10)
|
|
26
|
+
end
|
|
27
|
+
|
|
21
28
|
# we need to have this callback before any dependent: :destroy associations
|
|
22
29
|
# https://github.com/rails/rails/issues/3458
|
|
23
30
|
before_validation :clone_billing_address, if: :use_billing?
|
|
@@ -59,6 +66,14 @@ module Spree
|
|
|
59
66
|
#
|
|
60
67
|
attr_accessor :confirm_email, :terms_of_service
|
|
61
68
|
|
|
69
|
+
def self.find_by_password_reset_token(token)
|
|
70
|
+
find_by_token_for(:password_reset, token)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def self.find_by_password_reset_token!(token)
|
|
74
|
+
find_by_token_for!(:password_reset, token)
|
|
75
|
+
end
|
|
76
|
+
|
|
62
77
|
def self.multi_search(query)
|
|
63
78
|
sanitized_query = sanitize_query_for_multi_search(query)
|
|
64
79
|
return none if query.blank?
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spree
|
|
4
|
+
class AllowedOrigin < Spree.base_class
|
|
5
|
+
has_prefix_id :ao
|
|
6
|
+
|
|
7
|
+
include Spree::SingleStoreResource
|
|
8
|
+
|
|
9
|
+
belongs_to :store, class_name: 'Spree::Store'
|
|
10
|
+
|
|
11
|
+
validates :store, :origin, presence: true
|
|
12
|
+
validates :origin, uniqueness: { scope: [:store_id, *spree_base_uniqueness_scope] }
|
|
13
|
+
validate :origin_must_be_valid_http_url
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def origin_must_be_valid_http_url
|
|
18
|
+
return if origin.blank?
|
|
19
|
+
|
|
20
|
+
uri = URI.parse(origin)
|
|
21
|
+
|
|
22
|
+
unless uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)
|
|
23
|
+
errors.add(:origin, :invalid)
|
|
24
|
+
return
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
if uri.host.blank?
|
|
28
|
+
errors.add(:origin, :invalid)
|
|
29
|
+
return
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Origins must not have a path, query, or fragment
|
|
33
|
+
if uri.path.present? && uri.path != '/'
|
|
34
|
+
errors.add(:origin, :must_be_origin_only)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
if uri.query.present? || uri.fragment.present?
|
|
38
|
+
errors.add(:origin, :must_be_origin_only)
|
|
39
|
+
end
|
|
40
|
+
rescue URI::InvalidURIError
|
|
41
|
+
errors.add(:origin, :invalid)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
data/app/models/spree/asset.rb
CHANGED
|
@@ -1,20 +1,42 @@
|
|
|
1
1
|
module Spree
|
|
2
2
|
class Asset < Spree.base_class
|
|
3
|
-
has_prefix_id :
|
|
3
|
+
has_prefix_id :media
|
|
4
4
|
|
|
5
5
|
include Support::ActiveStorage
|
|
6
|
+
include Rails.application.routes.url_helpers
|
|
7
|
+
include Spree::ImageMethods # legacy, will be removed in Spree 6
|
|
6
8
|
include Spree::Metafields
|
|
7
9
|
include Spree::Metadata
|
|
8
10
|
|
|
11
|
+
# Legacy styles support (was in Spree::Image::Configuration::ActiveStorage)
|
|
12
|
+
# @deprecated Will be removed in Spree 6
|
|
13
|
+
def self.styles
|
|
14
|
+
@styles ||= Spree::Config.product_image_variant_sizes.transform_values do |dimensions|
|
|
15
|
+
"#{dimensions[0]}x#{dimensions[1]}>"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def default_style
|
|
20
|
+
:small
|
|
21
|
+
end
|
|
22
|
+
|
|
9
23
|
publishes_lifecycle_events
|
|
10
24
|
|
|
11
25
|
EXTERNAL_URL_METAFIELD_KEY = 'external.url'
|
|
26
|
+
MEDIA_TYPES = %w[image video external_video].freeze
|
|
27
|
+
|
|
28
|
+
after_initialize { self.media_type ||= 'image' }
|
|
12
29
|
|
|
13
30
|
belongs_to :viewable, polymorphic: true, touch: true
|
|
14
31
|
acts_as_list scope: [:viewable_id, :viewable_type]
|
|
15
32
|
|
|
16
33
|
delegate :key, :attached?, :variant, :variable?, :blob, :filename, :variation, to: :attachment
|
|
17
34
|
|
|
35
|
+
validates :media_type, inclusion: { in: MEDIA_TYPES }
|
|
36
|
+
validates :attachment, attached: true, content_type: Rails.application.config.active_storage.web_image_content_types,
|
|
37
|
+
if: -> { media_type == 'image' }
|
|
38
|
+
validates :external_video_url, presence: true, if: -> { media_type.in?(%w[video external_video]) }
|
|
39
|
+
|
|
18
40
|
WEBP_SAVER_OPTIONS = {
|
|
19
41
|
strip: true,
|
|
20
42
|
quality: 75,
|
|
@@ -43,14 +65,46 @@ module Spree
|
|
|
43
65
|
|
|
44
66
|
default_scope { includes(attachment_attachment: :blob) }
|
|
45
67
|
|
|
68
|
+
# STI was disabled in Spree::Image, keep it disabled here
|
|
69
|
+
self.inheritance_column = nil
|
|
70
|
+
|
|
46
71
|
store_accessor :private_metadata, :session_uploaded_assets_uuid
|
|
47
72
|
scope :with_session_uploaded_assets_uuid, lambda { |uuid|
|
|
48
73
|
where(session_id: uuid)
|
|
49
74
|
}
|
|
50
75
|
scope :with_external_url, ->(url) { url.present? ? with_metafield_key_value(EXTERNAL_URL_METAFIELD_KEY, url.strip) : none }
|
|
51
76
|
|
|
77
|
+
# Callbacks merged from Spree::Image
|
|
78
|
+
after_commit :touch_product_variants, if: :should_touch_product_variants?, on: :update
|
|
79
|
+
after_commit :update_viewable_thumbnail_on_create, on: :create
|
|
80
|
+
after_commit :update_viewable_thumbnail_on_destroy, on: :destroy
|
|
81
|
+
after_commit :update_viewable_thumbnail_on_reorder, on: :update, if: :saved_change_to_position?
|
|
82
|
+
after_commit :update_viewable_thumbnail_on_viewable_change, on: :update, if: :saved_change_to_viewable_id?
|
|
83
|
+
|
|
84
|
+
after_create :increment_viewable_media_count
|
|
85
|
+
after_destroy :decrement_viewable_media_count
|
|
86
|
+
|
|
52
87
|
def product
|
|
53
|
-
@product ||= viewable_type
|
|
88
|
+
@product ||= case viewable_type
|
|
89
|
+
when 'Spree::Variant' then viewable&.product
|
|
90
|
+
when 'Spree::Product' then viewable
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def focal_point
|
|
95
|
+
return nil if focal_point_x.nil? || focal_point_y.nil?
|
|
96
|
+
|
|
97
|
+
{ x: focal_point_x, y: focal_point_y }
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def focal_point=(point)
|
|
101
|
+
if point.nil?
|
|
102
|
+
self.focal_point_x = nil
|
|
103
|
+
self.focal_point_y = nil
|
|
104
|
+
else
|
|
105
|
+
self.focal_point_x = point[:x]
|
|
106
|
+
self.focal_point_y = point[:y]
|
|
107
|
+
end
|
|
54
108
|
end
|
|
55
109
|
|
|
56
110
|
def external_url
|
|
@@ -66,7 +120,71 @@ module Spree
|
|
|
66
120
|
end
|
|
67
121
|
|
|
68
122
|
def event_prefix
|
|
69
|
-
'
|
|
123
|
+
'media'
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# @deprecated
|
|
127
|
+
def styles
|
|
128
|
+
Spree::Deprecation.warn("Asset#styles is deprecated and will be removed in Spree 6.0. Please use active storage variants with cdn_image_url")
|
|
129
|
+
|
|
130
|
+
self.class.styles.map do |_, size|
|
|
131
|
+
width, height = size.chop.split('x').map(&:to_i)
|
|
132
|
+
|
|
133
|
+
{
|
|
134
|
+
url: generate_url(size: size),
|
|
135
|
+
size: size,
|
|
136
|
+
width: width,
|
|
137
|
+
height: height
|
|
138
|
+
}
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
private
|
|
143
|
+
|
|
144
|
+
def touch_product_variants
|
|
145
|
+
viewable.product.variants.touch_all
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def should_touch_product_variants?
|
|
149
|
+
viewable.is_a?(Spree::Variant) &&
|
|
150
|
+
viewable.is_master? &&
|
|
151
|
+
viewable.product.has_variants? &&
|
|
152
|
+
saved_change_to_position?
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def increment_viewable_media_count
|
|
156
|
+
case viewable_type
|
|
157
|
+
when 'Spree::Variant'
|
|
158
|
+
Spree::Variant.increment_counter(:media_count, viewable_id)
|
|
159
|
+
Spree::Product.increment_counter(:media_count, viewable.product_id)
|
|
160
|
+
when 'Spree::Product'
|
|
161
|
+
Spree::Product.increment_counter(:media_count, viewable_id)
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def decrement_viewable_media_count
|
|
166
|
+
case viewable_type
|
|
167
|
+
when 'Spree::Variant'
|
|
168
|
+
Spree::Variant.decrement_counter(:media_count, viewable_id)
|
|
169
|
+
Spree::Product.decrement_counter(:media_count, viewable.product_id)
|
|
170
|
+
when 'Spree::Product'
|
|
171
|
+
Spree::Product.decrement_counter(:media_count, viewable_id)
|
|
172
|
+
end
|
|
70
173
|
end
|
|
174
|
+
|
|
175
|
+
def update_viewable_thumbnail
|
|
176
|
+
case viewable_type
|
|
177
|
+
when 'Spree::Variant'
|
|
178
|
+
viewable.update_thumbnail!
|
|
179
|
+
viewable.product.update_thumbnail!
|
|
180
|
+
when 'Spree::Product'
|
|
181
|
+
viewable.update_thumbnail!
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
alias update_viewable_thumbnail_on_create update_viewable_thumbnail
|
|
186
|
+
alias update_viewable_thumbnail_on_destroy update_viewable_thumbnail
|
|
187
|
+
alias update_viewable_thumbnail_on_reorder update_viewable_thumbnail
|
|
188
|
+
alias update_viewable_thumbnail_on_viewable_change update_viewable_thumbnail
|
|
71
189
|
end
|
|
72
190
|
end
|
|
@@ -1,22 +1,10 @@
|
|
|
1
|
+
# @deprecated This module is now a no-op. All logic has been moved to Spree::Asset.
|
|
2
|
+
# Will be removed in Spree 6.0.
|
|
1
3
|
module Spree
|
|
2
4
|
class Image < Asset
|
|
3
5
|
module Configuration
|
|
4
6
|
module ActiveStorage
|
|
5
7
|
extend ActiveSupport::Concern
|
|
6
|
-
|
|
7
|
-
included do
|
|
8
|
-
# Returns image styles derived from Spree::Config.product_image_variant_sizes
|
|
9
|
-
# Format: { variant_name: 'WxH>' } for API compatibility
|
|
10
|
-
def self.styles
|
|
11
|
-
@styles ||= Spree::Config.product_image_variant_sizes.transform_values do |dimensions|
|
|
12
|
-
"#{dimensions[0]}x#{dimensions[1]}>"
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def default_style
|
|
17
|
-
:small
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
8
|
end
|
|
21
9
|
end
|
|
22
10
|
end
|
data/app/models/spree/image.rb
CHANGED
|
@@ -1,82 +1,6 @@
|
|
|
1
|
+
# Backward compatibility — all logic now lives in Spree::Asset.
|
|
2
|
+
# This class will be removed in Spree 6.0.
|
|
1
3
|
module Spree
|
|
2
4
|
class Image < Asset
|
|
3
|
-
include Spree::Image::Configuration::ActiveStorage # legacy to be removed in Spree 6
|
|
4
|
-
include Rails.application.routes.url_helpers
|
|
5
|
-
include Spree::ImageMethods # legacy, will be removed in Spree 6
|
|
6
|
-
|
|
7
|
-
validates :attachment, attached: true, content_type: Rails.application.config.active_storage.web_image_content_types
|
|
8
|
-
|
|
9
|
-
after_commit :touch_product_variants, if: :should_touch_product_variants?, on: :update
|
|
10
|
-
after_commit :update_variant_thumbnail, on: [:create, :destroy]
|
|
11
|
-
after_commit :update_variant_thumbnail_on_reorder, on: :update, if: :saved_change_to_position?
|
|
12
|
-
after_commit :update_variant_thumbnail_on_viewable_change, on: :update, if: :saved_change_to_viewable_id?
|
|
13
|
-
|
|
14
|
-
after_create :increment_viewable_image_count
|
|
15
|
-
after_destroy :decrement_viewable_image_count
|
|
16
|
-
|
|
17
|
-
# In Rails 5.x class constants are being undefined/redefined during the code reloading process
|
|
18
|
-
# in a rails development environment, after which the actual ruby objects stored in those class constants
|
|
19
|
-
# are no longer equal (subclass == self) what causes error ActiveRecord::SubclassNotFound
|
|
20
|
-
# Invalid single-table inheritance type: Spree::Image is not a subclass of Spree::Image.
|
|
21
|
-
# The line below prevents the error.
|
|
22
|
-
self.inheritance_column = nil
|
|
23
|
-
|
|
24
|
-
# @deprecated
|
|
25
|
-
def styles
|
|
26
|
-
Spree::Deprecation.warn("Image#styles is deprecated and will be removed in Spree 6.0. Please use active storage variants with cdn_image_url")
|
|
27
|
-
|
|
28
|
-
self.class.styles.map do |_, size|
|
|
29
|
-
width, height = size.chop.split('x').map(&:to_i)
|
|
30
|
-
|
|
31
|
-
{
|
|
32
|
-
url: generate_url(size: size),
|
|
33
|
-
size: size,
|
|
34
|
-
width: width,
|
|
35
|
-
height: height
|
|
36
|
-
}
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
private
|
|
41
|
-
|
|
42
|
-
def touch_product_variants
|
|
43
|
-
viewable.product.variants.touch_all
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def should_touch_product_variants?
|
|
47
|
-
viewable.is_a?(Spree::Variant) &&
|
|
48
|
-
viewable.is_master? &&
|
|
49
|
-
viewable.product.has_variants? &&
|
|
50
|
-
saved_change_to_position?
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def increment_viewable_image_count
|
|
54
|
-
return unless viewable.is_a?(Spree::Variant)
|
|
55
|
-
|
|
56
|
-
Spree::Variant.increment_counter(:image_count, viewable_id)
|
|
57
|
-
Spree::Product.increment_counter(:total_image_count, viewable.product_id)
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def decrement_viewable_image_count
|
|
61
|
-
return unless viewable.is_a?(Spree::Variant)
|
|
62
|
-
|
|
63
|
-
Spree::Variant.decrement_counter(:image_count, viewable_id)
|
|
64
|
-
Spree::Product.decrement_counter(:total_image_count, viewable.product_id)
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def update_variant_thumbnail
|
|
68
|
-
return unless viewable.is_a?(Spree::Variant)
|
|
69
|
-
|
|
70
|
-
viewable.update_thumbnail!
|
|
71
|
-
viewable.product.update_thumbnail!
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def update_variant_thumbnail_on_reorder
|
|
75
|
-
update_variant_thumbnail
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
def update_variant_thumbnail_on_viewable_change
|
|
79
|
-
update_variant_thumbnail
|
|
80
|
-
end
|
|
81
5
|
end
|
|
82
6
|
end
|
|
@@ -55,10 +55,10 @@ module Spree
|
|
|
55
55
|
delegate :name, :description, :brand, :category, to: :product
|
|
56
56
|
|
|
57
57
|
# Returns the thumbnail image for this line item
|
|
58
|
-
# Prefers variant
|
|
59
|
-
# @return [Spree::
|
|
58
|
+
# Prefers variant primary media, falls back to product primary media
|
|
59
|
+
# @return [Spree::Asset, nil]
|
|
60
60
|
def thumbnail
|
|
61
|
-
variant.
|
|
61
|
+
variant.primary_media || product.primary_media
|
|
62
62
|
end
|
|
63
63
|
delegate :tax_zone, to: :order
|
|
64
64
|
delegate :digital?, :can_supply?, to: :variant
|
data/app/models/spree/product.rb
CHANGED
|
@@ -114,10 +114,12 @@ module Spree
|
|
|
114
114
|
has_many :orders, through: :line_items
|
|
115
115
|
has_many :completed_orders, -> { reorder(nil).distinct.complete }, through: :line_items, source: :order
|
|
116
116
|
|
|
117
|
+
has_many :media, -> { order(:position) }, as: :viewable, dependent: :destroy, class_name: 'Spree::Asset'
|
|
118
|
+
|
|
117
119
|
has_many :variant_images, -> { order(:position) }, source: :images, through: :variants_including_master
|
|
118
120
|
has_many :variant_images_without_master, -> { order(:position) }, source: :images, through: :variants
|
|
119
121
|
|
|
120
|
-
belongs_to :
|
|
122
|
+
belongs_to :primary_media, class_name: 'Spree::Asset', optional: true, foreign_key: :primary_media_id
|
|
121
123
|
|
|
122
124
|
has_many :option_value_variants, class_name: 'Spree::OptionValueVariant', through: :variants
|
|
123
125
|
has_many :option_values, class_name: 'Spree::OptionValue', through: :variants
|
|
@@ -335,17 +337,26 @@ module Spree
|
|
|
335
337
|
@default_variant_id ||= default_variant.id
|
|
336
338
|
end
|
|
337
339
|
|
|
338
|
-
# Returns
|
|
339
|
-
# Uses
|
|
340
|
+
# Returns the product's media gallery.
|
|
341
|
+
# Uses product-level media if present, otherwise falls back to variant images.
|
|
342
|
+
# @return [ActiveRecord::Relation]
|
|
343
|
+
def gallery_media
|
|
344
|
+
return media if association(:media).loaded? ? media.any? : media.exists?
|
|
345
|
+
|
|
346
|
+
variant_images
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
# Returns true if the product has any media (product-level or variant-level).
|
|
350
|
+
# Uses counter cache for performance.
|
|
340
351
|
# @return [Boolean]
|
|
341
|
-
def
|
|
352
|
+
def has_media?
|
|
342
353
|
return variant_images.any? if association(:variant_images).loaded?
|
|
343
354
|
|
|
344
|
-
|
|
355
|
+
media_count.positive?
|
|
345
356
|
end
|
|
346
357
|
|
|
347
|
-
|
|
348
|
-
alias
|
|
358
|
+
alias has_images? has_media?
|
|
359
|
+
alias has_variant_images? has_media?
|
|
349
360
|
|
|
350
361
|
# Returns the variant that should be used for displaying images.
|
|
351
362
|
# Priority: master > default_variant > first variant with images
|
|
@@ -354,47 +365,47 @@ module Spree
|
|
|
354
365
|
@variant_for_images ||= find_variant_for_images
|
|
355
366
|
end
|
|
356
367
|
|
|
357
|
-
#
|
|
358
|
-
# Uses cached thumbnail_id which is updated when images are added/removed/reordered.
|
|
359
|
-
# @return [Spree::Image, nil]
|
|
368
|
+
# @deprecated Use #primary_media instead.
|
|
360
369
|
def default_image
|
|
361
|
-
|
|
370
|
+
Spree::Deprecation.warn('Spree::Product#default_image is deprecated and will be removed in Spree 6.0. Please use Spree::Product#primary_media instead.')
|
|
371
|
+
primary_media
|
|
362
372
|
end
|
|
363
373
|
|
|
364
|
-
#
|
|
365
|
-
# @deprecated Use Spree::Product#default_image instead.
|
|
374
|
+
# @deprecated Use #primary_media instead.
|
|
366
375
|
def featured_image
|
|
367
|
-
Spree::Deprecation.warn('Spree::Product#featured_image is deprecated and will be removed in Spree
|
|
376
|
+
Spree::Deprecation.warn('Spree::Product#featured_image is deprecated and will be removed in Spree 6.0. Please use Spree::Product#primary_media instead.')
|
|
377
|
+
primary_media
|
|
378
|
+
end
|
|
368
379
|
|
|
369
|
-
|
|
380
|
+
# @deprecated Use #primary_media instead.
|
|
381
|
+
def primary_image
|
|
382
|
+
Spree::Deprecation.warn('Spree::Product#primary_image is deprecated and will be removed in Spree 6.0. Please use Spree::Product#primary_media instead.')
|
|
383
|
+
primary_media
|
|
370
384
|
end
|
|
371
385
|
|
|
372
|
-
# Returns secondary
|
|
373
|
-
# @return [Spree::
|
|
386
|
+
# Returns secondary media for Product (for hover effects).
|
|
387
|
+
# @return [Spree::Asset, nil]
|
|
374
388
|
def secondary_image
|
|
375
389
|
variant_for_images&.secondary_image
|
|
376
390
|
end
|
|
377
391
|
|
|
378
|
-
#
|
|
379
|
-
alias primary_image default_image
|
|
380
|
-
|
|
381
|
-
# Returns the image count from the variant used for displaying images.
|
|
382
|
-
# @return [Integer]
|
|
392
|
+
# @deprecated Use media_count instead
|
|
383
393
|
def image_count
|
|
384
|
-
|
|
394
|
+
media_count
|
|
385
395
|
end
|
|
386
396
|
|
|
387
|
-
# Updates
|
|
388
|
-
#
|
|
397
|
+
# Updates primary_media_id to the first media item.
|
|
398
|
+
# Checks product-level media first, then falls back to variant images.
|
|
399
|
+
# Called when media is added, removed, or reordered.
|
|
389
400
|
def update_thumbnail!
|
|
390
|
-
|
|
391
|
-
update_column(:
|
|
401
|
+
first_media = media.order(:position).first || variant_images.order(:position).first
|
|
402
|
+
update_column(:primary_media_id, first_media&.id)
|
|
392
403
|
end
|
|
393
404
|
|
|
394
|
-
# Finds first variant with
|
|
405
|
+
# Finds first variant with media using preloaded data when available.
|
|
395
406
|
# @return [Spree::Variant, nil]
|
|
396
407
|
def find_variant_with_images
|
|
397
|
-
return variants.find(&:
|
|
408
|
+
return variants.find(&:has_media?) if variants.loaded?
|
|
398
409
|
|
|
399
410
|
variants.joins(:images).first
|
|
400
411
|
end
|
|
@@ -630,12 +641,12 @@ module Spree
|
|
|
630
641
|
|
|
631
642
|
private
|
|
632
643
|
|
|
633
|
-
# Determines which variant should be used for displaying
|
|
634
|
-
# Priority: master > default_variant > first variant with
|
|
644
|
+
# Determines which variant should be used for displaying media.
|
|
645
|
+
# Priority: master > default_variant > first variant with media
|
|
635
646
|
def find_variant_for_images
|
|
636
|
-
return master if master.
|
|
637
|
-
return default_variant if has_variants? && default_variant.
|
|
638
|
-
return find_variant_with_images if
|
|
647
|
+
return master if master.has_media?
|
|
648
|
+
return default_variant if has_variants? && default_variant.has_media?
|
|
649
|
+
return find_variant_with_images if has_media?
|
|
639
650
|
|
|
640
651
|
nil
|
|
641
652
|
end
|
data/app/models/spree/store.rb
CHANGED
|
@@ -109,6 +109,7 @@ module Spree
|
|
|
109
109
|
has_many :customer_groups, class_name: 'Spree::CustomerGroup', dependent: :destroy, inverse_of: :store
|
|
110
110
|
|
|
111
111
|
has_many :api_keys, class_name: 'Spree::ApiKey', dependent: :destroy
|
|
112
|
+
has_many :allowed_origins, class_name: 'Spree::AllowedOrigin', dependent: :destroy
|
|
112
113
|
|
|
113
114
|
#
|
|
114
115
|
# Validations
|
|
@@ -252,6 +253,36 @@ module Spree
|
|
|
252
253
|
formatted_url
|
|
253
254
|
end
|
|
254
255
|
|
|
256
|
+
# Returns the storefront origin URL for use in customer-facing emails and links.
|
|
257
|
+
# Uses the first allowed origin if configured, otherwise falls back to formatted_url.
|
|
258
|
+
#
|
|
259
|
+
# @return [String] e.g. "https://myshop.com"
|
|
260
|
+
def storefront_url
|
|
261
|
+
allowed_origins.order(:created_at).pick(:origin) || formatted_url
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# Returns true if the given URL's origin matches one of the store's allowed origins.
|
|
265
|
+
# Comparison is port-less: only scheme + host are matched, so storing
|
|
266
|
+
# `http://localhost` will match `http://localhost:3000`, `http://localhost:4000`, etc.
|
|
267
|
+
#
|
|
268
|
+
# @param url [String] the full URL to check
|
|
269
|
+
# @return [Boolean]
|
|
270
|
+
def allowed_origin?(url)
|
|
271
|
+
return false if url.blank?
|
|
272
|
+
|
|
273
|
+
uri = URI.parse(url)
|
|
274
|
+
request_origin = "#{uri.scheme}://#{uri.host}"
|
|
275
|
+
|
|
276
|
+
allowed_origins.pluck(:origin).any? do |stored|
|
|
277
|
+
stored_uri = URI.parse(stored)
|
|
278
|
+
"#{stored_uri.scheme}://#{stored_uri.host}" == request_origin
|
|
279
|
+
rescue URI::InvalidURIError
|
|
280
|
+
false
|
|
281
|
+
end
|
|
282
|
+
rescue URI::InvalidURIError
|
|
283
|
+
false
|
|
284
|
+
end
|
|
285
|
+
|
|
255
286
|
# Returns the states available for checkout for the store
|
|
256
287
|
# @param country [Spree::Country] the country to get the states for
|
|
257
288
|
# @return [Array<Spree::State>]
|
data/app/models/spree/variant.rb
CHANGED
|
@@ -48,8 +48,8 @@ module Spree
|
|
|
48
48
|
has_many :option_value_variants, class_name: 'Spree::OptionValueVariant'
|
|
49
49
|
has_many :option_values, through: :option_value_variants, dependent: :destroy, class_name: 'Spree::OptionValue'
|
|
50
50
|
|
|
51
|
-
has_many :images, -> { order(:position) }, as: :viewable, dependent: :destroy, class_name: 'Spree::
|
|
52
|
-
belongs_to :
|
|
51
|
+
has_many :images, -> { order(:position) }, as: :viewable, dependent: :destroy, class_name: 'Spree::Asset'
|
|
52
|
+
belongs_to :primary_media, class_name: 'Spree::Asset', optional: true, foreign_key: :primary_media_id
|
|
53
53
|
|
|
54
54
|
has_many :prices,
|
|
55
55
|
class_name: 'Spree::Price',
|
|
@@ -279,26 +279,35 @@ module Spree
|
|
|
279
279
|
is_master? ? name + ' - Master' : name + ' - ' + options_text
|
|
280
280
|
end
|
|
281
281
|
|
|
282
|
-
# Returns
|
|
282
|
+
# Returns the variant's media gallery.
|
|
283
|
+
# Currently returns direct images. In 6.0 will use variant_media join table.
|
|
284
|
+
# @return [ActiveRecord::Relation]
|
|
285
|
+
def gallery_media
|
|
286
|
+
images
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
# Returns true if the variant has media.
|
|
283
290
|
# Uses loaded association when available, otherwise falls back to counter cache.
|
|
284
291
|
# @return [Boolean]
|
|
285
|
-
def
|
|
292
|
+
def has_media?
|
|
286
293
|
return images.any? if images.loaded?
|
|
287
294
|
|
|
288
|
-
|
|
295
|
+
media_count.positive?
|
|
289
296
|
end
|
|
290
297
|
|
|
291
|
-
|
|
292
|
-
|
|
298
|
+
alias has_images? has_media?
|
|
299
|
+
|
|
300
|
+
# @deprecated Use #primary_media instead.
|
|
293
301
|
def default_image
|
|
294
|
-
|
|
302
|
+
Spree::Deprecation.warn('Spree::Variant#default_image is deprecated and will be removed in Spree 6.0. Please use Spree::Variant#primary_media instead.')
|
|
303
|
+
primary_media
|
|
295
304
|
end
|
|
296
305
|
|
|
297
|
-
# Updates
|
|
298
|
-
# Called when
|
|
306
|
+
# Updates primary_media_id to the first media item by position.
|
|
307
|
+
# Called when media is added, removed, or reordered.
|
|
299
308
|
def update_thumbnail!
|
|
300
|
-
|
|
301
|
-
update_column(:
|
|
309
|
+
first_media = images.order(:position).first
|
|
310
|
+
update_column(:primary_media_id, first_media&.id)
|
|
302
311
|
end
|
|
303
312
|
|
|
304
313
|
# Returns first Image for Variant.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Seeds
|
|
3
|
+
class AllowedOrigins
|
|
4
|
+
prepend Spree::ServiceModule::Base
|
|
5
|
+
|
|
6
|
+
def call
|
|
7
|
+
store = Spree::Store.default
|
|
8
|
+
return unless store&.persisted?
|
|
9
|
+
|
|
10
|
+
store.allowed_origins.find_or_create_by!(origin: 'http://localhost')
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<% height ||= 64 %>
|
|
3
3
|
<% logo_css ||= '' %>
|
|
4
4
|
|
|
5
|
-
<%= link_to current_store.
|
|
5
|
+
<%= link_to current_store.storefront_url, id: 'site-logo', style: "text-decoration: none;" do %>
|
|
6
6
|
<span style="display: none;"><%= current_store.name %></span>
|
|
7
7
|
<% if logo.present? && logo.attached? && logo.variable? %>
|
|
8
8
|
<% aspect_ratio = spree_asset_aspect_ratio(logo) %>
|
data/config/locales/en.yml
CHANGED
|
@@ -240,6 +240,10 @@ en:
|
|
|
240
240
|
messages:
|
|
241
241
|
blank: can't be blank
|
|
242
242
|
models:
|
|
243
|
+
spree/allowed_origin:
|
|
244
|
+
attributes:
|
|
245
|
+
origin:
|
|
246
|
+
must_be_origin_only: must be an origin (scheme and host) without path, query, or fragment
|
|
243
247
|
spree/calculator/tiered_flat_rate:
|
|
244
248
|
attributes:
|
|
245
249
|
base:
|
|
@@ -742,6 +746,8 @@ en:
|
|
|
742
746
|
all_products: All products
|
|
743
747
|
all_rights_reserved: All rights reserved
|
|
744
748
|
all_time: All time
|
|
749
|
+
allowed_origin: Allowed Origin
|
|
750
|
+
allowed_origins: Allowed Origins
|
|
745
751
|
already_have_account: Already have an account?
|
|
746
752
|
alt_text: Alternative Text
|
|
747
753
|
alternative_phone: Alternative Phone
|
|
@@ -976,6 +982,14 @@ en:
|
|
|
976
982
|
customer_group_rule:
|
|
977
983
|
choose_customer_groups: 'Select the customer group(s) to which this promotion should apply:'
|
|
978
984
|
customer_groups: Customer Groups
|
|
985
|
+
customer_mailer:
|
|
986
|
+
password_reset_email:
|
|
987
|
+
action: Reset Password
|
|
988
|
+
expiry_notice: This password reset link will expire shortly. If you did not request this, no action is needed.
|
|
989
|
+
greeting: Hi %{name},
|
|
990
|
+
ignore_notice: If you did not request a password reset, you can safely ignore this email. Your password will not be changed.
|
|
991
|
+
instructions: We received a request to reset your password. Click the button below to choose a new password.
|
|
992
|
+
subject: Password Reset
|
|
979
993
|
customer_removed_from_group: Customer removed from group
|
|
980
994
|
customer_return: Customer Return
|
|
981
995
|
customer_returns: Customer Returns
|
|
@@ -1104,6 +1118,7 @@ en:
|
|
|
1104
1118
|
blank: can't be blank
|
|
1105
1119
|
cannot_remove_icon: Cannot remove image
|
|
1106
1120
|
could_not_create_taxon: Could not create taxon
|
|
1121
|
+
must_be_origin_only: must be an origin (scheme and host) without path, query, or fragment
|
|
1107
1122
|
no_shipping_methods_available: No shipping methods available for selected location, please change your address and try again.
|
|
1108
1123
|
store_association_can_not_be_changed: The store association can not be changed
|
|
1109
1124
|
store_is_already_set: Store is already set
|
|
@@ -1418,6 +1433,7 @@ en:
|
|
|
1418
1433
|
new: New
|
|
1419
1434
|
new_address: New address
|
|
1420
1435
|
new_adjustment: New Adjustment
|
|
1436
|
+
new_allowed_origin: New Allowed Origin
|
|
1421
1437
|
new_api_key: New API Key
|
|
1422
1438
|
new_balance: New balance
|
|
1423
1439
|
new_billing_address: New Billing Address
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateSpreeAllowedOrigins < ActiveRecord::Migration[7.2]
|
|
4
|
+
def change
|
|
5
|
+
create_table :spree_allowed_origins do |t|
|
|
6
|
+
t.references :store, null: false
|
|
7
|
+
t.string :origin, null: false
|
|
8
|
+
t.timestamps
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
add_index :spree_allowed_origins, [:store_id, :origin], unique: true,
|
|
12
|
+
name: 'index_spree_allowed_origins_on_store_id_and_origin'
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
class AddProductMediaSupport < ActiveRecord::Migration[7.2]
|
|
2
|
+
def change
|
|
3
|
+
add_column :spree_assets, :media_type, :string
|
|
4
|
+
add_column :spree_assets, :focal_point_x, :decimal, precision: 5, scale: 4
|
|
5
|
+
add_column :spree_assets, :focal_point_y, :decimal, precision: 5, scale: 4
|
|
6
|
+
add_column :spree_assets, :external_video_url, :string
|
|
7
|
+
|
|
8
|
+
add_index :spree_assets, :media_type
|
|
9
|
+
|
|
10
|
+
rename_column :spree_variants, :image_count, :media_count
|
|
11
|
+
rename_column :spree_products, :total_image_count, :media_count
|
|
12
|
+
rename_column :spree_variants, :thumbnail_id, :primary_media_id
|
|
13
|
+
rename_column :spree_products, :thumbnail_id, :primary_media_id
|
|
14
|
+
|
|
15
|
+
reversible do |dir|
|
|
16
|
+
dir.up do
|
|
17
|
+
Spree::Asset.unscoped.where(media_type: nil).update_all(media_type: 'image')
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -107,6 +107,9 @@ module Spree
|
|
|
107
107
|
preference :coupon_codes_web_limit, :integer, default: 500 # number of coupon codes to be generated in the web process, more than this will be generated in a background job
|
|
108
108
|
preference :coupon_codes_total_limit, :integer, default: 5000 # the maximum number of coupon codes to be generated
|
|
109
109
|
|
|
110
|
+
# password reset
|
|
111
|
+
preference :customer_password_reset_expires_in, :integer, default: 15 # password reset token expiration time in minutes
|
|
112
|
+
|
|
110
113
|
# gift cards
|
|
111
114
|
preference :gift_card_batch_web_limit, :integer, default: 500 # number of gift card codes to be generated in the web process, more than this will be generated in a background job
|
|
112
115
|
preference :gift_card_batch_limit, :integer, default: 50_000
|
data/lib/spree/core/version.rb
CHANGED
|
@@ -2,6 +2,7 @@ module Spree
|
|
|
2
2
|
module PermittedAttributes
|
|
3
3
|
ATTRIBUTES = [
|
|
4
4
|
:address_attributes,
|
|
5
|
+
:allowed_origin_attributes,
|
|
5
6
|
:api_key_attributes,
|
|
6
7
|
:asset_attributes,
|
|
7
8
|
:checkout_attributes,
|
|
@@ -87,9 +88,12 @@ module Spree
|
|
|
87
88
|
state: [:name, :abbr] }
|
|
88
89
|
]
|
|
89
90
|
|
|
91
|
+
@@allowed_origin_attributes = [:origin]
|
|
92
|
+
|
|
90
93
|
@@api_key_attributes = [:name, :key_type]
|
|
91
94
|
|
|
92
|
-
@@asset_attributes = [:type, :viewable_id, :viewable_type, :attachment, :alt, :position
|
|
95
|
+
@@asset_attributes = [:type, :viewable_id, :viewable_type, :attachment, :alt, :position,
|
|
96
|
+
:media_type, :focal_point_x, :focal_point_y, :external_video_url]
|
|
93
97
|
|
|
94
98
|
@@checkout_attributes = [
|
|
95
99
|
:coupon_code, :email, :shipping_method_id, :special_instructions, :use_billing, :use_shipping,
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
FactoryBot.define do
|
|
2
2
|
factory :asset, class: Spree::Asset do
|
|
3
|
-
viewable_type {}
|
|
4
|
-
viewable_id {}
|
|
5
|
-
attachment_width { 340 }
|
|
6
|
-
attachment_height { 280 }
|
|
7
|
-
attachment_file_size { 128 }
|
|
8
3
|
position { 1 }
|
|
9
|
-
attachment_content_type { '.jpg' }
|
|
10
|
-
attachment_file_name { 'attachment.jpg' }
|
|
11
|
-
type {}
|
|
12
|
-
attachment_updated_at {}
|
|
13
4
|
alt {}
|
|
5
|
+
|
|
6
|
+
after(:build) do |asset|
|
|
7
|
+
if asset.media_type == 'image' && !asset.attachment.attached?
|
|
8
|
+
asset.attachment.attach(io: File.new(Spree::Core::Engine.root + 'spec/fixtures' + 'thinking-cat.jpg'), filename: 'thinking-cat.jpg')
|
|
9
|
+
end
|
|
10
|
+
end
|
|
14
11
|
end
|
|
15
12
|
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
FactoryBot.define do
|
|
2
|
-
factory :image, class: Spree::
|
|
2
|
+
factory :image, class: Spree::Asset do
|
|
3
|
+
media_type { 'image' }
|
|
4
|
+
|
|
3
5
|
before(:create) do |image|
|
|
4
6
|
if image.class.method_defined?(:attachment)
|
|
5
7
|
image.attachment.attach(io: File.new(Spree::Core::Engine.root + 'spec/fixtures' + 'thinking-cat.jpg'), filename: 'thinking-cat.jpg')
|
data/lib/tasks/images.rake
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
namespace :spree do
|
|
2
|
-
namespace :
|
|
3
|
-
desc 'Backfill
|
|
4
|
-
task
|
|
5
|
-
puts 'Backfilling variant
|
|
6
|
-
Spree::Variant.where(
|
|
7
|
-
|
|
8
|
-
variant.update_column(:
|
|
2
|
+
namespace :media do
|
|
3
|
+
desc 'Backfill primary_media_id for all variants and products'
|
|
4
|
+
task backfill_primary_media: :environment do
|
|
5
|
+
puts 'Backfilling variant primary_media...'
|
|
6
|
+
Spree::Variant.where(primary_media_id: nil).where.not(media_count: 0).find_each do |variant|
|
|
7
|
+
first_media = variant.gallery_media.first
|
|
8
|
+
variant.update_column(:primary_media_id, first_media.id) if first_media
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
puts 'Backfilling product
|
|
12
|
-
Spree::Product.where(
|
|
13
|
-
|
|
14
|
-
product.update_column(:
|
|
11
|
+
puts 'Backfilling product primary_media...'
|
|
12
|
+
Spree::Product.where(primary_media_id: nil).where.not(media_count: 0).find_each do |product|
|
|
13
|
+
first_media = product.gallery_media.first
|
|
14
|
+
product.update_column(:primary_media_id, first_media.id) if first_media
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
puts 'Done!'
|
data/lib/tasks/products.rake
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
namespace :spree do
|
|
2
2
|
namespace :products do
|
|
3
|
-
desc 'Reset counter caches (variant_count, classification_count,
|
|
3
|
+
desc 'Reset counter caches (variant_count, classification_count, media_count) on products'
|
|
4
4
|
task reset_counter_caches: :environment do |_t, _args|
|
|
5
5
|
puts 'Resetting product counter caches...'
|
|
6
6
|
|
|
7
7
|
Spree::Product.find_each do |product|
|
|
8
|
+
total_media = product.media.count + product.variant_images.where.not(id: product.media.select(:id)).count
|
|
9
|
+
|
|
8
10
|
product.update_columns(
|
|
9
11
|
variant_count: product.variants.count,
|
|
10
12
|
classification_count: product.classifications.count,
|
|
11
|
-
|
|
13
|
+
media_count: total_media,
|
|
12
14
|
updated_at: Time.current
|
|
13
15
|
)
|
|
14
16
|
print '.'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: spree_core
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.4.0.
|
|
4
|
+
version: 5.4.0.beta8
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sean Schofield
|
|
@@ -10,7 +10,7 @@ authors:
|
|
|
10
10
|
autorequire:
|
|
11
11
|
bindir: bin
|
|
12
12
|
cert_chain: []
|
|
13
|
-
date: 2026-03-
|
|
13
|
+
date: 2026-03-16 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: i18n-tasks
|
|
@@ -899,6 +899,7 @@ files:
|
|
|
899
899
|
- app/models/spree/adjustable/adjustments_updater.rb
|
|
900
900
|
- app/models/spree/adjustable/promotion_accumulator.rb
|
|
901
901
|
- app/models/spree/adjustment.rb
|
|
902
|
+
- app/models/spree/allowed_origin.rb
|
|
902
903
|
- app/models/spree/api_key.rb
|
|
903
904
|
- app/models/spree/asset.rb
|
|
904
905
|
- app/models/spree/asset/support/active_storage.rb
|
|
@@ -1253,6 +1254,7 @@ files:
|
|
|
1253
1254
|
- app/services/spree/sample_data/loader.rb
|
|
1254
1255
|
- app/services/spree/seeds/admin_user.rb
|
|
1255
1256
|
- app/services/spree/seeds/all.rb
|
|
1257
|
+
- app/services/spree/seeds/allowed_origins.rb
|
|
1256
1258
|
- app/services/spree/seeds/api_keys.rb
|
|
1257
1259
|
- app/services/spree/seeds/countries.rb
|
|
1258
1260
|
- app/services/spree/seeds/default_reimbursement_types.rb
|
|
@@ -1445,6 +1447,8 @@ files:
|
|
|
1445
1447
|
- db/migrate/20260220000000_create_spree_markets.rb
|
|
1446
1448
|
- db/migrate/20260226000000_add_locale_to_spree_orders.rb
|
|
1447
1449
|
- db/migrate/20260226100000_add_token_digest_to_spree_api_keys.rb
|
|
1450
|
+
- db/migrate/20260315000000_create_spree_allowed_origins.rb
|
|
1451
|
+
- db/migrate/20260315100000_add_product_media_support.rb
|
|
1448
1452
|
- db/sample_data/customers.csv
|
|
1449
1453
|
- db/sample_data/metafield_definitions.rb
|
|
1450
1454
|
- db/sample_data/orders.rb
|
|
@@ -1530,6 +1534,7 @@ files:
|
|
|
1530
1534
|
- lib/spree/testing_support/factories.rb
|
|
1531
1535
|
- lib/spree/testing_support/factories/address_factory.rb
|
|
1532
1536
|
- lib/spree/testing_support/factories/adjustment_factory.rb
|
|
1537
|
+
- lib/spree/testing_support/factories/allowed_origin_factory.rb
|
|
1533
1538
|
- lib/spree/testing_support/factories/api_key_factory.rb
|
|
1534
1539
|
- lib/spree/testing_support/factories/asset_factory.rb
|
|
1535
1540
|
- lib/spree/testing_support/factories/calculator_factory.rb
|
|
@@ -1666,9 +1671,9 @@ licenses:
|
|
|
1666
1671
|
- BSD-3-Clause
|
|
1667
1672
|
metadata:
|
|
1668
1673
|
bug_tracker_uri: https://github.com/spree/spree/issues
|
|
1669
|
-
changelog_uri: https://github.com/spree/spree/releases/tag/v5.4.0.
|
|
1674
|
+
changelog_uri: https://github.com/spree/spree/releases/tag/v5.4.0.beta8
|
|
1670
1675
|
documentation_uri: https://docs.spreecommerce.org/
|
|
1671
|
-
source_code_uri: https://github.com/spree/spree/tree/v5.4.0.
|
|
1676
|
+
source_code_uri: https://github.com/spree/spree/tree/v5.4.0.beta8
|
|
1672
1677
|
post_install_message:
|
|
1673
1678
|
rdoc_options: []
|
|
1674
1679
|
require_paths:
|