spree_emails 5.6.1 → 6.0.0.beta1
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/LICENSE +30 -0
- data/README.md +2 -2
- data/app/helpers/spree/base_helper.rb +45 -0
- data/app/helpers/spree/digital_asset_helper.rb +16 -0
- data/app/helpers/spree/fulfillment_helper.rb +12 -0
- data/app/helpers/spree/mail_helper.rb +0 -13
- data/app/mailers/spree/company_mailer.rb +40 -0
- data/app/mailers/spree/customer_mailer.rb +21 -0
- data/app/mailers/spree/digital_asset_mailer.rb +30 -0
- data/app/mailers/spree/fulfillment_mailer.rb +17 -0
- data/app/mailers/spree/order_mailer.rb +1 -1
- data/app/mailers/spree/return_mailer.rb +17 -0
- data/app/mailers/spree/seller_mailer.rb +68 -0
- data/app/mailers/spree/shipment_mailer.rb +4 -11
- data/app/subscribers/spree/company_email_subscriber.rb +19 -0
- data/app/subscribers/spree/customer_email_subscriber.rb +1 -1
- data/app/subscribers/spree/data_request_email_subscriber.rb +21 -0
- data/app/subscribers/spree/digital_asset_email_subscriber.rb +35 -0
- data/app/subscribers/spree/fulfillment_email_subscriber.rb +34 -0
- data/app/subscribers/spree/order_email_subscriber.rb +5 -2
- data/app/subscribers/spree/return_email_subscriber.rb +23 -0
- data/app/subscribers/spree/seller_email_subscriber.rb +46 -0
- data/app/views/spree/company_mailer/invitation_email.html.erb +8 -0
- data/app/views/spree/company_mailer/invitation_email.text.erb +11 -0
- data/app/views/spree/customer_mailer/data_export_email.html.erb +12 -0
- data/app/views/spree/customer_mailer/data_export_email.text.erb +9 -0
- data/app/views/spree/digital_asset_mailer/files_ready_email.html.erb +20 -0
- data/app/views/spree/digital_asset_mailer/files_ready_email.text.erb +10 -0
- data/app/views/spree/fulfillment_mailer/fulfilled_email.html.erb +22 -0
- data/app/views/spree/fulfillment_mailer/fulfilled_email.text.erb +18 -0
- data/app/views/spree/order_mailer/cancel_email.text.erb +1 -0
- data/app/views/spree/order_mailer/confirm_email.text.erb +1 -0
- data/app/views/spree/order_mailer/store_owner_notification_email.text.erb +1 -0
- data/app/views/spree/return_mailer/refunded_email.html.erb +22 -0
- data/app/views/spree/return_mailer/refunded_email.text.erb +20 -0
- data/app/views/spree/seller_mailer/approved_email.html.erb +8 -0
- data/app/views/spree/seller_mailer/approved_email.text.erb +8 -0
- data/app/views/spree/seller_mailer/rejected_email.html.erb +6 -0
- data/app/views/spree/seller_mailer/rejected_email.text.erb +7 -0
- data/app/views/spree/seller_mailer/suspended_email.html.erb +6 -0
- data/app/views/spree/seller_mailer/suspended_email.text.erb +7 -0
- data/app/views/spree/shared/_item_thumbnail_link.html.erb +12 -1
- data/app/views/spree/shared/_order_summary_section.html.erb +5 -0
- data/app/views/spree/shared/_po_number.text.erb +6 -0
- data/app/views/spree/shared/_purchased_items_table.html.erb +16 -11
- data/app/views/spree/shared/_purchased_items_table.text.erb +9 -10
- data/lib/spree/emails/engine.rb +4 -3
- data/lib/spree/emails/previews/fulfillment_preview.rb +12 -0
- data/lib/spree/emails/previews/return_preview.rb +12 -0
- metadata +43 -28
- data/app/assets/config/spree_emails_manifest.js +0 -1
- data/app/assets/images/logo/spree_50.png +0 -0
- data/app/assets/images/noimage/mini.png +0 -0
- data/app/assets/images/noimage/noimage/large.png +0 -0
- data/app/assets/images/noimage/noimage/mini.png +0 -0
- data/app/assets/images/noimage/noimage/product.png +0 -0
- data/app/assets/images/noimage/noimage/small.png +0 -0
- data/app/assets/images/noimage/small.png +0 -0
- data/app/mailers/spree/reimbursement_mailer.rb +0 -15
- data/app/subscribers/spree/reimbursement_email_subscriber.rb +0 -24
- data/app/subscribers/spree/shipment_email_subscriber.rb +0 -24
- data/app/views/spree/reimbursement_mailer/reimbursement_email.html.erb +0 -47
- data/app/views/spree/reimbursement_mailer/reimbursement_email.text.erb +0 -24
- data/app/views/spree/shared/purchased_items_table/_adjustment.html.erb +0 -3
- data/app/views/spree/shipment_mailer/shipped_email.html.erb +0 -20
- data/app/views/spree/shipment_mailer/shipped_email.text.erb +0 -17
- data/config/initializers/assets.rb +0 -3
- data/lib/spree/emails/previews/reimbursement_preview.rb +0 -12
- data/lib/spree/emails/previews/shipment_preview.rb +0 -12
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f1bfa005e59ba23cd5784e52fe434f5d7e77d0d76002aaaca68de2f8107f5ed6
|
|
4
|
+
data.tar.gz: '069b0d6d771268fd9c24ae09dead90ef6c803772a9f3bf62db28930f8cf2fea7'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 136a085fd33cb272a3cc1df61ea9c7a4bdda8e56698381a1ddd86bee9d003c4378a2e36bf6ce4ca9ea400918c604cc0a0e1218d78095af2a5019e059e62f80f9
|
|
7
|
+
data.tar.gz: c8568259e1bbbdb9b243cd04a6b8fbd5c3f13620a2e1fc9d91eefc417067d88647c9b8d7ba7873ef8008886d9ac2b7d1197e03f17757dcdbcff98d6f5b27a7ab
|
data/LICENSE
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-present, Vendo Sp. z o.o., Vendo Connect Inc.
|
|
4
|
+
Copyright (c) 2015-2024, Spark Solutions Sp. z o.o.
|
|
5
|
+
Copyright (c) 2007-2015, Spree Commerce Inc.
|
|
6
|
+
|
|
7
|
+
Redistribution and use in source and binary forms, with or without
|
|
8
|
+
modification, are permitted provided that the following conditions are met:
|
|
9
|
+
|
|
10
|
+
1. Redistributions of source code must retain the above copyright notice,
|
|
11
|
+
this list of conditions and the following disclaimer.
|
|
12
|
+
|
|
13
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
14
|
+
this list of conditions and the following disclaimer in the documentation
|
|
15
|
+
and/or other materials provided with the distribution.
|
|
16
|
+
|
|
17
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
18
|
+
contributors may be used to endorse or promote products derived from
|
|
19
|
+
this software without specific prior written permission.
|
|
20
|
+
|
|
21
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
22
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
23
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
24
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
25
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
26
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
27
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
28
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
29
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
30
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
CHANGED
|
@@ -149,7 +149,7 @@ module MyApp
|
|
|
149
149
|
|
|
150
150
|
def handle(event)
|
|
151
151
|
user_id = event.payload['id']
|
|
152
|
-
user = Spree.
|
|
152
|
+
user = Spree.customer_class.find_by(id: user_id)
|
|
153
153
|
return unless user
|
|
154
154
|
|
|
155
155
|
Spree::CustomMailer.welcome_email(user).deliver_later
|
|
@@ -209,4 +209,4 @@ bundle exec rspec
|
|
|
209
209
|
## Documentation
|
|
210
210
|
|
|
211
211
|
- [Email Customization Guide](https://docs.spreecommerce.org/developer/customization/emails)
|
|
212
|
-
- [Events System](https://docs.spreecommerce.org/developer/core-concepts/events)
|
|
212
|
+
- [Events System](https://docs.spreecommerce.org/developer/core-concepts/events)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module BaseHelper
|
|
3
|
+
# returns the URL of an object on the storefront
|
|
4
|
+
# @param resource [Spree::Product, Spree::Category] the resource to get the URL for
|
|
5
|
+
# @param options [Hash] the options for the URL
|
|
6
|
+
# @option options [String] :locale the locale of the resource, defaults to I18n.locale
|
|
7
|
+
# @option options [String] :store the store of the resource, defaults to current_store
|
|
8
|
+
# @option options [String] :relative whether to use the relative URL, defaults to false
|
|
9
|
+
# @option options [String] :preview_id the preview ID of the resource, usually the ID of the resource
|
|
10
|
+
# @option options [String] :variant_id the variant ID of the resource, usually the ID of the variant (only used for products)
|
|
11
|
+
# @return [String] the URL of the resource
|
|
12
|
+
def spree_storefront_resource_url(resource, options = {})
|
|
13
|
+
store = options[:store] || current_store
|
|
14
|
+
|
|
15
|
+
base_url = if options[:relative]
|
|
16
|
+
''
|
|
17
|
+
else
|
|
18
|
+
store.storefront_url
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
localize = if options[:locale].present?
|
|
22
|
+
"/#{options[:locale]}"
|
|
23
|
+
else
|
|
24
|
+
''
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
if resource.instance_of?(Spree::Product)
|
|
28
|
+
preview_id = ("preview_id=#{options[:preview_id]}" if options[:preview_id].present?)
|
|
29
|
+
|
|
30
|
+
variant_id = ("variant_id=#{options[:variant_id]}" if options[:variant_id].present?)
|
|
31
|
+
|
|
32
|
+
params = [preview_id, variant_id].compact_blank.join('&')
|
|
33
|
+
params = "?#{params}" if params.present?
|
|
34
|
+
|
|
35
|
+
"#{base_url + localize}/products/#{resource.slug}#{params}"
|
|
36
|
+
elsif resource.is_a?(Spree::Category)
|
|
37
|
+
"#{base_url + localize}/t/#{resource.permalink}"
|
|
38
|
+
elsif localize.blank?
|
|
39
|
+
base_url
|
|
40
|
+
else
|
|
41
|
+
base_url + localize
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module DigitalAssetHelper
|
|
3
|
+
# Emails need an absolute link — the route helper only produces a path, and
|
|
4
|
+
# the download endpoint lives on the backend rather than the storefront.
|
|
5
|
+
#
|
|
6
|
+
# The host is passed in rather than resolved per link: every link in one
|
|
7
|
+
# email belongs to the same order, so its store is already known.
|
|
8
|
+
#
|
|
9
|
+
# @param digital_link [Spree::DigitalLink]
|
|
10
|
+
# @param host [String] the backend's own URL
|
|
11
|
+
# @return [String]
|
|
12
|
+
def digital_link_download_url(digital_link, host)
|
|
13
|
+
Spree::Api::DigitalLinkUrls.download_url(digital_link, host)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module FulfillmentHelper
|
|
3
|
+
def fulfillment_tracking_link_to(fulfillment:, name: nil, html_options: {})
|
|
4
|
+
tracking_url = fulfillment.tracking_url.presence
|
|
5
|
+
return '' unless tracking_url
|
|
6
|
+
|
|
7
|
+
display_text = name || fulfillment.tracking.presence || tracking_url
|
|
8
|
+
|
|
9
|
+
link_to display_text, tracking_url, html_options
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -3,21 +3,8 @@ module Spree
|
|
|
3
3
|
include Spree::BaseHelper
|
|
4
4
|
include Spree::ImagesHelper
|
|
5
5
|
|
|
6
|
-
def variant_image_url(variant)
|
|
7
|
-
Spree::Deprecation.warn(
|
|
8
|
-
'Spree::MailHelper#variant_image_url is deprecated. Use spree_image_url instead. Will be removed in Spree 6.0.'
|
|
9
|
-
)
|
|
10
|
-
|
|
11
|
-
image = variant.primary_media
|
|
12
|
-
image.present? && image.attached? ? spree_image_url(image, variant: :mini) : image_url('noimage/small.png')
|
|
13
|
-
end
|
|
14
|
-
|
|
15
6
|
def name_for(order)
|
|
16
7
|
order.name || Spree.t('customer')
|
|
17
8
|
end
|
|
18
|
-
|
|
19
|
-
def store_logo
|
|
20
|
-
@store_logo ||= current_store&.mailer_logo || current_store&.logo
|
|
21
|
-
end
|
|
22
9
|
end
|
|
23
10
|
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
# Functional mail for the company directory — currently the invitation that
|
|
3
|
+
# brings a not-yet-registered buyer into a company node. Like the staff
|
|
4
|
+
# invitation, this is transactional, not marketing: the no-welcome-email
|
|
5
|
+
# rule on customer creation is untouched.
|
|
6
|
+
class CompanyMailer < BaseMailer
|
|
7
|
+
helper Spree::MailHelper
|
|
8
|
+
|
|
9
|
+
# The invite carries the plaintext token; the storefront owns the
|
|
10
|
+
# acceptance page, so the link resolves against the store URL like other
|
|
11
|
+
# customer-facing links.
|
|
12
|
+
#
|
|
13
|
+
# @param invitation [Spree::CompanyInvitation, Integer]
|
|
14
|
+
def invitation_email(invitation)
|
|
15
|
+
@invitation = invitation.respond_to?(:id) ? invitation : Spree::CompanyInvitation.find(invitation)
|
|
16
|
+
# Re-checked here, not only where the job was enqueued: a queue can sit
|
|
17
|
+
# for minutes, and an invitation revoked in that window must not still
|
|
18
|
+
# arrive carrying a working token.
|
|
19
|
+
return message.perform_deliveries = false unless @invitation.reload.pending?
|
|
20
|
+
|
|
21
|
+
@company = @invitation.company
|
|
22
|
+
store = @company.store
|
|
23
|
+
@accept_url = append_token(invitation_base_url(store), @invitation.token)
|
|
24
|
+
|
|
25
|
+
with_store_locale(store) do
|
|
26
|
+
mail(
|
|
27
|
+
to: @invitation.email,
|
|
28
|
+
subject: "#{store.name} #{Spree.t('company_mailer.invitation_email.subject', company: @company.name)}",
|
|
29
|
+
store_url: store.storefront_url
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def invitation_base_url(store)
|
|
37
|
+
"#{store.storefront_url.to_s.chomp('/')}/account/company-invitation"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -17,5 +17,26 @@ module Spree
|
|
|
17
17
|
)
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
|
+
|
|
21
|
+
# The finished subject access export (GDPR Art. 15). The link is signed
|
|
22
|
+
# and expires with the request, so the file is reachable by the person who
|
|
23
|
+
# asked for it and not by anyone who later reads the mailbox.
|
|
24
|
+
def data_export_email(data_request)
|
|
25
|
+
@data_request = data_request
|
|
26
|
+
@current_store = data_request.store
|
|
27
|
+
# A store route rather than a raw storage URL: a signed storage link
|
|
28
|
+
# cannot be built off-request on the Disk service, and mailing one would
|
|
29
|
+
# put a direct object address in an inbox where it outlives the request.
|
|
30
|
+
@download_url = Spree::Api::DataRequestUrls.download_url(data_request, @current_store.formatted_url)
|
|
31
|
+
@expires_at = data_request.expires_at
|
|
32
|
+
|
|
33
|
+
with_store_locale(@current_store) do
|
|
34
|
+
mail(
|
|
35
|
+
to: data_request.email,
|
|
36
|
+
subject: "#{@current_store.name} #{Spree.t('customer_mailer.data_export_email.subject')}",
|
|
37
|
+
store_url: @current_store.storefront_url
|
|
38
|
+
)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
20
41
|
end
|
|
21
42
|
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
class DigitalAssetMailer < BaseMailer
|
|
3
|
+
helper Spree::MailHelper
|
|
4
|
+
helper Spree::DigitalAssetHelper
|
|
5
|
+
|
|
6
|
+
# Sent once an order's downloads are ready. Kept separate from the order
|
|
7
|
+
# confirmation so it can be re-sent on its own, and so a storefront that
|
|
8
|
+
# replaces the confirmation email does not silently lose the links.
|
|
9
|
+
def files_ready_email(order, resend = false)
|
|
10
|
+
@order = order.respond_to?(:id) ? order : Spree::Order.find(order)
|
|
11
|
+
# The template reads each link's filename and expiry, both of which reach
|
|
12
|
+
# through the asset — preload rather than paying per link.
|
|
13
|
+
@digital_links = @order.digital_links.includes(digital_asset: { attachment_attachment: :blob })
|
|
14
|
+
return if @digital_links.empty?
|
|
15
|
+
|
|
16
|
+
current_store = @order.store
|
|
17
|
+
@download_host = current_store.formatted_url
|
|
18
|
+
with_store_locale(current_store, @order.locale) do
|
|
19
|
+
subject = order_email_subject(
|
|
20
|
+
current_store,
|
|
21
|
+
Spree.t('digital_asset_mailer.files_ready_email.subject'),
|
|
22
|
+
@order.number,
|
|
23
|
+
resend: resend
|
|
24
|
+
)
|
|
25
|
+
mail(to: @order.email, subject: subject, store_url: current_store.storefront_url,
|
|
26
|
+
template_path: 'spree/digital_asset_mailer', template_name: 'files_ready_email')
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
class FulfillmentMailer < BaseMailer
|
|
3
|
+
helper Spree::MailHelper
|
|
4
|
+
helper Spree::FulfillmentHelper
|
|
5
|
+
|
|
6
|
+
def fulfilled_email(fulfillment, resend = false)
|
|
7
|
+
@fulfillment = fulfillment.respond_to?(:id) ? fulfillment : Spree::Fulfillment.find(fulfillment)
|
|
8
|
+
@order = @fulfillment.order
|
|
9
|
+
current_store = @fulfillment.store
|
|
10
|
+
with_store_locale(current_store, @order.locale) do
|
|
11
|
+
subject = order_email_subject(current_store, Spree.t('fulfillment_mailer.fulfilled_email.subject'), @order.number, resend: resend)
|
|
12
|
+
mail(to: @order.email, subject: subject, store_url: current_store.storefront_url,
|
|
13
|
+
template_path: 'spree/fulfillment_mailer', template_name: 'fulfilled_email')
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -32,7 +32,7 @@ module Spree
|
|
|
32
32
|
def payment_link_email(order_id)
|
|
33
33
|
@order = Spree::Order.incomplete.not_canceled.find(order_id)
|
|
34
34
|
@current_store = @order.store
|
|
35
|
-
@checkout_payment_url =
|
|
35
|
+
@checkout_payment_url = URI.join(@current_store.storefront_url, "/checkout/#{@order.token}/payment").to_s
|
|
36
36
|
|
|
37
37
|
with_store_locale(@current_store, @order.locale) do
|
|
38
38
|
mail(to: @order.email, subject: Spree.t('order_mailer.payment_link_email.subject', number: @order.number),
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
class ReturnMailer < BaseMailer
|
|
3
|
+
helper Spree::MailHelper
|
|
4
|
+
|
|
5
|
+
# Tells the customer their returned items were refunded. Replaces the
|
|
6
|
+
# reimbursement email dropped with the ReturnAuthorization chain in 6.0.
|
|
7
|
+
def refunded_email(return_record, resend = false)
|
|
8
|
+
@return = return_record.respond_to?(:id) ? return_record : Spree::Return.find(return_record)
|
|
9
|
+
@order = @return.order
|
|
10
|
+
current_store = @return.store || Spree::Store.default
|
|
11
|
+
with_store_locale(current_store, @order.locale) do
|
|
12
|
+
subject = order_email_subject(current_store, Spree.t('return_mailer.refunded_email.subject'), @order.number, resend: resend)
|
|
13
|
+
mail(to: @order.email, subject: subject, store_url: current_store.storefront_url)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
# Tells a seller what just happened to them on the marketplace.
|
|
3
|
+
#
|
|
4
|
+
# Deliberately seller-facing only: the legacy module also copied every store
|
|
5
|
+
# admin on approvals and onboarding, which on a marketplace with many sellers
|
|
6
|
+
# is a lot of mail about something staff can already see in the dashboard.
|
|
7
|
+
# Marketplace-side notifications ride the event and webhook surface instead.
|
|
8
|
+
class SellerMailer < BaseMailer
|
|
9
|
+
helper Spree::MailHelper
|
|
10
|
+
|
|
11
|
+
# @param seller [Spree::Seller, Integer]
|
|
12
|
+
def approved_email(seller)
|
|
13
|
+
@seller = load_seller(seller)
|
|
14
|
+
# Where the seller signs in. Resolved rather than built from the store URL
|
|
15
|
+
# so a hosted dashboard, a dev Vite server and a mounted build all work.
|
|
16
|
+
@dashboard_url = Spree::Stores::DashboardUrl.call(store: store).presence
|
|
17
|
+
|
|
18
|
+
deliver_to_seller('seller_mailer.approved_email.subject')
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Carries no reason: the operator's note is an internal record, and a
|
|
22
|
+
# suspended seller is told to get in touch rather than handed a verdict.
|
|
23
|
+
def suspended_email(seller)
|
|
24
|
+
@seller = load_seller(seller)
|
|
25
|
+
|
|
26
|
+
deliver_to_seller('seller_mailer.suspended_email.subject')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def rejected_email(seller)
|
|
30
|
+
@seller = load_seller(seller)
|
|
31
|
+
|
|
32
|
+
deliver_to_seller('seller_mailer.rejected_email.subject')
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def load_seller(seller)
|
|
38
|
+
seller.respond_to?(:id) ? seller : Spree::Seller.find(seller)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def store
|
|
42
|
+
@store ||= @seller.store || Spree::Store.default
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# The team plus the address the seller gave for contact — before anyone has
|
|
46
|
+
# accepted an invitation the team is empty, and that address is the only way
|
|
47
|
+
# to reach them.
|
|
48
|
+
def recipients
|
|
49
|
+
(@seller.users.pluck(:email) << @seller.contact_email).compact_blank.uniq(&:downcase)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Takes the key, not the translated string: resolving the subject outside
|
|
53
|
+
# the block would render it in whatever locale the job happens to run
|
|
54
|
+
# under, so a seller could get an English subject over a German email.
|
|
55
|
+
def deliver_to_seller(subject_key)
|
|
56
|
+
addresses = recipients
|
|
57
|
+
return message.perform_deliveries = false if addresses.empty?
|
|
58
|
+
|
|
59
|
+
with_store_locale(store) do
|
|
60
|
+
mail(
|
|
61
|
+
to: addresses,
|
|
62
|
+
subject: Spree.t(subject_key, store_name: store.name),
|
|
63
|
+
store_url: store.storefront_url
|
|
64
|
+
)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -1,16 +1,9 @@
|
|
|
1
1
|
module Spree
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
helper Spree::ShipmentHelper
|
|
5
|
-
|
|
2
|
+
# Deprecated: use Spree::FulfillmentMailer. Removed in Spree 6.1.
|
|
3
|
+
class ShipmentMailer < FulfillmentMailer
|
|
6
4
|
def shipped_email(shipment, resend = false)
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
current_store = @shipment.store
|
|
10
|
-
with_store_locale(current_store, @order.locale) do
|
|
11
|
-
subject = order_email_subject(current_store, Spree.t('shipment_mailer.shipped_email.subject'), @order.number, resend: resend)
|
|
12
|
-
mail(to: @order.email, subject: subject, store_url: current_store.storefront_url)
|
|
13
|
-
end
|
|
5
|
+
Spree::Deprecation.warn('Spree::ShipmentMailer#shipped_email is deprecated and will be removed in Spree 6.1. Use Spree::FulfillmentMailer#fulfilled_email instead.')
|
|
6
|
+
fulfilled_email(shipment, resend)
|
|
14
7
|
end
|
|
15
8
|
end
|
|
16
9
|
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spree
|
|
4
|
+
# Sends the company invitation mail when an invitation is created. Riding
|
|
5
|
+
# the event rather than the write path lets a host app silence it (consumer
|
|
6
|
+
# transactional emails off) and send its own instead.
|
|
7
|
+
class CompanyEmailSubscriber < Spree::Subscriber
|
|
8
|
+
subscribes_to 'company_invitation.created'
|
|
9
|
+
|
|
10
|
+
def handle(event)
|
|
11
|
+
invitation = Spree::CompanyInvitation.find_by_prefix_id(event.payload['id'])
|
|
12
|
+
return unless invitation
|
|
13
|
+
return unless invitation.pending?
|
|
14
|
+
return unless invitation.store&.prefers_send_consumer_transactional_emails?
|
|
15
|
+
|
|
16
|
+
CompanyMailer.invitation_email(invitation.id).deliver_later
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -5,7 +5,7 @@ module Spree
|
|
|
5
5
|
subscribes_to 'customer.password_reset_requested'
|
|
6
6
|
|
|
7
7
|
def handle(event)
|
|
8
|
-
user = Spree.
|
|
8
|
+
user = Spree.customer_class.find_by(email: event.payload['email'])
|
|
9
9
|
return unless user
|
|
10
10
|
|
|
11
11
|
store = find_store(event)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spree
|
|
4
|
+
# Emails a finished subject access export to the person who asked for it.
|
|
5
|
+
#
|
|
6
|
+
# Erasure requests send nothing: the account's email address has just been
|
|
7
|
+
# replaced with an undeliverable one, and writing to the old address would
|
|
8
|
+
# re-record the very contact detail the request asked to remove.
|
|
9
|
+
class DataRequestEmailSubscriber < Spree::Subscriber
|
|
10
|
+
subscribes_to 'data_request.completed'
|
|
11
|
+
|
|
12
|
+
def handle(event)
|
|
13
|
+
data_request = Spree::DataRequest.find_by_prefix_id(event.payload['id'])
|
|
14
|
+
return unless data_request&.access?
|
|
15
|
+
return unless data_request.downloadable?
|
|
16
|
+
return unless data_request.store.prefers_send_consumer_transactional_emails?
|
|
17
|
+
|
|
18
|
+
CustomerMailer.data_export_email(data_request).deliver_later
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spree
|
|
4
|
+
class DigitalAssetEmailSubscriber < Spree::Subscriber
|
|
5
|
+
# Links are created during completion, before order.placed fires, so the
|
|
6
|
+
# order already knows everything the email needs.
|
|
7
|
+
subscribes_to 'order.placed', 'order.resend_digital_links_email'
|
|
8
|
+
|
|
9
|
+
on 'order.placed', :send_files_ready_email
|
|
10
|
+
on 'order.resend_digital_links_email', :resend_files_ready_email
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def send_files_ready_email(event)
|
|
15
|
+
deliver(event)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def resend_files_ready_email(event)
|
|
19
|
+
deliver(event, resend: true)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def deliver(event, resend: false)
|
|
23
|
+
order = find_order(event)
|
|
24
|
+
return unless order
|
|
25
|
+
return if order.digital_links.empty?
|
|
26
|
+
return unless order.store.prefers_send_consumer_transactional_emails?
|
|
27
|
+
|
|
28
|
+
DigitalAssetMailer.files_ready_email(order.id, resend).deliver_later
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def find_order(event)
|
|
32
|
+
Spree::Order.find_by_prefix_id(event.payload['id'])
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spree
|
|
4
|
+
class FulfillmentEmailSubscriber < Spree::Subscriber
|
|
5
|
+
subscribes_to 'fulfillment.fulfilled'
|
|
6
|
+
|
|
7
|
+
def handle(event)
|
|
8
|
+
return unless notify_customer?(event)
|
|
9
|
+
|
|
10
|
+
fulfillment = find_fulfillment(event)
|
|
11
|
+
return unless fulfillment
|
|
12
|
+
|
|
13
|
+
store = fulfillment.store
|
|
14
|
+
return unless store.prefers_send_consumer_transactional_emails?
|
|
15
|
+
|
|
16
|
+
FulfillmentMailer.fulfilled_email(fulfillment.id).deliver_later
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
# An admin shipping from the backoffice can suppress the shipment email for
|
|
22
|
+
# this one dispatch (a correction, a re-ship, goods handed over in person).
|
|
23
|
+
# Absent metadata means send, so events published by anything unaware of
|
|
24
|
+
# the flag keep the historic behavior.
|
|
25
|
+
def notify_customer?(event)
|
|
26
|
+
event.metadata.fetch('notify_customer', true) != false
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def find_fulfillment(event)
|
|
30
|
+
fulfillment_id = event.payload['id']
|
|
31
|
+
Spree::Fulfillment.find_by_prefix_id(fulfillment_id)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
module Spree
|
|
4
4
|
class OrderEmailSubscriber < Spree::Subscriber
|
|
5
|
-
subscribes_to 'order.
|
|
5
|
+
subscribes_to 'order.placed', 'order.canceled', 'order.resend_confirmation_email'
|
|
6
6
|
|
|
7
|
-
on 'order.
|
|
7
|
+
on 'order.placed', :send_confirmation_email
|
|
8
8
|
on 'order.canceled', :send_cancel_email
|
|
9
9
|
on 'order.resend_confirmation_email', :resend_confirmation_email
|
|
10
10
|
|
|
@@ -41,6 +41,9 @@ module Spree
|
|
|
41
41
|
return unless order
|
|
42
42
|
return if event.payload['notify_customer'] == false
|
|
43
43
|
|
|
44
|
+
store = order.store
|
|
45
|
+
return unless store.prefers_send_consumer_transactional_emails?
|
|
46
|
+
|
|
44
47
|
OrderMailer.cancel_email(order.id).deliver_later
|
|
45
48
|
end
|
|
46
49
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spree
|
|
4
|
+
class ReturnEmailSubscriber < Spree::Subscriber
|
|
5
|
+
subscribes_to 'return.refunded'
|
|
6
|
+
|
|
7
|
+
def handle(event)
|
|
8
|
+
return_record = find_return(event)
|
|
9
|
+
return unless return_record
|
|
10
|
+
|
|
11
|
+
store = return_record.store
|
|
12
|
+
return unless store.prefers_send_consumer_transactional_emails?
|
|
13
|
+
|
|
14
|
+
ReturnMailer.refunded_email(return_record.id).deliver_later
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def find_return(event)
|
|
20
|
+
Spree::Return.find_by_prefix_id(event.payload['id'])
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spree
|
|
4
|
+
# Turns the seller lifecycle events into seller-facing mail.
|
|
5
|
+
#
|
|
6
|
+
# Nothing is sent from the workflows themselves: a transition publishes an
|
|
7
|
+
# event, and mail is one listener among several (webhooks, analytics) rather
|
|
8
|
+
# than a step the transition waits on.
|
|
9
|
+
#
|
|
10
|
+
# `seller.invited` is deliberately absent — inviting a seller creates an
|
|
11
|
+
# Invitation, which already mails the invitee through InvitationEmailSubscriber.
|
|
12
|
+
# A second email would be the same news to the same address.
|
|
13
|
+
class SellerEmailSubscriber < Spree::Subscriber
|
|
14
|
+
subscribes_to 'seller.approved', 'seller.suspended', 'seller.rejected'
|
|
15
|
+
|
|
16
|
+
on 'seller.approved', :send_approved_email
|
|
17
|
+
on 'seller.suspended', :send_suspended_email
|
|
18
|
+
on 'seller.rejected', :send_rejected_email
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def send_approved_email(event)
|
|
23
|
+
deliver(event, :approved_email)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def send_suspended_email(event)
|
|
27
|
+
deliver(event, :suspended_email)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def send_rejected_email(event)
|
|
31
|
+
deliver(event, :rejected_email)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def deliver(event, mailer_method)
|
|
35
|
+
seller = find_seller(event)
|
|
36
|
+
return unless seller
|
|
37
|
+
return unless seller.store&.prefers_send_seller_transactional_emails?
|
|
38
|
+
|
|
39
|
+
SellerMailer.public_send(mailer_method, seller.id).deliver_later
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def find_seller(event)
|
|
43
|
+
Spree::Seller.find_by_prefix_id(event.payload['id'])
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<%= render layout: 'spree/shared/mailer_hero' do %>
|
|
2
|
+
<h1><%= Spree.t('company_mailer.invitation_email.title', company: @company.name) %></h1>
|
|
3
|
+
<p class="greeting"><%= Spree.t('company_mailer.invitation_email.greeting') %></p>
|
|
4
|
+
<p><%= Spree.t('company_mailer.invitation_email.instructions', company: @company.name, store: current_store.name) %></p>
|
|
5
|
+
<%= render 'spree/shared/mailer_button', url: @accept_url, label: Spree.t('company_mailer.invitation_email.action') %>
|
|
6
|
+
<p class="sub"><%= Spree.t('company_mailer.invitation_email.expiry_notice') %></p>
|
|
7
|
+
<p class="sub"><%= Spree.t('company_mailer.invitation_email.ignore_notice') %></p>
|
|
8
|
+
<% end %>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<%= Spree.t('company_mailer.invitation_email.greeting') %>
|
|
2
|
+
|
|
3
|
+
<%= Spree.t('company_mailer.invitation_email.instructions', company: @company.name, store: current_store.name) %>
|
|
4
|
+
|
|
5
|
+
<%= Spree.t('company_mailer.invitation_email.action') %>: <%= @accept_url %>
|
|
6
|
+
|
|
7
|
+
<%= Spree.t('company_mailer.invitation_email.expiry_notice') %>
|
|
8
|
+
|
|
9
|
+
<%= Spree.t('company_mailer.invitation_email.ignore_notice') %>
|
|
10
|
+
|
|
11
|
+
<%= Spree.t('order_mailer.store_team', store_name: current_store.name) %>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<%= render layout: 'spree/shared/mailer_hero' do %>
|
|
2
|
+
<h1><%= Spree.t('customer_mailer.data_export_email.action') %></h1>
|
|
3
|
+
<p class="greeting"><%= Spree.t('customer_mailer.data_export_email.greeting') %></p>
|
|
4
|
+
<p><%= Spree.t('customer_mailer.data_export_email.instructions') %></p>
|
|
5
|
+
<% if @download_url %>
|
|
6
|
+
<%= render 'spree/shared/mailer_button', url: @download_url, label: Spree.t('customer_mailer.data_export_email.action') %>
|
|
7
|
+
<% end %>
|
|
8
|
+
<% if @expires_at %>
|
|
9
|
+
<p class="sub"><%= Spree.t('customer_mailer.data_export_email.expiry_notice', date: I18n.l(@expires_at.to_date, format: :long)) %></p>
|
|
10
|
+
<% end %>
|
|
11
|
+
<p class="sub"><%= Spree.t('customer_mailer.data_export_email.ignore_notice') %></p>
|
|
12
|
+
<% end %>
|