PluginProbe
Tiered Pricing Table for WooCommerce / 8.0.2
Tiered Pricing Table for WooCommerce v8.0.2
8.0.2 7.1.7 7.1.5 7.1.4 7.1.1 6.5.0 6.4.0 6.1.0 trunk 1.0 1.1 2.0 2.0.1 2.0.2 2.1 2.1.1 2.1.2 2.2.0 2.2.1 2.2.2 2.2.3 2.3.0 2.3.1 2.3.2 2.3.3 All 91 releases
← All changes | src/Addons/ProductCatalogLoop/Settings/ProductCatalogLoopSettingsSection.php +366 -12 6.5.0 → 8.0.2 View file →
@@ -1,10 +1,12 @@
1 1 <?php namespace TierPricingTable\Addons\ProductCatalogLoop\Settings;
2 2
3 3 use TierPricingTable\Settings\CustomOptions\TPTDisplayType;
4 4 use TierPricingTable\Settings\CustomOptions\TPTQuantityMeasurementField;
5 +use TierPricingTable\Settings\CustomOptions\TPTCheckboxListOption;
5 6 use TierPricingTable\Settings\CustomOptions\TPTSwitchOption;
6 7 use TierPricingTable\Settings\CustomOptions\TPTTextTemplate;
8 +use TierPricingTable\Settings\Sections\GeneralSection\GeneralSection;
7 9 use TierPricingTable\Settings\Sections\SectionAbstract;
8 10 use TierPricingTable\Settings\Settings;
9 11 use TierPricingTable\TierPricingTablePlugin;
10 12
@@ -18,9 +20,9 @@
18 20 return Settings::SETTINGS_PREFIX . 'shop_loop_display_';
19 21 }
20 22
21 23 public function getName(): string {
22 - return __( 'Catalog Display', 'tier-pricing-table' );
24 + return __( 'Shop & Categories', 'tier-pricing-table' );
23 25 }
24 26
25 27 public function getSlug(): string {
26 28 return 'shop-loop-display';
@@ -32,15 +34,15 @@
32 34 unset( $availableLayouts['tooltip'] );
33 35
34 36 return array(
35 37 array(
36 - 'title' => __( 'Tiered Pricing on the shop page', 'tier-pricing-table' ),
38 + 'title' => __( 'Tiered Pricing on Shop & Category Pages', 'tier-pricing-table' ),
37 39 'desc' => __( 'Control how tiered pricing appears on shop and category pages.',
38 40 'tier-pricing-table' ),
39 41 'type' => 'title',
40 42 ),
41 43 array(
42 - 'title' => __( 'Enable on catalog pages', 'tier-pricing-table' ),
44 + 'title' => __( 'Enable on Shop & Categories', 'tier-pricing-table' ),
43 45 'id' => self::getOptionID( 'enabled' ),
44 46 'type' => TPTSwitchOption::FIELD_TYPE,
45 47 'default' => 'no',
46 48 'extended_description' => ( function () {
@@ -47,9 +49,9 @@
47 49 ob_start();
48 50 ?>
49 51 <p>
50 52 <?php
51 - esc_html_e( 'Turn this on to display tiered pricing directly within your product catalog.',
53 + esc_html_e( 'Turn this on to display tiered pricing directly within your shop and category pages.',
52 54 'tier-pricing-table' );
53 55 ?>
54 56 </p>
55 57 <p>
@@ -63,9 +65,9 @@
63 65 'tier-pricing-table' ),
64 66 'desc_tip' => true,
65 67 ),
66 68 array(
67 - 'title' => __( 'Position on catalog item', 'tier-pricing-table' ),
69 + 'title' => __( 'Position on product grid item', 'tier-pricing-table' ),
68 70 'id' => self::getOptionID( 'position' ),
69 71 'type' => 'select',
70 72 'default' => 'woocommerce_after_shop_loop_item__6',
71 73 'options' => array(
@@ -78,9 +80,75 @@
78 80 'tier-pricing-table' ),
79 81 'desc_tip' => true,
80 82 ),
81 83 array(
82 - 'title' => __( 'Add quantity selector', 'tier-pricing-table' ),
84 + 'title' => __( 'Where it shows', 'tier-pricing-table' ),
85 + 'id' => self::getOptionID( 'scope' ),
86 + 'type' => TPTDisplayType::FIELD_TYPE,
87 + 'options' => self::getScopeOptions(),
88 + 'descriptions' => array(
89 + 'everywhere' => __( 'Every product list', 'tier-pricing-table' ),
90 + 'selected' => __( 'Chosen lists and categories', 'tier-pricing-table' ),
91 + ),
92 + 'default' => 'everywhere',
93 + ),
94 + array(
95 + 'title' => __( 'Product lists', 'tier-pricing-table' ),
96 + 'id' => self::getOptionID( 'contexts' ),
97 + 'type' => TPTCheckboxListOption::FIELD_TYPE,
98 + 'options' => self::getContextOptions(),
99 + 'default' => '',
100 + 'desc' => __( 'The product lists that show tiered pricing. Leave every box unticked for all lists.', 'tier-pricing-table' ),
101 + ),
102 + array(
103 + 'title' => __( 'Only for products in categories', 'tier-pricing-table' ),
104 + 'id' => self::getOptionID( 'categories' ),
105 + 'type' => TPTCheckboxListOption::FIELD_TYPE,
106 + 'display' => 'category-search',
107 + 'placeholder' => __( 'All categories', 'tier-pricing-table' ),
108 + 'default' => '',
109 + 'desc' => __( 'Leave empty to show tiered pricing for every product. A category covers its child categories.', 'tier-pricing-table' ),
110 + ),
111 + array(
112 + 'title' => __( 'Tiers per grid item', 'tier-pricing-table' ),
113 + 'id' => self::getOptionID( 'tiers_limit' ),
114 + 'type' => 'number',
115 + 'default' => '',
116 + 'css' => 'width: 6em;',
117 + 'custom_attributes' => array( 'min' => 1, 'max' => 20, 'step' => 1, 'placeholder' => __( 'All', 'tier-pricing-table' ) ),
118 + 'desc' => __( 'How many tiers a grid item lists; the rest sit behind a "+N more" link to the product page. Leave empty to list them all.', 'tier-pricing-table' ),
119 + ),
120 + array(
121 + 'title' => __( 'Bulk savings badge', 'tier-pricing-table' ),
122 + 'id' => self::getOptionID( 'badge_enabled' ),
123 + 'type' => TPTSwitchOption::FIELD_TYPE,
124 + 'default' => 'no',
125 + 'desc' => __( 'A badge on the product image with the biggest discount the product\'s tiers give.', 'tier-pricing-table' ),
126 + ),
127 + array(
128 + 'title' => __( 'Badge text', 'tier-pricing-table' ),
129 + 'id' => self::getOptionID( 'badge_template' ),
130 + 'type' => TPTTextTemplate::FIELD_TYPE,
131 + 'placeholders' => self::getBadgePlaceholders(),
132 + 'default' => self::getDefaultBadgeTemplate(),
133 + ),
134 + array(
135 + 'title' => __( 'Badge position', 'tier-pricing-table' ),
136 + 'id' => self::getOptionID( 'badge_position' ),
137 + 'type' => 'select',
138 + 'default' => 'top-left',
139 + 'options' => self::getBadgePositionOptions(),
140 + ),
141 + array(
142 + 'title' => __( 'Badge color', 'tier-pricing-table' ),
143 + 'id' => self::getOptionID( 'badge_color' ),
144 + 'type' => 'color',
145 + 'default' => '',
146 + 'css' => 'width:6em;',
147 + 'desc' => __( 'Leave empty to use the active tier color.', 'tier-pricing-table' ),
148 + ),
149 + array(
150 + 'title' => __( 'Add quantity selector to grids', 'tier-pricing-table' ),
83 151 'id' => self::getOptionID( 'show_quantity_field' ),
84 152 'type' => TPTSwitchOption::FIELD_TYPE,
85 153 'default' => 'no',
86 154 'extended_description' => ( function () {
@@ -87,9 +155,9 @@
87 155 ob_start();
88 156 ?>
89 157 <p>
90 158 <?php
91 - esc_html_e( 'Add a quantity input field directly to items in the product catalog.', 'tier-pricing-table' );
159 + esc_html_e( 'Add a quantity input field directly to items on the shop and category pages.', 'tier-pricing-table' );
92 160 ?>
93 161 </p>
94 162 <p>
95 163 <b>
@@ -104,9 +172,9 @@
104 172 } )(),
105 173 'custom_attributes' => [ 'data-tiered-pricing-premium-option' => true ],
106 174 ),
107 175 array(
108 - 'title' => __( 'Dynamic total price', 'tier-pricing-table' ),
176 + 'title' => __( 'Dynamic price', 'tier-pricing-table' ),
109 177 'id' => self::getOptionID( 'dynamic_price' ),
110 178 'type' => TPTSwitchOption::FIELD_TYPE,
111 179 'default' => 'yes',
112 180 'extended_description' => ( function () {
@@ -113,9 +181,9 @@
113 181 ob_start();
114 182 ?>
115 183 <p>
116 184 <?php
117 - esc_html_e( 'Automatically update the displayed price when a customer changes the quantity from the catalog.',
185 + esc_html_e( 'Update the displayed price to the tier price when the customer changes the quantity in the grid. Needs a quantity field on the grid item, from this plugin, the theme or another plugin.',
118 186 'tier-pricing-table' );
119 187 ?>
120 188 </p>
121 189 <?php
@@ -161,13 +229,59 @@
161 229 'title' => __( 'Catalog layout', 'tier-pricing-table' ),
162 230 'id' => self::getOptionID( 'layout' ),
163 231 'type' => TPTDisplayType::FIELD_TYPE,
164 232 'options' => $availableLayouts,
233 +
165 234 'desc' => __( 'Select the specific visual template for the catalog page.', 'tier-pricing-table' ),
166 235 'desc_tip' => true,
167 236 'default' => 'table',
168 237 ),
169 238 array(
239 + 'title' => __( 'Table design style', 'tier-pricing-table' ),
240 + 'id' => self::getOptionID( 'pricing_table_style' ),
241 + 'type' => TPTDisplayType::FIELD_TYPE,
242 + 'options' => GeneralSection::getStyleOptions( true )['table'],
243 + 'default' => 'default',
244 + 'value' => self::getTableStyle(),
245 + 'desc_tip' => true,
246 + ),
247 + array(
248 + 'title' => __( 'Blocks design style', 'tier-pricing-table' ),
249 + 'id' => self::getOptionID( 'pricing_blocks_style' ),
250 + 'type' => TPTDisplayType::FIELD_TYPE,
251 + 'options' => GeneralSection::getStyleOptions( true )['blocks'],
252 + 'default' => 'default',
253 + 'value' => self::getBlocksStyle(),
254 + 'desc_tip' => true,
255 + ),
256 + array(
257 + 'title' => __( 'Options design style', 'tier-pricing-table' ),
258 + 'id' => self::getOptionID( 'pricing_options_style' ),
259 + 'type' => TPTDisplayType::FIELD_TYPE,
260 + 'options' => GeneralSection::getStyleOptions( true )['options'],
261 + 'default' => 'default',
262 + 'value' => self::getOptionsStyle(),
263 + 'desc_tip' => true,
264 + ),
265 + array(
266 + 'title' => __( 'Dropdown design style', 'tier-pricing-table' ),
267 + 'id' => self::getOptionID( 'pricing_dropdown_style' ),
268 + 'type' => TPTDisplayType::FIELD_TYPE,
269 + 'options' => GeneralSection::getStyleOptions( true )['dropdown'],
270 + 'default' => 'default',
271 + 'value' => self::getDropdownStyle(),
272 + 'desc_tip' => true,
273 + ),
274 + array(
275 + 'title' => __( 'Plain text design style', 'tier-pricing-table' ),
276 + 'id' => self::getOptionID( 'pricing_plain_text_style' ),
277 + 'type' => TPTDisplayType::FIELD_TYPE,
278 + 'options' => GeneralSection::getStyleOptions( true )['plain-text'],
279 + 'default' => 'default',
280 + 'value' => self::getPlainTextStyle(),
281 + 'desc_tip' => true,
282 + ),
283 + array(
170 284 'title' => __( 'Pricing title', 'tier-pricing-table' ),
171 285 'id' => self::getOptionID( 'title' ),
172 286 'type' => 'text',
173 287 'default' => '',
@@ -181,13 +295,47 @@
181 295 'options' => array(
182 296 'range' => __( 'Range', 'tier-pricing-table' ),
183 297 'static' => __( 'Static values', 'tier-pricing-table' ),
184 298 ),
185 - 'desc' => __( '"Range" displays the quantity range a tier applies to. "Static" displays only the minimum quantity required.',
186 - 'tier-pricing-table' ),
299 + 'descriptions' => array(
300 + 'range' => __( 'e.g. 10 - 24 pieces', 'tier-pricing-table' ),
301 + 'static' => __( 'e.g. 10+ pieces', 'tier-pricing-table' ),
302 + ),
187 303 'default' => 'range',
188 304 ),
189 305 array(
306 + 'title' => __( 'Spacing', 'tier-pricing-table' ),
307 + 'id' => self::getOptionID( 'layout_spacing' ),
308 + 'type' => 'number',
309 + 'default' => '',
310 + 'css' => 'width: 6em;',
311 + 'custom_attributes' => array( 'min' => 0, 'max' => 40, 'step' => 1, 'placeholder' => __( 'Auto', 'tier-pricing-table' ) ),
312 + 'desc' => __( 'Gap between the pricing blocks, options or card rows, in pixels. Leave empty to keep each design style\'s own spacing.', 'tier-pricing-table' ),
313 + ),
314 + array(
315 + 'title' => __( 'Cell padding', 'tier-pricing-table' ),
316 + 'id' => self::getOptionID( 'cell_padding' ),
317 + 'type' => 'number',
318 + 'default' => '',
319 + 'css' => 'width: 6em;',
320 + 'custom_attributes' => array( 'min' => 0, 'max' => 30, 'step' => 1, 'placeholder' => __( 'Auto', 'tier-pricing-table' ) ),
321 + 'desc' => __( 'Padding inside the cells of the table and horizontal table layouts, in pixels. Leave empty to keep each design style\'s own padding; a value also overrides the theme\'s cell padding.', 'tier-pricing-table' ),
322 + ),
323 + array(
324 + 'title' => __( 'Tier order', 'tier-pricing-table' ),
325 + 'id' => self::getOptionID( 'tiers_order' ),
326 + 'type' => TPTDisplayType::FIELD_TYPE,
327 + 'options' => array(
328 + 'asc' => __( 'Ascending', 'tier-pricing-table' ),
329 + 'desc' => __( 'Descending', 'tier-pricing-table' ),
330 + ),
331 + 'descriptions' => array(
332 + 'asc' => __( 'Smallest quantity first', 'tier-pricing-table' ),
333 + 'desc' => __( 'Biggest discount first', 'tier-pricing-table' ),
334 + ),
335 + 'default' => 'asc',
336 + ),
337 + array(
190 338 'title' => __( 'Active pricing tier color', 'tier-pricing-table' ),
191 339 'id' => self::getOptionID( 'selected_quantity_color' ),
192 340 'type' => 'color',
193 341 'css' => 'width:6em;',
@@ -193,8 +341,23 @@
193 341 'css' => 'width:6em;',
194 342 'default' => '#3858e9',
195 343 ),
196 344 array(
345 + 'title' => __( 'Discount badge color', 'tier-pricing-table' ),
346 + 'id' => self::getOptionID( 'discount_badge_color' ),
347 + 'type' => 'color',
348 + 'css' => 'width:6em;',
349 + 'default' => '',
350 + 'desc' => __( 'The discount badge of table styles #2 to #6. Styles #2 and #6 show a light tint of this color behind text in this color, styles #3 and #4 a solid badge with white text, style #5 colors its savings bar and percentage. Leave empty to keep the style\'s own colors (the active tier color on styles #3 to #5).', 'tier-pricing-table' ),
351 + ),
352 + array(
353 + 'title' => __( 'Active tier left border', 'tier-pricing-table' ),
354 + 'id' => self::getOptionID( 'table_active_border' ),
355 + 'type' => TPTSwitchOption::FIELD_TYPE,
356 + 'default' => 'yes',
357 + 'desc' => __( 'Table styles #1, #5 and #6 mark the active tier with a left border in the active tier color. Switch it off to keep the row highlight only.', 'tier-pricing-table' ),
358 + ),
359 + array(
197 360 'title' => __( 'Unit label', 'tier-pricing-table' ),
198 361 'id' => self::getOptionID( 'table_quantity_measurement' ),
199 362 'type' => TPTQuantityMeasurementField::FIELD_TYPE,
200 363 'default' => array(
@@ -236,14 +399,30 @@
236 399 'desc' => __( 'Leave empty to not show this column.', 'tier-pricing-table' ),
237 400 'type' => 'text',
238 401 ),
239 402 array(
240 - 'title' => __( 'Show percentage discount', 'tier-pricing-table' ),
403 + 'title' => __( 'Show discount', 'tier-pricing-table' ),
241 404 'id' => self::getOptionID( 'blocks_show_discount' ),
242 405 'type' => TPTSwitchOption::FIELD_TYPE,
243 406 'default' => 'yes',
244 407 ),
245 408 array(
409 + 'title' => __( 'Discount format', 'tier-pricing-table' ),
410 + 'id' => self::getOptionID( 'discount_format' ),
411 + 'type' => TPTDisplayType::FIELD_TYPE,
412 + 'options' => array(
413 + 'percentage' => __( 'Percentage', 'tier-pricing-table' ),
414 + 'amount' => __( 'Amount saved', 'tier-pricing-table' ),
415 + 'both' => __( 'Both', 'tier-pricing-table' ),
416 + ),
417 + 'descriptions' => array(
418 + 'percentage' => __( 'e.g. 10%', 'tier-pricing-table' ),
419 + 'amount' => __( 'e.g. $4.50', 'tier-pricing-table' ),
420 + 'both' => __( 'e.g. 10% ($4.50)', 'tier-pricing-table' ),
421 + ),
422 + 'default' => 'percentage',
423 + ),
424 + array(
246 425 'title' => __( 'Show regular product price', 'tier-pricing-table' ),
247 426 'id' => self::getOptionID( 'options_show_original_product_price' ),
248 427 'type' => TPTSwitchOption::FIELD_TYPE,
249 428 'default' => 'yes',
@@ -332,8 +511,126 @@
332 511 ),
333 512 );
334 513 }
335 514
515 + /**
516 + * The product lists tiered pricing can show in, keyed by the value stored in the "contexts" option.
517 + */
518 + public static function getContextOptions(): array {
519 + return array(
520 + 'shop' => __( 'Shop page', 'tier-pricing-table' ),
521 + 'category' => __( 'Category pages', 'tier-pricing-table' ),
522 + 'tag' => __( 'Tag and attribute pages', 'tier-pricing-table' ),
523 + 'search' => __( 'Search results', 'tier-pricing-table' ),
524 + 'related' => __( 'Related products', 'tier-pricing-table' ),
525 + 'upsells' => __( 'Upsells', 'tier-pricing-table' ),
526 + 'cross_sells' => __( 'Cross-sells in the cart', 'tier-pricing-table' ),
527 + 'shortcode' => __( 'Product shortcodes and other lists', 'tier-pricing-table' ),
528 + 'blocks' => __( 'Product Collection block', 'tier-pricing-table' ),
529 + );
530 + }
531 +
532 + public static function getScopeOptions(): array {
533 + return array(
534 + 'everywhere' => __( 'Everywhere', 'tier-pricing-table' ),
535 + 'selected' => __( 'Selected places', 'tier-pricing-table' ),
536 + );
537 + }
538 +
539 + /**
540 + * Whether the display is limited to chosen lists and categories.
541 + */
542 + public static function isScopeSelected(): bool {
543 + return 'selected' === get_option( self::getOptionID( 'scope' ), 'everywhere' );
544 + }
545 +
546 + /**
547 + * The enabled contexts; nothing stored, or the "everywhere" scope, means all of them.
548 + */
549 + public static function getContexts(): array {
550 + $known = array_keys( self::getContextOptions() );
551 +
552 + if ( ! self::isScopeSelected() ) {
553 + return $known;
554 + }
555 +
556 + $chosen = array_values( array_intersect( TPTCheckboxListOption::toArray( get_option( self::getOptionID( 'contexts' ), '' ) ), $known ) );
557 +
558 + return $chosen ? $chosen : $known;
559 + }
560 +
561 + /**
562 + * Product category ids the display is limited to; empty means every product.
563 + */
564 + public static function getCategories(): array {
565 + if ( ! self::isScopeSelected() ) {
566 + return array();
567 + }
568 +
569 + return array_values( array_filter( array_map( 'intval', TPTCheckboxListOption::toArray( get_option( self::getOptionID( 'categories' ), '' ) ) ) ) );
570 + }
571 +
572 + /**
573 + * Names of the chosen categories, keyed by id (for the pickers; the full list is never loaded).
574 + */
575 + public static function getCategoryNames( array $ids ): array {
576 + $names = array();
577 +
578 + foreach ( $ids as $id ) {
579 + $term = get_term( (int) $id, 'product_cat' );
580 + if ( $term && ! is_wp_error( $term ) ) {
581 + $path = array();
582 + foreach ( array_reverse( get_ancestors( $term->term_id, 'product_cat' ) ) as $ancestorId ) {
583 + $ancestor = get_term( $ancestorId, 'product_cat' );
584 + if ( $ancestor && ! is_wp_error( $ancestor ) ) {
585 + $path[] = $ancestor->name;
586 + }
587 + }
588 + $path[] = $term->name;
589 + $names[ (int) $id ] = implode( ' > ', $path );
590 + }
591 + }
592 +
593 + return $names;
594 + }
595 +
596 + public static function getTiersLimit(): int {
597 + return max( 0, (int) get_option( self::getOptionID( 'tiers_limit' ), '' ) );
598 + }
599 +
600 + public static function isBadgeEnabled(): bool {
601 + return 'yes' === get_option( self::getOptionID( 'badge_enabled' ), 'no' );
602 + }
603 +
604 + public static function getDefaultBadgeTemplate(): string {
605 + return __( 'Save up to {tp_max_discount}', 'tier-pricing-table' );
606 + }
607 +
608 + public static function getBadgeTemplate(): string {
609 + $template = (string) get_option( self::getOptionID( 'badge_template' ), '' );
610 +
611 + return '' !== trim( $template ) ? $template : self::getDefaultBadgeTemplate();
612 + }
613 +
614 + public static function getBadgePlaceholders(): array {
615 + return array( 'tp_max_discount', 'tp_lowest_price', 'tp_lowest_quantity', 'tp_max_saving' );
616 + }
617 +
618 + public static function getBadgePositionOptions(): array {
619 + return array(
620 + 'top-left' => __( 'Top left', 'tier-pricing-table' ),
621 + 'top-right' => __( 'Top right', 'tier-pricing-table' ),
622 + );
623 + }
624 +
625 + public static function getBadgePosition(): string {
626 + return 'top-right' === get_option( self::getOptionID( 'badge_position' ), 'top-left' ) ? 'top-right' : 'top-left';
627 + }
628 +
629 + public static function getBadgeColor(): string {
630 + return (string) get_option( self::getOptionID( 'badge_color' ), '' );
631 + }
632 +
336 633 public static function isEnabled(): bool {
337 634 return 'yes' === get_option( self::getOptionID( 'enabled' ), 'no' );
338 635 }
339 636
@@ -355,8 +652,41 @@
355 652 public static function getLayoutType(): string {
356 653 return get_option( self::getOptionID( 'layout' ), 'table' );
357 654 }
358 655
656 + /**
657 + * Catalog design styles. Until a catalog style is saved, the product page style applies (that is how
658 + * catalogs always rendered before they had their own style options).
659 + */
660 + public static function getTableStyle(): string {
661 + return self::catalogStyle( 'table', (string) get_option( self::getOptionID( 'pricing_table_style' ), '' ) ?: GeneralSection::getPricingTableStyle() );
662 + }
663 +
664 + public static function getBlocksStyle(): string {
665 + return self::catalogStyle( 'blocks', (string) get_option( self::getOptionID( 'pricing_blocks_style' ), '' ) ?: GeneralSection::getPricingBlocksStyle() );
666 + }
667 +
668 + public static function getOptionsStyle(): string {
669 + return self::catalogStyle( 'options', (string) get_option( self::getOptionID( 'pricing_options_style' ), '' ) ?: GeneralSection::getPricingOptionsStyle() );
670 + }
671 +
672 + public static function getDropdownStyle(): string {
673 + return self::catalogStyle( 'dropdown', (string) get_option( self::getOptionID( 'pricing_dropdown_style' ), '' ) ?: GeneralSection::getPricingDropdownStyle() );
674 + }
675 +
676 + public static function getPlainTextStyle(): string {
677 + return self::catalogStyle( 'plain-text', (string) get_option( self::getOptionID( 'pricing_plain_text_style' ), '' ) ?: GeneralSection::getPricingPlainTextStyle() );
678 + }
679 +
680 + /**
681 + * A style withheld from shop and category pages falls back to the default one there.
682 + */
683 + protected static function catalogStyle( string $layout, string $style ): string {
684 + $excluded = GeneralSection::getCatalogExcludedStyles();
685 +
686 + return in_array( $style, (array) ( $excluded[ $layout ] ?? array() ), true ) ? 'default' : $style;
687 + }
688 +
359 689 public static function getTitle(): string {
360 690 return get_option( self::getOptionID( 'title' ), '' );
361 691 }
362 692
@@ -363,10 +693,34 @@
363 693 public static function getQuantityType(): string {
364 694 return get_option( self::getOptionID( 'quantity_type' ), 'range' );
365 695 }
366 696
697 + public static function getLayoutSpacing(): string {
698 + return (string) get_option( self::getOptionID( 'layout_spacing' ), '' );
699 + }
700 +
701 + public static function getCellPadding(): string {
702 + return (string) get_option( self::getOptionID( 'cell_padding' ), '' );
703 + }
704 +
705 + public static function getTiersOrder(): string {
706 + return (string) get_option( self::getOptionID( 'tiers_order' ), 'asc' );
707 + }
708 +
709 + public static function getDiscountFormat(): string {
710 + return (string) get_option( self::getOptionID( 'discount_format' ), 'percentage' );
711 + }
712 +
367 713 public static function getSelectedQuantityColor(): string {
368 714 return get_option( self::getOptionID( 'selected_quantity_color' ), '#3858e9' );
715 + }
716 +
717 + public static function getDiscountBadgeColor(): string {
718 + return (string) get_option( self::getOptionID( 'discount_badge_color' ), '' );
719 + }
720 +
721 + public static function getActiveTierBorder(): string {
722 + return 'no' === get_option( self::getOptionID( 'table_active_border' ), 'yes' ) ? 'no' : 'yes';
369 723 }
370 724
371 725 public static function getTableQuantityMeasurement(): array {
372 726 return get_option( self::getOptionID( 'table_quantity_measurement' ), array(