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
tier-pricing-table / src / Settings / Settings.php

Settings.php in Tiered Pricing Table for WooCommerce 8.0.2, at src/Settings/Settings.php

414 lines 14.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace TierPricingTable\Settings;
4
5 use TierPricingTable\Core\ServiceContainerTrait;
6 use TierPricingTable\Settings\CustomOptions\TPTCheckboxListOption;
7 use TierPricingTable\Settings\CustomOptions\TPTDisplayType;
8 use TierPricingTable\Settings\CustomOptions\TPTLinkButton;
9 use TierPricingTable\Settings\CustomOptions\TPTQuantityMeasurementField;
10 use TierPricingTable\Settings\CustomOptions\TPTTableColumnsField;
11 use TierPricingTable\Settings\CustomOptions\TPTFeatureFlagOption;
12 use TierPricingTable\Settings\CustomOptions\TPTIntegrationOption;
13 use TierPricingTable\Settings\CustomOptions\TPTSwitchOption;
14 use TierPricingTable\Settings\CustomOptions\TPTTextTemplate;
15 use TierPricingTable\Settings\Sections\Advanced\AdvancedSection;
16 use TierPricingTable\Settings\Sections\GeneralSection\GeneralSection;
17 use TierPricingTable\Settings\Sections\Integrations\IntegrationSection;
18 use TierPricingTable\Settings\Sections\Multicurrency\MulticurrencySection;
19 use TierPricingTable\Settings\Sections\ProductAddons\ProductAddonsSection;
20 use TierPricingTable\Settings\Sections\SectionAbstract;
21 use TierPricingTable\TierPricingTablePlugin;
22 /**
23 * Class Settings
24 *
25 * @package TierPricingTable\Settings
26 */
27 class Settings {
28 use ServiceContainerTrait;
29 const SETTINGS_PREFIX = 'tier_pricing_table_';
30
31 const SETTINGS_PAGE = 'tiered_pricing_table_settings';
32
33 const DEFAULT_SECTION = 'general';
34
35 /**
36 * Settings
37 *
38 * @var array
39 */
40 private $settings = array();
41
42 /**
43 * Settings sections
44 *
45 * @var SectionAbstract[]
46 */
47 protected $sections = array();
48
49 /**
50 * Settings constructor.
51 */
52 public function __construct() {
53 $this->initCustomOptions();
54 $this->hooks();
55 if ( !tpt_fs()->can_use_premium_code() ) {
56 new PremiumSettingsManager();
57 }
58 if ( !tpt_fs()->can_use_premium_code() ) {
59 $template = 'upgrade-banner.php';
60 add_action( 'woocommerce_settings_' . self::SETTINGS_PAGE, function () use($template) {
61 $this->getContainer()->getFileManager()->includeTemplate( 'admin/banners/' . $template, [
62 'upgradeUrl' => tpt_fs_activation_url(),
63 'contactUsUrl' => TierPricingTablePlugin::getContactUsURL(),
64 'documentationUrl' => TierPricingTablePlugin::getDocumentationURL(),
65 ] );
66 } );
67 }
68 if ( tpt_fs()->can_use_premium_code() && !tpt_fs()->is_premium() ) {
69 add_action( 'woocommerce_settings_' . self::SETTINGS_PAGE, function () {
70 $this->getContainer()->getFileManager()->includeTemplate( 'admin/banners/free-version-used-premium-available.php' );
71 } );
72 }
73 add_action( 'admin_enqueue_scripts', function () {
74 $assetName = self::SETTINGS_PAGE . '_script';
75 wp_register_script(
76 $assetName,
77 $this->getContainer()->getFileManager()->locateJSAsset( 'admin/settings' ),
78 array('jquery'),
79 TierPricingTablePlugin::VERSION,
80 true
81 );
82 wp_enqueue_script( $assetName );
83 } );
84 }
85
86 protected function initCustomOptions() {
87 $this->getContainer()->add( 'settings.tpt_switch_option', new TPTSwitchOption() );
88 $this->getContainer()->add( 'settings.tpt_integration_option', new TPTIntegrationOption() );
89 $this->getContainer()->add( 'settings.tpt_feature_flag_option', new TPTFeatureFlagOption() );
90 $this->getContainer()->add( 'settings.tpt_text_template', new TPTTextTemplate() );
91 $this->getContainer()->add( 'settings.tpt_multiple_fields', new TPTTableColumnsField() );
92 $this->getContainer()->add( 'settings.tpt_display_type', new TPTDisplayType() );
93 $this->getContainer()->add( 'settings.tpt_checkbox_list', new TPTCheckboxListOption() );
94 $this->getContainer()->add( 'settings.tpt_link_button', new TPTLinkButton() );
95 $this->getContainer()->add( 'settings.tpt_quantity_measurement', new TPTQuantityMeasurementField() );
96 }
97
98 protected function initSections() {
99 }
100
101 /**
102 * Handle updating settings
103 */
104 public function updateSettings() {
105 woocommerce_update_options( $this->settings );
106 $this->getContainer()->getCache()->purge();
107 }
108
109 /**
110 * Order the settings sections deterministically: configuration first, then display, features,
111 * feature toggles, third-party sections, and maintenance last. Without this the tab order is an
112 * accident of hook registration, because every addon splices its section in relative to another.
113 *
114 * Sections with an unknown slug (third-party) keep their relative order and land before Tools.
115 *
116 * @param array $sections
117 *
118 * @return array
119 */
120 protected function sortSections( array $sections ) : array {
121 $order = (array) apply_filters( 'tiered_pricing_table/settings/sections_order', array(
122 'general' => 10,
123 'shop-loop-display' => 30,
124 'tier-labels' => 40,
125 'request-a-quote' => 50,
126 'integrations' => 70,
127 'multicurrency' => 80,
128 'product-addons' => 90,
129 'advanced' => 120,
130 ) );
131 // Stable sort: usort() is only guaranteed stable on PHP 8+, so tie-break on the original index.
132 $indexed = array();
133 foreach ( array_values( $sections ) as $index => $section ) {
134 $indexed[] = array($order[$section->getSlug()] ?? 100, $index, $section);
135 }
136 usort( $indexed, function ( $a, $b ) {
137 return ( $a[0] <=> $b[0] ?: $a[1] <=> $b[1] );
138 } );
139 return array_column( $indexed, 2 );
140 }
141
142 /**
143 * Init all settings
144 */
145 public function initSettings() {
146 $this->sections = apply_filters( 'tiered_pricing_table/settings/sections', array(
147 new GeneralSection(),
148 new AdvancedSection(),
149 new MulticurrencySection(),
150 new ProductAddonsSection(),
151 new IntegrationSection()
152 ) );
153 $this->sections = $this->sortSections( $this->sections );
154 // Only on the plugin's own settings tab: other WooCommerce tabs (REST API keys, webhooks, tax,
155 // shipping…) use the same "section" parameter and must never see it rewritten.
156 if ( $this->isOwnSettingsTab() ) {
157 // The "Tools" tab merged into "Advanced" in 7.2.1; old links and bookmarks keep working.
158 if ( isset( $_GET['section'] ) && 'tools' === $_GET['section'] ) {
159 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
160 $_GET['section'] = 'advanced';
161 }
162 // A removed or unknown section in the URL (old links to the "Calculations" tab, for example)
163 // opens the default section instead of an empty page. The sections read the request directly.
164 if ( isset( $_GET['section'] ) ) {
165 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
166 $known = array_map( function ( $section ) {
167 return $section->getSlug();
168 }, $this->sections );
169 if ( !in_array( sanitize_text_field( wp_unslash( $_GET['section'] ) ), $known, true ) ) {
170 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
171 unset($_GET['section']);
172 }
173 }
174 }
175 foreach ( $this->sections as $section ) {
176 if ( $section->isActive() ) {
177 $this->settings = $section->getSettings();
178 break;
179 }
180 }
181 }
182
183 /**
184 * Register hooks
185 */
186 public function hooks() {
187 add_action( 'init', array($this, 'initSettings') );
188 add_filter( 'woocommerce_settings_tabs_' . self::SETTINGS_PAGE, array($this, 'addTieredPricingTableSettings') );
189 add_filter( 'woocommerce_settings_tabs_array', array($this, 'addSettingsTab'), 50 );
190 add_action( 'woocommerce_update_options_' . self::SETTINGS_PAGE, array($this, 'updateSettings') );
191 add_action( 'woocommerce_settings_' . self::SETTINGS_PAGE, array($this, 'renderSections'), 99 );
192 }
193
194 /**
195 * Whether the request is for the plugin's own WooCommerce settings tab.
196 */
197 protected function isOwnSettingsTab() : bool {
198 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
199 return is_admin() && isset( $_GET['page'], $_GET['tab'] ) && 'wc-settings' === $_GET['page'] && self::SETTINGS_PAGE === $_GET['tab'];
200 }
201
202 public function renderSections() {
203 // Opening a section clears its "unread" indicator (store-wide)
204 foreach ( $this->sections as $section ) {
205 if ( $section->isActive() ) {
206 $section->markBadgeSeen();
207 }
208 }
209 ?>
210 <style>
211 h2 {
212 margin-top: 2em;
213 font-size: 1.45em;
214 }
215 </style>
216
217 <ul class="subsubsub" style="font-size: 1.1em; margin-top: 3px">
218 <?php
219 $sectionsCount = count( $this->sections );
220 ?>
221 <?php
222 foreach ( $this->sections as $index => $section ) {
223 ?>
224 <li>
225 <?php
226 if ( !$section->isActive() ) {
227 ?>
228 <a href="<?php
229 echo esc_attr( $section->getURL() );
230 ?>">
231 <?php
232 if ( $section->isIntegration() ) {
233 ?>
234 <small style="width: 10px; height: 10px; display: inline-block; margin-right: 10px;">
235 <span class="dashicons dashicons-admin-plugins"></span>
236 </small>
237 <b><?php
238 echo esc_html( $section->getName() );
239 ?></b>
240 <?php
241 } else {
242 ?>
243 <?php
244 echo esc_html( $section->getName() );
245 ?>
246 <?php
247 if ( $section->isBadgeUnseen() ) {
248 ?>
249 <span class="tpt-unread-badge" aria-hidden="true">1</span><span class="screen-reader-text"><?php
250 esc_html_e( 'New', 'tier-pricing-table' );
251 ?></span>
252 <?php
253 }
254 ?>
255 <?php
256 }
257 ?>
258
259 </a>
260 <?php
261 } else {
262 ?>
263
264 <?php
265 if ( $section->getSectionCSS() ) {
266 ?>
267 <style>
268 <?php
269 echo esc_html( $section->getSectionCSS() );
270 ?>
271 </style>
272 <?php
273 }
274 ?>
275
276 <a class="current" href="#">
277 <?php
278 echo esc_html( $section->getName() );
279 ?>
280 </a>
281 <?php
282 }
283 ?>
284 <?php
285 if ( $index < $sectionsCount - 1 ) {
286 ?>
287 |
288 <?php
289 }
290 ?>
291 </li>
292 <?php
293 }
294 ?>
295 </ul>
296 <br class="clear">
297 <?php
298 }
299
300 /**
301 * Add own settings tab
302 *
303 * @param $settingsTabs
304 *
305 * @return array
306 */
307 public static function addSettingsTab( $settingsTabs ) : array {
308 $settingsTabs = ( is_array( $settingsTabs ) ? $settingsTabs : array() );
309 $settingsTabs[self::SETTINGS_PAGE] = __( 'Tiered Pricing', 'tier-pricing-table' );
310 return $settingsTabs;
311 }
312
313 /**
314 * Add settings to WooCommerce
315 */
316 public function addTieredPricingTableSettings() {
317 woocommerce_admin_fields( $this->settings );
318 }
319
320 /**
321 * Get setting by name
322 *
323 * @param string $optionName
324 * @param mixed $default
325 *
326 * @return mixed
327 */
328 public function get( string $optionName, $default = null ) {
329 return get_option( self::SETTINGS_PREFIX . $optionName, $default );
330 }
331
332 public static function deleteOptions() {
333 GeneralSection::deleteOptions();
334 AdvancedSection::deleteOptions();
335 IntegrationSection::deleteOptions();
336 }
337
338 /**
339 * Get url to settings page
340 *
341 * @return string
342 */
343 public function getLink() : string {
344 return admin_url( 'admin.php?page=wc-settings&tab=tiered_pricing_table_settings' );
345 }
346
347 public static function hex2rgba( $color, $opacity = false ) : string {
348 $default = 'rgb(0,0,0)';
349 if ( empty( $color ) ) {
350 return $default;
351 }
352 if ( '#' === $color[0] ) {
353 $color = substr( $color, 1 );
354 }
355 if ( strlen( $color ) == 6 ) {
356 $hex = array($color[0] . $color[1], $color[2] . $color[3], $color[4] . $color[5]);
357 } elseif ( strlen( $color ) == 3 ) {
358 $hex = array($color[0] . $color[0], $color[1] . $color[1], $color[2] . $color[2]);
359 } else {
360 return $default;
361 }
362 $rgb = array_map( 'hexdec', $hex );
363 if ( $opacity ) {
364 if ( abs( $opacity ) > 1 ) {
365 $opacity = 1.0;
366 }
367 $output = 'rgba(' . implode( ',', $rgb ) . ',' . $opacity . ')';
368 } else {
369 $output = 'rgb(' . implode( ',', $rgb ) . ')';
370 }
371 return $output;
372 }
373
374 public static function shadeHexWithOpacity( $hex, $opacity, $bgHex = '#FFFFFF' ) {
375 // Normalize and parse the foreground hex
376 $hex = ltrim( $hex, '#' );
377 if ( strlen( $hex ) === 3 ) {
378 $hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
379 }
380 if ( strlen( $hex ) !== 6 ) {
381 return false;
382 }
383 // Normalize and parse the background hex
384 $bgHex = ltrim( $bgHex, '#' );
385 if ( strlen( $bgHex ) === 3 ) {
386 $bgHex = $bgHex[0] . $bgHex[0] . $bgHex[1] . $bgHex[1] . $bgHex[2] . $bgHex[2];
387 }
388 if ( strlen( $bgHex ) !== 6 ) {
389 return false;
390 }
391 // Clamp opacity
392 $opacity = max( 0, min( 1, $opacity ) );
393 // Extract RGB
394 $r = hexdec( substr( $hex, 0, 2 ) );
395 $g = hexdec( substr( $hex, 2, 2 ) );
396 $b = hexdec( substr( $hex, 4, 2 ) );
397 $bgR = hexdec( substr( $bgHex, 0, 2 ) );
398 $bgG = hexdec( substr( $bgHex, 2, 2 ) );
399 $bgB = hexdec( substr( $bgHex, 4, 2 ) );
400 // Blend with background using alpha compositing
401 $newR = round( $opacity * $r + (1 - $opacity) * $bgR );
402 $newG = round( $opacity * $g + (1 - $opacity) * $bgG );
403 $newB = round( $opacity * $b + (1 - $opacity) * $bgB );
404 // Return final hex
405 return sprintf(
406 '#%02X%02X%02X',
407 $newR,
408 $newG,
409 $newB
410 );
411 }
412
413 }
414