PluginProbe
Product Labels, Quick View, Buy Now, Pre-Orders, Frequently Bought Together & More for WooCommerce – Merchant / 2.3.2
Product Labels, Quick View, Buy Now, Pre-Orders, Frequently Bought Together & More for WooCommerce – Merchant v2.3.2
2.3.2 2.3.1 2.3.0 2.2.8 2.2.7 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.11.0 1.11.1 1.11.2 1.6 1.7 1.8 1.8.1 1.8.2 1.8.3 1.9.0 1.9.1 1.9.10 1.9.11 All 60 releases
← All changes | inc/modules/pre-orders/class-pre-orders.php +566 -353 1.82.3.2 View file →
@@ -1,353 +1,566 @@
1 -<?php
2 -
3 -/**
4 - * Pre Orders.
5 - *
6 - * @package Merchant
7 - */
8 -
9 -if ( ! defined( 'ABSPATH' ) ) {
10 - exit; // Exit if accessed directly
11 -}
12 -
13 -/**
14 - * Pre Orders Class.
15 - *
16 - */
17 -class Merchant_Pre_Orders extends Merchant_Add_Module {
18 -
19 - /**
20 - * Module ID.
21 - *
22 - */
23 - const MODULE_ID = 'pre-orders';
24 -
25 - /**
26 - * Is module preview.
27 - *
28 - */
29 - public static $is_module_preview = false;
30 -
31 - /**
32 - * Main functionality dependency.
33 - *
34 - */
35 - public $main_func;
36 -
37 - /**
38 - * Constructor.
39 - *
40 - */
41 - public function __construct( Merchant_Pre_Orders_Main_Functionality $main_func ) {
42 -
43 - // Module id.
44 - $this->module_id = self::MODULE_ID;
45 -
46 - // WooCommerce only.
47 - $this->wc_only = true;
48 -
49 - // Parent construct.
50 - parent::__construct();
51 -
52 - $this->main_func = $main_func;
53 -
54 - // Module section.
55 - $this->module_section = 'boost-revenue';
56 -
57 - // Module default settings.
58 - $this->module_default_settings = array(
59 - 'button_text' => __( 'Pre Order Now!', 'merchant' ),
60 - 'additional_text' => __( 'Ships on {date}.', 'merchant' )
61 - );
62 -
63 - // Mount preview url.
64 - $preview_url = site_url( '/' );
65 -
66 - if ( function_exists( 'wc_get_page_id' ) ) {
67 - $preview_url = get_permalink( wc_get_page_id( 'shop' ) );
68 - }
69 -
70 - // Module data.
71 - $this->module_data = Merchant_Admin_Modules::$modules_data[ self::MODULE_ID ];
72 - $this->module_data[ 'preview_url' ] = $preview_url;
73 -
74 - // Module options path.
75 - $this->module_options_path = MERCHANT_DIR . 'inc/modules/' . self::MODULE_ID . '/admin/options.php';
76 -
77 - // Is module preview page.
78 - if ( is_admin() && parent::is_module_settings_page() ) {
79 - self::$is_module_preview = true;
80 -
81 - // Enqueue admin styles.
82 - add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_css' ) );
83 -
84 - // Render module essencial instructions before the module page body content.
85 - add_action( 'merchant_admin_after_module_page_page_header', array( $this, 'admin_module_essencial_instructions' ) );
86 -
87 - // Admin preview box.
88 - add_filter( 'merchant_module_preview', array( $this, 'render_admin_preview' ), 10, 2 );
89 -
90 - // Custom CSS.
91 - // The custom CSS should be added here as well due to ensure preview box works properly.
92 - add_filter( 'merchant_custom_css', array( $this, 'admin_custom_css' ) );
93 -
94 - }
95 -
96 - if ( Merchant_Modules::is_module_active( self::MODULE_ID ) && is_admin() ) {
97 - // Init translations.
98 - $this->init_translations();
99 - }
100 -
101 - if ( ! Merchant_Modules::is_module_active( self::MODULE_ID ) ) {
102 - return;
103 - }
104 -
105 - // TODO: Refactor the 'Merchant_Pre_Orders_Main_Functionality' class to load admin things separated from frontend things.
106 - $main_func->init();
107 -
108 - // Return early if it's on admin but not in the respective module settings page.
109 - if ( is_admin() && ! parent::is_module_settings_page() ) {
110 - return;
111 - }
112 -
113 - // Enqueue styles.
114 - add_action( 'merchant_enqueue_before_main_css_js', array( $this, 'enqueue_css' ) );
115 -
116 - // Enqueue scripts.
117 - add_action( 'merchant_enqueue_after_main_css_js', array( $this, 'enqueue_scripts' ) );
118 -
119 - // Localize script.
120 - add_filter( 'merchant_localize_script', array( $this, 'localize_script' ) );
121 -
122 - // Custom CSS.
123 - add_filter( 'merchant_custom_css', array( $this, 'frontend_custom_css' ) );
124 -
125 - }
126 -
127 - /**
128 - * Init translations.
129 - *
130 - * @return void
131 - */
132 - public function init_translations() {
133 - $settings = $this->get_module_settings();
134 - if ( ! empty( $settings['button_text'] ) ) {
135 - Merchant_Translator::register_string( $settings['button_text'], esc_html__( 'Pre orders button text', 'merchant' ) );
136 - }
137 - }
138 -
139 - /**
140 - * Admin enqueue CSS.
141 - *
142 - * @return void
143 - */
144 - public function admin_enqueue_css() {
145 - $page = ( ! empty( $_GET['page'] ) ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
146 - $module = ( ! empty( $_GET['module'] ) ) ? sanitize_text_field( wp_unslash( $_GET['module'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
147 -
148 - if ( 'merchant' === $page && self::MODULE_ID === $module ) {
149 - wp_enqueue_style( 'merchant-' . self::MODULE_ID, MERCHANT_URI . 'assets/css/modules/' . self::MODULE_ID . '/pre-orders.min.css', [], MERCHANT_VERSION );
150 - wp_enqueue_style( 'merchant-admin-' . self::MODULE_ID, MERCHANT_URI . 'assets/css/modules/' . self::MODULE_ID . '/admin/preview.min.css', array(), MERCHANT_VERSION );
151 - }
152 - }
153 -
154 - /**
155 - * Enqueue CSS.
156 - *
157 - * @return void
158 - */
159 - public function enqueue_css() {
160 -
161 - // Specific module styles.
162 - wp_enqueue_style( 'merchant-' . self::MODULE_ID, MERCHANT_URI . 'assets/css/modules/' . self::MODULE_ID . '/pre-orders.min.css', array(), MERCHANT_VERSION );
163 - }
164 -
165 - /**
166 - * Enqueue scripts.
167 - *
168 - * @return void
169 - */
170 - public function enqueue_scripts() {
171 -
172 - // Register and enqueue the main module script.
173 - wp_enqueue_script( 'merchant-' . self::MODULE_ID, MERCHANT_URI . 'assets/js/modules/' . self::MODULE_ID . '/pre-orders.min.js', array(), MERCHANT_VERSION, true );
174 - }
175 -
176 - /**
177 - * Localize script with module settings.
178 - *
179 - * @param array $setting The merchant global object setting parameter.
180 - * @return array $setting The merchant global object setting parameter.
181 - */
182 - public function localize_script( $setting ) {
183 - $module_settings = $this->get_module_settings();
184 -
185 - $setting[ 'pre_orders' ] = true;
186 - $setting[ 'pre_orders_add_button_title' ] = Merchant_Translator::translate( $module_settings[ 'button_text' ] );
187 -
188 - return $setting;
189 - }
190 -
191 - /**
192 - * Render module essencial instructions.
193 - *
194 - * @return void
195 - */
196 - public function admin_module_essencial_instructions() { ?>
197 - <div class="merchant-module-page-settings">
198 - <div class="merchant-module-page-setting-box merchant-module-page-setting-box-style-2">
199 - <div class="merchant-module-page-setting-title"><?php echo esc_html__( 'Tag Pre-Orders', 'merchant' ); ?></div>
200 - <div class="merchant-module-page-setting-fields">
201 - <div class="merchant-module-page-setting-field merchant-module-page-setting-field-content">
202 - <div class="merchant-module-page-setting-field-inner">
203 - <div class="merchant-tag-pre-orders">
204 - <i class="dashicons dashicons-info"></i>
205 - <p><?php echo esc_html__( 'Pre-orders captured by Merchant are tagged with "MerchantPreOrder" and can be found in your WooCommerce Order Section.', 'merchant' ); ?> <?php echo sprintf( '<a href="%s" target="_blank">%s</a>', esc_url( admin_url( 'edit.php?post_type=shop_order' ) ), esc_html__( 'View Pre-Orders', 'merchant' ) ); ?></p>
206 - </div>
207 - </div>
208 - </div>
209 - </div>
210 - </div>
211 - </div>
212 -
213 - <?php
214 - }
215 -
216 - /**
217 - * Render admin preview
218 - *
219 - * @param Merchant_Admin_Preview $preview
220 - * @param string $module
221 - *
222 - * @return Merchant_Admin_Preview
223 - */
224 - public function render_admin_preview( $preview, $module ) {
225 - if ( self::MODULE_ID === $module ) {
226 - $settings = $this->get_module_settings();
227 -
228 - // Additional text.
229 - $additional_text = $settings[ 'additional_text' ];
230 - $time_format = date_i18n( get_option( 'date_format' ), strtotime( gmdate( 'Y-m-d', strtotime('+2 days') ) ) );
231 - $text = $this->main_func->replaceDateTxt( $additional_text, $time_format );
232 -
233 - ob_start();
234 - self::admin_preview_content( $settings, $text );
235 - $content = ob_get_clean();
236 -
237 - // HTML.
238 - $preview->set_html( $content );
239 -
240 - // Button Text.
241 - $preview->set_text( 'button_text', '.add_to_cart_button' );
242 -
243 - // Additional Text.
244 - $preview->set_text( 'additional_text', '.merchant-pre-orders-date', array(
245 - array(
246 - '{date}'
247 - ),
248 - array(
249 - $time_format
250 - )
251 - ) );
252 -
253 - }
254 -
255 - return $preview;
256 - }
257 -
258 - /**
259 - * Admin preview content.
260 - *
261 - * @return void
262 - */
263 - public function admin_preview_content( $settings, $text ) {
264 - ?>
265 -
266 - <div class="mrc-preview-single-product-elements">
267 - <div class="mrc-preview-left-column">
268 - <div class="mrc-preview-product-image-wrapper">
269 - <div class="mrc-preview-product-image"></div>
270 - <div class="mrc-preview-product-image-thumbs">
271 - <div class="mrc-preview-product-image-thumb"></div>
272 - <div class="mrc-preview-product-image-thumb"></div>
273 - <div class="mrc-preview-product-image-thumb"></div>
274 - </div>
275 - </div>
276 - </div>
277 - <div class="mrc-preview-right-column">
278 - <div class="mrc-preview-text-placeholder"></div>
279 - <div class="mrc-preview-text-placeholder mrc-mw-70"></div>
280 - <div class="mrc-preview-text-placeholder mrc-mw-40 mrc-hide-on-smaller-screens"></div>
281 - <div class="mrc-preview-text-placeholder mrc-mw-30 mrc-hide-on-smaller-screens"></div>
282 - <div class="merchant-pre-ordered-product">
283 - <div class="merchant-pre-orders-date"><?php echo sprintf( '<div class="merchant-pre-orders-date">%s</div>', esc_html( $text ) ); ?></div>
284 - <a href="#" class="add_to_cart_button"><?php echo esc_html( $settings[ 'button_text' ] ); ?></a>
285 - </div>
286 - </div>
287 - </div>
288 -
289 - <?php
290 - }
291 -
292 - /**
293 - * Custom CSS.
294 - *
295 - * @return string
296 - */
297 - public function get_module_custom_css() {
298 - $css = '';
299 -
300 - // Text Color.
301 - $css .= Merchant_Custom_CSS::get_variable_css( 'pre-orders', 'text-color', '#FFF', '.merchant-pre-ordered-product', '--mrc-po-text-color' );
302 -
303 - // Text Color (hover).
304 - $css .= Merchant_Custom_CSS::get_variable_css( 'pre-orders', 'text-hover-color', '#FFF', '.merchant-pre-ordered-product', '--mrc-po-text-hover-color' );
305 -
306 - // Border Color.
307 - $css .= Merchant_Custom_CSS::get_variable_css( 'pre-orders', 'border-color', '#212121', '.merchant-pre-ordered-product', '--mrc-po-border-color' );
308 -
309 - // Border Color (hover).
310 - $css .= Merchant_Custom_CSS::get_variable_css( 'pre-orders', 'border-hover-color', '#414141', '.merchant-pre-ordered-product', '--mrc-po-border-hover-color' );
311 -
312 - // Background Color.
313 - $css .= Merchant_Custom_CSS::get_variable_css( 'pre-orders', 'background-color', '#212121', '.merchant-pre-ordered-product', '--mrc-po-background-color' );
314 -
315 - // Background Color (hover).
316 - $css .= Merchant_Custom_CSS::get_variable_css( 'pre-orders', 'background-hover-color', '#414141', '.merchant-pre-ordered-product', '--mrc-po-background-hover-color' );
317 -
318 - return $css;
319 - }
320 -
321 - /**
322 - * Admin custom CSS.
323 - *
324 - * @param string $css The custom CSS.
325 - * @return string $css The custom CSS.
326 - */
327 - public function admin_custom_css( $css ) {
328 - $css .= $this->get_module_custom_css();
329 -
330 - return $css;
331 - }
332 -
333 - /**
334 - * Frontend custom CSS.
335 - *
336 - * @param string $css The custom CSS.
337 - * @return string $css The custom CSS.
338 - */
339 - public function frontend_custom_css( $css ) {
340 - $css .= $this->get_module_custom_css();
341 -
342 - return $css;
343 - }
344 -
345 -}
346 -
347 -// Main functionality.
348 -require MERCHANT_DIR . 'inc/modules/pre-orders/class-pre-orders-main-functionality.php';
349 -
350 -// Initialize the module.
351 -add_action( 'init', function() {
352 - new Merchant_Pre_Orders( new Merchant_Pre_Orders_Main_Functionality() );
353 -} );
1 +<?php
2 +/**
3 + * Merchant - Pre Orders
4 + *
5 + * @package Merchant
6 + */
7 +
8 +if ( ! defined( 'ABSPATH' ) ) {
9 + exit; // Exit if accessed directly
10 +}
11 +
12 +/**
13 + * Pre Orders Class.
14 + *
15 + * This class handles the initialization and setup of the Pre Orders module, including
16 + * admin settings, asset enqueueing, and frontend styling.
17 + *
18 + * @property string $module_id The module ID.
19 + * @property bool $wc_only The WooCommerce only flag.
20 + * @property string $module_section The module section.
21 + * @property array $module_default_settings The module default settings.
22 + * @property array $module_data The module data.
23 + * @property string $module_options_path The module options path.
24 + */
25 +class Merchant_Pre_Orders extends Merchant_Add_Module {
26 +
27 + /**
28 + * Module ID.
29 + *
30 + * @var string The module ID.
31 + */
32 + const MODULE_ID = 'pre-orders';
33 +
34 + /**
35 + * Is module preview.
36 + *
37 + * @var bool The is module preview flag.
38 + */
39 + public static $is_module_preview = false;
40 +
41 + /**
42 + * Main functionality dependency.
43 + *
44 + * @var Merchant_Pre_Orders_Main_Functionality The main functionality instance.
45 + */
46 + public $main_func;
47 +
48 + /**
49 + * Set the module as having analytics.
50 + *
51 + * @var bool
52 + */
53 + protected $has_analytics = true;
54 +
55 + /**
56 + * Initialize the module's option group definitions.
57 + *
58 + * @return array<int, array<string, mixed>> Array of option group arrays.
59 + */
60 + protected function init_option_groups() {
61 + return require MERCHANT_DIR . 'inc/modules/' . self::MODULE_ID . '/admin/options.php';
62 + }
63 +
64 + /**
65 + * Fire action before rendering option groups.
66 + *
67 + * @return void
68 + */
69 + protected function before_render_option_groups() {
70 + /**
71 + * Fires before module options are rendered.
72 + *
73 + * @since 1.0
74 + *
75 + * @param string $module_id The module ID.
76 + */
77 + do_action( 'merchant_admin_before_include_modules_options', self::MODULE_ID );
78 + }
79 +
80 + /**
81 + * Constructor.
82 + *
83 + * @param Merchant_Pre_Orders_Main_Functionality $main_func The main functionality instance.
84 + */
85 + public function __construct( Merchant_Pre_Orders_Main_Functionality $main_func ) {
86 + // Module id.
87 + $this->module_id = self::MODULE_ID;
88 +
89 + // WooCommerce only.
90 + $this->wc_only = true;
91 +
92 + // Parent construct.
93 + parent::__construct();
94 +
95 + $this->main_func = $main_func;
96 +
97 + // Module section.
98 + $this->module_section = 'boost-revenue';
99 +
100 + // Module default settings.
101 + $this->module_default_settings = array(
102 + 'button_text' => __( 'Pre Order Now!', 'merchant' ),
103 + 'additional_text' => __( 'Ships on {date}.', 'merchant' ),
104 + );
105 +
106 + // Module data.
107 + $this->module_data = Merchant_Admin_Modules::$modules_data[ self::MODULE_ID ];
108 +
109 + // AI prompt examples.
110 + $this->ai_examples = array(
111 + 'blurb' => esc_html__( 'See what AI can do for your pre-orders, from launching a new campaign to adjusting the discount, shipping date, or button text on one already running.', 'merchant' ),
112 + 'prompts' => array(
113 + esc_html__( 'Explore the abilities WPVibe gives you access to, then create a pre-order for the Galaxy Pro Headphones with an expected shipping date of March 15, 2026', 'merchant' ),
114 + esc_html__( 'Check what abilities you have available through WPVibe, then set up a pre-order for the entire Spring Collection category with a fixed $10 discount instead of a percentage', 'merchant' ),
115 + esc_html__( "Look at your available WPVibe abilities, then change the button text on the Galaxy Pro Headphones pre-order to 'Reserve Yours'", 'merchant' ),
116 + esc_html__( 'See what abilities WPVibe exposes, then exclude the Wired Earbuds product from the Spring Collection pre-order', 'merchant' ),
117 + ),
118 + );
119 +
120 + // Module options path.
121 + $this->module_options_path = MERCHANT_DIR . 'inc/modules/' . self::MODULE_ID . '/admin/options.php';
122 +
123 + // Is module preview page.
124 + if ( is_admin() && parent::is_module_settings_page() ) {
125 + self::$is_module_preview = true;
126 +
127 + // Enqueue admin styles.
128 + add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_css' ) );
129 + add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_js' ) );
130 +
131 + // Localize Script.
132 + add_filter( 'merchant_admin_localize_script', array( $this, 'localize_script' ) );
133 +
134 + // Admin preview box.
135 + add_filter( 'merchant_module_preview', array( $this, 'render_admin_preview' ), 10, 2 );
136 +
137 + // Custom CSS.
138 + // The custom CSS should be added here as well due to ensure preview box works properly.
139 + add_filter( 'merchant_custom_css', array( $this, 'admin_custom_css' ) );
140 + }
141 +
142 + add_action( 'merchant_admin_before_include_modules_options', array( $this, 'help_banner' ) );
143 +
144 + if ( Merchant_Modules::is_module_active( self::MODULE_ID ) && is_admin() ) {
145 + // Init translations.
146 + $this->init_translations();
147 +
148 + // Include product metabox for per-product settings.
149 + require_once MERCHANT_DIR . 'inc/modules/' . self::MODULE_ID . '/admin/class-pre-orders-metabox.php';
150 + }
151 +
152 +
153 + if ( ! Merchant_Modules::is_module_active( self::MODULE_ID ) ) {
154 + return;
155 + }
156 +
157 + // TODO: Refactor the 'Merchant_Pre_Orders_Main_Functionality' class to load admin things separated from frontend things.
158 + $main_func->init();
159 +
160 + // Return early if it's on admin but not in the respective module settings page.
161 + if ( is_admin() && ! wp_doing_ajax() && ! parent::is_module_settings_page() ) {
162 + return;
163 + }
164 +
165 + // Enqueue styles.
166 + add_action( 'merchant_enqueue_before_main_css_js', array( $this, 'enqueue_css' ) );
167 +
168 + // Enqueue scripts.
169 + add_action( 'merchant_enqueue_after_main_css_js', array( $this, 'enqueue_scripts' ) );
170 +
171 + // Localize script.
172 + add_filter( 'merchant_localize_script', array( $this, 'localize_script' ) );
173 +
174 + // Custom CSS.
175 + add_filter( 'merchant_custom_css', array( $this, 'frontend_custom_css' ) );
176 +
177 + $this->supply_missing_id( 'rules' );
178 + }
179 +
180 + /**
181 + * Init translations for module settings.
182 + *
183 + * Registers strings for the Merchant translator tool.
184 + *
185 + * @return void
186 + */
187 + public function init_translations() {
188 + $settings = $this->get_module_settings();
189 + if ( ! empty( $settings['rules'] ) ) {
190 + foreach ( $settings['rules'] as $rule ) {
191 + if ( ! empty( $rule['button_text'] ) ) {
192 + Merchant_Translator::register_string( $rule['button_text'], 'Pre order button text' );
193 + }
194 + if ( ! empty( $rule['additional_text'] ) ) {
195 + Merchant_Translator::register_string( $rule['additional_text'], 'Pre order additional information' );
196 + }
197 + if ( ! empty( $rule['cart_label_text'] ) ) {
198 + Merchant_Translator::register_string( $rule['cart_label_text'], 'Label text on cart' );
199 + }
200 + }
201 + }
202 + }
203 +
204 + /**
205 + * Enqueue admin-specific CSS.
206 + *
207 + * @return void
208 + */
209 + public function admin_enqueue_css() {
210 + if ( parent::is_module_settings_page() ) {
211 + wp_enqueue_style( 'merchant-' . self::MODULE_ID, MERCHANT_URI . 'assets/css/modules/' . self::MODULE_ID . '/pre-orders.min.css', array(), MERCHANT_VERSION );
212 + wp_enqueue_style( 'merchant-admin-' . self::MODULE_ID, MERCHANT_URI . 'assets/css/modules/' . self::MODULE_ID . '/admin/preview.min.css', array(), MERCHANT_VERSION );
213 + }
214 + }
215 +
216 + /**
217 + * Enqueue admin-specific JavaScript.
218 + *
219 + * @return void
220 + */
221 + public function admin_enqueue_js() {
222 + if ( parent::is_module_settings_page() ) {
223 + wp_enqueue_script(
224 + 'merchant-admin-' . self::MODULE_ID,
225 + MERCHANT_URI . 'assets/js/modules/' . self::MODULE_ID . '/admin/preview.min.js',
226 + array( 'jquery' ),
227 + MERCHANT_VERSION,
228 + true
229 + );
230 + }
231 + }
232 +
233 + /**
234 + * Enqueue frontend-specific CSS.
235 + *
236 + * @return void
237 + */
238 + public function enqueue_css() {
239 + // Specific module styles.
240 + wp_enqueue_style( 'merchant-' . self::MODULE_ID, MERCHANT_URI . 'assets/css/modules/' . self::MODULE_ID . '/pre-orders.min.css', array(), MERCHANT_VERSION );
241 +
242 + // add inline style
243 + if ( is_singular( 'product' ) ) {
244 + wp_add_inline_style( 'merchant-' . self::MODULE_ID, $this->add_inline_style() );
245 + }
246 + }
247 +
248 + /**
249 + * Add inline CSS styles based on the active rule.
250 + *
251 + * @return string The generated inline CSS.
252 + */
253 + public function add_inline_style() {
254 + ob_start();
255 + $rule = $this->current_rule();
256 + if ( ! empty( $rule ) ) {
257 + ?>
258 + .woocommerce .merchant-pre-ordered-product{
259 + --mrc-po-text-color: <?php echo esc_attr( $rule['text-color'] ?? '#FFF' ); ?>;
260 + --mrc-po-text-hover-color: <?php echo esc_attr( $rule['text-hover-color'] ?? '#FFF' ); ?>;
261 + --mrc-po-border-color: <?php echo esc_attr( $rule['border-color'] ?? '#212121' ); ?>;
262 + --mrc-po-border-hover-color: <?php echo esc_attr( $rule['border-hover-color'] ?? '#414141' ); ?>;
263 + --mrc-po-border-width: <?php echo esc_attr( ( $rule['border-width'] ?? 0 ) . 'px' ); ?>;
264 + --mrc-po-border-radius: <?php echo esc_attr( ( $rule['border-radius'] ?? 0 ) . 'px' ); ?>;
265 + --mrc-po-background-color: <?php echo esc_attr( $rule['background-color'] ?? '#212121' ); ?>;
266 + --mrc-po-background-hover-color: <?php echo esc_attr( $rule['background-hover-color'] ?? '#414141' ); ?>;
267 + }
268 + <?php
269 + }
270 +
271 + return ob_get_clean();
272 + }
273 +
274 + /**
275 + * Enqueue frontend-specific scripts.
276 + *
277 + * @return void
278 + */
279 + public function enqueue_scripts() {
280 + // Register and enqueue the main module script.
281 + wp_enqueue_script( 'merchant-' . self::MODULE_ID, MERCHANT_URI . 'assets/js/modules/' . self::MODULE_ID . '/pre-orders.min.js', array(), MERCHANT_VERSION, true );
282 + }
283 +
284 + /**
285 + * Localize the main module script with settings and translations.
286 + *
287 + * @param array $setting The localized settings array.
288 + *
289 + * @return array The updated settings array.
290 + */
291 + public function localize_script( $setting ) {
292 + $setting['pre_orders'] = true;
293 + $rule = $this->current_rule();
294 + if ( ! empty( $rule ) && ! empty( $rule['button_text'] ) ) {
295 + $setting['pre_orders_add_button_title'] = Merchant_Translator::translate( $rule['button_text'] );
296 + } else {
297 + $setting['pre_orders_add_button_title'] = esc_html__( 'Pre Order Now!', 'merchant' );
298 + }
299 +
300 + $setting['shipping_date_missing_text'] = esc_html__( 'Please set a shipping date first', 'merchant' );
301 +
302 + return $setting;
303 + }
304 +
305 + /**
306 + * Render the admin preview for the module.
307 + *
308 + * @param Merchant_Admin_Preview $preview The preview object.
309 + * @param string $module The module ID.
310 + *
311 + * @return Merchant_Admin_Preview The updated preview object.
312 + */
313 + public function render_admin_preview( $preview, $module ) {
314 + if ( self::MODULE_ID === $module ) {
315 + $settings = $this->get_module_settings();
316 +
317 + // Additional text.
318 + $additional_text = $settings['additional_text'];
319 + $time_format = date_i18n( get_option( 'date_format' ), strtotime( gmdate( 'Y-m-d', strtotime( '+2 days' ) ) ) );
320 + $text = $this->main_func->replace_date_text( $additional_text, $time_format );
321 +
322 + ob_start();
323 + self::admin_preview_content( $settings, $text );
324 + $content = ob_get_clean();
325 +
326 + // HTML.
327 + $preview->set_html( $content );
328 +
329 + // Button Text.
330 + $preview->set_text( 'button_text', '.add_to_cart_button' );
331 +
332 + // Additional Text.
333 + $preview->set_text( 'additional_text', '.merchant-pre-orders-date', array(
334 + array(
335 + '{date}',
336 + ),
337 + array(
338 + $time_format,
339 + ),
340 + ) );
341 + }
342 +
343 + return $preview;
344 + }
345 +
346 + /**
347 + * Admin preview HTML content.
348 + *
349 + * @param array $settings The module settings.
350 + * @param string $text The formatted additional text.
351 + *
352 + * @return void
353 + */
354 + public function admin_preview_content( $settings, $text ) {
355 + ?>
356 + <div class="mrc-preview-single-product-elements">
357 + <div class="mrc-preview-left-column">
358 + <div class="mrc-preview-product-image-wrapper">
359 + <div class="mrc-preview-product-image"></div>
360 + <div class="mrc-preview-product-image-thumbs">
361 + <div class="mrc-preview-product-image-thumb"></div>
362 + <div class="mrc-preview-product-image-thumb"></div>
363 + <div class="mrc-preview-product-image-thumb"></div>
364 + </div>
365 + </div>
366 + </div>
367 + <div class="mrc-preview-right-column">
368 + <div class="mrc-preview-text-placeholder"></div>
369 + <div class="mrc-preview-text-placeholder mrc-mw-70"></div>
370 + <div class="mrc-preview-text-placeholder mrc-mw-40 mrc-hide-on-smaller-screens"></div>
371 + <div class="mrc-preview-text-placeholder mrc-mw-30 mrc-hide-on-smaller-screens"></div>
372 + <div class="merchant-pre-ordered-product">
373 + <div class="merchant-pre-orders-date"><?php echo esc_html( $text ); ?></div>
374 + <a href="#" class="add_to_cart_button"><?php echo esc_html( $settings['button_text'] ); ?></a>
375 + </div>
376 + </div>
377 + </div>
378 + <?php
379 + }
380 +
381 + /**
382 + * Get Custom CSS for the module.
383 + *
384 + * @return string The generated CSS.
385 + */
386 + public function get_module_custom_css() {
387 + $css = '';
388 +
389 + if ( is_admin() || is_singular( 'product' ) ) {
390 + // Text Color.
391 + $css .= Merchant_Custom_CSS::get_variable_css( 'pre-orders', 'text-color', '#FFF', '.merchant-pre-ordered-product', '--mrc-po-text-color' );
392 +
393 + // Text Color (hover).
394 + $css .= Merchant_Custom_CSS::get_variable_css( 'pre-orders', 'text-hover-color', '#FFF', '.merchant-pre-ordered-product', '--mrc-po-text-hover-color' );
395 +
396 + // Border Color.
397 + $css .= Merchant_Custom_CSS::get_variable_css( 'pre-orders', 'border-color', '#212121', '.merchant-pre-ordered-product', '--mrc-po-border-color' );
398 +
399 + // Border Color (hover).
400 + $css .= Merchant_Custom_CSS::get_variable_css( 'pre-orders', 'border-hover-color', '#414141', '.merchant-pre-ordered-product', '--mrc-po-border-hover-color' );
401 +
402 + // Border Width.
403 + $css .= Merchant_Custom_CSS::get_variable_css( 'pre-orders', 'border-width', 0, '.merchant-pre-ordered-product', '--mrc-po-border-width', 'px' );
404 +
405 + // Border Radius.
406 + $css .= Merchant_Custom_CSS::get_variable_css( 'pre-orders', 'border-radius', 0, '.merchant-pre-ordered-product', '--mrc-po-border-radius', 'px' );
407 +
408 + // Background Color.
409 + $css .= Merchant_Custom_CSS::get_variable_css( 'pre-orders', 'background-color', '#212121', '.merchant-pre-ordered-product', '--mrc-po-background-color' );
410 +
411 + // Background Color (hover).
412 + $css .= Merchant_Custom_CSS::get_variable_css( 'pre-orders', 'background-hover-color', '#414141', '.merchant-pre-ordered-product', '--mrc-po-background-hover-color' );
413 + }
414 +
415 + return $css;
416 + }
417 +
418 + /**
419 + * Filter for admin custom CSS.
420 + *
421 + * @param string $css The custom CSS.
422 + *
423 + * @return string The updated custom CSS.
424 + */
425 + public function admin_custom_css( $css ) {
426 + $css .= $this->get_module_custom_css();
427 +
428 + return $css;
429 + }
430 +
431 + /**
432 + * Filter for frontend custom CSS.
433 + *
434 + * @param string $css The custom CSS.
435 + *
436 + * @return string The updated custom CSS.
437 + */
438 + public function frontend_custom_css( $css ) {
439 + $css .= $this->get_module_custom_css();
440 +
441 + return $css;
442 + }
443 +
444 + /**
445 + * Get the currently applicable product rule.
446 + *
447 + * @return array The rule array if found, empty array otherwise.
448 + */
449 + private function current_rule() {
450 + if ( is_singular( 'product' ) ) {
451 + $product_id = get_queried_object_id();
452 + $product = wc_get_product( $product_id );
453 +
454 + if ( ! $product ) {
455 + return array();
456 + }
457 +
458 + $rule = Merchant_Pre_Orders_Main_Functionality::available_product_rule( $product->get_id() );
459 + if ( empty( $rule ) && $product instanceof \WC_Product_Variable ) {
460 + $available_variations = $product->get_available_variations();
461 + foreach ( $available_variations as $variation ) {
462 + $rule = Merchant_Pre_Orders_Main_Functionality::available_product_rule( $variation['variation_id'] );
463 + if ( ! empty( $rule ) ) {
464 + break;
465 + }
466 + }
467 + }
468 +
469 + return $rule;
470 + }
471 +
472 + return array();
473 + }
474 +
475 + /**
476 + * Get the WooCommerce orders screen URL, aware of HPOS.
477 + *
478 + * @return string The orders screen URL.
479 + */
480 + private function orders_screen_url() {
481 + if ( class_exists( \Automattic\WooCommerce\Utilities\OrderUtil::class ) && \Automattic\WooCommerce\Utilities\OrderUtil::custom_orders_table_usage_is_enabled() ) {
482 + return admin_url( 'admin.php?page=wc-orders' );
483 + }
484 +
485 + return admin_url( 'edit.php?post_type=shop_order' );
486 + }
487 +
488 + /**
489 + * Display a help banner in the module settings page.
490 + *
491 + * @param string $module_id The module ID.
492 + *
493 + * @return void
494 + */
495 + public function help_banner( $module_id ) {
496 + if ( $module_id === self::MODULE_ID ) {
497 + $orders_url = $this->orders_screen_url();
498 + ?>
499 + <div class="merchant-module-page-setting-fields">
500 + <div class="merchant-module-page-setting-field merchant-module-page-setting-field-content">
501 + <div class="merchant-module-page-setting-field-inner">
502 + <div class="merchant-tag-pre-orders">
503 + <i class="dashicons dashicons-info"></i>
504 + <p>
505 + <?php
506 + echo esc_html__(
507 + 'Pre-orders captured by Merchant are tagged with "Merchant PreOrder" and can be found in your WooCommerce Order Section. You can control pre-order settings on a per-product basis from the individual product page.',
508 + 'merchant'
509 + );
510 + printf(
511 + '<a href="%1s" target="_blank">%2s</a>',
512 + esc_url( $orders_url ),
513 + esc_html__( 'View Pre-Orders', 'merchant' )
514 + );
515 + ?></p>
516 + </div>
517 + </div>
518 + </div>
519 + </div>
520 + <?php
521 + }
522 + }
523 +
524 + /**
525 + * Supply missing flexible_id for the rules setting.
526 + *
527 + * @param string $setting_key The setting key for the flexible items.
528 + *
529 + * @return void
530 + */
531 + public function supply_missing_id( $setting_key = 'offers' ) {
532 + $option = 'merchant_' . $this->module_id . '_missing_id_flag';
533 +
534 + if ( get_option( $option, false ) || ! method_exists( 'Merchant_Admin_Options', 'set' ) ) {
535 + return;
536 + }
537 +
538 + $flexible_items = Merchant_Admin_Options::get( $this->module_id, $setting_key, array() );
539 + if ( ! empty( $flexible_items ) ) {
540 + $update = 0;
541 + foreach ( $flexible_items as $key => $item ) {
542 + if ( empty( $item['flexible_id'] ) ) {
543 + $flexible_items[ $key ]['flexible_id'] = wp_generate_uuid4();
544 + ++ $update;
545 + }
546 + }
547 +
548 + if ( $update > 0 ) {
549 + Merchant_Admin_Options::set( $this->module_id, $setting_key, $flexible_items );
550 + }
551 +
552 + update_option( $option, true, false );
553 + }
554 + }
555 +}
556 +
557 +// Rules Repository.
558 +require_once MERCHANT_DIR . 'inc/modules/pre-orders/classes/class-pre-orders-rules.php';
559 +
560 +// Main functionality.
561 +require_once MERCHANT_DIR . 'inc/modules/pre-orders/class-pre-orders-main-functionality.php';
562 +
563 +// Initialize the module.
564 +add_action( 'init', function () {
565 + Merchant_Modules::create_module( new Merchant_Pre_Orders( new Merchant_Pre_Orders_Main_Functionality() ) );
566 +} );