initCustomOptions(); $this->hooks(); if ( !tpt_fs()->can_use_premium_code() ) { new PremiumSettingsManager(); } if ( !tpt_fs()->can_use_premium_code() ) { $template = 'upgrade-banner.php'; add_action( 'woocommerce_settings_' . self::SETTINGS_PAGE, function () use($template) { $this->getContainer()->getFileManager()->includeTemplate( 'admin/banners/' . $template, [ 'upgradeUrl' => tpt_fs_activation_url(), 'contactUsUrl' => TierPricingTablePlugin::getContactUsURL(), 'documentationUrl' => TierPricingTablePlugin::getDocumentationURL(), ] ); } ); } if ( tpt_fs()->can_use_premium_code() && !tpt_fs()->is_premium() ) { add_action( 'woocommerce_settings_' . self::SETTINGS_PAGE, function () { $this->getContainer()->getFileManager()->includeTemplate( 'admin/banners/free-version-used-premium-available.php' ); } ); } add_action( 'admin_enqueue_scripts', function () { $assetName = self::SETTINGS_PAGE . '_script'; wp_register_script( $assetName, $this->getContainer()->getFileManager()->locateJSAsset( 'admin/settings' ), array('jquery'), TierPricingTablePlugin::VERSION, true ); wp_enqueue_script( $assetName ); } ); } protected function initCustomOptions() { $this->getContainer()->add( 'settings.tpt_switch_option', new TPTSwitchOption() ); $this->getContainer()->add( 'settings.tpt_integration_option', new TPTIntegrationOption() ); $this->getContainer()->add( 'settings.tpt_feature_flag_option', new TPTFeatureFlagOption() ); $this->getContainer()->add( 'settings.tpt_text_template', new TPTTextTemplate() ); $this->getContainer()->add( 'settings.tpt_multiple_fields', new TPTTableColumnsField() ); $this->getContainer()->add( 'settings.tpt_display_type', new TPTDisplayType() ); $this->getContainer()->add( 'settings.tpt_checkbox_list', new TPTCheckboxListOption() ); $this->getContainer()->add( 'settings.tpt_link_button', new TPTLinkButton() ); $this->getContainer()->add( 'settings.tpt_quantity_measurement', new TPTQuantityMeasurementField() ); } protected function initSections() { } /** * Handle updating settings */ public function updateSettings() { woocommerce_update_options( $this->settings ); $this->getContainer()->getCache()->purge(); } /** * Order the settings sections deterministically: configuration first, then display, features, * feature toggles, third-party sections, and maintenance last. Without this the tab order is an * accident of hook registration, because every addon splices its section in relative to another. * * Sections with an unknown slug (third-party) keep their relative order and land before Tools. * * @param array $sections * * @return array */ protected function sortSections( array $sections ) : array { $order = (array) apply_filters( 'tiered_pricing_table/settings/sections_order', array( 'general' => 10, 'shop-loop-display' => 30, 'tier-labels' => 40, 'request-a-quote' => 50, 'integrations' => 70, 'multicurrency' => 80, 'product-addons' => 90, 'advanced' => 120, ) ); // Stable sort: usort() is only guaranteed stable on PHP 8+, so tie-break on the original index. $indexed = array(); foreach ( array_values( $sections ) as $index => $section ) { $indexed[] = array($order[$section->getSlug()] ?? 100, $index, $section); } usort( $indexed, function ( $a, $b ) { return ( $a[0] <=> $b[0] ?: $a[1] <=> $b[1] ); } ); return array_column( $indexed, 2 ); } /** * Init all settings */ public function initSettings() { $this->sections = apply_filters( 'tiered_pricing_table/settings/sections', array( new GeneralSection(), new AdvancedSection(), new MulticurrencySection(), new ProductAddonsSection(), new IntegrationSection() ) ); $this->sections = $this->sortSections( $this->sections ); // Only on the plugin's own settings tab: other WooCommerce tabs (REST API keys, webhooks, tax, // shipping…) use the same "section" parameter and must never see it rewritten. if ( $this->isOwnSettingsTab() ) { // The "Tools" tab merged into "Advanced" in 7.2.1; old links and bookmarks keep working. if ( isset( $_GET['section'] ) && 'tools' === $_GET['section'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended $_GET['section'] = 'advanced'; } // A removed or unknown section in the URL (old links to the "Calculations" tab, for example) // opens the default section instead of an empty page. The sections read the request directly. if ( isset( $_GET['section'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended $known = array_map( function ( $section ) { return $section->getSlug(); }, $this->sections ); if ( !in_array( sanitize_text_field( wp_unslash( $_GET['section'] ) ), $known, true ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended unset($_GET['section']); } } } foreach ( $this->sections as $section ) { if ( $section->isActive() ) { $this->settings = $section->getSettings(); break; } } } /** * Register hooks */ public function hooks() { add_action( 'init', array($this, 'initSettings') ); add_filter( 'woocommerce_settings_tabs_' . self::SETTINGS_PAGE, array($this, 'addTieredPricingTableSettings') ); add_filter( 'woocommerce_settings_tabs_array', array($this, 'addSettingsTab'), 50 ); add_action( 'woocommerce_update_options_' . self::SETTINGS_PAGE, array($this, 'updateSettings') ); add_action( 'woocommerce_settings_' . self::SETTINGS_PAGE, array($this, 'renderSections'), 99 ); } /** * Whether the request is for the plugin's own WooCommerce settings tab. */ protected function isOwnSettingsTab() : bool { // phpcs:ignore WordPress.Security.NonceVerification.Recommended return is_admin() && isset( $_GET['page'], $_GET['tab'] ) && 'wc-settings' === $_GET['page'] && self::SETTINGS_PAGE === $_GET['tab']; } public function renderSections() { // Opening a section clears its "unread" indicator (store-wide) foreach ( $this->sections as $section ) { if ( $section->isActive() ) { $section->markBadgeSeen(); } } ?>