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/PricingTable.php +163 -1 7.1.7 → 8.0.2 View file →
@@ -178,8 +178,19 @@
178 178 $templateType = 'options';
179 179 }
180 180 }
181 181
182 + // layouts with a single style option: dropdown, plain text
183 + $styledLayouts = array(
184 + 'dropdown' => 'dropdown_style',
185 + 'plain-text' => 'plain_text_style',
186 + );
187 +
188 + if ( isset( $styledLayouts[ $displayType ] ) ) {
189 + $style = $settings[ $styledLayouts[ $displayType ] ] ?? 'default';
190 + $templateType = 'default' !== $style && '' !== $style ? $displayType . '-' . $style : $displayType;
191 + }
192 +
182 193 $template = "tiered-pricing-$templateType.php";
183 194
184 195 do_action( 'tiered_pricing_table/before_rendering_tiered_pricing/inner', $priceRule, $product, $settings );
185 196
@@ -196,8 +207,10 @@
196 207 'settings' => $settings,
197 208 'pricing_type' => $priceRule->getType(),
198 209 'id' => $this->getUniqueTieredPricingId(),
199 210 ) );
211 +
212 + $this->renderMoreTiersLink( $parentProduct );
200 213 } else {
201 214 // Provide fallback data for products without pricing rules,
202 215 // which allows the frontend JS (e.g. dynamic totals, you save, etc.)
203 216 // to function for all products via the 'tiered_price_update' event.
@@ -243,8 +256,148 @@
243 256 <?php
244 257 }
245 258 }
246 259
260 + /**
261 + * Discount of a tier as the store shows it: a percentage, the amount saved per item, or both.
262 + *
263 + * @param float $percent Discount in percent, as the templates compute it.
264 + * @param PricingRule $pricingRule
265 + * @param WC_Product $product
266 + * @param int|null $quantity Tier quantity, or null for the base tier (its discount is the sale price).
267 + * @param array $settings Renderer settings.
268 + * @param string $style 'plain' ("10%", "$4.50", "10% ($4.50)") or 'off' ("10% off", "$4.50 off", "10% off, save $4.50").
269 + *
270 + * @return string HTML
271 + */
272 + public static function formatDiscount(
273 + float $percent,
274 + PricingRule $pricingRule,
275 + WC_Product $product,
276 + ?int $quantity,
277 + array $settings,
278 + string $style = 'plain'
279 + ): string {
280 + $format = $settings['discount_format'] ?? 'percentage';
281 + $format = in_array( $format, array( 'percentage', 'amount', 'both' ), true ) ? $format : 'percentage';
282 +
283 + $percentLabel = round( $percent, 2 ) . '%';
284 + $amountLabel = '';
285 +
286 + if ( 'percentage' !== $format ) {
287 + $saving = 0;
288 +
289 + if ( null === $quantity ) {
290 + if ( $product->is_on_sale() ) {
291 + $saving = (float) $product->get_regular_price() - (float) $product->get_sale_price();
292 + }
293 + } else {
294 + $base = CalculationLogic::calculateDiscountBasedOnRegularPrice() ? $product->get_regular_price() : $product->get_price();
295 + $saving = (float) $base - (float) $pricingRule->getTierPrice( $quantity, false );
296 + }
297 +
298 + $amountLabel = wc_price( wc_get_price_to_display( $product, array( 'price' => max( 0, $saving ) ) ) );
299 + }
300 +
301 + if ( 'off' === $style ) {
302 + switch ( $format ) {
303 + case 'amount':
304 + // translators: %s: amount saved per item
305 + return sprintf( __( '%s off', 'tier-pricing-table' ), $amountLabel );
306 + case 'both':
307 + // translators: 1: discount percentage, 2: amount saved per item
308 + return sprintf( __( '%1$s off, save %2$s', 'tier-pricing-table' ), $percentLabel, $amountLabel );
309 + default:
310 + // translators: %s: discount percentage
311 + return sprintf( __( '%s off', 'tier-pricing-table' ), $percentLabel );
312 + }
313 + }
314 +
315 + switch ( $format ) {
316 + case 'amount':
317 + return $amountLabel;
318 + case 'both':
319 + return $percentLabel . ' (' . $amountLabel . ')';
320 + default:
321 + return $percentLabel;
322 + }
323 + }
324 +
325 + /**
326 + * The "Spacing" and "Cell padding" options as a style attribute for a layout's root element: CSS
327 + * variables the stylesheet reads for the gap between tiers and the padding inside table cells.
328 + * Empty when neither option is set (each style keeps its own spacing and padding).
329 + */
330 + public static function layoutStyleAttribute( array $settings ): string {
331 + $variables = array();
332 +
333 + foreach ( array( 'layout_spacing' => '--tiered-pricing-gap', 'cell_padding' => '--tiered-pricing-cell-padding' ) as $option => $variable ) {
334 + $value = $settings[ $option ] ?? '';
335 +
336 + if ( '' === $value || null === $value || ! is_numeric( $value ) ) {
337 + continue;
338 + }
339 +
340 + $variables[] = $variable . ': ' . max( 0, min( 80, (int) $value ) ) . 'px';
341 + }
342 +
343 + return $variables ? ' style="' . esc_attr( implode( '; ', $variables ) ) . '"' : '';
344 + }
345 +
346 + /**
347 + * @deprecated 7.1.9 Use layoutStyleAttribute().
348 + */
349 + public static function spacingAttribute( array $settings ): string {
350 + return self::layoutStyleAttribute( $settings );
351 + }
352 +
353 + /**
354 + * Tiers the last rendered layout left out because of the "tiers per grid item" limit.
355 + */
356 + protected static $hiddenTiers = 0;
357 +
358 + /**
359 + * Tier rows in the configured order (ascending by quantity, or the biggest discount first), cut to
360 + * the "tiers per grid item" limit when one is set.
361 + *
362 + * @param string[] $rows Rendered rows, ascending.
363 + * @param array $settings Renderer settings.
364 + *
365 + * @return string HTML (the rows were escaped when rendered)
366 + */
367 + public static function orderTiers( array $rows, array $settings ): string {
368 + if ( 'desc' === ( $settings['tiers_order'] ?? 'asc' ) ) {
369 + $rows = array_reverse( $rows );
370 + }
371 +
372 + $limit = (int) ( $settings['tiers_limit'] ?? 0 );
373 + self::$hiddenTiers = 0;
374 +
375 + if ( $limit > 0 && count( $rows ) > $limit ) {
376 + self::$hiddenTiers = count( $rows ) - $limit;
377 + $rows = array_slice( $rows, 0, $limit );
378 + }
379 +
380 + return implode( '', $rows );
381 + }
382 +
383 + /**
384 + * The "+N more tiers" link to the product page, after a layout the limit shortened.
385 + */
386 + protected function renderMoreTiersLink( WC_Product $parentProduct ) {
387 + $hidden = self::$hiddenTiers;
388 + self::$hiddenTiers = 0;
389 +
390 + if ( $hidden < 1 ) {
391 + return;
392 + }
393 +
394 + /* translators: %d: number of tiers not listed */
395 + $label = sprintf( _n( '+%d more tier', '+%d more tiers', $hidden, 'tier-pricing-table' ), $hidden );
396 +
397 + echo '<a class="tiered-pricing-more-tiers" href="' . esc_url( $parentProduct->get_permalink() ) . '">' . esc_html( $label ) . '</a>';
398 + }
399 +
247 400 protected function getDefaultSettings( $productId = false ): array {
248 401 $hasRules = true;
249 402
250 403 if ( $productId ) {
@@ -259,9 +412,9 @@
259 412 'no' ) === 'yes';
260 413
261 414 $settings = array(
262 415 'display_context' => 'product-page',
263 - 'display' => $this->getContainer()->getSettings()->get( 'display', 'yes' ) === 'yes',
416 + 'display' => GeneralSection::isAutomaticDisplayEnabled(),
264 417 'display_type' => $this->getContainer()->getSettings()->get( 'display_type', 'table' ),
265 418 'title' => $this->getContainer()->getSettings()->get( 'table_title', '' ),
266 419 'table_class' => $this->getContainer()->getSettings()->get( 'table_css_class', '' ),
267 420 'quantity_column_title' => $this->getContainer()->getSettings()->get( 'head_quantity_text',
@@ -276,8 +429,14 @@
276 429 'clickable_rows' => $this->getContainer()->getSettings()->get( 'clickable_table_rows',
277 430 'yes' ) === 'yes',
278 431 'active_tier_color' => $this->getContainer()->getSettings()->get( 'selected_quantity_color',
279 432 '#3858e9' ),
433 + 'discount_format' => $this->getContainer()->getSettings()->get( 'discount_format', 'percentage' ),
434 + 'layout_spacing' => $this->getContainer()->getSettings()->get( 'layout_spacing', '' ),
435 + 'cell_padding' => $this->getContainer()->getSettings()->get( 'cell_padding', '' ),
436 + 'discount_badge_color' => $this->getContainer()->getSettings()->get( 'discount_badge_color', '' ),
437 + 'active_tier_border' => $this->getContainer()->getSettings()->get( 'table_active_border', 'yes' ),
438 + 'tiers_order' => $this->getContainer()->getSettings()->get( 'tiers_order', 'asc' ),
280 439 'tooltip_border' => $this->getContainer()->getSettings()->get( 'tooltip_border',
281 440 'yes' ) === 'yes',
282 441
283 442 'table_style' => GeneralSection::getPricingTableStyle(),
@@ -283,8 +442,11 @@
283 442 'table_style' => GeneralSection::getPricingTableStyle(),
284 443 'compact_layout' => $this->getContainer()->getSettings()->get( 'compact_layout', 'no' ),
285 444 'blocks_style' => GeneralSection::getPricingBlocksStyle(),
286 445 'options_style' => GeneralSection::getPricingOptionsStyle(),
446 +
447 + 'dropdown_style' => GeneralSection::getPricingDropdownStyle(),
448 + 'plain_text_style' => GeneralSection::getPricingPlainTextStyle(),
287 449
288 450 'options_show_total' => GeneralSection::isShowOptionTotal(),
289 451 'options_show_original_product_price' => GeneralSection::isShowOriginalProductPrice(),
290 452 'options_show_default_option' => GeneralSection::isDefaultOptionEnabled(),