PluginProbe ʕ •ᴥ•ʔ
SureCart – Ecommerce Made Easy For Selling Physical Products, Digital Downloads, Subscriptions, Donations, & Payments / 3.0.3
SureCart – Ecommerce Made Easy For Selling Physical Products, Digital Downloads, Subscriptions, Donations, & Payments v3.0.3
4.4.2 4.4.1 4.4.0 4.3.3 4.3.2 4.3.1 4.3.0 4.2.3 4.2.2 4.2.1 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.10 1.1.11 1.1.12 1.1.13 1.1.14 1.1.15 1.1.16 1.1.17 1.1.18 1.1.19 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 1.1.9 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.11.0 1.11.1 1.11.2 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.7.0 1.7.1 1.7.2 1.8.0 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.9.0 1.9.1 1.9.2 1.9.3 1.9.4 1.9.5 2.0.0 2.0.1 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.10.0 2.10.1 2.11.0 2.11.1 2.11.2 2.11.3 2.11.4 2.12.0 2.13.0 2.14.0 2.14.1 2.15.0 2.15.1 2.16.0 2.16.1 2.16.2 2.16.3 2.17.0 2.17.1 2.17.2 2.18.0 2.19.0 2.19.2 2.19.3 2.19.4 2.2.0 2.2.1 2.20.0 2.20.1 2.20.2 2.20.3 2.20.4 2.20.5 2.20.6 2.21.0 2.22.0 2.22.1 2.23.0 2.24.0 2.25.0 2.25.1 2.25.2 2.26.0 2.27.0 2.27.1 2.28.0 2.29.0 2.29.1 2.29.2 2.29.3 2.29.4 2.3.0 2.3.1 2.30.0 2.31.0 2.31.1 2.31.2 2.31.3 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.40.0 2.40.1 2.5.0 2.5.1 2.5.2 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.8.0 2.8.1 2.8.2 2.8.3 2.8.4 2.9.0 3.0.0 3.0.0-RC1 3.0.0-RC2 3.0.0-beta1 3.0.0-beta2 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.10.0 3.10.1 3.11.0 3.12.0 3.13.0 3.13.1 3.13.2 3.13.3 3.13.4 3.14.0 3.15.0 3.15.1 3.15.2 3.15.3 3.15.4 3.15.5 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.16.5 3.16.6 3.16.7 3.16.8 3.17.0 3.17.1 3.17.2 3.17.3 3.17.4 3.17.5 3.17.6 3.18.0 3.19.0 3.19.1 3.19.2 3.2.0 3.2.1 3.2.2 3.20.0 3.20.1 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.4.3 3.5.0 3.5.1 3.5.2 3.5.3 3.6.0 3.6.1 3.6.2 3.7.0 3.7.1 3.7.2 3.7.3 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.9.0 4.0.0 4.0.1 4.0.2 4.0.3 trunk 4.1.0 0.2.19.1 4.1.1 1.0.0 4.2.0 1.0.1 1.0.2
surecart / app / src / Integrations / Bricks / BricksDynamicDataService.php
surecart / app / src / Integrations / Bricks Last commit date
Concerns 1 year ago Elements 1 year ago scripts 1 year ago BricksDynamicDataService.php 1 year ago BricksElementsService.php 1 year ago BricksServiceProvider.php 1 year ago
BricksDynamicDataService.php
725 lines
1 <?php
2
3 namespace SureCart\Integrations\Bricks;
4
5 use SureCart\Support\Currency;
6
7 /**
8 * This class handles bricks dynamic data functionality.
9 */
10 class BricksDynamicDataService {
11 /**
12 * Provider name.
13 *
14 * @var string
15 */
16 protected $name = 'surecart';
17
18 /**
19 * Bootstrap the service.
20 *
21 * @return void
22 */
23 public function bootstrap() {
24 add_action( 'bricks/dynamic_tags_list', [ $this, 'dynamicTags' ] );
25 add_filter( 'bricks/dynamic_data/render_tag', [ $this, 'getTheTagValue' ], 10, 2 );
26 add_filter( 'bricks/frontend/render_data', [ $this, 'render' ], 10, 3 );
27 add_filter( 'bricks/dynamic_data/render_content', [ $this, 'render' ], 10, 3 );
28
29 // make sure scripts load in footer.
30 if ( ! defined( 'ACSS_FLAG_LOAD_DASHBOARD_SCRIPTS_IN_FOOTER' ) ) {
31 define( 'ACSS_FLAG_LOAD_DASHBOARD_SCRIPTS_IN_FOOTER', true );
32 }
33 }
34
35 /**
36 * Check if we are in the admin editor.
37 *
38 * @return bool
39 */
40 public function is_admin_editor() {
41 return ! bricks_is_frontend() || bricks_is_builder_call();
42 }
43
44 /**
45 * Register Tags.
46 *
47 * @param array $tags Existing tags.
48 *
49 * @return array
50 */
51 public function dynamicTags( $tags = [] ) {
52 return array_merge(
53 $tags,
54 [
55 [
56 'slug' => 'sc_product_price',
57 'name' => '{sc_product_price}',
58 'label' => esc_html__( 'Product price', 'surecart' ),
59 'group' => esc_html__( 'SureCart Product', 'surecart' ),
60 ],
61 [
62 'slug' => 'sc_product_selected_price',
63 'name' => '{sc_product_selected_price}',
64 'label' => esc_html__( 'Product selected price', 'surecart' ),
65 'group' => esc_html__( 'SureCart Product', 'surecart' ),
66 ],
67 [
68 'slug' => 'sc_product_scratch_price',
69 'name' => '{sc_product_scratch_price}',
70 'label' => esc_html__( 'Product scratch price', 'surecart' ),
71 'group' => esc_html__( 'SureCart Product', 'surecart' ),
72 ],
73 [
74 'slug' => 'sc_product_selected_scratch_price',
75 'name' => '{sc_product_selected_scratch_price}',
76 'label' => esc_html__( 'Product selected scratch price', 'surecart' ),
77 'group' => esc_html__( 'SureCart Product', 'surecart' ),
78 ],
79 [
80 'slug' => 'sc_product_price_range',
81 'name' => '{sc_product_price_range}',
82 'label' => esc_html__( 'Product price range', 'surecart' ),
83 'group' => esc_html__( 'SureCart Product', 'surecart' ),
84 ],
85 [
86 'slug' => 'sc_product_description',
87 'name' => '{sc_product_description}',
88 'label' => esc_html__( 'Product description', 'surecart' ),
89 'group' => esc_html__( 'SureCart Product', 'surecart' ),
90 ],
91 [
92 'slug' => 'sc_product_stock',
93 'name' => '{sc_product_stock}',
94 'label' => esc_html__( 'Product stock', 'surecart' ),
95 'group' => esc_html__( 'SureCart Product', 'surecart' ),
96 ],
97 [
98 'slug' => 'sc_product_sku',
99 'name' => '{sc_product_sku}',
100 'label' => esc_html__( 'Product SKU', 'surecart' ),
101 'group' => esc_html__( 'SureCart Product', 'surecart' ),
102 ],
103 [
104 'slug' => 'sc_product_on_sale',
105 'name' => '{sc_product_on_sale}',
106 'label' => esc_html__( 'Product on sale', 'surecart' ),
107 'group' => esc_html__( 'SureCart Product', 'surecart' ),
108 ],
109 [
110 'slug' => 'sc_product_trial',
111 'name' => '{sc_product_trial}',
112 'label' => esc_html__( 'Product trial', 'surecart' ),
113 'group' => esc_html__( 'SureCart Product', 'surecart' ),
114 ],
115 [
116 'slug' => 'sc_product_selected_trial',
117 'name' => '{sc_product_selected_trial}',
118 'label' => esc_html__( 'Product selected price trial', 'surecart' ),
119 'group' => esc_html__( 'SureCart Product', 'surecart' ),
120 ],
121 [
122 'slug' => 'sc_product_billing_interval',
123 'name' => '{sc_product_billing_interval}',
124 'label' => esc_html__( 'Product billing interval', 'surecart' ),
125 'group' => esc_html__( 'SureCart Product', 'surecart' ),
126 ],
127 [
128 'slug' => 'sc_product_selected_billing_interval',
129 'name' => '{sc_product_selected_billing_interval}',
130 'label' => esc_html__( 'Product selected price billing interval', 'surecart' ),
131 'group' => esc_html__( 'SureCart Product', 'surecart' ),
132 ],
133 [
134 'slug' => 'sc_product_setup_fee',
135 'name' => '{sc_product_setup_fee}',
136 'label' => esc_html__( 'Product setup fee', 'surecart' ),
137 'group' => esc_html__( 'SureCart Product', 'surecart' ),
138 ],
139 [
140 'slug' => 'sc_product_selected_setup_fee',
141 'name' => '{sc_product_selected_setup_fee}',
142 'label' => esc_html__( 'Product selected price setup fee', 'surecart' ),
143 'group' => esc_html__( 'SureCart Product', 'surecart' ),
144 ],
145 [
146 'slug' => 'sc_price_name',
147 'name' => '{sc_price_name}',
148 'label' => esc_html__( 'Price name', 'surecart' ),
149 'group' => esc_html__( 'SureCart Product', 'surecart' ),
150 ],
151 [
152 'slug' => 'sc_price_amount',
153 'name' => '{sc_price_amount}',
154 'label' => esc_html__( 'Price amount', 'surecart' ),
155 'group' => esc_html__( 'SureCart Product', 'surecart' ),
156 ],
157 [
158 'slug' => 'sc_price_trial',
159 'name' => '{sc_price_trial}',
160 'label' => esc_html__( 'Price trial', 'surecart' ),
161 'group' => esc_html__( 'SureCart Product', 'surecart' ),
162 ],
163 [
164 'slug' => 'sc_price_setup_fee',
165 'name' => '{sc_price_setup_fee}',
166 'label' => esc_html__( 'Price setup fee', 'surecart' ),
167 'group' => esc_html__( 'SureCart Product', 'surecart' ),
168 ],
169 ]
170 );
171 }
172
173 /**
174 * Parse tag
175 *
176 * @param string $name Tag name.
177 * @param object $post Post object.
178 * @param array $filters Filters.
179 */
180 public function parseTag( $name, $post, $filters ) {
181 $post_id = isset( $post->ID ) ? $post->ID : '';
182 $product = $post_id ? sc_get_product( $post_id ) : false;
183
184 switch ( $name ) {
185 // static product price.
186 case 'product_price':
187 // Support ':value' filter to get the price value as a simple string (e.g.: 65.3, 2.5, 5 ).
188 if ( isset( $filters['value'] ) ) {
189 return esc_html( $product ? Currency::maybeConvertAmount( $product->initial_amount ) : '' );
190 }
191
192 // Support ':raw' filter to get the price value as a simple string (e.g.: 1250, 250, 5 ).
193 if ( isset( $filters['raw'] ) ) {
194 return esc_html( $product ? $product->initial_amount : '' );
195 }
196
197 // preview in the admin editor.
198 if ( $this->is_admin_editor() ) {
199 return ( $product->display_amount ?? Currency::format( 1200 ) );
200 }
201
202 return $product->display_amount;
203
204 // the dymamically selected price for the product.
205 case 'product_selected_price':
206 // preview in the admin editor.
207 if ( $this->is_admin_editor() ) {
208 return "<span class='wp-block-surecart-product-selected-price-amount'>" . ( $product->display_amount ?? Currency::format( 1200 ) ) . '</span>';
209 }
210
211 // IMPORTANT: Don't remove the trailing space or the block may break in some contexts.
212 return '<!-- wp:surecart/product-selected-price-amount --><!-- /wp:surecart/product-selected-price-amount --> ';
213
214 // the static price range.
215 case 'product_price_range':
216 return esc_html( $product ? $product->range_display_amount : '' );
217
218 // static product scratch price.
219 case 'product_scratch_price':
220 // Support ':value' filter to get the price value as a simple string (e.g.: 65.3, 2.5, 5 ).
221 if ( isset( $filters['value'] ) ) {
222 return esc_html( $product ? Currency::maybeConvertAmount( $product->scratch_amount ) : '' );
223 }
224
225 // Support ':raw' filter to get the price value as a simple string (e.g.: 1450, 250, 5 ).
226 if ( isset( $filters['raw'] ) ) {
227 return esc_html( $product ? $product->scratch_amount : '' );
228 }
229
230 // preview in the admin editor.
231 if ( $this->is_admin_editor() ) {
232 return ( $product->scratch_display_amount ?? Currency::format( 1400 ) );
233 }
234
235 return $product->scratch_display_amount;
236
237 // the dymamically selected scratch price for the product.
238 case 'product_selected_scratch_price':
239 // preview in the admin editor.
240 if ( $this->is_admin_editor() ) {
241 return "<span class='wp-block-surecart-product-selected-price-scratch-amount sc-price__amount'>" . ( $product->scratch_display_amount ?? '$14' ) . '</span>';
242 }
243
244 // IMPORTANT: Don't remove the trailing space or the block may break in some contexts.
245 return '<!-- wp:surecart/product-selected-price-scratch-amount --><!-- /wp:surecart/product-selected-price-scratch-amount --> ';
246
247 case 'product_description':
248 $clean_excerpt = trim( wp_strip_all_tags( $post->post_excerpt ) );
249 if ( empty( $clean_excerpt ) ) {
250 return '';
251 }
252 return '<span class="sc-prose">' . wp_kses_post( ! empty( $filters['num_words'] ) ? \Bricks\Helpers::get_the_excerpt( $post, $filters['num_words'], null, true ) : $post->post_excerpt ?? '' ) . '</span>';
253
254 case 'product_stock':
255 // unlimited stock, don't display stock.
256 if ( $product ? $product->has_unlimited_stock : '' ) {
257 return '';
258 }
259 if ( isset( $filters['meta_key'] ) && 'on_hand' === $filters['meta_key'] ) {
260 return esc_html( $product ? $product->stock : '' );
261 }
262 if ( isset( $filters['meta_key'] ) && 'held' === $filters['meta_key'] ) {
263 return esc_html( $product ? $product->held_stock : '' );
264 }
265 return esc_html( $product ? $product->available_stock : '' );
266
267 case 'product_sku':
268 return esc_html( $product ? $product->sku : '' );
269
270 case 'product_on_sale':
271 return esc_html( $product ? $product->is_on_sale : '' );
272
273 case 'product_trial':
274 if ( $this->is_admin_editor() ) {
275 return $product->trial_text ?? esc_html__( 'Starting in 15 days.', 'surecart' );
276 }
277 return $product->trial_text;
278
279 case 'product_selected_trial':
280 if ( $this->is_admin_editor() ) {
281 return "<span class='wp-block-surecart-product-selected-price-trial'>" . esc_html__( 'Starting in 15 days.', 'surecart' ) . '</span>';
282 }
283 // IMPORTANT: Don't remove the trailing space or the block may break in some contexts.
284 return '<!-- wp:surecart/product-selected-price-trial --><!-- /wp:surecart/product-selected-price-trial --> ';
285
286 case 'product_billing_interval':
287 if ( $this->is_admin_editor() ) {
288 return $product->billing_interval_text ?? esc_html__( '/ day (3 payments)', 'surecart' );
289 }
290 return $product->billing_interval_text ?? '';
291
292 case 'product_selected_billing_interval':
293 if ( $this->is_admin_editor() ) {
294 return "<span class='wp-block-surecart-product-selected-price-interval sc-price__amount'>" . esc_html__( '/ day (3 payments)', 'surecart' ) . '</span>';
295 }
296 // IMPORTANT: Don't remove the trailing space or the block may break in some contexts.
297 return '<!-- wp:surecart/product-selected-price-interval --><!-- /wp:surecart/product-selected-price-interval --> ';
298
299 case 'product_setup_fee':
300 if ( $this->is_admin_editor() ) {
301 // translators: %s: Setup Fee amount.
302 return $product->setup_fee_text ?? esc_html( sprintf( __( '%s setup fee.', 'surecart' ), Currency::format( 100 ) ) );
303 }
304 // IMPORTANT: Don't remove the trailing space or the block may break in some contexts.
305 return $product->setup_fee_text ?? '';
306
307 case 'product_selected_setup_fee':
308 if ( $this->is_admin_editor() ) {
309 // translators: %s: Setup Fee amount.
310 return "<span class='wp-block-surecart-product-selected-price-fees'>" . esc_html( sprintf( __( '%s setup fee.', 'surecart' ), Currency::format( 100 ) ) ) . '</span>';
311 }
312 // IMPORTANT: Don't remove the trailing space or the block may break in some contexts.
313 return '<!-- wp:surecart/product-selected-price-fees --><!-- /wp:surecart/product-selected-price-fees --> ';
314
315 case 'price_name':
316 if ( $this->is_admin_editor() ) {
317 // translators: %s: Setup Fee amount.
318 return "<span class='wp-block-surecart-price-name'>" . esc_html__( 'Price name', 'surecart' ) . '</span>';
319 }
320 // IMPORTANT: Don't remove the trailing space or the block may break in some contexts.
321 return '<!-- wp:surecart/price-name --><!-- /wp:surecart/price-name --> ';
322
323 case 'price_amount':
324 if ( $this->is_admin_editor() ) {
325 // translators: %s: Setup Fee amount.
326 return "<span class='wp-block-surecart-price-amount'>" . sprintf( esc_attr__( '%1$s %2$s', 'surecart' ), Currency::format( 200 ), '/mo' ) . '</span>';
327 }
328 // IMPORTANT: Don't remove the trailing space or the block may break in some contexts.
329 return '<!-- wp:surecart/price-amount --><!-- /wp:surecart/price-amount --> ';
330
331 case 'price_trial':
332 if ( $this->is_admin_editor() ) {
333 // translators: %s: Setup Fee amount.
334 return "<span class='wp-block-surecart-product-price-trial'>" . esc_html__( 'Starting in 15 days', 'surecart' ) . '</span>';
335 }
336 // IMPORTANT: Don't remove the trailing space or the block may break in some contexts.
337 return '<!-- wp:surecart/price-trial --><!-- /wp:surecart/price-trial --> ';
338
339 case 'price_setup_fee':
340 if ( $this->is_admin_editor() ) {
341 // translators: %s: Setup Fee amount.
342 return "<span class='wp-block-surecart-product-price-setup-fee'>" . esc_html__( '$12 Signup Fee', 'surecart' ) . '</span>';
343 }
344 // IMPORTANT: Don't remove the trailing space or the block may break in some contexts.
345 return '<!-- wp:surecart/price-setup-fee --><!-- /wp:surecart/price-setup-fee --> ';
346 }
347
348 return $name;
349 }
350
351 /**
352 * Dynamic tag exists in $content: Replaces dynamic tag with requested data.
353 *
354 * @param string $content Content.
355 * @param WP_Post $post Post object.
356 * @param string $context Context.
357 *
358 * @return string
359 */
360 public function render( $content, $post, $context = 'text' ) {
361 /**
362 * \w: Matches any word character (alphanumeric & underscore).
363 * Equivalent to [A-Za-z0-9_]
364 * "À-ÖØ-öø-ÿ" Add the accented characters
365 * "-" Needed because some post types handles are like "my-post-type"
366 * ":" Needed for extra arguments to dynamic data tags (e.g. post_excerpt:20 or wp_user_meta:my_meta_key)
367 * "|" and "," needed for the post terms like {post_terms_post_tag:sep} where sep could be a pipe or comma
368 * "(", ")" and "'" for the function arguments of the dynamic tag {echo}
369 * "@" to support email addresses as arguments of the dynamic tag {echo} #3kazphp
370 *
371 * @since 1.9.4: "u" modifier: Pattern strings are treated as UTF-8 to support Cyrillic, Arabic, etc.
372 * @since 1.10: "$", "+", "%", "#", "!", "=", "<", ">", "&", "~", "[", "]", ";" as arguments of the dynamic tag {echo}
373 *
374 * @see https://regexr.com/
375 */
376 $pattern = '/{([\wÀ-ÖØ-öø-ÿ\-\s\.\/:\(\)\'@|,$%#!+=<>&~\[\];]+)}/u';
377
378 /**
379 * Matches the echo tag pattern (#86bwebj6m)
380 *
381 * @since 1.9.8
382 */
383 $echo_pattern = '/echo:([a-zA-Z0-9_]+)/';
384
385 // Get a list of tags to exclude from the Dynamic Data logic.
386 $exclude_tags = apply_filters( 'bricks/dynamic_data/exclude_tags', [] );
387
388 /**
389 * STEP: Determine how many times we need to run the DD parser
390 *
391 * Previously we ran the parser by counting the number of open curly braces in the content. (@since 1.8)
392 * But this is not reliable because the content could contain curly braces in the code elements or any shortcodes.
393 * Causing the website to load extremely slow.
394 *
395 * @since 1.8.2 (#862jyyryg)
396 */
397 // Get all registered tags except the excluded ones.
398 // Example: [0 => "post_title", 1 => "woo_product_price", 2 => "echo"].
399 $registered_tags = array_filter(
400 array_column( $this->dynamicTags(), 'slug' ),
401 function ( $tag ) use ( $exclude_tags ) {
402 return ! in_array( $tag, $exclude_tags, true );
403 }
404 );
405
406 $dd_tags_in_content = [];
407 $dd_tags_found = [];
408 $echo_tags_found = [];
409
410 // Find all dynamic data tags in the content.
411 preg_match_all( $pattern, $content, $dd_tags_in_content );
412
413 $dd_tags_in_content = ! empty( $dd_tags_in_content[1] ) ? $dd_tags_in_content[1] : [];
414
415 // Find all echo tags in the content.
416 preg_match_all( $echo_pattern, $content, $echo_tags_found );
417
418 // Combine the dynamic data tags from the content and the echo tags.
419 if ( ! empty( $echo_tags_found[0] ) ) {
420 $dd_tags_in_content = array_merge( $dd_tags_in_content, $echo_tags_found[0] );
421 }
422
423 if ( ! empty( $dd_tags_in_content ) ) {
424 /**
425 * $dd_tags_in_content only matches the pattern, but some codes from Code element could match the pattern too.
426 * Example: function test() { return 'Hello World'; } will match the pattern, but it's not a dynamic data tag.
427 *
428 * Find all dynamic data tags in the content which starts with dynamic data tag from $registered_tags
429 * Cannot use array_in or array_intersect because $registered_tags only contains the tag name, somemore tags could have filters like {echo:my_function( 'Hello World' )
430 *
431 * Example: $registered_tags = [0 => "post_title", 1 => "woo_product_price", 2 => "echo"]
432 * Example: $dd_tags_in_content = [0 => "post_title", 1 => "woo_product_price:value", 2 => "echo:my_function('Hello World')"]
433 */
434 $dd_tags_found = array_filter(
435 $dd_tags_in_content,
436 function ( $tag ) use ( $registered_tags ) {
437 foreach ( $registered_tags as $all_tag ) {
438 /**
439 * Skip not our tags.
440 */
441 if ( strpos( $tag, 'sc_' ) === 0 ) {
442 return true;
443 }
444
445 if ( strpos( $tag, $all_tag ) === 0 ) {
446 return true;
447 }
448 }
449 return false;
450 }
451 );
452 }
453
454 // Get the count of found dynamic data tags.
455 $dd_tag_count = count( $dd_tags_found );
456
457 // STEP: Run the parser based on the count of found dynamic data tags.
458 for ( $i = 0; $i < $dd_tag_count; $i++ ) {
459 preg_match_all( $pattern, $content, $matches );
460
461 if ( empty( $matches[0] ) ) {
462 return $content;
463 }
464
465 $run_again = false;
466
467 foreach ( $matches[1] as $key => $match ) {
468 $tag = $matches[0][ $key ];
469
470 if ( in_array( $match, $exclude_tags, true ) ) {
471 continue;
472 }
473
474 $value = $this->getTheTagValue( $match, $post, $context );
475
476 // Value is a WP_Error: Set value to false to avoid error in builder.
477 if ( is_a( $value, 'WP_Error' ) ) {
478 $value = false;
479 }
480
481 $content = str_replace( $tag, $value, $content );
482 }
483
484 if ( $run_again ) {
485 ++$dd_tag_count;
486 }
487 }
488
489 return $content;
490 }
491
492 /**
493 * Main function to render the tag value for WooCommerce provider
494 *
495 * @param string $tag Tag name.
496 * @param object $post Post object.
497 *
498 * @return string
499 */
500 public function getTheTagValue( $tag, $post ) {
501 $tags = $this->dynamicTags();
502
503 // Check if the tag exists in the registered tags.
504 $tag_exists = array_search( strtok( $tag, ':' ), array_column( $tags, 'slug' ), true );
505 if ( false === $tag_exists ) {
506 return $tag;
507 }
508
509 // parse tag and args.
510 $parsed = is_string( $tag ) ? $this->parse_tag_and_args( $tag ) : [
511 'tag' => $tag,
512 'args' => [],
513 ];
514 $tag = $parsed['tag'];
515 $args = $parsed['args'];
516
517 // Check for filter args.
518 $filters = $this->get_filters_from_args( $args );
519
520 // Get the tag name.
521 $name = isset( $tags[ $tag ]['render'] ) ? $tags[ $tag ]['render'] : str_replace( 'sc_', '', $tag );
522
523 return $this->parseTag( $name, $post, $filters );
524 }
525
526 /**
527 * Parse the tag and extract arguments
528 *
529 * @param string $tag The original tag string.
530 * @return array An array containing the parsed tag and arguments.
531 */
532 private function parse_tag_and_args( $tag ) {
533 $original_tag = $tag;
534 $args = [];
535
536 // Special case to allow using "@" as "echo:" tag parameter.
537 // TODO NEXT: More rebust DD argument parser (@see #86bzunbgf)
538 if ( strpos( $tag, 'echo:' ) === 0 ) {
539 return [
540 'tag' => 'echo',
541 'args' => [ substr( $tag, 5 ) ], // Everything after 'echo:'
542 'original_tag' => $original_tag,
543 ];
544 }
545
546 // Check if tag has ':' or '@' indicating it has arguments
547 if ( strpos( $tag, ':' ) !== false || strpos( $tag, '@' ) !== false ) {
548 // If there's a ':' before the first '@', split at the first ':'
549 if ( strpos( $tag, ':' ) !== false && ( strpos( $tag, '@' ) === false || strpos( $tag, ':' ) < strpos( $tag, '@' ) ) ) {
550 list($tag, $args_string) = explode( ':', $tag, 2 );
551 } else {
552 // If there's no ':' before the first '@', the tag is before the '@'
553 $args_string = $tag;
554 $tag = strtok( $tag, ' @' );
555 }
556
557 // Check if the args_string contains key-value pairs marked with '@'
558 if ( strpos( $args_string, '@' ) !== false ) {
559 list($standard_args, $kv_args_string) = explode( '@', $args_string, 2 );
560
561 // Add standard arguments to the args array
562 if ( ! empty( $standard_args ) ) {
563 $standard_args_array = explode( ':', $standard_args );
564 foreach ( $standard_args_array as $arg ) {
565 $args[] = trim( $arg );
566 }
567 }
568
569 // Split the key-value pairs
570 $kv_pairs = explode( '@', $kv_args_string );
571 foreach ( $kv_pairs as $pair ) {
572 list($key, $value) = explode( ':', $pair, 2 );
573 $args[ trim( $key ) ] = trim( $value );
574 }
575 } else {
576 // No key-value pairs, just standard arguments
577 $standard_args_array = explode( ':', $args_string );
578 foreach ( $standard_args_array as $arg ) {
579 $args[] = trim( $arg );
580 }
581 }
582 }
583
584 return [
585 'tag' => $tag,
586 'args' => $args,
587 'original_tag' => $original_tag,
588 ];
589 }
590
591 /**
592 * Calculate dynamic data filters according to the args received
593 *
594 * @param array $args
595 * @return array
596 */
597 public function get_filters_from_args( $args ) {
598 $filters = [
599 'object_type' => '',
600 ];
601
602 if ( empty( $args ) || ! is_array( $args ) ) {
603 return $filters;
604 }
605
606 foreach ( $args as $arg ) {
607 // Trim number of words or avatar size (in px)
608 if ( is_numeric( $arg ) ) {
609 $filters['num_words'] = $arg;
610 }
611
612 // Add context to the archive title
613 elseif ( $arg == 'context' || $arg == 'prefix' ) {
614 $filters['add_context'] = true;
615 }
616
617 // Output as image tag
618 elseif ( $arg == 'image' ) {
619 $filters['image'] = true;
620 }
621
622 // Wrap value in a link
623 elseif ( $arg == 'link' ) {
624 $filters['link'] = true;
625 }
626
627 // Open link in newTab
628 elseif ( $arg == 'newTab' ) {
629 $filters['newTab'] = true;
630 }
631
632 // Create a callable link
633 elseif ( $arg == 'tel' ) {
634 $filters['tel'] = true;
635 }
636
637 /**
638 * Return value instead of label
639 *
640 * Useful for dynamic data element conditions like MB checkbox_list, ACF true_false, etc. where the user can specify the value & label.
641 *
642 * @since 1.5.7
643 */
644 elseif ( $arg == 'value' ) {
645 $filters['value'] = true;
646 }
647
648 /**
649 * Return raw value (skip parsing DD tag)
650 *
651 * Useful to skip rendering one specific DD tag
652 *
653 * @since 1.6
654 */
655 elseif ( $arg == 'raw' ) {
656 $filters['raw'] = true;
657 }
658
659 /**
660 * Return URL
661 *
662 * Useful for field type 'file'
663 *
664 * NOTE: Undocumented
665 *
666 * @since 1.6
667 */
668 elseif ( $arg == 'url' ) {
669 $filters['url'] = true;
670 }
671
672 /**
673 * Keep formatting
674 *
675 * Useful for dynamic data with HTML
676 *
677 * Example: {post_excerpt:format}
678 *
679 * @since 1.6.2
680 */
681 elseif ( $arg == 'format' ) {
682 $filters['format'] = true;
683 }
684
685 /**
686 * Return plain text
687 *
688 * Strip HTML tags
689 *
690 * Example: {post_terms_category:plain}
691 *
692 * @since 1.7.2
693 */
694 elseif ( $arg == 'plain' ) {
695 $filters['plain'] = true;
696 }
697
698 /**
699 * Return array value
700 *
701 * Useful for dynamic data with array
702 *
703 * Example : {acf_link_field:array_value|title}
704 *
705 * @since 1.8
706 */
707 elseif ( strpos( $arg, 'array_value|' ) === 0 ) {
708 $filters['array_value'] = str_replace( 'array_value|', '', $arg );
709 }
710
711 // Default key: used for 1) user meta_key, 2) post terms separator or 3) image size, 4) date format
712 else {
713 $filters['meta_key'][] = $arg;
714 }
715 }
716
717 // Note: Use case where the date format contains a colon. E.g. "{post_date:jS F Y h:ia}"
718 if ( isset( $filters['meta_key'] ) ) {
719 $filters['meta_key'] = implode( ':', $filters['meta_key'] );
720 }
721
722 return $filters;
723 }
724 }
725