init
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace MailPoet\WooCommerce;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Newsletter\NewslettersRepository;
|
||||
use MailPoet\Newsletter\Renderer\Blocks\Coupon;
|
||||
use MailPoet\NewsletterProcessingException;
|
||||
use MailPoet\WP\DateTime;
|
||||
|
||||
class CouponPreProcessor {
|
||||
|
||||
/** @var bool */
|
||||
private $generated = false;
|
||||
|
||||
/** @var NewslettersRepository */
|
||||
private $newslettersRepository;
|
||||
|
||||
/** @var Helper */
|
||||
private $wcHelper;
|
||||
|
||||
public function __construct(
|
||||
Helper $wcHelper,
|
||||
NewslettersRepository $newslettersRepository
|
||||
) {
|
||||
$this->wcHelper = $wcHelper;
|
||||
$this->newslettersRepository = $newslettersRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws NewsletterProcessingException
|
||||
*/
|
||||
public function processCoupons(NewsletterEntity $newsletter, array $blocks, bool $preview = false): array {
|
||||
if ($preview) {
|
||||
return $blocks;
|
||||
}
|
||||
|
||||
$generated = $this->ensureCouponForBlocks($blocks, $newsletter);
|
||||
$body = $newsletter->getBody();
|
||||
|
||||
if ($generated && $body && $this->shouldPersist($newsletter)) {
|
||||
$updatedBody = array_merge(
|
||||
$body,
|
||||
[
|
||||
'content' => array_merge(
|
||||
$body['content'],
|
||||
['blocks' => $blocks]
|
||||
),
|
||||
]
|
||||
);
|
||||
$newsletter->setBody($updatedBody);
|
||||
$this->newslettersRepository->flush();
|
||||
}
|
||||
return $blocks;
|
||||
}
|
||||
|
||||
private function ensureCouponForBlocks(array &$blocks, NewsletterEntity $newsletter): bool {
|
||||
|
||||
foreach ($blocks as &$innerBlock) {
|
||||
if (isset($innerBlock['blocks']) && !empty($innerBlock['blocks'])) {
|
||||
$this->ensureCouponForBlocks($innerBlock['blocks'], $newsletter);
|
||||
}
|
||||
if (isset($innerBlock['type']) && $innerBlock['type'] === Coupon::TYPE) {
|
||||
if (!$this->wcHelper->isWooCommerceActive()) {
|
||||
throw NewsletterProcessingException::create()->withMessage(__('WooCommerce is not active', 'mailpoet'));
|
||||
}
|
||||
if ($this->shouldGenerateCoupon($innerBlock)) {
|
||||
try {
|
||||
$innerBlock['couponId'] = $this->addOrUpdateCoupon($innerBlock, $newsletter);
|
||||
$this->generated = true;
|
||||
} catch (\Exception $e) {
|
||||
throw NewsletterProcessingException::create()->withMessage($e->getMessage())->withCode($e->getCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->generated;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $couponBlock
|
||||
* @param NewsletterEntity $newsletter
|
||||
* @return int
|
||||
* @throws \WC_Data_Exception|\Exception
|
||||
*/
|
||||
private function addOrUpdateCoupon(array $couponBlock, NewsletterEntity $newsletter) {
|
||||
$coupon = $this->wcHelper->createWcCoupon($couponBlock['couponId'] ?? '');
|
||||
if ($this->shouldGenerateCoupon($couponBlock)) {
|
||||
$code = isset($couponBlock['code']) && $couponBlock['code'] !== Coupon::CODE_PLACEHOLDER ? $couponBlock['code'] : $this->generateRandomCode();
|
||||
$coupon->set_code($code);
|
||||
}
|
||||
|
||||
$coupon->set_description(
|
||||
sprintf(
|
||||
// translators: %1$s is newsletter id and %2$s is the subject.
|
||||
_x('Auto Generated coupon by MailPoet for email: %1$s: %2$s', 'Coupon block code generation', 'mailpoet'),
|
||||
$newsletter->getId(),
|
||||
$newsletter->getSubject()
|
||||
)
|
||||
);
|
||||
|
||||
// general
|
||||
$coupon->set_discount_type($couponBlock['discountType']);
|
||||
|
||||
if (isset($couponBlock['amount'])) {
|
||||
$coupon->set_amount($couponBlock['amount']);
|
||||
}
|
||||
|
||||
if (isset($couponBlock['expiryDay'])) {
|
||||
$expiration = (new DateTime())->getCurrentDateTime()
|
||||
->modify("+{$couponBlock['expiryDay']} day")
|
||||
->getTimestamp();
|
||||
$coupon->set_date_expires($expiration);
|
||||
}
|
||||
|
||||
$coupon->set_free_shipping($couponBlock['freeShipping'] ?? false);
|
||||
|
||||
// usage restriction
|
||||
$coupon->set_minimum_amount($couponBlock['minimumAmount'] ?? '');
|
||||
$coupon->set_maximum_amount($couponBlock['maximumAmount'] ?? '');
|
||||
$coupon->set_individual_use($couponBlock['individualUse'] ?? false);
|
||||
$coupon->set_exclude_sale_items($couponBlock['excludeSaleItems'] ?? false);
|
||||
|
||||
$coupon->set_product_ids($this->getItemIds($couponBlock['productIds'] ?? []));
|
||||
$coupon->set_excluded_product_ids($this->getItemIds($couponBlock['excludedProductIds'] ?? []));
|
||||
|
||||
$coupon->set_product_categories($this->getItemIds($couponBlock['productCategoryIds'] ?? []));
|
||||
$coupon->set_excluded_product_categories($this->getItemIds($couponBlock['excludedProductCategoryIds'] ?? []));
|
||||
|
||||
$coupon->set_email_restrictions(explode(',', $couponBlock['emailRestrictions'] ?? ''));
|
||||
|
||||
// usage limit
|
||||
$coupon->set_usage_limit($couponBlock['usageLimit'] ?? 0);
|
||||
$coupon->set_usage_limit_per_user($couponBlock['usageLimitPerUser'] ?? 0);
|
||||
return $coupon->save();
|
||||
}
|
||||
|
||||
private function getItemIds(array $items): array {
|
||||
if (empty($items)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return array_map(function ($item) {
|
||||
return $item['id'];
|
||||
}, $items);
|
||||
}
|
||||
|
||||
private function generateRandomSegment($length) {
|
||||
$characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
$segment = '';
|
||||
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
$randomIndex = rand(0, strlen($characters) - 1);
|
||||
$segment .= $characters[$randomIndex];
|
||||
}
|
||||
|
||||
return $segment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates Coupon code for XXXX-XXXXXX-XXXX pattern
|
||||
*/
|
||||
private function generateRandomCode(): string {
|
||||
$part1 = $this->generateRandomSegment(4);
|
||||
$part2 = $this->generateRandomSegment(6);
|
||||
$part3 = $this->generateRandomSegment(4);
|
||||
|
||||
return $part1 . '-' . $part2 . '-' . $part3;
|
||||
}
|
||||
|
||||
private function shouldGenerateCoupon(array $block): bool {
|
||||
return empty($block['couponId']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only emails that can have their body-HTML re-generated should persist the generated couponId
|
||||
*/
|
||||
private function shouldPersist(NewsletterEntity $newsletter): bool {
|
||||
return in_array($newsletter->getType(), NewsletterEntity::TYPES_WITH_RESETTABLE_BODY);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\WooCommerce;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use Automattic\WooCommerce\Admin\API\Reports\Customers\Stats\DataStore;
|
||||
use MailPoet\DI\ContainerWrapper;
|
||||
use MailPoet\RuntimeException;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
|
||||
class Helper {
|
||||
/** @var WPFunctions */
|
||||
private $wp;
|
||||
|
||||
public function __construct(
|
||||
WPFunctions $wp
|
||||
) {
|
||||
$this->wp = $wp;
|
||||
}
|
||||
|
||||
public function isWooCommerceActive() {
|
||||
return class_exists('WooCommerce') && $this->wp->isPluginActive('woocommerce/woocommerce.php');
|
||||
}
|
||||
|
||||
public function getWooCommerceVersion() {
|
||||
return $this->isWooCommerceActive() ? get_plugin_data(WP_PLUGIN_DIR . '/woocommerce/woocommerce.php', false, false)['Version'] : null;
|
||||
}
|
||||
|
||||
public function getPurchaseStates(): array {
|
||||
|
||||
return (array)$this->wp->applyFilters(
|
||||
'mailpoet_purchase_order_states',
|
||||
['completed']
|
||||
);
|
||||
}
|
||||
|
||||
public function isWooCommerceBlocksActive($min_version = '') {
|
||||
if (!class_exists('\Automattic\WooCommerce\Blocks\Package')) {
|
||||
return false;
|
||||
}
|
||||
if ($min_version) {
|
||||
return version_compare(\Automattic\WooCommerce\Blocks\Package::get_version(), $min_version, '>=');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function isWooCommerceCustomOrdersTableEnabled(): bool {
|
||||
if (
|
||||
$this->isWooCommerceActive()
|
||||
&& method_exists('\Automattic\WooCommerce\Utilities\OrderUtil', 'custom_orders_table_usage_is_enabled')
|
||||
&& \Automattic\WooCommerce\Utilities\OrderUtil::custom_orders_table_usage_is_enabled()
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function WC() {
|
||||
return WC();
|
||||
}
|
||||
|
||||
public function wcGetCustomerOrderCount($userId) {
|
||||
return wc_get_customer_order_count($userId);
|
||||
}
|
||||
|
||||
public function wcGetOrder($order = false) {
|
||||
return wc_get_order($order);
|
||||
}
|
||||
|
||||
public function wcGetOrders(array $args) {
|
||||
return wc_get_orders($args);
|
||||
}
|
||||
|
||||
public function wcCreateOrder(array $args) {
|
||||
return wc_create_order($args);
|
||||
}
|
||||
|
||||
public function wcPrice($price, array $args = []) {
|
||||
return wc_price($price, $args);
|
||||
}
|
||||
|
||||
public function wcGetProduct($theProduct = false) {
|
||||
return wc_get_product($theProduct);
|
||||
}
|
||||
|
||||
public function wcGetPageId(string $page): ?int {
|
||||
if ($this->isWooCommerceActive()) {
|
||||
return (int)wc_get_page_id($page);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function wcGetPriceDecimals(): int {
|
||||
return wc_get_price_decimals();
|
||||
}
|
||||
|
||||
public function wcGetPriceDecimalSeperator(): string {
|
||||
return wc_get_price_decimal_separator();
|
||||
}
|
||||
|
||||
public function wcGetPriceThousandSeparator(): string {
|
||||
return wc_get_price_thousand_separator();
|
||||
}
|
||||
|
||||
public function getWoocommercePriceFormat(): string {
|
||||
return get_woocommerce_price_format();
|
||||
}
|
||||
|
||||
public function getWoocommerceCurrency() {
|
||||
return get_woocommerce_currency();
|
||||
}
|
||||
|
||||
public function getWoocommerceCurrencySymbol() {
|
||||
return get_woocommerce_currency_symbol();
|
||||
}
|
||||
|
||||
public function woocommerceFormField($key, $args, $value) {
|
||||
return woocommerce_form_field($key, $args, $value);
|
||||
}
|
||||
|
||||
public function wcLightOrDark($color, $dark, $light) {
|
||||
return wc_light_or_dark($color, $dark, $light);
|
||||
}
|
||||
|
||||
public function wcHexIsLight($color) {
|
||||
return wc_hex_is_light($color);
|
||||
}
|
||||
|
||||
public function getOrdersCountCreatedBefore(string $dateTime): int {
|
||||
$ordersCount = $this->wcGetOrders([
|
||||
'status' => 'all',
|
||||
'type' => 'shop_order',
|
||||
'date_created' => '<' . $dateTime,
|
||||
'limit' => 1,
|
||||
'paginate' => true,
|
||||
])->total;
|
||||
|
||||
return intval($ordersCount);
|
||||
}
|
||||
|
||||
public function getRawPrice($price, array $args = []) {
|
||||
$htmlPrice = $this->wcPrice($price, $args);
|
||||
return html_entity_decode(strip_tags($htmlPrice));
|
||||
}
|
||||
|
||||
public function getAllowedCountries(): array {
|
||||
return (new \WC_Countries)->get_allowed_countries() ?? [];
|
||||
}
|
||||
|
||||
public function getCustomersCount(): int {
|
||||
if (!$this->isWooCommerceActive() || !class_exists(DataStore::class)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$dataStore = new DataStore();
|
||||
$result = (array)$dataStore->get_data([
|
||||
'fields' => ['customers_count'],
|
||||
]);
|
||||
return isset($result['customers_count']) ? intval($result['customers_count']) : 0;
|
||||
}
|
||||
|
||||
public function wasMailPoetInstalledViaWooCommerceOnboardingWizard(): bool {
|
||||
$wp = ContainerWrapper::getInstance()->get(WPFunctions::class);
|
||||
$installedViaWooCommerce = false;
|
||||
$wooCommerceOnboardingProfile = $wp->getOption('woocommerce_onboarding_profile');
|
||||
|
||||
if (
|
||||
is_array($wooCommerceOnboardingProfile)
|
||||
&& isset($wooCommerceOnboardingProfile['business_extensions'])
|
||||
&& is_array($wooCommerceOnboardingProfile['business_extensions'])
|
||||
&& in_array('mailpoet', $wooCommerceOnboardingProfile['business_extensions'])
|
||||
) {
|
||||
$installedViaWooCommerce = true;
|
||||
}
|
||||
|
||||
return $installedViaWooCommerce;
|
||||
}
|
||||
|
||||
public function getOrdersTableName() {
|
||||
if (!method_exists('\Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore', 'get_orders_table_name')) {
|
||||
throw new RuntimeException('Cannot get orders table name when running a WooCommerce version that doesn\'t support custom order tables.');
|
||||
}
|
||||
|
||||
return \Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore::get_orders_table_name();
|
||||
}
|
||||
|
||||
public function getAddressesTableName() {
|
||||
if (!method_exists('\Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore', 'get_addresses_table_name')) {
|
||||
throw new RuntimeException('Cannot get addresses table name when running a WooCommerce version that doesn\'t support custom order tables.');
|
||||
}
|
||||
|
||||
return \Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore::get_addresses_table_name();
|
||||
}
|
||||
|
||||
public function wcGetCouponTypes(): array {
|
||||
return wc_get_coupon_types();
|
||||
}
|
||||
|
||||
public function wcGetCouponCodeById(int $id): string {
|
||||
return wc_get_coupon_code_by_id($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $data Coupon data, object, ID or code.
|
||||
*/
|
||||
public function createWcCoupon($data) {
|
||||
return new \WC_Coupon($data);
|
||||
}
|
||||
|
||||
public function getOrderStatuses(): array {
|
||||
return wc_get_order_statuses();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|\WP_Post[]
|
||||
*/
|
||||
public function getCouponList(
|
||||
int $pageSize = 10,
|
||||
int $pageNumber = 1,
|
||||
?string $discountType = null,
|
||||
?string $search = null,
|
||||
array $includeCouponIds = []
|
||||
): array {
|
||||
$args = [
|
||||
'posts_per_page' => $pageSize,
|
||||
'orderby' => 'name',
|
||||
'order' => 'asc',
|
||||
'post_type' => 'shop_coupon',
|
||||
'post_status' => 'publish',
|
||||
'paged' => $pageNumber,
|
||||
];
|
||||
// Filtering coupons by discount type
|
||||
if ($discountType) {
|
||||
$args['meta_key'] = 'discount_type';
|
||||
$args['meta_value'] = $discountType;
|
||||
}
|
||||
// Search coupon by a query string
|
||||
if ($search) {
|
||||
$args['s'] = $search;
|
||||
}
|
||||
|
||||
$includeCoupons = [];
|
||||
// We need to include the coupon with the given ID in the first page
|
||||
if ($includeCouponIds && $pageNumber === 1) {
|
||||
$includeArgs = $args;
|
||||
$includeArgs['include'] = $includeCouponIds;
|
||||
$includeCoupons = $this->wp->getPosts($includeArgs);
|
||||
}
|
||||
|
||||
// We remove duplicates because one of the remaining pages might contain the coupon with the given ID
|
||||
$result = array_merge($includeCoupons, $this->wp->getPosts($args));
|
||||
$result = array_unique($result, SORT_REGULAR);
|
||||
return array_values($result);
|
||||
}
|
||||
|
||||
public function wcGetPriceDecimalSeparator() {
|
||||
return wc_get_price_decimal_separator();
|
||||
}
|
||||
|
||||
public function getLatestCoupon(): ?string {
|
||||
$coupons = $this->wp->getPosts([
|
||||
'numberposts' => 1,
|
||||
'orderby' => 'date_created',
|
||||
'order' => 'desc',
|
||||
'post_type' => 'shop_coupon',
|
||||
'post_status' => 'publish',
|
||||
]);
|
||||
$coupon = reset($coupons);
|
||||
|
||||
return $coupon ? $coupon->post_title : null; // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
|
||||
}
|
||||
|
||||
public function getPaymentGateways() {
|
||||
return $this->WC()->payment_gateways();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all available shipping methods formatted
|
||||
* in a way to be used in the 'used shipping method' segment.
|
||||
*/
|
||||
public function getShippingMethodInstancesData(): array {
|
||||
$shippingZones = \WC_Shipping_Zones::get_zones();
|
||||
$formattedShippingMethodData = [];
|
||||
|
||||
foreach ($shippingZones as $shippingZone) {
|
||||
$formattedShippingMethodData = array_merge(
|
||||
$formattedShippingMethodData,
|
||||
$this->formatShippingMethods($shippingZone['shipping_methods'], $shippingZone['zone_name'])
|
||||
);
|
||||
}
|
||||
|
||||
// special shipping zone that includes locations not covered by the configured shipping zones
|
||||
$outOfCoverageShippingZone = new \WC_Shipping_Zone(0);
|
||||
$formattedShippingMethodData = array_merge(
|
||||
$formattedShippingMethodData,
|
||||
$this->formatShippingMethods($outOfCoverageShippingZone->get_shipping_methods(), $outOfCoverageShippingZone->get_zone_name())
|
||||
);
|
||||
|
||||
$keyedZones = [];
|
||||
|
||||
foreach ($formattedShippingMethodData as $shippingMethodArray) {
|
||||
$keyedZones[$shippingMethodArray['instanceId']] = $shippingMethodArray;
|
||||
}
|
||||
|
||||
return $keyedZones;
|
||||
}
|
||||
|
||||
public function wcGetAttributeTaxonomies(): array {
|
||||
return wc_get_attribute_taxonomies();
|
||||
}
|
||||
|
||||
protected function formatShippingMethods(array $shippingMethods, string $shippingZoneName): array {
|
||||
$formattedShippingMethods = [];
|
||||
|
||||
foreach ($shippingMethods as $shippingMethod) {
|
||||
$formattedShippingMethods[] = [
|
||||
'instanceId' => (string)$shippingMethod->instance_id, // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
|
||||
'name' => "{$shippingMethod->title} ({$shippingZoneName})",
|
||||
];
|
||||
}
|
||||
|
||||
return $formattedShippingMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the current request is processing a WooCommerce checkout.
|
||||
* Works for both the normal checkout and the block checkout.
|
||||
*
|
||||
* This solution is not ideal, but I checked with a few WooCommerce developers,
|
||||
* and it is what they suggested. There is no helper function provided by Woo
|
||||
* for this.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isCheckoutRequest(): bool {
|
||||
$requestUri = !empty($_SERVER['REQUEST_URI']) ? sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])) : '';
|
||||
$isRegularCheckout = is_checkout();
|
||||
$isBlockCheckout = WC()->is_rest_api_request()
|
||||
&& (strpos($requestUri, 'wc/store/checkout') !== false || strpos($requestUri, 'wc/store/v1/checkout') !== false);
|
||||
|
||||
return $isRegularCheckout || $isBlockCheckout;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace MailPoet\WooCommerce\Integrations;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Entities\SegmentEntity;
|
||||
use MailPoet\Entities\SubscriberEntity;
|
||||
use MailPoet\Entities\SubscriberSegmentEntity;
|
||||
use MailPoet\Subscribers\SubscribersRepository;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
|
||||
class AutomateWooHooks {
|
||||
const AUTOMATE_WOO_PLUGIN_SLUG = 'automatewoo/automatewoo.php';
|
||||
|
||||
/** @var SubscribersRepository */
|
||||
private $subscribersRepository;
|
||||
|
||||
/** @var WPFunctions */
|
||||
private $wp;
|
||||
|
||||
public function __construct(
|
||||
SubscribersRepository $subscribersRepository,
|
||||
WPFunctions $wp
|
||||
) {
|
||||
$this->subscribersRepository = $subscribersRepository;
|
||||
$this->wp = $wp;
|
||||
}
|
||||
|
||||
public function isAutomateWooActive(): bool {
|
||||
return $this->wp->isPluginActive(self::AUTOMATE_WOO_PLUGIN_SLUG);
|
||||
}
|
||||
|
||||
public function areMethodsAvailable(): bool {
|
||||
return class_exists('AutomateWoo\Customer_Factory') && method_exists('AutomateWoo\Customer_Factory', 'get_by_email') &&
|
||||
class_exists('AutomateWoo\Customer') && method_exists('AutomateWoo\Customer', 'opt_out');
|
||||
}
|
||||
|
||||
public function isAutomateWooReady(): bool {
|
||||
return $this->isAutomateWooActive() && $this->areMethodsAvailable();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \AutomateWoo\Customer|false
|
||||
*/
|
||||
public function getAutomateWooCustomer(string $email) {
|
||||
// AutomateWoo\Customer_Factory::get_by_email() returns false if customer is not found
|
||||
// Second parameter is set to false to prevent creating new customer if not found
|
||||
return \AutomateWoo\Customer_Factory::get_by_email($email, false);
|
||||
}
|
||||
|
||||
public function setup(): void {
|
||||
if (!$this->isAutomateWooReady()) {
|
||||
return;
|
||||
}
|
||||
$this->wp->addAction(SubscriberEntity::HOOK_SUBSCRIBER_STATUS_CHANGED, [$this, 'syncSubscriber'], 10, 1);
|
||||
$this->wp->addAction('mailpoet_segment_subscribed', [$this, 'maybeOptInSubscriber'], 10, 1);
|
||||
}
|
||||
|
||||
public function optOutSubscriber($subscriber): void {
|
||||
if (!$this->isAutomateWooReady() || !$subscriber) {
|
||||
return;
|
||||
}
|
||||
|
||||
$automateWooCustomer = $this->getAutomateWooCustomer($subscriber->getEmail());
|
||||
if (!$automateWooCustomer) {
|
||||
return;
|
||||
}
|
||||
|
||||
$automateWooCustomer->opt_out();
|
||||
}
|
||||
|
||||
public function optInSubscriber($subscriber): void {
|
||||
if (!$this->isAutomateWooReady() || !$subscriber) {
|
||||
return;
|
||||
}
|
||||
|
||||
$automateWooCustomer = $this->getAutomateWooCustomer($subscriber->getEmail());
|
||||
if (!$automateWooCustomer) {
|
||||
return;
|
||||
}
|
||||
|
||||
$automateWooCustomer->opt_in();
|
||||
}
|
||||
|
||||
public function syncSubscriber(int $subscriberId): void {
|
||||
$subscriber = $this->subscribersRepository->findOneById($subscriberId);
|
||||
if (!$subscriber || !$subscriber->getEmail()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->isWooCommerceSubscribed($subscriber)) {
|
||||
$this->optInSubscriber($subscriber);
|
||||
} else {
|
||||
$this->optOutSubscriber($subscriber);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opt-In the subscriber in AW only if the subscriber belongs to WooCommerce list.
|
||||
*/
|
||||
public function maybeOptInSubscriber(SubscriberSegmentEntity $subscriberSegment) {
|
||||
if ($subscriberSegment->getSegment() && $subscriberSegment->getSegment()->getType() === SegmentEntity::TYPE_WC_USERS) {
|
||||
$this->optInSubscriber($subscriberSegment->getSubscriber());
|
||||
}
|
||||
}
|
||||
|
||||
private function isWooCommerceSubscribed(SubscriberEntity $subscriber) {
|
||||
return $subscriber->getStatus() === SubscriberEntity::STATUS_SUBSCRIBED
|
||||
&& $this->subscribersRepository->getWooCommerceSegmentSubscriber($subscriber->getEmail());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<?php
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace MailPoet\WooCommerce;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task;
|
||||
use MailPoet\Config\Menu;
|
||||
use MailPoet\DI\ContainerWrapper;
|
||||
use MailPoet\Settings\SettingsController;
|
||||
|
||||
/**
|
||||
* MailPoet task that is added to the WooCommerce homepage.
|
||||
*/
|
||||
class MailPoetTask extends Task {
|
||||
public function get_id(): string { // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
|
||||
return 'mailpoet_task';
|
||||
}
|
||||
|
||||
public function get_title(): string { // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
|
||||
if ($this->is_complete()) {
|
||||
return esc_html__('MailPoet is ready to send marketing emails from your store', 'mailpoet');
|
||||
}
|
||||
|
||||
return esc_html__('Set up email marketing with MailPoet', 'mailpoet');
|
||||
}
|
||||
|
||||
public function get_content(): string { // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* String that is displayed below the title of the task indicating the estimated completion time.
|
||||
*/
|
||||
public function get_time(): string { // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Link used when the user clicks on the title of the task.
|
||||
*/
|
||||
public function get_action_url(): string { // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
|
||||
if ($this->is_complete()) {
|
||||
return admin_url('admin.php?page=' . Menu::MAIN_PAGE_SLUG);
|
||||
}
|
||||
|
||||
return admin_url('admin.php?page=' . Menu::WELCOME_WIZARD_PAGE_SLUG . '&mailpoet_wizard_loaded_via_woocommerce');
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the task is completed.
|
||||
* If the setting 'version' is not null it means the welcome wizard
|
||||
* was already completed so we mark this task as completed as well.
|
||||
*/
|
||||
public function is_complete(): bool { // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
|
||||
$settings = ContainerWrapper::getInstance()->get(SettingsController::class);
|
||||
$version = $settings->get('version');
|
||||
|
||||
return $version !== null;
|
||||
}
|
||||
}
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace MailPoet\WooCommerce\MultichannelMarketing;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use Automattic\WooCommerce\Admin\Marketing\MarketingCampaign;
|
||||
use Automattic\WooCommerce\Admin\Marketing\MarketingCampaignType;
|
||||
use Automattic\WooCommerce\Admin\Marketing\MarketingChannelInterface;
|
||||
use Automattic\WooCommerce\Admin\Marketing\Price;
|
||||
use MailPoet\Config\Menu;
|
||||
|
||||
class MPMarketingChannel implements MarketingChannelInterface {
|
||||
|
||||
/**
|
||||
* @var MarketingCampaignType[]
|
||||
*/
|
||||
private $campaignTypes;
|
||||
|
||||
/**
|
||||
* @var MPMarketingChannelDataController
|
||||
*/
|
||||
private $channelDataController;
|
||||
|
||||
const CAMPAIGN_TYPE_NEWSLETTERS = 'mailpoet-newsletters';
|
||||
const CAMPAIGN_TYPE_POST_NOTIFICATIONS = 'mailpoet-post-notifications';
|
||||
const CAMPAIGN_TYPE_AUTOMATIONS = 'mailpoet-automations';
|
||||
|
||||
public function __construct(
|
||||
MPMarketingChannelDataController $channelDataController
|
||||
) {
|
||||
$this->channelDataController = $channelDataController;
|
||||
$this->campaignTypes = $this->generateCampaignTypes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unique identifier string for the marketing channel extension, also known as the plugin slug.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_slug(): string { // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
|
||||
return 'mailpoet';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the marketing channel.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_name(): string { // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
|
||||
return __('MailPoet', 'mailpoet');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description of the marketing channel.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_description(): string { // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
|
||||
return __('Create and send newsletters, post notifications and welcome emails from your WordPress.', 'mailpoet');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the channel icon.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_icon_url(): string { // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
|
||||
return $this->channelDataController->getIconUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the setup status of the marketing channel.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_setup_completed(): bool { // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
|
||||
return $this->channelDataController->isMPSetupComplete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL to the settings page, or the link to complete the setup/onboarding if the channel has not been set up yet.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_setup_url(): string { // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
|
||||
if ($this->channelDataController->isMPSetupComplete()) {
|
||||
return admin_url('admin.php?page=' . Menu::MAIN_PAGE_SLUG);
|
||||
}
|
||||
|
||||
return admin_url('admin.php?page=' . Menu::WELCOME_WIZARD_PAGE_SLUG . '&mailpoet_wizard_loaded_via_woocommerce_marketing_dashboard');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the status of the marketing channel's product listings.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_product_listings_status(): string { // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
|
||||
if (!$this->channelDataController->isMailPoetSendingServiceEnabled()) {
|
||||
return self::PRODUCT_LISTINGS_NOT_APPLICABLE;
|
||||
}
|
||||
|
||||
// Check for error status. It's null by default when there isn't an error
|
||||
$sendingStatus = $this->channelDataController->getMailPoetSendingStatus();
|
||||
|
||||
if ($sendingStatus) {
|
||||
return self::PRODUCT_LISTINGS_SYNC_FAILED;
|
||||
}
|
||||
|
||||
return self::PRODUCT_LISTINGS_SYNCED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of channel issues/errors (e.g. account-related errors, product synchronization issues, etc.).
|
||||
*
|
||||
* @return int The number of issues to resolve, or 0 if there are no issues with the channel.
|
||||
*/
|
||||
public function get_errors_count(): int { // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
|
||||
return $this->channelDataController->getErrorCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of marketing campaign types that the channel supports.
|
||||
*
|
||||
* @return MarketingCampaignType[] Array of marketing campaign type objects.
|
||||
*/
|
||||
public function get_supported_campaign_types(): array { // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
|
||||
return $this->campaignTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of the channel's marketing campaigns.
|
||||
*
|
||||
* @return MarketingCampaign[]
|
||||
*/
|
||||
public function get_campaigns(): array { // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
|
||||
$allCampaigns = $this->generateCampaigns();
|
||||
|
||||
if (empty($allCampaigns)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $allCampaigns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the marketing channel campaign types
|
||||
*
|
||||
* @return MarketingCampaignType[]
|
||||
*/
|
||||
protected function generateCampaignTypes(): array {
|
||||
return [
|
||||
self::CAMPAIGN_TYPE_NEWSLETTERS => new MarketingCampaignType(
|
||||
'mailpoet-newsletters',
|
||||
$this,
|
||||
__('MailPoet Newsletters', 'mailpoet'),
|
||||
__(
|
||||
'Send a newsletter with images, buttons, dividers, and social bookmarks. Or, just send a basic text email.',
|
||||
'mailpoet',
|
||||
),
|
||||
admin_url('admin.php?page=' . Menu::EMAILS_PAGE_SLUG . '&loadedvia=woo_multichannel_dashboard#/new/standard'),
|
||||
$this->get_icon_url()
|
||||
),
|
||||
self::CAMPAIGN_TYPE_POST_NOTIFICATIONS => new MarketingCampaignType(
|
||||
'mailpoet-post-notifications',
|
||||
$this,
|
||||
__('MailPoet Post Notifications', 'mailpoet'),
|
||||
__(
|
||||
'Let MailPoet email your subscribers with your latest content. You can send daily, weekly, monthly, or even immediately after publication.',
|
||||
'mailpoet',
|
||||
),
|
||||
admin_url('admin.php?page=' . Menu::EMAILS_PAGE_SLUG . '&loadedvia=woo_multichannel_dashboard#/new/notification'),
|
||||
$this->get_icon_url()
|
||||
),
|
||||
self::CAMPAIGN_TYPE_AUTOMATIONS => new MarketingCampaignType(
|
||||
'mailpoet-automations',
|
||||
$this,
|
||||
__('MailPoet Automations', 'mailpoet'),
|
||||
__('Set up automations to send abandoned cart reminders, welcome new subscribers, celebrate first-time buyers, and much more.', 'mailpoet'),
|
||||
admin_url('admin.php?page=' . Menu::AUTOMATION_TEMPLATES_PAGE_SLUG . '&loadedvia=woo_multichannel_dashboard'),
|
||||
$this->get_icon_url()
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
protected function generateCampaigns(): array {
|
||||
return array_map(
|
||||
function (array $data) {
|
||||
$cost = null;
|
||||
|
||||
if (isset($data['price'])) {
|
||||
$cost = new Price((string)$data['price']['amount'], $data['price']['currency']);
|
||||
}
|
||||
|
||||
return new MarketingCampaign(
|
||||
$data['id'],
|
||||
$data['campaignType'],
|
||||
$data['name'],
|
||||
$data['url'],
|
||||
$cost,
|
||||
);
|
||||
},
|
||||
array_merge(
|
||||
$this->channelDataController->getAutomations($this->campaignTypes[self::CAMPAIGN_TYPE_AUTOMATIONS]),
|
||||
$this->channelDataController->getPostNotificationNewsletters($this->campaignTypes[self::CAMPAIGN_TYPE_POST_NOTIFICATIONS]),
|
||||
$this->channelDataController->getStandardNewsletterList($this->campaignTypes[self::CAMPAIGN_TYPE_NEWSLETTERS])
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace MailPoet\WooCommerce\MultichannelMarketing;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
class MPMarketingChannelController {
|
||||
|
||||
/**
|
||||
* @var MPMarketingChannelDataController
|
||||
*/
|
||||
private $channelDataController;
|
||||
|
||||
public function __construct(
|
||||
MPMarketingChannelDataController $channelDataController
|
||||
) {
|
||||
$this->channelDataController = $channelDataController;
|
||||
}
|
||||
|
||||
public function registerMarketingChannel($registeredMarketingChannels): array {
|
||||
return array_merge($registeredMarketingChannels, [
|
||||
new MPMarketingChannel(
|
||||
$this->channelDataController
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
+227
@@ -0,0 +1,227 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace MailPoet\WooCommerce\MultichannelMarketing;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Automation\Engine\Data\Automation;
|
||||
use MailPoet\Automation\Engine\Storage\AutomationStorage;
|
||||
use MailPoet\Automation\Integrations\MailPoet\Analytics\Controller\OverviewStatisticsController;
|
||||
use MailPoet\Automation\Integrations\MailPoet\Analytics\Entities\QueryWithCompare;
|
||||
use MailPoet\Config\Menu;
|
||||
use MailPoet\Newsletter\NewslettersRepository;
|
||||
use MailPoet\Newsletter\Statistics\NewsletterStatisticsRepository;
|
||||
use MailPoet\Newsletter\Statistics\WooCommerceRevenue;
|
||||
use MailPoet\Services\AuthorizedEmailsController;
|
||||
use MailPoet\Services\Bridge;
|
||||
use MailPoet\Settings\SettingsController;
|
||||
use MailPoet\Util\CdnAssetUrl;
|
||||
use MailPoet\WooCommerce\Helper;
|
||||
use MailPoetVendor\Carbon\Carbon;
|
||||
|
||||
/**
|
||||
* Created to pass data to the `MPMarketingChannel`.
|
||||
*
|
||||
* We create an instance of this class with the MailPoet DI ContainerInterface
|
||||
*
|
||||
* This provides the class access to other MailPoet services,
|
||||
* preventing us from overloading the `MPMarketingChannelController` class with imports and duplicating efforts
|
||||
*/
|
||||
class MPMarketingChannelDataController {
|
||||
|
||||
/** @var CdnAssetUrl */
|
||||
private $cdnAssetUrl;
|
||||
|
||||
/**
|
||||
* @var SettingsController
|
||||
*/
|
||||
private $settings;
|
||||
|
||||
/**
|
||||
* @var Bridge
|
||||
*/
|
||||
private $bridge;
|
||||
|
||||
/**
|
||||
* @var NewslettersRepository
|
||||
*/
|
||||
private $newsletterRepository;
|
||||
|
||||
/**
|
||||
* @var Helper
|
||||
*/
|
||||
private $woocommerceHelper;
|
||||
|
||||
/**
|
||||
* @var AutomationStorage
|
||||
*/
|
||||
private $automationStorage;
|
||||
|
||||
/**
|
||||
* @var NewsletterStatisticsRepository
|
||||
*/
|
||||
private $newsletterStatisticsRepository;
|
||||
|
||||
/**
|
||||
* @var OverviewStatisticsController
|
||||
*/
|
||||
private $overviewStatisticsController;
|
||||
|
||||
public function __construct(
|
||||
CdnAssetUrl $cdnAssetUrl,
|
||||
SettingsController $settings,
|
||||
Bridge $bridge,
|
||||
NewslettersRepository $newsletterRepository,
|
||||
Helper $woocommerceHelper,
|
||||
AutomationStorage $automationStorage,
|
||||
NewsletterStatisticsRepository $newsletterStatisticsRepository,
|
||||
OverviewStatisticsController $overviewStatisticsController
|
||||
) {
|
||||
$this->cdnAssetUrl = $cdnAssetUrl;
|
||||
$this->settings = $settings;
|
||||
$this->bridge = $bridge;
|
||||
$this->newsletterRepository = $newsletterRepository;
|
||||
$this->automationStorage = $automationStorage;
|
||||
$this->woocommerceHelper = $woocommerceHelper;
|
||||
$this->newsletterStatisticsRepository = $newsletterStatisticsRepository;
|
||||
$this->overviewStatisticsController = $overviewStatisticsController;
|
||||
}
|
||||
|
||||
public function getIconUrl(): string {
|
||||
return $this->cdnAssetUrl->generateCdnUrl('icon-white-123x128.png');
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the task is completed.
|
||||
* If the setting 'version' is not null it means the welcome wizard
|
||||
* was already completed so we mark this task as completed as well.
|
||||
*/
|
||||
public function isMPSetupComplete(): bool {
|
||||
$version = $this->settings->get('version');
|
||||
|
||||
return $version !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is MSS Enabled?
|
||||
* @return bool
|
||||
*/
|
||||
public function isMailPoetSendingServiceEnabled(): bool {
|
||||
return $this->bridge->isMailpoetSendingServiceEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for error status. It's null by default when there isn't an error
|
||||
* @return mixed
|
||||
*/
|
||||
public function getMailPoetSendingStatus() {
|
||||
return $this->settings->get('mta_log.status');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of errors available
|
||||
* Mostly likely sending errors
|
||||
* @return int
|
||||
*/
|
||||
public function getErrorCount(): int {
|
||||
$error = $this->settings->get('mta_log.error');
|
||||
|
||||
$count = 0;
|
||||
|
||||
if (!empty($error)) {
|
||||
$count++;
|
||||
}
|
||||
|
||||
$validationError = $this->settings->get(AuthorizedEmailsController::AUTHORIZED_EMAIL_ADDRESSES_ERROR_SETTING);
|
||||
|
||||
if ($validationError && isset($validationError['invalid_sender_address'])) {
|
||||
$count++;
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
public function getStandardNewsletterList($campaignType): array {
|
||||
return $this->getNewsletterTypeLists(
|
||||
// fetch the most recently sent post-notification history newsletters limited to ten
|
||||
$this->newsletterRepository->getStandardNewsletterListWithMultipleStatuses(10),
|
||||
$campaignType
|
||||
);
|
||||
}
|
||||
|
||||
public function getPostNotificationNewsletters($campaignType): array {
|
||||
return $this->getNewsletterTypeLists(
|
||||
// fetch the most recently sent post-notification history newsletters limited to ten
|
||||
$this->newsletterRepository->getNotificationHistoryItems(10),
|
||||
$campaignType
|
||||
);
|
||||
}
|
||||
|
||||
public function getAutomations($campaignType): array {
|
||||
$result = [];
|
||||
|
||||
// Fetch Automation stats within the last 90 days
|
||||
$primaryAfter = new \DateTimeImmutable((string)Carbon::now()->subDays(90)->toISOString());
|
||||
$primaryBefore = new \DateTimeImmutable((string)Carbon::now()->toISOString());
|
||||
$now = new \DateTimeImmutable('');
|
||||
|
||||
$query = new QueryWithCompare($primaryAfter, $primaryBefore, $now, $now);
|
||||
$userCurrency = $this->woocommerceHelper->getWoocommerceCurrency();
|
||||
|
||||
foreach ($this->automationStorage->getAutomations([Automation::STATUS_ACTIVE]) as $automation) {
|
||||
$automationId = (string)$automation->getId();
|
||||
|
||||
$automationStatistics = $this->overviewStatisticsController->getStatisticsForAutomation($automation, $query);
|
||||
|
||||
$result[] = [
|
||||
'id' => $automationId,
|
||||
'name' => $automation->getName(),
|
||||
'campaignType' => $campaignType,
|
||||
'url' => admin_url('admin.php?page=' . Menu::AUTOMATION_ANALYTICS_PAGE_SLUG . '&id=' . $automationId),
|
||||
'price' => [
|
||||
'amount' => isset($automationStatistics['revenue']['current']) ? $this->formatPrice($automationStatistics['revenue']['current']) : 0,
|
||||
'currency' => $userCurrency,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function getNewsletterTypeLists($allNewsletters, $campaignType): array {
|
||||
$result = [];
|
||||
|
||||
$userCurrency = $this->woocommerceHelper->getWoocommerceCurrency();
|
||||
|
||||
// fetch the most recent newsletters limited to ten
|
||||
foreach ($allNewsletters as $newsletter) {
|
||||
$newsLetterId = (string)$newsletter->getId();
|
||||
|
||||
/** @var ?WooCommerceRevenue $wooRevenue */
|
||||
$wooRevenue = $this->newsletterStatisticsRepository->getWooCommerceRevenue($newsletter);
|
||||
|
||||
$result[] = [
|
||||
'id' => $newsLetterId,
|
||||
'name' => $newsletter->getSubject(),
|
||||
'campaignType' => $campaignType,
|
||||
'url' => admin_url('admin.php?page=' . Menu::EMAILS_PAGE_SLUG . '/#/stats/' . $newsLetterId),
|
||||
'price' => [
|
||||
'amount' => $wooRevenue ? $this->formatPrice($wooRevenue->getValue()) : 0,
|
||||
'currency' => $userCurrency,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format amount to 2 dp
|
||||
* @param string|int|float $amount
|
||||
* @return string
|
||||
*/
|
||||
private function formatPrice($amount): string {
|
||||
return number_format((float)$amount, 2, '.', '');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<?php
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\WooCommerce;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Config\Renderer;
|
||||
use MailPoet\Settings\SettingsController;
|
||||
|
||||
class Settings {
|
||||
|
||||
/** @var Renderer */
|
||||
private $renderer;
|
||||
|
||||
/** @var SettingsController */
|
||||
private $settings;
|
||||
|
||||
public function __construct(
|
||||
Renderer $renderer,
|
||||
SettingsController $settings
|
||||
) {
|
||||
$this->renderer = $renderer;
|
||||
$this->settings = $settings;
|
||||
}
|
||||
|
||||
public function disableWooCommerceSettings() {
|
||||
if (
|
||||
!isset($_GET['tab'])
|
||||
|| $_GET['tab'] !== 'email'
|
||||
|| isset($_GET['section'])
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
//The templates are in our control and the inputs are sanitized.
|
||||
//phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $this->renderer->render('woocommerce/settings_button.html', [
|
||||
'woocommerce_template_id' => (int)$this->settings->get(TransactionalEmails::SETTING_EMAIL_ID),
|
||||
]);
|
||||
if (!(bool)$this->settings->get('woocommerce.use_mailpoet_editor')) {
|
||||
return;
|
||||
}
|
||||
// The templates are in our control and the inputs are sanitized.
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $this->renderer->render('woocommerce/settings_overlay.html', [
|
||||
'woocommerce_template_id' => (int)$this->settings->get(TransactionalEmails::SETTING_EMAIL_ID),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace MailPoet\WooCommerce;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Entities\SubscriberEntity;
|
||||
use MailPoet\Subscribers\SubscribersRepository;
|
||||
use WC_Order;
|
||||
|
||||
class SubscriberEngagement {
|
||||
|
||||
/** @var Helper */
|
||||
private $woocommerceHelper;
|
||||
|
||||
/** @var SubscribersRepository */
|
||||
private $subscribersRepository;
|
||||
|
||||
public function __construct(
|
||||
Helper $woocommerceHelper,
|
||||
SubscribersRepository $subscribersRepository
|
||||
) {
|
||||
$this->woocommerceHelper = $woocommerceHelper;
|
||||
$this->subscribersRepository = $subscribersRepository;
|
||||
}
|
||||
|
||||
public function updateSubscriberEngagement($orderId): void {
|
||||
$order = $this->woocommerceHelper->wcGetOrder($orderId);
|
||||
if (!$order instanceof WC_Order) {
|
||||
return;
|
||||
}
|
||||
|
||||
$subscriber = $this->subscribersRepository->findOneBy(['email' => $order->get_billing_email()]);
|
||||
if (!$subscriber instanceof SubscriberEntity) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->subscribersRepository->maybeUpdateLastEngagement($subscriber);
|
||||
}
|
||||
|
||||
public function updateSubscriberLastPurchase($orderId): void {
|
||||
$order = $this->woocommerceHelper->wcGetOrder($orderId);
|
||||
if (!$order instanceof WC_Order) {
|
||||
return;
|
||||
}
|
||||
|
||||
$subscriber = $this->subscribersRepository->findOneBy(['email' => $order->get_billing_email()]);
|
||||
if (!$subscriber instanceof SubscriberEntity) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->subscribersRepository->maybeUpdateLastPurchaseAt($subscriber);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\WooCommerce;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Entities\SubscriberEntity;
|
||||
use MailPoet\Segments\SegmentsRepository;
|
||||
use MailPoet\Settings\SettingsController;
|
||||
use MailPoet\Subscribers\ConfirmationEmailMailer;
|
||||
use MailPoet\Subscribers\Source;
|
||||
use MailPoet\Subscribers\SubscriberSegmentRepository;
|
||||
use MailPoet\Subscribers\SubscribersRepository;
|
||||
use MailPoet\Util\Helpers;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
use MailPoetVendor\Carbon\Carbon;
|
||||
|
||||
class Subscription {
|
||||
const CHECKOUT_OPTIN_INPUT_NAME = 'mailpoet_woocommerce_checkout_optin';
|
||||
const CHECKOUT_OPTIN_PRESENCE_CHECK_INPUT_NAME = 'mailpoet_woocommerce_checkout_optin_present';
|
||||
const OPTIN_ENABLED_SETTING_NAME = 'woocommerce.optin_on_checkout.enabled';
|
||||
const OPTIN_SEGMENTS_SETTING_NAME = 'woocommerce.optin_on_checkout.segments';
|
||||
const OPTIN_MESSAGE_SETTING_NAME = 'woocommerce.optin_on_checkout.message';
|
||||
const OPTIN_POSITION_SETTING_NAME = 'woocommerce.optin_on_checkout.position';
|
||||
|
||||
private $allowedHtml = [
|
||||
'input' => [
|
||||
'type' => true,
|
||||
'name' => true,
|
||||
'id' => true,
|
||||
'class' => true,
|
||||
'value' => true,
|
||||
'checked' => true,
|
||||
],
|
||||
'span' => [
|
||||
'class' => true,
|
||||
],
|
||||
'label' => [
|
||||
'class' => true,
|
||||
'data-automation-id' => true,
|
||||
'for' => true,
|
||||
],
|
||||
'p' => [
|
||||
'class' => true,
|
||||
'id' => true,
|
||||
'data-priority' => true,
|
||||
],
|
||||
];
|
||||
|
||||
/** @var SettingsController */
|
||||
private $settings;
|
||||
|
||||
/** @var WPFunctions */
|
||||
private $wp;
|
||||
|
||||
/** @var Helper */
|
||||
private $wcHelper;
|
||||
|
||||
/** @var ConfirmationEmailMailer */
|
||||
private $confirmationEmailMailer;
|
||||
|
||||
/** @var SubscribersRepository */
|
||||
private $subscribersRepository;
|
||||
|
||||
/** @var SegmentsRepository */
|
||||
private $segmentsRepository;
|
||||
|
||||
/** @var SubscriberSegmentRepository */
|
||||
private $subscriberSegmentRepository;
|
||||
|
||||
public function __construct(
|
||||
SettingsController $settings,
|
||||
ConfirmationEmailMailer $confirmationEmailMailer,
|
||||
WPFunctions $wp,
|
||||
Helper $wcHelper,
|
||||
SubscribersRepository $subscribersRepository,
|
||||
SegmentsRepository $segmentsRepository,
|
||||
SubscriberSegmentRepository $subscriberSegmentRepository
|
||||
) {
|
||||
$this->settings = $settings;
|
||||
$this->wp = $wp;
|
||||
$this->wcHelper = $wcHelper;
|
||||
$this->confirmationEmailMailer = $confirmationEmailMailer;
|
||||
$this->subscribersRepository = $subscribersRepository;
|
||||
$this->segmentsRepository = $segmentsRepository;
|
||||
$this->subscriberSegmentRepository = $subscriberSegmentRepository;
|
||||
}
|
||||
|
||||
public function extendWooCommerceCheckoutForm() {
|
||||
$inputName = self::CHECKOUT_OPTIN_INPUT_NAME;
|
||||
$checked = false;
|
||||
if (!empty($_POST[self::CHECKOUT_OPTIN_INPUT_NAME])) {
|
||||
$checked = true;
|
||||
}
|
||||
$labelString = $this->settings->get(self::OPTIN_MESSAGE_SETTING_NAME);
|
||||
$template = (string)$this->wp->applyFilters(
|
||||
'mailpoet_woocommerce_checkout_optin_template',
|
||||
wp_kses(
|
||||
$this->getSubscriptionField($inputName, $checked, $labelString),
|
||||
$this->allowedHtml
|
||||
),
|
||||
$inputName,
|
||||
$checked,
|
||||
$labelString
|
||||
);
|
||||
// The template has been sanitized above and can be considered safe.
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $template;
|
||||
if ($template) {
|
||||
$field = $this->getSubscriptionPresenceCheckField();
|
||||
echo wp_kses($field, $this->allowedHtml);
|
||||
}
|
||||
}
|
||||
|
||||
private function getSubscriptionField($inputName, $checked, $labelString) {
|
||||
$checked = checked($checked, true, false);
|
||||
|
||||
return '<label class="woocommerce-form__label woocommerce-form__label-for-checkbox checkbox" data-automation-id="woo-commerce-subscription-opt-in">
|
||||
<input id="mailpoet_woocommerce_checkout_optin" class="woocommerce-form__input woocommerce-form__input-checkbox input-checkbox" ' . $checked . ' type="checkbox" name="' . $this->wp->escAttr($inputName) . '" value="1" />
|
||||
<span>' . $this->wp->escHtml($labelString) . '</span>
|
||||
</label>';
|
||||
}
|
||||
|
||||
private function getSubscriptionPresenceCheckField() {
|
||||
$field = $this->wcHelper->woocommerceFormField(
|
||||
self::CHECKOUT_OPTIN_PRESENCE_CHECK_INPUT_NAME,
|
||||
[
|
||||
'type' => 'hidden',
|
||||
'return' => true,
|
||||
],
|
||||
1
|
||||
);
|
||||
if ($field) {
|
||||
return $field;
|
||||
}
|
||||
// Workaround for older WooCommerce versions (below 4.6.0) that don't support hidden fields
|
||||
// We can remove it after we drop support of older WooCommerce
|
||||
$field = $this->wcHelper->woocommerceFormField(
|
||||
self::CHECKOUT_OPTIN_PRESENCE_CHECK_INPUT_NAME,
|
||||
[
|
||||
'type' => 'text',
|
||||
'return' => true,
|
||||
],
|
||||
1
|
||||
);
|
||||
return str_replace('type="text"', 'type="hidden"', $field);
|
||||
}
|
||||
|
||||
public function subscribeOnOrderPay($orderId) {
|
||||
$wcOrder = $this->wcHelper->wcGetOrder($orderId);
|
||||
if (!$wcOrder instanceof \WC_Order) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$data['billing_email'] = $wcOrder->get_billing_email();
|
||||
$this->subscribeOnCheckout($orderId, $data);
|
||||
}
|
||||
|
||||
public function subscribeOnCheckout($orderId, $data) {
|
||||
$this->triggerAutomateWooOptin();
|
||||
if (empty($data['billing_email'])) {
|
||||
// no email in posted order data
|
||||
return null;
|
||||
}
|
||||
|
||||
$subscriber = $this->subscribersRepository->findOneBy(
|
||||
['email' => $data['billing_email'], 'isWoocommerceUser' => 1]
|
||||
);
|
||||
|
||||
if (!$subscriber) {
|
||||
// no subscriber: WooCommerce sync didn't work
|
||||
return null;
|
||||
}
|
||||
|
||||
$checkoutOptin = !empty($_POST[self::CHECKOUT_OPTIN_INPUT_NAME]);
|
||||
|
||||
return $this->handleSubscriberOptin($subscriber, $checkoutOptin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe a subscriber.
|
||||
*
|
||||
* @param SubscriberEntity $subscriber Subscriber object
|
||||
* @param bool $shouldSubscribe Whether the subscriber should be subscribed
|
||||
*/
|
||||
public function handleSubscriberOptin(SubscriberEntity $subscriber, bool $shouldSubscribe): bool {
|
||||
$wcSegment = $this->segmentsRepository->getWooCommerceSegment();
|
||||
|
||||
$segmentIds = (array)$this->settings->get(self::OPTIN_SEGMENTS_SETTING_NAME, []);
|
||||
$moreSegmentsToSubscribe = [];
|
||||
if (!empty($segmentIds)) {
|
||||
$moreSegmentsToSubscribe = $this->segmentsRepository->findBy(['id' => $segmentIds]);
|
||||
}
|
||||
$signupConfirmation = $this->settings->get('signup_confirmation');
|
||||
|
||||
if ($shouldSubscribe) {
|
||||
$subscriber->setSource(Source::WOOCOMMERCE_CHECKOUT);
|
||||
|
||||
if (
|
||||
($subscriber->getStatus() === SubscriberEntity::STATUS_SUBSCRIBED)
|
||||
|| ((bool)$signupConfirmation['enabled'] === false)
|
||||
) {
|
||||
$this->subscribe($subscriber);
|
||||
} else {
|
||||
$this->requireSubscriptionConfirmation($subscriber);
|
||||
}
|
||||
|
||||
$this->subscriberSegmentRepository->subscribeToSegments($subscriber, array_merge([$wcSegment], $moreSegmentsToSubscribe));
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function hideAutomateWooOptinCheckbox(): void {
|
||||
if (!$this->wp->isPluginActive('automatewoo/automatewoo.php')) {
|
||||
return;
|
||||
}
|
||||
// Hide AutomateWoo checkout opt-in so we won't end up with two opt-ins
|
||||
$this->wp->removeAction(
|
||||
'woocommerce_checkout_after_terms_and_conditions',
|
||||
['AutomateWoo\Frontend', 'output_checkout_optin_checkbox']
|
||||
);
|
||||
}
|
||||
|
||||
private function triggerAutomateWooOptin(): void {
|
||||
if (
|
||||
!$this->wp->isPluginActive('automatewoo/automatewoo.php')
|
||||
|| empty($_POST[self::CHECKOUT_OPTIN_INPUT_NAME])
|
||||
) {
|
||||
return;
|
||||
}
|
||||
// Emulate checkout opt-in triggering for AutomateWoo
|
||||
$_POST['automatewoo_optin'] = 'On';
|
||||
}
|
||||
|
||||
private function subscribe(SubscriberEntity $subscriber) {
|
||||
$subscriber->setStatus(SubscriberEntity::STATUS_SUBSCRIBED);
|
||||
if (empty($subscriber->getConfirmedIp()) && empty($subscriber->getConfirmedAt())) {
|
||||
$subscriber->setConfirmedIp(Helpers::getIP());
|
||||
$subscriber->setConfirmedAt(new Carbon());
|
||||
}
|
||||
|
||||
$this->subscribersRepository->persist($subscriber);
|
||||
$this->subscribersRepository->flush();
|
||||
}
|
||||
|
||||
private function requireSubscriptionConfirmation(SubscriberEntity $subscriber) {
|
||||
$subscriber->setStatus(SubscriberEntity::STATUS_UNCONFIRMED);
|
||||
$this->subscribersRepository->persist($subscriber);
|
||||
$this->subscribersRepository->flush();
|
||||
|
||||
try {
|
||||
$this->confirmationEmailMailer->sendConfirmationEmailOnce($subscriber);
|
||||
} catch (\Exception $e) {
|
||||
// ignore errors
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace MailPoet\WooCommerce;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Entities\NewsletterOptionFieldEntity;
|
||||
use MailPoet\Logging\LoggerFactory;
|
||||
use MailPoet\Newsletter\NewslettersRepository;
|
||||
use MailPoet\Statistics\StatisticsWooCommercePurchasesRepository;
|
||||
|
||||
class Tracker {
|
||||
|
||||
/** @var StatisticsWooCommercePurchasesRepository */
|
||||
private $wooPurchasesRepository;
|
||||
|
||||
/** @var LoggerFactory */
|
||||
private $loggerFactory;
|
||||
|
||||
/** @var Helper */
|
||||
private $wooHelper;
|
||||
|
||||
/** @var NewslettersRepository */
|
||||
private $newslettersRepository;
|
||||
|
||||
public function __construct(
|
||||
StatisticsWooCommercePurchasesRepository $wooPurchasesRepository,
|
||||
NewslettersRepository $newslettersRepository,
|
||||
Helper $wooHelper,
|
||||
LoggerFactory $loggerFactory
|
||||
) {
|
||||
$this->wooPurchasesRepository = $wooPurchasesRepository;
|
||||
$this->newslettersRepository = $newslettersRepository;
|
||||
$this->wooHelper = $wooHelper;
|
||||
$this->loggerFactory = $loggerFactory;
|
||||
}
|
||||
|
||||
public function addTrackingData(array $data): array {
|
||||
try {
|
||||
$currency = $this->wooHelper->getWoocommerceCurrency();
|
||||
$analyticsData = $this->newslettersRepository->getAnalytics();
|
||||
$data['extensions']['mailpoet'] = [
|
||||
'campaigns_count' => $analyticsData['campaigns_count'],
|
||||
];
|
||||
$campaignData = $this->formatCampaignsData($this->wooPurchasesRepository->getRevenuesByCampaigns($currency));
|
||||
$data['extensions']['mailpoet'] = array_merge($data['extensions']['mailpoet'], $campaignData);
|
||||
} catch (\Throwable $e) {
|
||||
$this->loggerFactory->getLogger(LoggerFactory::TOPIC_TRACKING)->error($e->getMessage());
|
||||
return $data;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array{revenue: float, campaign_id: string|null, campaign_type: string, orders_count: int}> $campaignsData
|
||||
* @return array<string, string|int|float>
|
||||
*/
|
||||
private function formatCampaignsData(array $campaignsData): array {
|
||||
return array_reduce($campaignsData, function($result, array $campaign): array {
|
||||
$newsletter = $this->newslettersRepository->findOneById((int)$campaign['campaign_id']);
|
||||
$keyPrefix = 'campaign_' . ($campaign['campaign_id'] ?? 0);
|
||||
$result[$keyPrefix . '_revenue'] = $campaign['revenue'];
|
||||
$result[$keyPrefix . '_orders_count'] = $campaign['orders_count'];
|
||||
$result[$keyPrefix . '_type'] = $campaign['campaign_type'];
|
||||
$result[$keyPrefix . '_event'] = $newsletter ? (string)$newsletter->getOptionValue(NewsletterOptionFieldEntity::NAME_EVENT) : '';
|
||||
return $result;
|
||||
}, []);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\WooCommerce;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Newsletter\NewslettersRepository;
|
||||
use MailPoet\Settings\SettingsController;
|
||||
use MailPoet\WooCommerce\TransactionalEmails\Renderer;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
|
||||
class TransactionalEmailHooks {
|
||||
/** @var WPFunctions */
|
||||
private $wp;
|
||||
|
||||
/** @var SettingsController */
|
||||
private $settings;
|
||||
|
||||
/** @var Renderer */
|
||||
private $renderer;
|
||||
|
||||
/** @var NewslettersRepository */
|
||||
private $newsletterRepository;
|
||||
|
||||
/** @var TransactionalEmails */
|
||||
private $transactionalEmails;
|
||||
|
||||
public function __construct(
|
||||
WPFunctions $wp,
|
||||
SettingsController $settings,
|
||||
Renderer $renderer,
|
||||
NewslettersRepository $newsletterRepository,
|
||||
TransactionalEmails $transactionalEmails
|
||||
) {
|
||||
$this->wp = $wp;
|
||||
$this->settings = $settings;
|
||||
$this->renderer = $renderer;
|
||||
$this->newsletterRepository = $newsletterRepository;
|
||||
$this->transactionalEmails = $transactionalEmails;
|
||||
}
|
||||
|
||||
public function useTemplateForWoocommerceEmails() {
|
||||
$this->wp->addAction('woocommerce_email', function($wcEmails) {
|
||||
/** @var callable */
|
||||
$emailHeaderCallback = [$wcEmails, 'email_header'];
|
||||
/** @var callable */
|
||||
$emailFooterCallback = [$wcEmails, 'email_footer'];
|
||||
$this->wp->removeAction('woocommerce_email_header', $emailHeaderCallback);
|
||||
$this->wp->removeAction('woocommerce_email_footer', $emailFooterCallback);
|
||||
$this->wp->addAction('woocommerce_email_header', function($emailHeading) {
|
||||
$newsletterEntity = $this->getNewsletter();
|
||||
if ($newsletterEntity) {
|
||||
$this->renderer->render($newsletterEntity, $emailHeading);
|
||||
// The HTML is generated from a $newsletter entity and can be considered safe
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $this->renderer->getHTMLBeforeContent();
|
||||
}
|
||||
});
|
||||
$this->wp->addAction('woocommerce_email_footer', function() {
|
||||
// The HTML is generated from a $newsletter entity and can be considered safe
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $this->renderer->getHTMLAfterContent();
|
||||
// Woo uses output buffer to collect the rendered email content. We read it ourselves modify it and push it back to the buffer.
|
||||
$newsletterEntity = $this->getNewsletter();
|
||||
$renderedEmail = ob_get_clean();
|
||||
if ($newsletterEntity && $renderedEmail) {
|
||||
ob_start();
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $this->renderer->updateRenderedContent($newsletterEntity, $renderedEmail);
|
||||
}
|
||||
});
|
||||
$this->wp->addAction('woocommerce_email_styles', [$this, 'alterEmailStyles']);
|
||||
});
|
||||
}
|
||||
|
||||
private function getNewsletter(): ?NewsletterEntity {
|
||||
if (empty($this->settings->get(TransactionalEmails::SETTING_EMAIL_ID))) {
|
||||
return null;
|
||||
}
|
||||
$newsletter = $this->newsletterRepository->findOneById($this->settings->get(TransactionalEmails::SETTING_EMAIL_ID));
|
||||
if (!$newsletter) {
|
||||
// the newsletter should always be present in the database, if it s not we shouldn't keep using this feature
|
||||
// we need to recreate the newsletter and turn off the feature
|
||||
$this->transactionalEmails->init();
|
||||
$this->settings->set('woocommerce.use_mailpoet_editor', false);
|
||||
}
|
||||
return $newsletter;
|
||||
}
|
||||
|
||||
public function overrideStylesForWooEmails() {
|
||||
$this->wp->addAction('option_woocommerce_email_background_color', function($value) {
|
||||
$newsletter = $this->getNewsletter();
|
||||
if (!$newsletter instanceof NewsletterEntity) return $value;
|
||||
return $newsletter->getGlobalStyle('body', 'backgroundColor') ?? $value;
|
||||
});
|
||||
$this->wp->addAction('option_woocommerce_email_base_color', function($value) {
|
||||
$newsletter = $this->getNewsletter();
|
||||
if (!$newsletter instanceof NewsletterEntity) return $value;
|
||||
return $newsletter->getGlobalStyle('woocommerce', 'brandingColor') ?? $value;
|
||||
});
|
||||
$this->wp->addAction('option_woocommerce_email_body_background_color', function($value) {
|
||||
$newsletter = $this->getNewsletter();
|
||||
if (!$newsletter instanceof NewsletterEntity) return $value;
|
||||
return $newsletter->getGlobalStyle('wrapper', 'backgroundColor') ?? $value;
|
||||
});
|
||||
$this->wp->addAction('option_woocommerce_email_text_color', function($value) {
|
||||
$newsletter = $this->getNewsletter();
|
||||
if (!$newsletter instanceof NewsletterEntity) return $value;
|
||||
return $newsletter->getGlobalStyle('text', 'fontColor') ?? $value;
|
||||
});
|
||||
}
|
||||
|
||||
public function alterEmailStyles($styles) {
|
||||
$newsletter = $this->getNewsletter();
|
||||
if (!$newsletter) {
|
||||
return $styles;
|
||||
}
|
||||
return $this->renderer->enhanceCss($styles, $newsletter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\WooCommerce;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Config\Env;
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Newsletter\NewslettersRepository;
|
||||
use MailPoet\Settings\SettingsController;
|
||||
use MailPoet\WooCommerce\TransactionalEmails\Template;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
|
||||
class TransactionalEmails {
|
||||
const SETTING_EMAIL_ID = 'woocommerce.transactional_email_id';
|
||||
|
||||
/** @var WPFunctions */
|
||||
private $wp;
|
||||
|
||||
/** @var SettingsController */
|
||||
private $settings;
|
||||
|
||||
/** @var Template */
|
||||
private $template;
|
||||
|
||||
/** @var Helper */
|
||||
private $woocommerceHelper;
|
||||
|
||||
/** @var array */
|
||||
private $emailHeadings = [];
|
||||
|
||||
/** @var NewslettersRepository */
|
||||
private $newslettersRepository;
|
||||
|
||||
public function __construct(
|
||||
WPFunctions $wp,
|
||||
SettingsController $settings,
|
||||
Template $template,
|
||||
Helper $woocommerceHelper,
|
||||
NewslettersRepository $newslettersRepository
|
||||
) {
|
||||
$this->wp = $wp;
|
||||
$this->settings = $settings;
|
||||
$this->template = $template;
|
||||
$this->woocommerceHelper = $woocommerceHelper;
|
||||
$this->newslettersRepository = $newslettersRepository;
|
||||
}
|
||||
|
||||
public function setupEmailHeadings() {
|
||||
$this->emailHeadings = [
|
||||
'new_account' => [
|
||||
'option_name' => 'woocommerce_new_order_settings',
|
||||
'default' => __('New Order: #{order_number}', 'woocommerce'),
|
||||
],
|
||||
'processing_order' => [
|
||||
'option_name' => 'woocommerce_customer_processing_order_settings',
|
||||
'default' => __('Thank you for your order', 'woocommerce'),
|
||||
],
|
||||
'completed_order' => [
|
||||
'option_name' => 'woocommerce_customer_completed_order_settings',
|
||||
'default' => __('Thanks for shopping with us', 'woocommerce'),
|
||||
],
|
||||
'customer_note' => [
|
||||
'option_name' => 'woocommerce_customer_note_settings',
|
||||
'default' => __('A note has been added to your order', 'woocommerce'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function init() {
|
||||
$savedEmailId = (bool)$this->settings->get(self::SETTING_EMAIL_ID, false);
|
||||
if (!$savedEmailId) {
|
||||
$email = $this->createNewsletter();
|
||||
$this->settings->set(self::SETTING_EMAIL_ID, $email->getId());
|
||||
}
|
||||
}
|
||||
|
||||
public function getEmailHeadings() {
|
||||
if (empty($this->emailHeadings)) {
|
||||
$this->setupEmailHeadings();
|
||||
}
|
||||
|
||||
$values = [];
|
||||
foreach ($this->emailHeadings as $name => $heading) {
|
||||
$settings = $this->wp->getOption($heading['option_name']);
|
||||
if (!$settings) {
|
||||
$values[$name] = $this->replacePlaceholders($heading['default']);
|
||||
} else {
|
||||
$value = !empty($settings['heading']) ? $settings['heading'] : $heading['default'];
|
||||
$values[$name] = $this->replacePlaceholders($value);
|
||||
}
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
private function createNewsletter() {
|
||||
$wcEmailSettings = $this->getWCEmailSettings();
|
||||
$newsletter = new NewsletterEntity;
|
||||
$newsletter->setType(NewsletterEntity::TYPE_WC_TRANSACTIONAL_EMAIL);
|
||||
$newsletter->setSubject('WooCommerce Transactional Email');
|
||||
$newsletter->setBody($this->template->create($wcEmailSettings));
|
||||
$this->newslettersRepository->persist($newsletter);
|
||||
$this->newslettersRepository->flush();
|
||||
return $newsletter;
|
||||
}
|
||||
|
||||
private function replacePlaceholders($text) {
|
||||
$title = $this->wp->wpSpecialcharsDecode($this->wp->getOption('blogname'), ENT_QUOTES);
|
||||
$address = $this->wp->wpParseUrl($this->wp->homeUrl(), PHP_URL_HOST);
|
||||
$orderDate = date('Y-m-d');
|
||||
return str_replace(
|
||||
['{site_title}', '{site_address}', '{order_date}', '{order_number}'],
|
||||
[$title, $address, $orderDate, '0001'],
|
||||
$text
|
||||
);
|
||||
}
|
||||
|
||||
public function getWCEmailSettings() {
|
||||
$wcEmailSettings = [
|
||||
'woocommerce_email_background_color' => '#f7f7f7',
|
||||
'woocommerce_email_base_color' => '#333333',
|
||||
'woocommerce_email_body_background_color' => '#ffffff',
|
||||
'woocommerce_email_footer_text' => _x('Footer text', 'Default footer text for a WooCommerce transactional email', 'mailpoet'),
|
||||
'woocommerce_email_header_image' => Env::$assetsUrl . '/img/newsletter_editor/wc-default-logo.png',
|
||||
'woocommerce_email_text_color' => '#111111',
|
||||
];
|
||||
$result = [];
|
||||
foreach ($wcEmailSettings as $name => $default) {
|
||||
$value = $this->wp->getOption($name);
|
||||
$key = preg_replace('/^woocommerce_email_/', '', $name);
|
||||
$result[$key] = $value ?: $default;
|
||||
}
|
||||
$result['base_text_color'] = $this->woocommerceHelper->wcLightOrDark($result['base_color'], '#202020', '#ffffff');
|
||||
if ($this->woocommerceHelper->wcHexIsLight($result['body_background_color'])) {
|
||||
$result['link_color'] = $this->woocommerceHelper->wcHexIsLight($result['base_color']) ? $result['base_text_color'] : $result['base_color'];
|
||||
} else {
|
||||
$result['link_color'] = $this->woocommerceHelper->wcHexIsLight($result['base_color']) ? $result['base_color'] : $result['base_text_color'];
|
||||
}
|
||||
$result['footer_text'] = $this->replacePlaceholders($result['footer_text']);
|
||||
// The footer text is placed inside a paragraph in a text block so we keep only tags we allow in the text block in the newsletter editor
|
||||
$result['footer_text'] = strip_tags($result['footer_text'], '<em><strong><br><a><span><s><del>');
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\WooCommerce\TransactionalEmails;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Newsletter\Editor\LayoutHelper;
|
||||
use MailPoet\WooCommerce\TransactionalEmails;
|
||||
|
||||
class ContentPreprocessor {
|
||||
public const WC_HEADING_PLACEHOLDER = '[mailpoet_woocommerce_heading_placeholder]';
|
||||
public const WC_CONTENT_PLACEHOLDER = '[mailpoet_woocommerce_content_placeholder]';
|
||||
|
||||
public const WC_HEADING_BEFORE = '
|
||||
<table width="100%" border="0" cellpadding="0" cellspacing="0" style="border-spacing:0;mso-table-lspace:0;mso-table-rspace:0">
|
||||
<tr>
|
||||
<td class="mailpoet_text" valign="top" style="padding-top:20px;padding-bottom:20px;word-break:break-word;word-wrap:break-word;">';
|
||||
public const WC_HEADING_AFTER = '
|
||||
</td>
|
||||
</tr>
|
||||
</table>';
|
||||
|
||||
/** @var TransactionalEmails */
|
||||
private $transactionalEmails;
|
||||
|
||||
public function __construct(
|
||||
TransactionalEmails $transactionalEmails
|
||||
) {
|
||||
$this->transactionalEmails = $transactionalEmails;
|
||||
}
|
||||
|
||||
public function preprocessContent() {
|
||||
return $this->renderPlaceholderBlock(self::WC_CONTENT_PLACEHOLDER);
|
||||
}
|
||||
|
||||
public function preprocessHeader() {
|
||||
$wcEmailSettings = $this->transactionalEmails->getWCEmailSettings();
|
||||
$content = self::WC_HEADING_BEFORE . '<h1 id="mailpoet-woo-email-header" style="color:' . $wcEmailSettings['base_text_color'] . ';">' . self::WC_HEADING_PLACEHOLDER . '</h1>' . self::WC_HEADING_AFTER;
|
||||
return $this->renderTextBlock($content, ['backgroundColor' => $wcEmailSettings['base_color']]);
|
||||
}
|
||||
|
||||
private function renderTextBlock(string $text, array $styles = []): array {
|
||||
return [
|
||||
LayoutHelper::row([
|
||||
LayoutHelper::col([[
|
||||
'type' => 'text',
|
||||
'text' => $text,
|
||||
]]),
|
||||
], $styles),
|
||||
];
|
||||
}
|
||||
|
||||
private function renderPlaceholderBlock(string $placeholder): array {
|
||||
return [
|
||||
LayoutHelper::row([
|
||||
LayoutHelper::col([[
|
||||
'type' => 'placeholder',
|
||||
'placeholder' => $placeholder,
|
||||
]]),
|
||||
]),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\WooCommerce\TransactionalEmails;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Newsletter\Renderer\Renderer as NewsletterRenderer;
|
||||
use MailPoet\Newsletter\Shortcodes\Shortcodes;
|
||||
use MailPoetVendor\csstidy;
|
||||
use MailPoetVendor\csstidy_print;
|
||||
|
||||
class Renderer {
|
||||
const CONTENT_CONTAINER_ID = 'mailpoet_woocommerce_container';
|
||||
|
||||
/** @var csstidy */
|
||||
private $cssParser;
|
||||
|
||||
/** @var NewsletterRenderer */
|
||||
private $renderer;
|
||||
|
||||
/** @var string */
|
||||
private $htmlBeforeContent;
|
||||
|
||||
/** @var string */
|
||||
private $htmlAfterContent;
|
||||
|
||||
/** @var Shortcodes */
|
||||
private $shortcodes;
|
||||
|
||||
public function __construct(
|
||||
csstidy $cssParser,
|
||||
NewsletterRenderer $renderer,
|
||||
Shortcodes $shortcodes
|
||||
) {
|
||||
$this->cssParser = $cssParser;
|
||||
$this->htmlBeforeContent = '';
|
||||
$this->htmlAfterContent = '';
|
||||
$this->renderer = $renderer;
|
||||
$this->shortcodes = $shortcodes;
|
||||
}
|
||||
|
||||
public function render(NewsletterEntity $newsletter, ?string $subject = null) {
|
||||
$preparedNewsletter = $this->prepareNewsletterForRendering($newsletter);
|
||||
$renderedNewsletter = $this->renderer->renderAsPreview($preparedNewsletter, 'html', $subject);
|
||||
$headingText = $subject ?? '';
|
||||
|
||||
$renderedHtml = $this->processShortcodes($preparedNewsletter, $renderedNewsletter);
|
||||
|
||||
$renderedHtml = str_replace(ContentPreprocessor::WC_HEADING_PLACEHOLDER, $headingText, $renderedHtml);
|
||||
$html = explode(ContentPreprocessor::WC_CONTENT_PLACEHOLDER, $renderedHtml);
|
||||
$this->htmlBeforeContent = $html[0];
|
||||
$this->htmlAfterContent = $html[1];
|
||||
}
|
||||
|
||||
public function getHTMLBeforeContent() {
|
||||
if (empty($this->htmlBeforeContent)) {
|
||||
throw new \Exception("You should call 'render' before 'getHTMLBeforeContent'");
|
||||
}
|
||||
return $this->htmlBeforeContent . '<!--WooContent--><div id="' . self::CONTENT_CONTAINER_ID . '"><div id="body_content"><div id="body_content_inner"><table style="width: 100%"><tr><td style="padding: 10px 20px;">';
|
||||
}
|
||||
|
||||
public function getHTMLAfterContent() {
|
||||
if (empty($this->htmlAfterContent)) {
|
||||
throw new \Exception("You should call 'render' before 'getHTMLAfterContent'");
|
||||
}
|
||||
return '<!--WooContent--></td></tr></table></div></div></div>' . $this->htmlAfterContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* In this method we alter the rendered content that is output when processing the WooCommerce email template.
|
||||
* - We update inlined font-family rules in the content block generated by Woo
|
||||
*/
|
||||
public function updateRenderedContent(NewsletterEntity $newsletter, string $content): string {
|
||||
$isSavedWithStyledWooBlock = $newsletter->getGlobalStyle('woocommerce', 'isSavedWithUpdatedStyles');
|
||||
// For Backward compatibility do not apply styles for content unless the template was edited with the editor
|
||||
// and user visually checked and is aware of updated styles feature.
|
||||
if (!$isSavedWithStyledWooBlock) {
|
||||
return $content;
|
||||
}
|
||||
$contentParts = explode('<!--WooContent-->', $content);
|
||||
if (count($contentParts) !== 3) {
|
||||
return $content;
|
||||
}
|
||||
[$beforeWooContent, $wooContent, $afterWooContent] = $contentParts;
|
||||
$fontFamily = $newsletter->getGlobalStyle('text', 'fontFamily');
|
||||
$replaceFontFamilyCallback = function ($matches) use ($fontFamily) {
|
||||
$pattern = '/font-family\s*:\s*[^;]+;/i';
|
||||
$style = $matches[1];
|
||||
$style = preg_replace($pattern, "font-family:$fontFamily;", $style);
|
||||
return 'style="' . esc_attr($style) . '"';
|
||||
};
|
||||
$stylePattern = '/style="(.*?)"/i';
|
||||
$wooContent = (string)preg_replace_callback($stylePattern, $replaceFontFamilyCallback, $wooContent);
|
||||
return implode('', [$beforeWooContent, $wooContent, $afterWooContent]);
|
||||
}
|
||||
|
||||
/**
|
||||
* In this method we alter CSS that is later inlined into the WooCommerce email template. WooCommerce use Emogrifier to inline CSS.
|
||||
* The inlining is called after the rendering and after the modifications we apply to the rendered content in self::updateRenderedContent
|
||||
* - We prefix the original selectors to avoid inlining those rules into content added int the MailPoet's editor.
|
||||
* - We update the font-family in the original CSS if it's set in the editor.
|
||||
* - We update the font-size for the inner content if it's set in the editor.
|
||||
*/
|
||||
public function enhanceCss(string $css, NewsletterEntity $newsletter): string {
|
||||
$this->cssParser->settings['compress_colors'] = false;
|
||||
$this->cssParser->parse($css);
|
||||
foreach ($this->cssParser->css as $index => $rules) {
|
||||
$this->cssParser->css[$index] = [];
|
||||
foreach ($rules as $selectors => $properties) {
|
||||
$properties = $this->updateStyleDefinition($selectors, $newsletter, $properties);
|
||||
$selectors = explode(',', $selectors);
|
||||
$selectors = array_map(function($selector) {
|
||||
return '#' . self::CONTENT_CONTAINER_ID . ' ' . $selector;
|
||||
}, $selectors);
|
||||
$selectors = implode(',', $selectors);
|
||||
|
||||
$this->cssParser->css[$index][$selectors] = $properties;
|
||||
}
|
||||
}
|
||||
|
||||
/** @var csstidy_print */
|
||||
$print = $this->cssParser->print;
|
||||
$css = $print->plain();
|
||||
|
||||
// Enforce the special heading color for the WooCommerce email header
|
||||
$wooHeadingColor = $newsletter->getGlobalStyle('woocommerce', 'headingFontColor');
|
||||
if ($wooHeadingColor) {
|
||||
$css .= "#mailpoet-woo-email-header { color: $wooHeadingColor !important; }";
|
||||
}
|
||||
return $css;
|
||||
}
|
||||
|
||||
private function processShortcodes(NewsletterEntity $newsletter, $content) {
|
||||
$this->shortcodes->setQueue(null);
|
||||
$this->shortcodes->setSubscriber(null);
|
||||
$this->shortcodes->setNewsletter($newsletter);
|
||||
return $this->shortcodes->replace($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method prepares the newsletter for rendering
|
||||
* - We ensure that the font-family and branding color are used as default for all headings
|
||||
*/
|
||||
private function prepareNewsletterForRendering(NewsletterEntity $newsletter): NewsletterEntity {
|
||||
$newsletterClone = clone($newsletter);
|
||||
$headingFontFamily = $newsletter->getGlobalStyle('woocommerce', 'headingFontFamily');
|
||||
if ($headingFontFamily) {
|
||||
$newsletterClone->setGlobalStyle('h1', 'fontFamily', $headingFontFamily);
|
||||
$newsletterClone->setGlobalStyle('h2', 'fontFamily', $headingFontFamily);
|
||||
$newsletterClone->setGlobalStyle('h3', 'fontFamily', $headingFontFamily);
|
||||
}
|
||||
$brandingColor = $newsletter->getGlobalStyle('woocommerce', 'brandingColor');
|
||||
$contentHeadingColor = $newsletter->getGlobalStyle('woocommerce', 'contentHeadingFontColor') ?? $brandingColor;
|
||||
if ($contentHeadingColor) {
|
||||
$newsletterClone->setGlobalStyle('h1', 'color', $contentHeadingColor);
|
||||
$newsletterClone->setGlobalStyle('h2', 'color', $contentHeadingColor);
|
||||
$newsletterClone->setGlobalStyle('h3', 'color', $contentHeadingColor);
|
||||
}
|
||||
return $newsletterClone;
|
||||
}
|
||||
|
||||
private function updateStyleDefinition(string $selectors, NewsletterEntity $newsletter, $properties) {
|
||||
// For Backward compatibility do not apply styles for content unless the template was edited with the editor
|
||||
// and user visually checked and is aware of updated styles feature.
|
||||
$isSavedWithStyledWooBlock = $newsletter->getGlobalStyle('woocommerce', 'isSavedWithUpdatedStyles');
|
||||
if (!$isSavedWithStyledWooBlock) {
|
||||
return $properties;
|
||||
}
|
||||
|
||||
if (!is_array($properties)) {
|
||||
$properties = [];
|
||||
}
|
||||
$fontFamily = $newsletter->getGlobalStyle('text', 'fontFamily');
|
||||
$headingFontFamily = $newsletter->getGlobalStyle('woocommerce', 'headingFontFamily');
|
||||
$fontSize = $newsletter->getGlobalStyle('text', 'fontSize');
|
||||
$brandingColor = $newsletter->getGlobalStyle('woocommerce', 'brandingColor');
|
||||
$contentHeadingColor = $newsletter->getGlobalStyle('woocommerce', 'contentHeadingFontColor') ?? $brandingColor;
|
||||
|
||||
// Update font family if it's set in the editor
|
||||
if ($fontFamily && !empty($properties['font-family'])) {
|
||||
$properties['font-family'] = $fontFamily;
|
||||
}
|
||||
// Update font size for inner content
|
||||
if ($fontSize && ($selectors === '#body_content_inner')) {
|
||||
$properties['font-size'] = $fontSize;
|
||||
}
|
||||
|
||||
// Update heading font sizes and font family
|
||||
$supportedHeadings = ['h1', 'h2', 'h3'];
|
||||
foreach ($supportedHeadings as $heading) {
|
||||
$headingFontSize = $newsletter->getGlobalStyle($heading, 'fontSize');
|
||||
if ($headingFontSize && ($selectors === $heading)) {
|
||||
$properties['font-size'] = $headingFontSize;
|
||||
}
|
||||
if ($headingFontFamily && ($selectors === $heading)) {
|
||||
$properties['font-family'] = $headingFontFamily;
|
||||
}
|
||||
if ($contentHeadingColor && ($selectors === $heading)) {
|
||||
$properties['color'] = $contentHeadingColor;
|
||||
}
|
||||
}
|
||||
return $properties;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,724 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\WooCommerce\TransactionalEmails;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Config\Env;
|
||||
|
||||
class Template {
|
||||
public function create($wcEmailSettings) {
|
||||
$socialIconUrl = Env::$assetsUrl . '/img/newsletter_editor/social-icons';
|
||||
return [
|
||||
'content' =>
|
||||
[
|
||||
'type' => 'container',
|
||||
'columnLayout' => false,
|
||||
'orientation' => 'vertical',
|
||||
'image' =>
|
||||
[
|
||||
'src' => null,
|
||||
'display' => 'scale',
|
||||
],
|
||||
'styles' =>
|
||||
[
|
||||
'block' =>
|
||||
[
|
||||
'backgroundColor' => 'transparent',
|
||||
],
|
||||
],
|
||||
'blocks' =>
|
||||
[
|
||||
0 =>
|
||||
[
|
||||
'type' => 'container',
|
||||
'columnLayout' => false,
|
||||
'orientation' => 'horizontal',
|
||||
'image' =>
|
||||
[
|
||||
'src' => null,
|
||||
'display' => 'scale',
|
||||
],
|
||||
'styles' =>
|
||||
[
|
||||
'block' =>
|
||||
[
|
||||
'backgroundColor' => 'transparent',
|
||||
],
|
||||
],
|
||||
'blocks' =>
|
||||
[
|
||||
0 =>
|
||||
[
|
||||
'type' => 'container',
|
||||
'columnLayout' => false,
|
||||
'orientation' => 'vertical',
|
||||
'image' =>
|
||||
[
|
||||
'src' => null,
|
||||
'display' => 'scale',
|
||||
],
|
||||
'styles' =>
|
||||
[
|
||||
'block' =>
|
||||
[
|
||||
'backgroundColor' => 'transparent',
|
||||
],
|
||||
],
|
||||
'blocks' =>
|
||||
[
|
||||
0 =>
|
||||
[
|
||||
'type' => 'spacer',
|
||||
'styles' =>
|
||||
[
|
||||
'block' =>
|
||||
[
|
||||
'backgroundColor' => 'transparent',
|
||||
'height' => '20px',
|
||||
],
|
||||
],
|
||||
],
|
||||
1 =>
|
||||
[
|
||||
'type' => 'image',
|
||||
'link' => '',
|
||||
'src' => $wcEmailSettings['header_image'],
|
||||
'alt' => '',
|
||||
'fullWidth' => false,
|
||||
'width' => '180px',
|
||||
'height' => '362px',
|
||||
'styles' =>
|
||||
[
|
||||
'block' =>
|
||||
[
|
||||
'textAlign' => 'center',
|
||||
],
|
||||
],
|
||||
],
|
||||
2 =>
|
||||
[
|
||||
'type' => 'spacer',
|
||||
'styles' =>
|
||||
[
|
||||
'block' =>
|
||||
[
|
||||
'backgroundColor' => 'transparent',
|
||||
'height' => '20px',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
1 =>
|
||||
[
|
||||
'type' => 'woocommerceHeading',
|
||||
],
|
||||
2 =>
|
||||
[
|
||||
'type' => 'container',
|
||||
'columnLayout' => false,
|
||||
'orientation' => 'horizontal',
|
||||
'image' =>
|
||||
[
|
||||
'src' => null,
|
||||
'display' => 'scale',
|
||||
],
|
||||
'styles' =>
|
||||
[
|
||||
'block' =>
|
||||
[
|
||||
'backgroundColor' => 'transparent',
|
||||
],
|
||||
],
|
||||
'blocks' =>
|
||||
[
|
||||
0 =>
|
||||
[
|
||||
'type' => 'container',
|
||||
'columnLayout' => false,
|
||||
'orientation' => 'vertical',
|
||||
'image' =>
|
||||
[
|
||||
'src' => null,
|
||||
'display' => 'scale',
|
||||
],
|
||||
'styles' =>
|
||||
[
|
||||
'block' =>
|
||||
[
|
||||
'backgroundColor' => 'transparent',
|
||||
],
|
||||
],
|
||||
'blocks' =>
|
||||
[
|
||||
0 =>
|
||||
[
|
||||
'type' => 'spacer',
|
||||
'styles' =>
|
||||
[
|
||||
'block' =>
|
||||
[
|
||||
'backgroundColor' => 'transparent',
|
||||
'height' => '20px',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
3 =>
|
||||
[
|
||||
'type' => 'woocommerceContent',
|
||||
],
|
||||
4 =>
|
||||
[
|
||||
'type' => 'container',
|
||||
'columnLayout' => false,
|
||||
'orientation' => 'horizontal',
|
||||
'image' =>
|
||||
[
|
||||
'src' => null,
|
||||
'display' => 'scale',
|
||||
],
|
||||
'styles' =>
|
||||
[
|
||||
'block' =>
|
||||
[
|
||||
'backgroundColor' => 'transparent',
|
||||
],
|
||||
],
|
||||
'blocks' =>
|
||||
[
|
||||
0 =>
|
||||
[
|
||||
'type' => 'container',
|
||||
'columnLayout' => false,
|
||||
'orientation' => 'vertical',
|
||||
'image' =>
|
||||
[
|
||||
'src' => null,
|
||||
'display' => 'scale',
|
||||
],
|
||||
'styles' =>
|
||||
[
|
||||
'block' =>
|
||||
[
|
||||
'backgroundColor' => 'transparent',
|
||||
],
|
||||
],
|
||||
'blocks' =>
|
||||
[
|
||||
0 =>
|
||||
[
|
||||
'type' => 'spacer',
|
||||
'styles' =>
|
||||
[
|
||||
'block' =>
|
||||
[
|
||||
'backgroundColor' => 'transparent',
|
||||
'height' => '20px',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
5 =>
|
||||
[
|
||||
'type' => 'container',
|
||||
'columnLayout' => false,
|
||||
'orientation' => 'horizontal',
|
||||
'image' =>
|
||||
[
|
||||
'src' => null,
|
||||
'display' => 'scale',
|
||||
],
|
||||
'styles' =>
|
||||
[
|
||||
'block' =>
|
||||
[
|
||||
'backgroundColor' => 'transparent',
|
||||
],
|
||||
],
|
||||
'blocks' =>
|
||||
[
|
||||
0 =>
|
||||
[
|
||||
'type' => 'container',
|
||||
'columnLayout' => false,
|
||||
'orientation' => 'vertical',
|
||||
'image' =>
|
||||
[
|
||||
'src' => null,
|
||||
'display' => 'scale',
|
||||
],
|
||||
'styles' =>
|
||||
[
|
||||
'block' =>
|
||||
[
|
||||
'backgroundColor' => 'transparent',
|
||||
],
|
||||
],
|
||||
'blocks' =>
|
||||
[
|
||||
0 =>
|
||||
[
|
||||
'type' => 'spacer',
|
||||
'styles' =>
|
||||
[
|
||||
'block' =>
|
||||
[
|
||||
'backgroundColor' => 'transparent',
|
||||
'height' => '20px',
|
||||
],
|
||||
],
|
||||
],
|
||||
1 =>
|
||||
[
|
||||
'type' => 'text',
|
||||
'text' => '<p style="text-align: center;">' . $wcEmailSettings['footer_text'] . '</p>',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'globalStyles' =>
|
||||
[
|
||||
'text' =>
|
||||
[
|
||||
'fontColor' => $wcEmailSettings['text_color'],
|
||||
'fontFamily' => 'Arial',
|
||||
'fontSize' => '14px',
|
||||
'lineHeight' => '1.6',
|
||||
],
|
||||
'h1' =>
|
||||
[
|
||||
'fontColor' => $wcEmailSettings['base_color'],
|
||||
'fontFamily' => 'Source Sans Pro',
|
||||
'fontSize' => '36px',
|
||||
'lineHeight' => '1.6',
|
||||
],
|
||||
'h2' =>
|
||||
[
|
||||
'fontColor' => $wcEmailSettings['base_color'],
|
||||
'fontFamily' => 'Verdana',
|
||||
'fontSize' => '24px',
|
||||
'lineHeight' => '1.6',
|
||||
],
|
||||
'h3' =>
|
||||
[
|
||||
'fontColor' => $wcEmailSettings['base_color'],
|
||||
'fontFamily' => 'Trebuchet MS',
|
||||
'fontSize' => '22px',
|
||||
'lineHeight' => '1.6',
|
||||
],
|
||||
'link' =>
|
||||
[
|
||||
'fontColor' => $wcEmailSettings['link_color'],
|
||||
'textDecoration' => 'underline',
|
||||
],
|
||||
'wrapper' =>
|
||||
[
|
||||
'backgroundColor' => $wcEmailSettings['body_background_color'],
|
||||
],
|
||||
'body' =>
|
||||
[
|
||||
'backgroundColor' => $wcEmailSettings['background_color'],
|
||||
],
|
||||
'woocommerce' =>
|
||||
[
|
||||
'brandingColor' => $wcEmailSettings['base_color'],
|
||||
'headingFontColor' => $wcEmailSettings['base_text_color'],
|
||||
'headingFontFamily' => 'Arial',
|
||||
],
|
||||
],
|
||||
'blockDefaults' =>
|
||||
[
|
||||
'automatedLatestContent' =>
|
||||
[
|
||||
'amount' => '5',
|
||||
'withLayout' => false,
|
||||
'contentType' => 'post',
|
||||
'inclusionType' => 'include',
|
||||
'displayType' => 'excerpt',
|
||||
'titleFormat' => 'h1',
|
||||
'titleAlignment' => 'left',
|
||||
'titleIsLink' => false,
|
||||
'imageFullWidth' => false,
|
||||
'featuredImagePosition' => 'belowTitle',
|
||||
'showAuthor' => 'no',
|
||||
'authorPrecededBy' => 'Author:',
|
||||
'showCategories' => 'no',
|
||||
'categoriesPrecededBy' => 'Categories:',
|
||||
'readMoreType' => 'button',
|
||||
'readMoreText' => 'Read more',
|
||||
'readMoreButton' =>
|
||||
[
|
||||
'text' => 'Read more',
|
||||
'url' => '[postLink]',
|
||||
'context' => 'automatedLatestContent.readMoreButton',
|
||||
'styles' =>
|
||||
[
|
||||
'block' =>
|
||||
[
|
||||
'backgroundColor' => '#2ea1cd',
|
||||
'borderColor' => '#0074a2',
|
||||
'borderWidth' => '1px',
|
||||
'borderRadius' => '5px',
|
||||
'borderStyle' => 'solid',
|
||||
'width' => '180px',
|
||||
'lineHeight' => '40px',
|
||||
'fontColor' => '#ffffff',
|
||||
'fontFamily' => 'Verdana',
|
||||
'fontSize' => '18px',
|
||||
'fontWeight' => 'normal',
|
||||
'textAlign' => 'center',
|
||||
],
|
||||
],
|
||||
],
|
||||
'sortBy' => 'newest',
|
||||
'showDivider' => true,
|
||||
'divider' =>
|
||||
[
|
||||
'context' => 'automatedLatestContent.divider',
|
||||
'styles' =>
|
||||
[
|
||||
'block' =>
|
||||
[
|
||||
'backgroundColor' => 'transparent',
|
||||
'padding' => '13px',
|
||||
'borderStyle' => 'solid',
|
||||
'borderWidth' => '3px',
|
||||
'borderColor' => '#aaaaaa',
|
||||
],
|
||||
],
|
||||
],
|
||||
'backgroundColor' => '#ffffff',
|
||||
'backgroundColorAlternate' => '#eeeeee',
|
||||
],
|
||||
'automatedLatestContentLayout' =>
|
||||
[
|
||||
'amount' => '5',
|
||||
'withLayout' => true,
|
||||
'contentType' => 'post',
|
||||
'inclusionType' => 'include',
|
||||
'displayType' => 'excerpt',
|
||||
'titleFormat' => 'h1',
|
||||
'titleAlignment' => 'left',
|
||||
'titleIsLink' => false,
|
||||
'imageFullWidth' => false,
|
||||
'featuredImagePosition' => 'alternate',
|
||||
'showAuthor' => 'no',
|
||||
'authorPrecededBy' => 'Author:',
|
||||
'showCategories' => 'no',
|
||||
'categoriesPrecededBy' => 'Categories:',
|
||||
'readMoreType' => 'button',
|
||||
'readMoreText' => 'Read more',
|
||||
'readMoreButton' =>
|
||||
[
|
||||
'text' => 'Read more',
|
||||
'url' => '[postLink]',
|
||||
'context' => 'automatedLatestContentLayout.readMoreButton',
|
||||
'styles' =>
|
||||
[
|
||||
'block' =>
|
||||
[
|
||||
'backgroundColor' => '#2ea1cd',
|
||||
'borderColor' => '#0074a2',
|
||||
'borderWidth' => '1px',
|
||||
'borderRadius' => '5px',
|
||||
'borderStyle' => 'solid',
|
||||
'width' => '180px',
|
||||
'lineHeight' => '40px',
|
||||
'fontColor' => '#ffffff',
|
||||
'fontFamily' => 'Verdana',
|
||||
'fontSize' => '18px',
|
||||
'fontWeight' => 'normal',
|
||||
'textAlign' => 'center',
|
||||
],
|
||||
],
|
||||
],
|
||||
'sortBy' => 'newest',
|
||||
'showDivider' => true,
|
||||
'divider' =>
|
||||
[
|
||||
'context' => 'automatedLatestContentLayout.divider',
|
||||
'styles' =>
|
||||
[
|
||||
'block' =>
|
||||
[
|
||||
'backgroundColor' => 'transparent',
|
||||
'padding' => '13px',
|
||||
'borderStyle' => 'solid',
|
||||
'borderWidth' => '3px',
|
||||
'borderColor' => '#aaaaaa',
|
||||
],
|
||||
],
|
||||
],
|
||||
'backgroundColor' => '#ffffff',
|
||||
'backgroundColorAlternate' => '#eeeeee',
|
||||
],
|
||||
'button' =>
|
||||
[
|
||||
'text' => 'Button',
|
||||
'url' => '',
|
||||
'styles' =>
|
||||
[
|
||||
'block' =>
|
||||
[
|
||||
'backgroundColor' => '#2ea1cd',
|
||||
'borderColor' => '#0074a2',
|
||||
'borderWidth' => '1px',
|
||||
'borderRadius' => '5px',
|
||||
'borderStyle' => 'solid',
|
||||
'width' => '180px',
|
||||
'lineHeight' => '40px',
|
||||
'fontColor' => '#ffffff',
|
||||
'fontFamily' => 'Verdana',
|
||||
'fontSize' => '18px',
|
||||
'fontWeight' => 'normal',
|
||||
'textAlign' => 'center',
|
||||
],
|
||||
],
|
||||
],
|
||||
'divider' =>
|
||||
[
|
||||
'styles' =>
|
||||
[
|
||||
'block' =>
|
||||
[
|
||||
'backgroundColor' => 'transparent',
|
||||
'padding' => '13px',
|
||||
'borderStyle' => 'solid',
|
||||
'borderWidth' => '3px',
|
||||
'borderColor' => '#aaaaaa',
|
||||
],
|
||||
],
|
||||
],
|
||||
'footer' =>
|
||||
[
|
||||
'text' => '<p><a href="[link:subscription_unsubscribe_url]">Unsubscribe</a> | <a href="[link:subscription_manage_url]">Manage subscription</a><br />Add your postal address here!</p>',
|
||||
'styles' =>
|
||||
[
|
||||
'block' =>
|
||||
[
|
||||
'backgroundColor' => 'transparent',
|
||||
],
|
||||
'text' =>
|
||||
[
|
||||
'fontColor' => '#222222',
|
||||
'fontFamily' => 'Arial',
|
||||
'fontSize' => '12px',
|
||||
'textAlign' => 'center',
|
||||
],
|
||||
'link' =>
|
||||
[
|
||||
'fontColor' => '#6cb7d4',
|
||||
'textDecoration' => 'none',
|
||||
],
|
||||
],
|
||||
],
|
||||
'posts' =>
|
||||
[
|
||||
'amount' => '10',
|
||||
'withLayout' => true,
|
||||
'contentType' => 'post',
|
||||
'postStatus' => 'publish',
|
||||
'inclusionType' => 'include',
|
||||
'displayType' => 'excerpt',
|
||||
'titleFormat' => 'h1',
|
||||
'titleAlignment' => 'left',
|
||||
'titleIsLink' => false,
|
||||
'imageFullWidth' => false,
|
||||
'featuredImagePosition' => 'alternate',
|
||||
'showAuthor' => 'no',
|
||||
'authorPrecededBy' => 'Author:',
|
||||
'showCategories' => 'no',
|
||||
'categoriesPrecededBy' => 'Categories:',
|
||||
'readMoreType' => 'link',
|
||||
'readMoreText' => 'Read more',
|
||||
'readMoreButton' =>
|
||||
[
|
||||
'text' => 'Read more',
|
||||
'url' => '[postLink]',
|
||||
'context' => 'posts.readMoreButton',
|
||||
'styles' =>
|
||||
[
|
||||
'block' =>
|
||||
[
|
||||
'backgroundColor' => '#2ea1cd',
|
||||
'borderColor' => '#0074a2',
|
||||
'borderWidth' => '1px',
|
||||
'borderRadius' => '5px',
|
||||
'borderStyle' => 'solid',
|
||||
'width' => '180px',
|
||||
'lineHeight' => '40px',
|
||||
'fontColor' => '#ffffff',
|
||||
'fontFamily' => 'Verdana',
|
||||
'fontSize' => '18px',
|
||||
'fontWeight' => 'normal',
|
||||
'textAlign' => 'center',
|
||||
],
|
||||
],
|
||||
],
|
||||
'sortBy' => 'newest',
|
||||
'showDivider' => true,
|
||||
'divider' =>
|
||||
[
|
||||
'context' => 'posts.divider',
|
||||
'styles' =>
|
||||
[
|
||||
'block' =>
|
||||
[
|
||||
'backgroundColor' => 'transparent',
|
||||
'padding' => '13px',
|
||||
'borderStyle' => 'solid',
|
||||
'borderWidth' => '3px',
|
||||
'borderColor' => '#aaaaaa',
|
||||
],
|
||||
],
|
||||
],
|
||||
'backgroundColor' => '#ffffff',
|
||||
'backgroundColorAlternate' => '#eeeeee',
|
||||
],
|
||||
'products' =>
|
||||
[
|
||||
'amount' => '10',
|
||||
'withLayout' => true,
|
||||
'contentType' => 'product',
|
||||
'postStatus' => 'publish',
|
||||
'inclusionType' => 'include',
|
||||
'displayType' => 'excerpt',
|
||||
'titleFormat' => 'h1',
|
||||
'titleAlignment' => 'left',
|
||||
'titleIsLink' => false,
|
||||
'imageFullWidth' => false,
|
||||
'featuredImagePosition' => 'alternate',
|
||||
'pricePosition' => 'below',
|
||||
'readMoreType' => 'link',
|
||||
'readMoreText' => 'Buy now',
|
||||
'readMoreButton' =>
|
||||
[
|
||||
'text' => 'Buy now',
|
||||
'url' => '[postLink]',
|
||||
'context' => 'posts.readMoreButton',
|
||||
'styles' =>
|
||||
[
|
||||
'block' =>
|
||||
[
|
||||
'backgroundColor' => '#2ea1cd',
|
||||
'borderColor' => '#0074a2',
|
||||
'borderWidth' => '1px',
|
||||
'borderRadius' => '5px',
|
||||
'borderStyle' => 'solid',
|
||||
'width' => '180px',
|
||||
'lineHeight' => '40px',
|
||||
'fontColor' => '#ffffff',
|
||||
'fontFamily' => 'Verdana',
|
||||
'fontSize' => '18px',
|
||||
'fontWeight' => 'normal',
|
||||
'textAlign' => 'center',
|
||||
],
|
||||
],
|
||||
],
|
||||
'sortBy' => 'newest',
|
||||
'showDivider' => true,
|
||||
'divider' =>
|
||||
[
|
||||
'context' => 'posts.divider',
|
||||
'styles' =>
|
||||
[
|
||||
'block' =>
|
||||
[
|
||||
'backgroundColor' => 'transparent',
|
||||
'padding' => '13px',
|
||||
'borderStyle' => 'solid',
|
||||
'borderWidth' => '3px',
|
||||
'borderColor' => '#aaaaaa',
|
||||
],
|
||||
],
|
||||
],
|
||||
'backgroundColor' => '#ffffff',
|
||||
'backgroundColorAlternate' => '#eeeeee',
|
||||
],
|
||||
'social' =>
|
||||
[
|
||||
'iconSet' => 'default',
|
||||
'styles' =>
|
||||
[
|
||||
'block' =>
|
||||
[
|
||||
'textAlign' => 'center',
|
||||
],
|
||||
],
|
||||
'icons' =>
|
||||
[
|
||||
0 =>
|
||||
[
|
||||
'type' => 'socialIcon',
|
||||
'iconType' => 'facebook',
|
||||
'link' => 'http://www.facebook.com',
|
||||
'image' => $socialIconUrl . '/01-social/Facebook.png',
|
||||
'height' => '32px',
|
||||
'width' => '32px',
|
||||
'text' => 'Facebook',
|
||||
],
|
||||
1 =>
|
||||
[
|
||||
'type' => 'socialIcon',
|
||||
'iconType' => 'twitter',
|
||||
'link' => 'http://www.twitter.com',
|
||||
'image' => $socialIconUrl . '/01-social/Twitter.png',
|
||||
'height' => '32px',
|
||||
'width' => '32px',
|
||||
'text' => 'Twitter',
|
||||
],
|
||||
],
|
||||
],
|
||||
'spacer' =>
|
||||
[
|
||||
'styles' =>
|
||||
[
|
||||
'block' =>
|
||||
[
|
||||
'backgroundColor' => 'transparent',
|
||||
'height' => '20px',
|
||||
],
|
||||
],
|
||||
'type' => 'spacer',
|
||||
],
|
||||
'header' =>
|
||||
[
|
||||
'text' => 'Display problems? <a href="[link:newsletter_view_in_browser_url]">Open this email in your web browser.</a>',
|
||||
'styles' =>
|
||||
[
|
||||
'block' =>
|
||||
[
|
||||
'backgroundColor' => 'transparent',
|
||||
],
|
||||
'text' =>
|
||||
[
|
||||
'fontColor' => '#222222',
|
||||
'fontFamily' => 'Arial',
|
||||
'fontSize' => '12px',
|
||||
'textAlign' => 'center',
|
||||
],
|
||||
'link' =>
|
||||
[
|
||||
'fontColor' => '#6cb7d4',
|
||||
'textDecoration' => 'underline',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<?php
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace MailPoet\WooCommerce\WooCommerceSubscriptions;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\WP\Functions;
|
||||
|
||||
class Helper {
|
||||
|
||||
|
||||
private $wp;
|
||||
|
||||
public function __construct(
|
||||
Functions $wp
|
||||
) {
|
||||
$this->wp = $wp;
|
||||
}
|
||||
|
||||
public function isWooCommerceSubscriptionsActive() {
|
||||
return $this->wp->isPluginActive('woocommerce-subscriptions/woocommerce-subscriptions.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function wcsGetSubscriptionStatuses(): array {
|
||||
if (!function_exists('wcs_get_subscription_statuses')) {
|
||||
return [];
|
||||
}
|
||||
return wcs_get_subscription_statuses();
|
||||
}
|
||||
|
||||
public function wcsGetBillingPeriodStrings(): array {
|
||||
if (!function_exists('wcs_get_subscription_period_strings')) {
|
||||
return [];
|
||||
}
|
||||
$strings = wcs_get_subscription_period_strings();
|
||||
if (!is_array($strings)) {
|
||||
return [];
|
||||
}
|
||||
return $strings;
|
||||
}
|
||||
|
||||
public function wcsGetSubscriptionTrialPeriodStrings(): array {
|
||||
if (!function_exists('wcs_get_subscription_trial_period_strings')) {
|
||||
return [];
|
||||
}
|
||||
return wcs_get_subscription_trial_period_strings();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @return false|\WC_Subscription
|
||||
*/
|
||||
public function wcsGetSubscription(int $id) {
|
||||
if (!function_exists('wcs_get_subscription')) {
|
||||
return false;
|
||||
}
|
||||
return wcs_get_subscription($id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<?php
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace MailPoet\WooCommerce;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Cron\CronHelper;
|
||||
use MailPoet\Router\Endpoints\CronDaemon;
|
||||
use MailPoet\Settings\SettingsController;
|
||||
|
||||
class WooSystemInfo {
|
||||
|
||||
|
||||
private $cronHelper;
|
||||
|
||||
/** @var SettingsController */
|
||||
private $settings;
|
||||
|
||||
public function __construct(
|
||||
CronHelper $cronHelper,
|
||||
SettingsController $settings
|
||||
) {
|
||||
$this->cronHelper = $cronHelper;
|
||||
$this->settings = $settings;
|
||||
}
|
||||
|
||||
public function sendingMethod(): string {
|
||||
return $this->settings->get('mta.method');
|
||||
}
|
||||
|
||||
public function transactionalEmails(): string {
|
||||
return $this->settings->get('send_transactional_emails') ?
|
||||
__('Current sending method', 'mailpoet') :
|
||||
__('Default WordPress sending method', 'mailpoet');
|
||||
|
||||
}
|
||||
|
||||
public function taskSchedulerMethod(): string {
|
||||
return $this->settings->get('cron_trigger.method');
|
||||
}
|
||||
|
||||
public function cronPingUrl(): string {
|
||||
return $this->cronHelper->getCronUrl(CronDaemon::ACTION_PING);
|
||||
}
|
||||
|
||||
public function toArray(): array {
|
||||
return [
|
||||
'sending_method' => $this->sendingMethod(),
|
||||
'transactional_emails' => $this->transactionalEmails(),
|
||||
'task_scheduler_method' => $this->taskSchedulerMethod(),
|
||||
'cron_ping_url' => $this->cronPingUrl(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace MailPoet\WooCommerce;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Config\Renderer;
|
||||
|
||||
class WooSystemInfoController {
|
||||
|
||||
|
||||
/** @var WooSystemInfo */
|
||||
private $systemInfo;
|
||||
|
||||
private $renderer;
|
||||
|
||||
public function __construct(
|
||||
WooSystemInfo $systemInfo,
|
||||
Renderer $renderer
|
||||
) {
|
||||
$this->systemInfo = $systemInfo;
|
||||
$this->renderer = $renderer;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
|
||||
$output = $this->renderer->render('woo_system_info.html', [
|
||||
'system_info' => $this->systemInfo->toArray(),
|
||||
]);
|
||||
|
||||
// We are in control of the template and the data can be considered safe at this point
|
||||
echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
public function addFields($response) {
|
||||
$response->data['mailpoet'] = $this->systemInfo->toArray();
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function addSchema($schema) {
|
||||
$schema['mailpoet'] = [
|
||||
[
|
||||
'description' => __('MailPoet', 'mailpoet'),
|
||||
'type' => 'object',
|
||||
'context' => ['view'],
|
||||
'readonly' => true,
|
||||
'properties' => [
|
||||
'sending_method' => [
|
||||
'description' => __('What method is used to sent out newsletters?', 'mailpoet'),
|
||||
'type' => 'boolean',
|
||||
'context' => ['view'],
|
||||
'readonly' => true,
|
||||
],
|
||||
'transactional_emails' => [
|
||||
'description' => __('With which method are transactional emails sent?', 'mailpoet'),
|
||||
'type' => 'string',
|
||||
'context' => ['view'],
|
||||
'readonly' => true,
|
||||
],
|
||||
'task_scheduler_method' => [
|
||||
'description' => __('What method controls the cron job?', 'mailpoet'),
|
||||
'type' => 'string',
|
||||
'context' => ['view'],
|
||||
'readonly' => true,
|
||||
],
|
||||
'cron_ping_url' => [
|
||||
'description' => __('The URL which needs to be pinged to get the cron started?', 'mailpoet'),
|
||||
'type' => 'string',
|
||||
'context' => ['view'],
|
||||
'readonly' => true,
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
return $schema;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<?php
|
||||
Reference in New Issue
Block a user