| @@ -58,9 +58,12 @@ | ||
| 58 | 58 | * Order Refunded -> fluent_cart/order_fully_refunded (customer + admin) |
| 59 | 59 | * Order Shipping Status Changed => fluent_cart/shipping_status_changed (customer + admin) |
| 60 | 60 | * Reminder Events: |
| 61 | 61 | * - fluent_cart/renewal_reminder_due |
| 62 | - * - fluent_cart/renewal_reminder_overdue | |
| 62 | + * - fluent_cart/renewal_reminder_overdue (manual/on-demand payment reminder) | |
| 63 | + * - fluent_cart/renewal_overdue_first (scheduled, first overdue stage) | |
| 64 | + * - fluent_cart/renewal_overdue_followup (scheduled, intermediate overdue stages) | |
| 65 | + * - fluent_cart/renewal_overdue_final (scheduled, last overdue stage) | |
| 63 | 66 | * - fluent_cart/subscription_renewal_reminder |
| 64 | 67 | * - fluent_cart/subscription_trial_end_reminder |
| 65 | 68 | */ |
| 66 | 69 | |
| @@ -546,8 +549,59 @@ | ||
| 546 | 549 | 'is_default_body' => 'yes', |
| 547 | 550 | 'email_body' => '', |
| 548 | 551 | ] |
| 549 | 552 | ], |
| 553 | + 'renewal_overdue_first_customer' => [ | |
| 554 | + 'event' => 'renewal_overdue_first', | |
| 555 | + 'group' => 'scheduler', | |
| 556 | + 'group_label' => __('Scheduler / Reminder Actions', 'fluent-cart'), | |
| 557 | + 'title' => __('First overdue renewal reminder to customer', 'fluent-cart'), | |
| 558 | + 'description' => __('This email will be sent at the first overdue reminder day after a renewal due date while the payment is still pending.', 'fluent-cart'), | |
| 559 | + 'recipient' => 'customer', | |
| 560 | + 'smartcode_groups' => [], | |
| 561 | + 'template_path' => 'order.reminder.renewal_overdue.first.customer', | |
| 562 | + 'is_async' => false, | |
| 563 | + 'settings' => [ | |
| 564 | + 'active' => 'yes', | |
| 565 | + 'subject' => __('Payment Reminder for Renewal #{{order.order_ref}}', 'fluent-cart'), | |
| 566 | + 'is_default_body' => 'yes', | |
| 567 | + 'email_body' => '', | |
| 568 | + ] | |
| 569 | + ], | |
| 570 | + 'renewal_overdue_followup_customer' => [ | |
| 571 | + 'event' => 'renewal_overdue_followup', | |
| 572 | + 'group' => 'scheduler', | |
| 573 | + 'group_label' => __('Scheduler / Reminder Actions', 'fluent-cart'), | |
| 574 | + 'title' => __('Follow-up overdue renewal reminder to customer', 'fluent-cart'), | |
| 575 | + 'description' => __('This email will be sent at the intermediate overdue reminder days while the renewal payment remains pending.', 'fluent-cart'), | |
| 576 | + 'recipient' => 'customer', | |
| 577 | + 'smartcode_groups' => [], | |
| 578 | + 'template_path' => 'order.reminder.renewal_overdue.followup.customer', | |
| 579 | + 'is_async' => false, | |
| 580 | + 'settings' => [ | |
| 581 | + 'active' => 'yes', | |
| 582 | + 'subject' => __('Payment Overdue for Renewal #{{order.order_ref}}', 'fluent-cart'), | |
| 583 | + 'is_default_body' => 'yes', | |
| 584 | + 'email_body' => '', | |
| 585 | + ] | |
| 586 | + ], | |
| 587 | + 'renewal_overdue_final_customer' => [ | |
| 588 | + 'event' => 'renewal_overdue_final', | |
| 589 | + 'group' => 'scheduler', | |
| 590 | + 'group_label' => __('Scheduler / Reminder Actions', 'fluent-cart'), | |
| 591 | + 'title' => __('Final overdue renewal notice to customer', 'fluent-cart'), | |
| 592 | + 'description' => __('This email will be sent at the last configured overdue reminder day while the renewal payment is still pending.', 'fluent-cart'), | |
| 593 | + 'recipient' => 'customer', | |
| 594 | + 'smartcode_groups' => [], | |
| 595 | + 'template_path' => 'order.reminder.renewal_overdue.final.customer', | |
| 596 | + 'is_async' => false, | |
| 597 | + 'settings' => [ | |
| 598 | + 'active' => 'yes', | |
| 599 | + 'subject' => __('Final Notice: Payment Overdue for Renewal #{{order.order_ref}}', 'fluent-cart'), | |
| 600 | + 'is_default_body' => 'yes', | |
| 601 | + 'email_body' => '', | |
| 602 | + ] | |
| 603 | + ], | |
| 550 | 604 | 'subscription_trial_end_reminder_admin' => [ |
| 551 | 605 | 'event' => 'subscription_trial_end_reminder', |
| 552 | 606 | 'group' => 'scheduler', |
| 553 | 607 | 'group_label' => __('Scheduler / Reminder Actions', 'fluent-cart'), |
| @@ -581,10 +635,123 @@ | ||
| 581 | 635 | 'is_default_body' => 'yes', |
| 582 | 636 | 'email_body' => '', |
| 583 | 637 | ] |
| 584 | 638 | ], |
| 639 | + 'review_submitted_admin' => [ | |
| 640 | + 'event' => 'review_created', | |
| 641 | + 'group' => 'review', | |
| 642 | + 'group_label' => __('Review Actions', 'fluent-cart'), | |
| 643 | + 'title' => __('Send mail to admin when a new review is submitted', 'fluent-cart'), | |
| 644 | + 'description' => __('Email admin when a customer submits a new review.', 'fluent-cart'), | |
| 645 | + 'recipient' => 'admin', | |
| 646 | + 'smartcode_groups' => [], | |
| 647 | + 'template_path' => 'review.submitted.admin', | |
| 648 | + 'is_async' => false, | |
| 649 | + 'pre_header' => __('A new product review has been submitted.', 'fluent-cart'), | |
| 650 | + 'settings' => [ | |
| 651 | + 'active' => 'yes', | |
| 652 | + 'subject' => __('New Review Submitted', 'fluent-cart'), | |
| 653 | + 'is_default_body' => 'yes', | |
| 654 | + 'email_body' => '', | |
| 655 | + ] | |
| 656 | + ], | |
| 657 | + 'review_approved_customer' => [ | |
| 658 | + // The settled hook, not the transition: fired from a queued | |
| 659 | + // job once the review has been re-read and is still approved, | |
| 660 | + // the way order_paid_done is to order_paid. A bulk approve is | |
| 661 | + // fifty of these, and none of them is sent inside that request. | |
| 662 | + 'event' => 'review_approved_done', | |
| 663 | + 'group' => 'review', | |
| 664 | + 'group_label' => __('Review Actions', 'fluent-cart'), | |
| 665 | + 'title' => __('Send mail to the reviewer when their review is approved', 'fluent-cart'), | |
| 666 | + 'description' => __('Email the customer who wrote a review once it is approved and visible on the product page.', 'fluent-cart'), | |
| 667 | + 'recipient' => 'customer', | |
| 668 | + // Declared, because the customer recipient resolves through an | |
| 669 | + // order and a review has none. The resolver behind this code is | |
| 670 | + // the one every review notification uses. | |
| 671 | + 'to' => '{{review.recipient_email}}', | |
| 672 | + 'smartcode_groups' => [], | |
| 673 | + 'template_path' => 'review.approved.customer', | |
| 674 | + 'is_async' => false, | |
| 675 | + 'pre_header' => __('Your review is now live.', 'fluent-cart'), | |
| 676 | + 'settings' => [ | |
| 677 | + 'active' => 'yes', | |
| 678 | + 'subject' => __('Your review of {{review.product_title}} is now live', 'fluent-cart'), | |
| 679 | + 'is_default_body' => 'yes', | |
| 680 | + 'email_body' => '', | |
| 681 | + ] | |
| 682 | + ], | |
| 683 | + 'review_replied_customer' => [ | |
| 684 | + // Fired from a queued job once the reply and the review it | |
| 685 | + // answers have both been re-read and are still published. | |
| 686 | + 'event' => 'review_replied_done', | |
| 687 | + 'group' => 'review', | |
| 688 | + 'group_label' => __('Review Actions', 'fluent-cart'), | |
| 689 | + 'title' => __('Send mail to the reviewer when the store replies to their review', 'fluent-cart'), | |
| 690 | + 'description' => __('Email the customer who wrote a review when the store posts a reply to it.', 'fluent-cart'), | |
| 691 | + 'recipient' => 'customer', | |
| 692 | + // The review's author, resolved the way every review | |
| 693 | + // notification resolves it — {{review.*}} is the review being | |
| 694 | + // answered, not the reply. | |
| 695 | + 'to' => '{{review.recipient_email}}', | |
| 696 | + 'smartcode_groups' => [], | |
| 697 | + 'template_path' => 'review.replied.customer', | |
| 698 | + 'is_async' => false, | |
| 699 | + 'pre_header' => __('The store has replied to your review.', 'fluent-cart'), | |
| 700 | + 'settings' => [ | |
| 701 | + 'active' => 'yes', | |
| 702 | + 'subject' => __('{{settings.store_name}} replied to your review of {{review.product_title}}', 'fluent-cart'), | |
| 703 | + 'is_default_body' => 'yes', | |
| 704 | + 'email_body' => '', | |
| 705 | + ] | |
| 706 | + ], | |
| 585 | 707 | ]; |
| 586 | 708 | |
| 709 | + } | |
| 710 | + | |
| 711 | + /** | |
| 712 | + * The names of the notifications that would send for an event — the same | |
| 713 | + * gate getNotificationsOfEvent() applies, without rendering a body. | |
| 714 | + * | |
| 715 | + * For a caller that only needs to know whether, and which, before it | |
| 716 | + * commits to something — a once-only lease, say — and will render at | |
| 717 | + * send time through mailByEmailName(). Rendering here as well would draw | |
| 718 | + * every template twice per send. | |
| 719 | + * | |
| 720 | + * Kept in step with getNotificationsOfEvent(): the four conditions below | |
| 721 | + * are that method's, in the same order. | |
| 722 | + * | |
| 723 | + * @param string $event | |
| 724 | + * @return string[] notification names, in registry order | |
| 725 | + */ | |
| 726 | + public static function activeNotificationNamesOfEvent($event): array | |
| 727 | + { | |
| 728 | + $mailingSettings = static::getSettings(); | |
| 729 | + $names = []; | |
| 730 | + | |
| 731 | + foreach (static::getNotifications() as $key => $notification) { | |
| 732 | + if (Arr::get($notification, 'event') !== $event) { | |
| 733 | + continue; | |
| 734 | + } | |
| 735 | + | |
| 736 | + $settings = Arr::get($notification, 'settings'); | |
| 737 | + if (!in_array($notification['event'], ['order_placed_offline', 'renewal_reminder_overdue']) && Arr::get($settings, 'active') !== 'yes') { | |
| 738 | + continue; | |
| 739 | + } | |
| 740 | + | |
| 741 | + $recipient = Arr::get($notification, 'recipient'); | |
| 742 | + if (!Arr::get($notification, 'to') && !in_array($recipient, ['admin', 'customer', 'user', 'subscriber'])) { | |
| 743 | + continue; | |
| 744 | + } | |
| 745 | + | |
| 746 | + if (empty(static::resolveRecipientTemplate($notification, $mailingSettings))) { | |
| 747 | + continue; | |
| 748 | + } | |
| 749 | + | |
| 750 | + $names[] = (string) $key; | |
| 751 | + } | |
| 752 | + | |
| 753 | + return $names; | |
| 587 | 754 | } |
| 588 | 755 | |
| 589 | 756 | public static function getNotificationsOfEvent($event, $viewData): array |
| 590 | 757 | { |